株式会社Berry
🦆

stepfunctions+lambdaで3Dデータ処理パイプラインを構築した話

に公開

こんにちは。株式会社Berryでエンジニアをしている齋藤です。

大量の3Dデータを効率よく処理したい!

けど、サーバーを立てるとインスタンスの管理やスケーリングの対応が面倒。
そんなことありませんか?ありますよね。

そんな皆様のために、Berryで構築しているサーバーレスな3Dモデル処理パイプラインをご紹介します。

TL;DR

  • 医療機関からアップロードされる3Dデータを社員が手動で処理している作業がある
  • 3Dデータを処理するパイプラインをStep Functions+lambdaで構築
  • lambda内にc++, cgal, pclを用いたプログラムを設置することで処理速度や処理の自由度を確保
  • Houdiniみたいなノードベースのプロシージャルモデリングをサーバーレスにつくれた。たのしい。

この記事のスコープ

  • 説明すること:本パイプラインで使用してる技術スタック、パイプラインの概要
  • 説明しないこと:実際の構築手順、各lambdaの実装内容

自動化したい処理

Berryでは患者様の頭蓋変形を矯正するためのヘルメットを提供しています。
このヘルメットは患者様の頭部に合わせて作成されるため、患者様の頭部の3Dモデルが必要です。この3Dモデルは医療機関で3Dスキャナを用いて取得され、Berryにアップロードされます。

このとき、スキャン一回で頭部全てがスキャンできるわけではないため、医療機関様からは様々な方向からスキャンされた3Dデータがアップロードされます。

これまでは、社員の手作業で複数のスキャンを一体化し、3Dモデルを作成していましたが、スキャンの数が増えるにつれて手作業での処理が大変になってきました。

そこで、この処理を自動化するためのバックエンドパイプラインを構築することにしました。

パイプラインの構築

概要

パイプラインは以下のように構成されています。

  1. 3Dデータの処理リクエストがAWS SQSに送信される
  2. SQSからLambdaが発火し、state machineを起動する
  3. state machineは、Lambdaを順次起動し、点群データの一体化処理を行う
  4. 一体化処理が完了したら、Lambdaが処理結果をS3に保存する

技術スタック

パイプラインの構築にあたり、以下の技術を採用しました。

  • AWS Step Functions (statemachine)
    • ノードベースなGUIで処理フローを構築し、実行してくれるサービス
    • メリット
      • GUIで処理フローが確認でき、非エンジニアでも理解しやすい
      • リトライやエラーハンドリングが簡単に設定できる
      • ログが見やすい
    • デメリット
      • ローカルでの開発・テストが難しい
      • 各ノード(state)のinputを指定する方法がわかりにくい
  • AWS Lambda
    • サーバーレスなコンピューティングサービス
    • メリット
      • 使った分だけ課金されるため、コストを抑えられる
      • カスタムイメージによる実行を利用することで自由度が高くなる
    • デメリット
      • EC2を用いた処理に比べ、マシンリソースが制限される
      • 非GPU環境による制限が大きく、一部利用できないライブラリがある
  • pcl / CGAL
    • C++で実装された3Dデータ処理ライブラリ
    • ユースケース
      • pcl: 点群データのフィルタリング、icpによる位置合わせ
      • CGAL: ポリゴンメッシュの生成、穴埋め、スムージング
    • メリット
      • 処理の自由度が高く、自社のユースケースに合わせてアルゴリズムをカスタマイズできる
      • C++で実装されているため、処理速度が速い
    • デメリット
      • C++/cmakeの知識が必要でオンボーディングコストが高い
  • その他
    • pytorch
      • MLによる背景部分のマスク、頭部特徴点の抽出
    • docker
      • lambdaのカスタムイメージを作成するために使用
      • C++の開発環境を統一するために使用
    • cloudformation / aws sam
      • インフラのコード化・CI/CDのために使用

statemachineの概要

単機能のlambdaを多数用意し、Step Functionsでそれらをつなげることで、処理の流れを構築しています。

構築イメージとしてはblenderのgeometry nodesやhoudiniのSOP networkのイメージです。

各lambdaの細かいデプロイについては省略しますが、下記でlambdaの一例を紹介します。

lambdaの一例

lambdaはカスタムイメージによる構築を行っていますが、一例として点群をメッシュ化するlambdaのDockerfileの一部を示します。

構築方法については公式ドキュメントを参照してください。

コアとなるロジックはC++を用いて実装・ビルドし、pythonのsubprocessから呼び出す形にしています。

