株式会社 ALBERT でアルバイト中の grafi です。なんかこれまで Qiita に投稿することはなかったんですけど、初投稿してみます。
動機
データ分析などをしていると、Python で画像を読み込んだり簡単な加工を施したりということが多いですので、OpenCVが欲しいところです。だいたいの人は Anaconda で済ましているのでしょうが、Python のエコシステムから離れてしまうのは個人的に嫌であり、またリンクするライブラリも好きに指定したいです。
そういうわけでOpenCVをビルドすることになります。ビルド方法はあちこちに説明があると思うので割愛します。そうすると Python のモジュールとして cv2.なんとか.so
などのファイルが作成されます。これをグローバルにインストールするには make install
するだけです。
しかし、virtualenv
(あるいは venv
)で作った環境からは、--system-site-packages
をつけない限りこの cv2
モジュールが見えません。cv2.なんとか.so
を virtualenv
環境内の site-packages
にコピーすれば動くには動きますが、どうにもカッコ悪いです。
なのでこれを wheel にできないかと思いました。
やりかた
本質的にやりたいことは「一つのビルド済み拡張モジュールを wheel にしたい」といえますが、こういうことはサポートされていないようです。
そのため色々錯誤しましたが、なんとかできました。
主に参考にしたのは
- How can I make a Python Wheel from an existing native library? - Stack Overflow
- Package only binary compiled .so files of a python library compiled with Cython - Stack Overflow
- Including Data Files — setuptools 35.0.2 documentation
の三つです。
まず、ディレクトリ構成は以下のようになります。
--+-- cv2 --+-- _native --+-- cv2.なんとか.so
| | |
| | +-- __init__.py
| |
| +-- __init__.py
|
+-- setup.py
|
+-- MANIFEST.in
cv2
ディレクトリ直下の __init__.py
には
import * from _native.cv2
と書きます。
_native
ディレクトリ以下の __init__.py
は空で構いません。
setup.py
は以下のように、
from setuptools import setup
from setuptools.dist import Distribution
class BinaryDistribution(Distribution):
def has_ext_modules(foo):
return True
setup(
name='cv2',
version='1.0',
packages=['cv2'],
include_package_data=True,
distclass=BinaryDistribution,
)
また MANIFEST.in
は
include cv2/_native/cv2.なんとか.so
と書きます。
これで python setup.py bdist_wheel
を実行すると、dist
以下に cv2-1.0-cp36-cp36m-linux_x86_64.whl
みたいなファイル名の wheel ができます。めでたしめでたし。
解説
やってることは単純で、ダミーのパッケージを作って本来の cv2
モジュール内の定義を全て export しているだけです。本来の cv2
モジュールは一応隠しておきたいところですが、cv2.なんとか.so
を _cv2.なんとか.so
にリネームすると動作しなくなるので、別のパッケージの中に入れることで解決しました。
has_ext_modules = True
として強引に拡張モジュールが入っていると教えてやることで、他の OS に持っていくとインストールが拒否されるような wheel にすることができます。
応用
numpy
や scipy
は自前でコンパイルして高性能な行列演算ライブラリにリンクしないと性能がでないですが、これもコンパイルした結果を wheel にしておくと便利です。この詳細は後日書きます。