LoginSignup
1
3

More than 1 year has passed since last update.

LambdaでS3の.glbファイルのオフスクリーンレンダリング【Python,Docker,OSmesa】

Posted at

はじめに

LambdaでPyrenderを使ってオフスクリーンレンダリングをしたいのですが、レイヤを作るだけではOpenGLが使えないので使えません。OpenGLの機能をGPUの無いLambdaで実行するためにOSmesaというライブラリを使ってCPUでレンダリングします。自分用メモです。

前提

  • AWSアカウントを持っている
  • Dockerの実行環境

やったこと

レイヤだけでは対応できないのでDockerイメージを作ってコンテナでデプロイします。Lambdaのベースイメージでは(自分がやった限りでは)うまくいかなかったので、Ubuntuのベースイメージを使います。フォルダ構成は以下の通りです。

.
│  app.py
│  Dockerfile
│  env.txt
|  entry.sh
└─ requirements.txt

Dockerfileは以下の通りです。

Dockerfile
FROM ubuntu:18.04

ARG FUNCTION_DIR="/function"
RUN mkdir -p ${FUNCTION_DIR}
WORKDIR ${FUNCTION_DIR}

RUN apt-get update
RUN apt-get install -y \
    python3.6 \
    python3-pip \
    python-opengl \
    libosmesa6 \
    autoconf \
    automake \
    libtool \
    libssl-dev \
    cmake \
    git \
    curl

ENV PYOPENGL_PLATFORM=osmesa

ADD https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie /usr/bin/aws-lambda-rie

COPY requirements.txt  .
RUN pip3 install -U pip
RUN pip3 install -r requirements.txt --target "${FUNCTION_DIR}"

COPY app.py ${FUNCTION_DIR}
COPY entry.sh /
RUN chmod 755 /usr/bin/aws-lambda-rie /entry.sh

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

ENVで環境変数PYOPENGL_PLATFORMosmesaを指定します。aws-lambda-rieはローカルテスト用です。

他ファイルは以下のように記述します。

app.py
import os
os.environ['PYOPENGL_PLATFORM'] = 'osmesa'
import base64
import requests
import io
import numpy as np
import cv2
import requests
import boto3
import json

import trimesh
from pyrender import PerspectiveCamera,\
                    PointLight,\
                    Primitive, Mesh, Scene,\
                    OffscreenRenderer

def lambda_handler(event, context):

    path = event['queryStringParameters']['path']

    width = int(1280/2)
    height = int(720/2)

    bucket_name = 'my-bucket'
    key = path
    s3_client = boto3.client('s3')
    sasurl = s3_client.generate_presigned_url(
        'get_object',
        Params={'Bucket': bucket_name,'Key': key},
        ExpiresIn=30)

    # sasで取得
    model = requests.get(sasurl).content
    model = io.BytesIO(model)
    try:
        model_gltf = trimesh.load(model,file_type='glb')
        model_trimesh = model_gltf.geometry[list(model_gltf.geometry.keys())[0]]
        model_mesh = Mesh.from_trimesh(model_trimesh)
        centroid = model_mesh.centroid
        extents = model_mesh.extents
        model_pose = np.eye(4)
        r = max(extents) # 最大の辺が大きさ1になるように正規化する
        model_pose = np.array([
            [1.0/r, 0.0,  0.0, -centroid[0]/r],
            [0.0, 1.0/r, 0.0, -centroid[1]/r],
            [0.0, 0.0,  1.0/r, -centroid[2]/r],
            [0.0, 0.0,  0.0, 1.0],
        ])
        cam = PerspectiveCamera(yfov=(np.pi / 3.0))
        cam_pose = np.array([
            [1.0,  0.0,  0.0,  0.0],
            [0.0,  1.0,  0.0,  0.0],
            [0.0,  0.0,  1.0,  1.5],
            [0.0,  0.0,  0.0,  1.0]
        ])
        light = PointLight(intensity=10)
        light_pose = np.array([
            [1.0, 0.0, 0.0, 0.0],
            [0.0, 1.0, 0.0, 0.0],
            [0.0, 0.0, 1.0, 1.5],
            [0.0, 0.0, 0.0, 1.0]
        ])
        scene = Scene()
        model_node = scene.add(model_mesh, pose=model_pose)

        cam_node = scene.add(cam, pose=cam_pose)
        light_node = scene.add(light, pose=light_pose)
        r = OffscreenRenderer(viewport_width=width, viewport_height=height)
        color, depth = r.render(scene)
        r.delete()

        color = cv2.cvtColor(color,cv2.COLOR_RGB2BGR)
        result, color = cv2.imencode('.png', color)
        thumbnail = color.tobytes()
        s3_client.put_object(Body=thumbnail,Bucket=bucket_name,Key=path.replace('.glb','.png'))
        results = 'complete'
    except Exception as e:
        print(e)
        results = 'failed'

    return {
        'statusCode': 200,
        'body': results
    }
entry.sh
#!/bin/sh
if [ -z "${AWS_LAMBDA_RUNTIME_API}" ]; then
    exec /usr/bin/aws-lambda-rie python3 -m awslambdaric $1
else
    exec python3 -m awslambdaric $1
fi
requirements.txt
boto3
pyopengl==3.1.5
pyrender
awslambdaric
trimesh
opencv-python
requests

上記のDockerfileをビルドしてECRにpush、Lambdaでコンテナイメージを設定します。なお、env.txtはローカルでのテスト用です。コンテナ実行時にcredentialを渡します。ECRへのpushやローカルでのテストのやり方などは自分のこの過去の記事を参考にしてみてください。

おわりに

OSmesaについての情報がもっと欲しいです。今時GPUがないPCがないのでしょうがないのかなと思いました。

間違い等ありましたらご指摘いただけると助かります。

参考

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3