C++の採用により、cgalやpclなど、3Dデータ処理の高度なアルゴリズムを自由度高く使用することができ、処理の最適化に一役買っています。
python側ではaws-lambda-powertoolsを用いて、lambdaのロギングや型チェックを行っています。

FROM ubuntu:22.04 as builder
RUN ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

# ビルドに必要なパッケージをインストール
RUN apt update && apt install -y \
    build-essential \
    gcc \
    g++ \
    make \
    libtool \
    texinfo \
    dpkg-dev \
    pkg-config \
    git \
    cmake \
    wget \
    tzdata \
    libboost-all-dev \
    libcgal-dev \
    libeigen3-dev \
    tar \
    libgl1-mesa-dev \
    libopencv-dev

# c++のソースコードをコピー
COPY ./srcCpp /srcCpp
WORKDIR /srcCpp

# c++のビルド
RUN mkdir -p build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make -j4

# ビルド結果と依存ライブラリをコピー
WORKDIR /tmp
RUN rm -rf dependencies
RUN mkdir -p dependencies
RUN ldd /srcCpp/build/constructMeshV2 | grep "=> /" | awk '{print $3}' | xargs -I '{}' cp -v '{}' dependencies/

FROM ubuntu:22.04 as runtime

RUN ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
RUN apt update

ENV LAMBDA_TASK_ROOT="/app"
ENV FUNCTION_DIR="${LAMBDA_TASK_ROOT}"
RUN mkdir -p ${LAMBDA_TASK_ROOT}
WORKDIR ${LAMBDA_TASK_ROOT}

ENV TZ=Asia/Tokyo

# pythonのセットアップ
RUN apt install -y python3.13
RUN apt install -y python3-pip
RUN update-alternatives --install /usr/bin/python python /usr/bin/python3.13 1
RUN python -m pip install --upgrade pip setuptools wheel

# python用のlambda runtime interface clientを入れる
RUN pip install --target ${LAMBDA_TASK_ROOT} awslambdaric
RUN pip install aws-lambda-powertools boto3

RUN apt install -y libstdc++6 libc6 libglib2.0-0

COPY src/utils ${LAMBDA_TASK_ROOT}/utils
COPY src/app.py ${LAMBDA_TASK_ROOT}/app.py
COPY --from=builder /srcCpp/build/constructMeshV2 ${LAMBDA_TASK_ROOT}/constructMeshV2
COPY --from=builder /tmp/dependencies /usr/lib/x86_64-linux-gnu/

COPY src/entry_script.sh ${LAMBDA_TASK_ROOT}/entry_script.sh
RUN chmod +x ${LAMBDA_TASK_ROOT}/entry_script.sh
ENV EXECUTABLE_PATH="${LAMBDA_TASK_ROOT}/constructMeshV2"

ENTRYPOINT [ "./entry_script.sh" ]
CMD [ "app.lambda_handler" ]

達成したこと

パイプラインの構築によって、全て手動でやっていた工程が半分程度の時間まで圧縮されました!

この工程はオペレーションチームの中でも大きな負担となっていたため、パイプラインの構築によってオペレーションチームの負担を軽減できたことは大きな成果です。

課題

  • 使用技術が多岐に渡るため、オンボーディングコストが高い。
    • AWSサーバーレスサービスの知識
    • C++/cmakeの知識
    • dockerの知識
    • pythonの知識
    • 3Dデータ処理、機械学習、画像処理に関する知識
  • デプロイのCI/CDが未実装
    • aws cloudformationのデプロイは手動で行っているため、CI/CDを実装する必要がある
    • 各lambdaでアップデートのあるもののみをコンテナビルド・ECRへのpushを行う必要がある
  • lambdaのマシンリソース不足
    • 機械学習をサーバーレス推論させた場合に実行時間が問題となる

まとめ・感想

というわけで、サーバーレスな3Dデータ処理パイプラインのご紹介でした。

個人的にはAWS SAMを用いたインフラのコード化や, CGAL/pclなどのよりコアな3Dライブラリに触れられたことが大きな収穫でした。

何より、3Dデータ処理のアルゴリズムを自分で実装しながらプロシージャルモデリングを自動化し、バックエンドシステムに組み込めたことが楽しかったです。

手を広げすぎてしまったためにオンボーディングコストが課題となっていますが、今後は初学者向けのドキュメントを整備し、多くのメンバーに触ってもらえるようにしていきたいと思います。

We are hiring!

私たちは"あらゆる人が必要な時に必要な医療を受けられる社会を実現する"ことを目標に日々邁進しております。Berryの考え方や製品に少しでも興味が持てた方はお気軽に応募下さい。

https://www.wantedly.com/projects/2023878

株式会社Berry
株式会社Berry

Discussion