Poetryを使ってRaspberryPi上にOpenCV4の環境を構築する

## はじめに
Pythonの依存関係を管理するツールである[Poetry](https://org-technology.com/posts/python-poetry.html)を使って、Raspberry Pi 4（OS：Raspbian Lite）上に OpenCV4 の環境構築をした際、個人的にいくつかハマったポイントがあったので、ここにメモとして残しておきます。

間違いや改善点などがありましたら、指摘していただけると幸いです。

なお、Poetryがあらかじめインストールされていることを前提としています。
Poetryのインストールや使い方に関しては、[こちらの記事](https://org-technology.com/posts/python-poetry.html#orgabeec4b)が参考になると思います。

### 環境
Raspberry Pi 側の環境は以下のようになっています。

|  |  |
|---|---|
|機種 |Raspberry Pi 4 Model B 4GB |
|OS |Raspbian Buster Lite |
|Poetry version |1.0.5 |
|Python |3.7.3 |
|X Server |X.Org X Server 1.20.4 |

<details><summary>バージョン情報を確認した際のコマンドと実行結果</summary><div>

```
$ lsb_release -a
No LSB modules are available.
Distributor ID: Raspbian
Description:    Raspbian GNU/Linux 10 (buster)
Release:        10
Codename:       buster
$ sudo cat /etc/debian_version
10.3
$ poetry --version
Poetry version 1.0.5
$ python3.7 -V
Python 3.7.3
$ Xorg -version

X.Org X Server 1.20.4
X Protocol Version 11, Revision 0
Build Operating System: Linux 4.15.0-48-generic armv8l Raspbian
Current Operating System: Linux pi4b-02 4.19.97-v7l+ #1294 SMP Thu Jan 30 13:21:14 GMT 2020 armv7l
Kernel command line: coherent_pool=1M 8250.nr_uarts=0 cma=64M cma=256M  smsc95xx.macaddr=DC:A6:32:70:FB:B0 vc_mem.mem_base=0x3ec00000 vc_mem.mem_size=0x40000000  console=ttyS0,115200 console=tty1 root=PARTUUID=6c586e13-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
Build Date: 05 June 2019  12:49:54PM
xorg-server 2:1.20.4-1+rpt1 (https://www.debian.org/support)
Current version of pixman: 0.36.0
        Before reporting problems, check http://wiki.x.org
        to make sure that you have the latest version.
```
</div></details>

また、動作確認として画像を表示する際`X転送`を利用しいます。
動作確認の際に用いた、クライアント側の環境は以下のようになっています。

|  |  |
|---|---|
|OS |Windows 10 Pro（バージョン：1903） |
|SSH クライアント |WSL 1（Ubuntu 18.04） |
|VcXsrv X Server |Version *1.20.6.0*(12 Jan 2020) |

## 仮想環境の作成
作業ディレクトリに移動し、以下のコマンドで仮想環境を作成する。

```
$ cd ~/work/OpencvPythonExperiment/  # 任意の作業ディレクトリへ移動
$ python3.7 -m venv .venv  # 仮想環境を作成
```

なお、仮想環境を作成する際以下のようなメッセージが表示されることがある。（きちんと調べたわけではないが、`pip`がインストールされていない環境だと以下のようなメッセージが表示される？）

```
$ python3.7 -m venv .venv
The virtual environment was not created successfully because ensurepip is not
available.  On Debian/Ubuntu systems, you need to install the python3-venv
package using the following command.

    apt-get install python3-venv

You may need to use sudo with that command.  After installing the python3-venv
package, recreate your virtual environment.
```

この場合、以下のコマンドを実行することで解決した。

```
$ sudo apt update
$ sudo apt upgrade
$ sudo apt install python3.7-venv  # マイナーバージョンを明示的に指定し、インストール
$ python3.7 -m venv .venv  # 仮想環境を作成
```

## OpenCV のインストール
### pyproject.toml ファイルの作成
作業ディレクトリに移動し、以下のコマンドで対話的に`pyproject.toml`ファイルを作成できる。

```
$ poetry init
（以下略）
```

適当に入力していったら、以下のようなファイルが生成された。
自分で`pyproject.toml`ファイルを作成するのが面倒な人は、以下の内容をそのままコピペすればとりあえず大丈夫だと思う。

``` pyproject.toml
name = "opencv-python-experiment"
version = "0.1.0"
description = ""
authors = ["Takahiro55555"]

[tool.poetry.dependencies]
python = "^3.7"

[tool.poetry.dev-dependencies]

[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"
```

### OpenCVのインストール
以下のコマンドを実行し、OpenCV をインストールしようとしたが失敗した。

```
$ poetry add opencv-contrib-python
```

具体的には、以下のようなエラーが表示された。

```
[EnvCommandError]
Command ['/home/pi/work/OpencvPythonExperiment/.venv/bin/pip', 'install', '--no-deps', 'opencv-contrib-python==4.2.0.34'] errored with the following return code 1, and output:
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting opencv-contrib-python==4.2.0.34
  Could not find a version that satisfies the requirement opencv-contrib-python==4.2.0.34 (from versions: 3.4.2.16, 3.4.2.17, 3.4.3.18, 3.4.4.19, 3.4.6.27, 3.4.7.28, 4.0.1.24, 4.1.0.25, 4.1.1.26)
No matching distribution found for opencv-contrib-python==4.2.0.34
```
エラーを見てみると、インストールしようとしている OpenCV のバージョンが、インストール可能な OpenCV のバージョンのリストに含まれていないためエラーが発生したようである。

そこで、インストール可能な OpenCV のバージョンを明示的に指定し再度インストールを試みたが、新たなエラーが発生した。

```
$ poetry add opencv-contrib-python=4.1.1.26
```

具体的には、以下のようなエラーが表示された。

```
[EnvCommandError]
Command ['/home/pi/work/OpencvPythonExperiment/.venv/bin/pip', 'install', '--no-deps', 'opencv-contrib-python==4.1.1.26'] errored with the following return code 1, and output:
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting opencv-contrib-python==4.1.1.26
  Downloading https://www.piwheels.org/simple/opencv-contrib-python/opencv_contrib_python-4.1.1.26-cp37-cp37m-linux_armv7l.whl (15.9MB)
THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS FILE. If you have updated the package versions, please update the hashes. Otherwise, examine the package contents carefully; someone may have tampered with them.
    opencv-contrib-python==4.1.1.26 from https://www.piwheels.org/simple/opencv-contrib-python/opencv_contrib_python-4.1.1.26-cp37-cp37m-linux_armv7l.whl#sha256=8000d53db64bc9b5b093d9cae4cecb540af97f1dfdd967d8ac29bc5f5032528b:
        Expected sha256 8000d53db64bc9b5b093d9cae4cecb540af97f1dfdd967d8ac29bc5f5032528b
             Got        47247bbcadb068654922b0d94b3cc5f8437a3ab74d549668efec2e489a5f21aa
```

そこで、仮想環境内の`pip`をアップデートした後、再度 OpenCV のインストールを試みたら無事インストールが成功した。

```
$ .venv/bin/pip install -U pip
$ poetry add opencv-contrib-python=4.1.1.26
```

## OpenCV の import 時のエラーと解決法
さっそく、OpenCV を使おうとしたが`import`時に以下のようなエラーが発生した。

```
$ poetry shell
(.venv) $ python
Python 3.7.3 (default, Dec 20 2019, 18:57:59)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/pi/work/OpencvPythonExperiment/.venv/lib/python3.7/site-packages/cv2/__init__.py", line 3, in <module>
    from .cv2 import *
ImportError: libhdf5_serial.so.103: cannot open shared object file: No such file or directory
```

### エラーと対応するパッケージのインストール
[こちらの記事](https://qiita.com/atuyosi/items/5f73baa08c3408f248e8#%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%A8%E5%AF%BE%E5%BF%9C%E3%81%99%E3%82%8B%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E5%90%8D%E3%81%AE%E3%83%AA%E3%82%B9%E3%83%88)を参考にして、不足しているパッケージをインストールした。
自分の環境では、上記の参考記事でインストールしている一部のパッケージのバージョンが古くなっていたり不足しているものがあったため、修正・追加したものを以下に示す。

なお、以下でインストールしているパッケージをまとめたものを先に示す。

```
sudo apt install libhdf5-103
sudo apt install libharfbuzz0b
sudo apt install liblapack3
sudo apt install libatlas-base-dev
sudo apt install libwebp6
sudo apt install libtiff5
sudo apt install libjasper1
sudo apt install libilmbase23
sudo apt install libopenexr23
sudo apt install libavcodec-extra58
sudo apt install libavformat58
sudo apt install libswscale5
sudo apt install libqtgui4
sudo apt install libqt4-test 
```

#### HDF5

``` Error.txt
ImportError: libhdf5_serial.so.103: cannot open shared object file: No such file or directory
```
-> `sudo apt install libhdf5-103`


#### HarfBuzz

``` Error.txt
ImportError: libharfbuzz.so.0: cannot open shared object file: No such file or directory
```
-> `sudo apt install libharfbuzz0b`


#### ???

``` Error.txt
ImportError: liblapack.so.3: cannot open shared object file: No such file or directory
```
-> `sudo apt install liblapack3`


#### ???

``` Error.txt
ImportError: libcblas.so.3: cannot open shared object file: No such file or directory
```
-> `sudo apt install libatlas-base-dev`


#### WebP

``` Error.txt
ImportError: libwebp.so.6: cannot open shared object file: No such file or directory
```
-> `sudo apt install libwebp6`


#### ???

``` Error.txt
ImportError: libtiff.so.5: cannot open shared object file: No such file or directory
```
-> `sudo apt install libtiff5`


#### Jasper

``` Error.txt
ImportError: libjasper.so.1: cannot open shared object file: No such file or directory
```
-> `sudo apt install libjasper1`


#### ???

``` Error.txt
ImportError: libImath-2_2.so.23: cannot open shared object file: No such file or directory
```
-> `sudo apt install libilmbase23`


#### OpenEXR

``` Error.txt
ImportError: libIlmImf-2_2.so.23: cannot open shared object file: No such file or directory
```
-> `sudo apt install libopenexr23`


#### FFmpeg関連

``` Error.txt
ImportError: libavcodec.so.58: cannot open shared object file: No such file or directory
```
-> `sudo apt install libavcodec-extra58`


``` Error.txt
ImportError: libavformat.so.58: cannot open shared object file: No such file or directory
```
-> `sudo apt install libavformat58`



``` Error.txt
ImportError: libswscale.so.5: cannot open shared object file: No such file or director
```
-> `sudo apt install libswscale5`



#### Qt関連

``` Error.txt
ImportError: libQtGui.so.4: cannot open shared object file: No such file or directory
```
-> `sudo apt install libqtgui4`


``` Error.txt
ImportError: libQtTest.so.4: cannot open shared object file: No such file or directory
```
-> `sudo apt install libqt4-test `


### パッケージの不足以外に起因するエラー
[こちらの記事](https://qiita.com/XM03/items/48463fd910470b226f22#%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%AE%E7%AA%81%E7%A0%B4%E6%96%B9%E6%B3%95)を参考にし、無事解決した。

``` Error.txt
ImportError: /home/pi/work/OpencvPythonExperiment/.venv/lib/python3.7/site-packages/cv2/cv2.cpython-37m-arm-linux-gnueabihf.so: undefined symbol: __atomic_fetch_add_8
```
`.bashrc`を変更する前に、上記の記事の解決策が上手くいくかどうかを検証する。

```
$ poetry shell  # 仮想環境をactivateする
(.venv) $ export LD_PRELOAD=/usr/lib/arm-linux-gnueabihf/libatomic.so.1  # 上記の記事に記述されていた解決策
(.venv) $ python  # 動作確認
Python 3.7.3 (default, Dec 20 2019, 18:57:59)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
>>> cv2.__version__
'4.1.1
>>> exit()  # インタプリタを終了する
```
試しに、OpenCV のバージョンを確認したところ、無事に確認できた。
なので、`.bashrc`に以下のように追記する。

```
$ echo "export LD_PRELOAD=/usr/lib/arm-linux-gnueabihf/libatomic.so.1" >> ~/.bashrc
$ poetry shell  # 仮想環境をactivateする
(.venv) $ python  # 動作確認
Python 3.7.3 (default, Dec 20 2019, 18:57:59)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
>>> cv2.__version__
'4.1.1'
```
こちらも、エラーが発生しないことを確認できた。

## OpenCV を実際に使ってみる
試しに OpenCV を使って、画像を表示してみる。
なお、画像ファイル（`lenna.png`）はあらかじめ`.venv`があるディレクトリに置いています。

```
$ ls -a
.  ..  lenna.png  poetry.lock  pyproject.toml  .venv
$ poetry shell  # 仮想環境をactivateする
(.venv) $ python
Python 3.7.3 (default, Dec 20 2019, 18:57:59)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
>>> import cv2
>>> img = cv2.imread("lenna.png")
>>> cv2.imshow("color",img)
>>> cv2.waitKey(0)  # 画像ウィンドウを選択し、何かキーを押すとインタプリタに再度入力ができるようになる
235
>>> cv2.destroyAllWindows()
```

以下のように無事に表示された（X転送を使用）。
![opencv-on-the-raspberry-py02.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202199/16f3890e-526e-031e-2ed7-7bd5f5785758.png)


## 補足
[エラーと対応するパッケージのインストール](#エラーと対応するパッケージのインストール)で[参考にした記事](https://qiita.com/atuyosi/items/5f73baa08c3408f248e8#%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%A8%E5%AF%BE%E5%BF%9C%E3%81%99%E3%82%8B%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E5%90%8D%E3%81%AE%E3%83%AA%E3%82%B9%E3%83%88)では、以下のように`Gtk3`もインストールしていた。

> ```
> ImportError: libgtk-3.so.0: cannot open shared object file: No such file or directory
> ```
> -> libgtk-3-0 (libgtk-3-dev)

自分の環境では`Gtk3`関連のエラーは発生しなかったが、`Gtk3`関連のエラーが発生した場合は参考になるかもしれない。

### 参考文献
- [Introduction | Documentation | Poetry - Python dependency management and packaging made easy.](https://python-poetry.org/docs/)
- [Poetry: Python の依存関係管理とパッケージングを支援するツール](https://org-technology.com/posts/python-poetry.html#orgabeec4b)
- [pipでアップデートするときのコマンド pip update](https://qiita.com/HyunwookPark/items/242a8ceea656416b6da8#pip%E8%87%AA%E4%BD%93%E3%81%AE%E3%82%A2%E3%83%83%E3%83%97%E3%83%87%E3%83%BC%E3%83%88)
- [Raspbian Liteでビルド済みのOpenCVパッケージを使うための依存パッケージいろいろ](https://qiita.com/atuyosi/items/5f73baa08c3408f248e8#%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%A8%E5%AF%BE%E5%BF%9C%E3%81%99%E3%82%8B%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E5%90%8D%E3%81%AE%E3%83%AA%E3%82%B9%E3%83%88)
- [ラズパイでpython3にopencvを入れたらエラーが出た【対処法】](https://qiita.com/XM03/items/48463fd910470b226f22#%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%AE%E7%AA%81%E7%A0%B4%E6%96%B9%E6%B3%95)