TIFF地獄とは
TIFFは暗黒大陸とも言われるほど(言われてない)広大な規格である。いま存在する処理系はどれも、規格のごく一部を実装したものにすぎない。
広大な規格であるがゆえに、そのごく一部を実装するだけでも、莫大な数のライブラリをリンクする必要がある。
2019年1月現在、Christoph Gohlkeという冒険家が、この暗黒大陸に挑戦している。彼が進めているプロジェクトが、tifffileとimagecodecsである。
imagecodecsは、「OSSの画像コーデックライブラリをすべて集めてPythonで使えるようにしよう」という壮大なものらしい。
必然的に、AWS Lambdaのような古い環境では、ビルドが非常に面倒である。しかしimagecodecsを避けると、失う面積は大きい。
準備
要Docker。
Numpy / SciPy
AWS公式のNumpy・SciPyレイヤは、PyPIから取れるwhlと互換性がない。おそらくビルド時にいろいろ最適化してある。公式のレイヤをダウンロードする方法はないようなので、諦めてPyPIのwhlをレイヤに固める。
追記:AWS Lambdaの展開後250MB制限がきついので、節約術を使った。strip -s
と、共有ライブラリ(gfortran、OpenBLAS)をシンボリックリンクでひとつにまとめた。60MBほどの節約になっている。
FROM lambci/lambda:build-python3.6
ENV AWS_DEFAULT_REGION ap-northeast-1
ENV HOME /home/hoge
RUN mkdir $HOME
WORKDIR $HOME
RUN pip install --upgrade pip
RUN pip install numpy==1.16.1 scipy==1.2.0 -t ~/python/lib/python3.6/site-packages/
RUN find ~/python/lib/python3.6/site-packages/ -name *.so|xargs -n 1 strip -s
ENV SITE_PACKAGES $HOME/python/lib/python3.6/site-packages/
RUN rm $SITE_PACKAGES/scipy/.libs/libopenblasp-r0-8dca6697.3.0.dev.so $SITE_PACKAGES/scipy/.libs/libgfortran-ed201abd.so.3.0.0
RUN ln -s $SITE_PACKAGES/numpy/.libs/libopenblasp-r0-382c8f3a.3.5.dev.so $SITE_PACKAGES/scipy/.libs/libopenblasp-r0-8dca6697.3.0.dev.so
RUN ln -s $SITE_PACKAGES/numpy/.libs/libgfortran-ed201abd.so.3.0.0 $SITE_PACKAGES/scipy/.libs/libgfortran-ed201abd.so.3.0.0
CMD zip -ry9 ~/scipy_layer.zip python && cp ~/scipy_layer.zip /share
version: '2'
services:
app:
build:
context: .
volumes:
- .:/share
docker-compose build
docker-compose up
できたscipy_layer.zipをAWS Lambdaにレイヤとして登録しておく。
ライブラリのソース
以下のライブラリのソースをダウンロードして、ワークディレクトリに置く。yumで入るものでは古すぎるため。
- blosc
- libpng
- libjpeg-turbo
- zlib
- libwebp
imagecodecsもソースが必要。
pip download --no-binary :all: imagecodecs
これ↑だとnumpyも一緒にダウンロードされてしまうが、防ぐ方法が見当たらない。
imagecodecsのsetup.pyを編集
AWS Lambdaのzlibとlibjpeg-turboが古いので、静的リンクする必要がある。extra_objectsのところを加える。
ext_modules = [
Extension(
'imagecodecs._imagecodecs',
sources,
include_dirs=include_dirs,
libraries=libraries,
define_macros=define_macros,
extra_objects=['/home/hoge/foo/lib/libz.a',
'/home/hoge/foo/lib64/libturbojpeg.a',
'/home/hoge/foo/lib64/libjpeg.a'],
)
]
編集したsetup.pyはワークディレクトリに置く。
FROM lambci/lambda:build-python3.6
ENV AWS_DEFAULT_REGION ap-northeast-1
ENV HOME /home/hoge
RUN mkdir $HOME
WORKDIR $HOME
COPY c-blosc-master.zip .
RUN unzip c-blosc-master.zip
COPY libpng-libpng16.zip .
RUN unzip libpng-libpng16.zip
COPY libjpeg-turbo-master.zip .
RUN unzip libjpeg-turbo-master.zip
COPY zlib-1.2.11.tar.gz .
RUN tar zxf zlib-1.2.11.tar.gz
COPY libwebp-1.0.1.zip .
RUN unzip libwebp-1.0.1.zip
RUN yum update -y
RUN sed -i -e 's/enabled=0/enabled=1/' /etc/yum.repos.d/epel.repo
RUN yum install -y cmake3 openjpeg2-devel libzstd-devel lz4-devel jxrlib-devel nasm
RUN pip install --upgrade pip
RUN pip install numpy==1.16.0
RUN mkdir foo
RUN mkdir ~/c-blosc-master/build
WORKDIR $HOME/c-blosc-master/build
RUN cmake3 -D CMAKE_INSTALL_PREFIX=$HOME/foo ..
RUN make install
RUN mkdir ~/libpng-libpng16/build
WORKDIR $HOME/libpng-libpng16/build
RUN cmake3 -D CMAKE_INSTALL_PREFIX=$HOME/foo ..
RUN make install
RUN mkdir ~/libjpeg-turbo-master/build
WORKDIR $HOME/libjpeg-turbo-master/build
RUN cmake3 -D CMAKE_INSTALL_PREFIX=$HOME/foo -D CMAKE_POSITION_INDEPENDENT_CODE=ON ..
RUN make install
WORKDIR $HOME/zlib-1.2.11
RUN CFLAGS='-mstackrealign -fPIC -O3' ./configure --prefix=$HOME/foo
RUN make install
RUN mkdir ~/libwebp-1.0.1/build
WORKDIR $HOME/libwebp-1.0.1/build
RUN cmake3 -D CMAKE_INSTALL_PREFIX=$HOME/foo ..
RUN make install
ENV LD_LIBRARY_PATH $LD_LIBRARY_PATH:$HOME/foo/lib:$HOME/foo/lib64
ENV CPATH $HOME/foo/include
ENV LIBRARY_PATH $HOME/foo/lib:$HOME/foo/lib64
WORKDIR $HOME
COPY imagecodecs-2019.1.1.tar.gz .
RUN tar zxf imagecodecs-2019.1.1.tar.gz
COPY setup.py $HOME/imagecodecs-2019.1.1
WORKDIR $HOME/imagecodecs-2019.1.1
RUN mkdir ~/bar
RUN pip install . -t ~/bar
RUN mkdir -p ~/fuga/python/lib/python3.6/site-packages
RUN mv ~/bar/imagecodecs* ~/fuga/python/lib/python3.6/site-packages
RUN mkdir ~/fuga/lib
RUN cp -d /usr/lib64/libopenjp2.so* /usr/lib64/libzstd.so* /usr/lib64/liblz4.so* \
/usr/lib64/libjpegxr.so* \
/usr/lib64/libjxrglue.so* ~/foo/lib/*.so* \
~/foo/lib64/*.so* ~/fuga/lib/
WORKDIR $HOME/fuga
CMD zip -ry9 ~/imagecodecs_layer.zip * && cp ~/imagecodecs_layer.zip /share
上記のDockerfileと、さきほどと同じdocker-compose.ymlを、ワークディレクトリに置く。
ビルド
ワークディレクトリには以下のファイルがあるものとする。
$ ls
c-blosc-master.zip Dockerfile imagecodecs-2019.1.1.tar.gz libpng-libpng16.zip setup.py
docker-compose.yml imagecodecs_layer.zip libjpeg-turbo-master.zip libwebp-1.0.1.zip zlib-1.2.11.tar.gz
もしファイル名が違う場合はDockerfileを編集。
docker-compose build
docker-compose up
ワークディレクトリにimagecodecs_layer.zipができる。
import
imagecodecsをインポートする前に、レイヤ内の共有ライブラリをロードする。こうしないと共有ライブラリが見つからずにエラーになる。
import ctypes
ctypes.CDLL('/opt/lib/liblz4.so.1')
ctypes.CDLL('/opt/lib/libzstd.so.1')
ctypes.CDLL('/opt/lib/libpng16.so.16')
ctypes.CDLL('/opt/lib/libblosc.so.1')
ctypes.CDLL('/opt/lib/libopenjp2.so.7')
ctypes.CDLL('/opt/lib/libjpegxr.so.0')
ctypes.CDLL('/opt/lib/libjxrglue.so.0')
import imagecodecs
これらも静的リンクするほうがいいかも。