はじめに
LambdaでPyrenderを使ってオフスクリーンレンダリングをしたいのですが、レイヤを作るだけではOpenGLが使えないので使えません。OpenGLの機能をGPUの無いLambdaで実行するためにOSmesaというライブラリを使ってCPUでレンダリングします。自分用メモです。
前提
- AWSアカウントを持っている
- Dockerの実行環境
やったこと
レイヤだけでは対応できないのでDockerイメージを作ってコンテナでデプロイします。Lambdaのベースイメージでは(自分がやった限りでは)うまくいかなかったので、Ubuntuのベースイメージを使います。フォルダ構成は以下の通りです。
.
│ app.py
│ Dockerfile
│ env.txt
| entry.sh
└─ requirements.txt
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_PLATFORM
にosmesa
を指定します。aws-lambda-rie
はローカルテスト用です。
他ファイルは以下のように記述します。
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
}
#!/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
boto3
pyopengl==3.1.5
pyrender
awslambdaric
trimesh
opencv-python
requests
上記のDockerfileをビルドしてECRにpush、Lambdaでコンテナイメージを設定します。なお、env.txt
はローカルでのテスト用です。コンテナ実行時にcredentialを渡します。ECRへのpushやローカルでのテストのやり方などは自分のこの過去の記事を参考にしてみてください。
おわりに
OSmesaについての情報がもっと欲しいです。今時GPUがないPCがないのでしょうがないのかなと思いました。
間違い等ありましたらご指摘いただけると助かります。