Python OpenCV  で日本語を含むパス及びファイルが読み取れない問題

# OpenCVでは日本語のパスに対応していない!?

　`cv2.VideoCapture`を普通に実行していると普通に動いたが、pyinstallerでexe化すると文字化けしてエラーが出てしまった。（違いがよくわからないが）調べてみるとOpenCVではANSI文字列以外、つまり日本語のパスに対応していない模様。なので`cv2.imread`, `cv2.imwrite`, `cv2.VideoCapture`等で日本語を含むパスを指定するには工夫が必要。
　 [こちら](https://qiita.com/SKYS/items/cbde3775e2143cad7455)ではnumpyを噛ませて解決してましたが、なんか難しそうだし`cv2.VideoCapture`でのやり方もよくわからない。[こちら](https://teratail.com/questions/268813)でファイルをリネームすればいいというのを採用。

# 解決方法

対象ファイルのディレクトリに移動して、一時的に対象ファイルの名前を変更して処理する。

### コード例（`cv2.imread`）

1. 対象ファイルがあるディレクトリに移動
2. 対象ファイルの名前を変更
3. 対象ファイルを読み取る
4. 対象ファイルの名前を戻す

```python
import cv2
import os

def imread(path):
    tmp_dir = os.getcwd()
    # 1. 対象ファイルがあるディレクトリに移動
    if len(path.split("/")) > 1:
        file_dir = "/".join(path.split("/")[:-1])
        os.chdir(file_dir)
    # 2. 対象ファイルの名前を変更
    tmp_name = "tmp_name"
    os.rename(path.split("/")[-1], tmp_name)
    # 3. 対象ファイルを読み取る
    img = cv2.imread(tmp_name)
    # 4. 対象ファイルの名前を戻す
    os.rename(tmp_name, path.split("/")[-1])
    # カレントディレクトリをもとに戻す
    os.chdir(tmp_dir)
    return img
```

　はじめに`os.getcwd()`でカレントディレクトリを取得、最後に戻す。その後対象ファイルが存在するディレクトリに移動するが、すでにそのディレクトリにいる場合は何もしない。`os.chdir(path)`でpathディレクトリに移動できる。

### コード例（`cv2.imwrite`）
　`cv2.imwrite`で画像を保存するときは
1. 保存するディレクトリに移動
2. 対象ファイルを保存
3. 対象ファイルの名前を戻す
とするとよい

```python
import cv2
import os

def imwrite(path, img):
    tmp_dir = os.getcwd()
    # 1. 保存するディレクトリに移動
    if len(path.split("/")) > 1:
        file_dir = "/".join(path.split("/")[:-1])
        os.chdir(file_dir)
    # 2. 対象ファイルを保存
    tmp_name = "tmp_name.png"
    cv2.imwrite(tmp_name, img)
    # 3. 対象ファイルの名前を戻す
    if os.path.exists(path.split("/")[-1]):  # ファイルが既にあれば削除
        os.remove(path.split("/")[-1])
    os.rename(tmp_name, path.split("/")[-1])
    # カレントディレクトリをもとに戻す
    os.chdir(tmp_dir)
```
`cv2.imwrite`ではデフォルトで既存の画像を上書きするのでこちらもその仕様にした。存在するファイルの名前に変更することはできないので、存在する場合は削除してから名前を変更するようにした。

### コード例（`cv2.VideoCapture`）
　`cv2.VideoCapture`の場合は少し注意が必要。ファイルを読み取った後`cap.release()`としないと名前を変えることはできないので、処理が終わった後に名前を戻す。それにより関数化ができない。

1. 対象ファイルがあるディレクトリに移動
2. 対象ファイルの名前を変更
3. 対象ファイルを読み取る
4. 対象ファイルの名前を戻す

```python
import cv2
import os

path = "C:/Users/***/Desktop/フォルダ/画像.bmp"

# 1. 対象ファイルがあるディレクトリに移動
file_dir = "/".join(path.split("/")[:-1])
os.chdir(file_dir)
# 2. 対象ファイルの名前を変更
tmp_name = "tmp_name"
os.rename(path.split("/")[-1], tmp_name)
# 3. 対象ファイルを読み取る
cap = cv2.VideoCapture(tmp_name)
'''
任意の処理
'''
# 4. 対象ファイルの名前を戻す
cap.release()
os.rename(tmp_name, path.split("/")[-1])
```


`cap.release()`しないと

```
PermissionError: [WinError 32] プロセスはファイルにアクセスできません。別のプロセスが使用中です。
```
というエラーが出るので注意。プログラムで使用中だから

# もうちょいくわしく

### エラー挙動

　`cv2.imread`を日本語を含むパスを指定した場合の挙動を確認

```python
import cv2

path = "C:/Users/sd18080/Desktop/フォルダ/画像.bmp"
img = cv2.imread(path)
print(img)
```
結果

```
None
```

エラーすら表示されない…！
OpenCVではそもそも存在しないファイルを参照しようとしてもエラーが出ないよう。
試しに上記を`path="aaaaa"`と適当に存在しないファイルを設定した場合も同様の結果となった。

### `cv2.VideoCapture`のエラー挙動

`cv2.VideoCapture`は存在しないファイルを指定するとエラーが表示される

```python
import cv2

path = "C:/Users/***/Desktop/フォルダ/画像.bmp"
cap = cv2.VideoCapture(path)
```

これを実行すると

```
warning: Error opening file (/build/opencv/modules/videoio/src/cap_ffmpeg_impl.hpp:908)
warning: C:\Users\***\Desktop\繝輔か繝ｫ繝\逕ｻ蜒・bmp (/build/opencv/modules/videoio/src/cap_ffmpeg_impl.hpp:909)
[ERROR:0] VIDEOIO(cvCreateFileCapture_Images(filename.c_str())): raised OpenCV exception:

OpenCV(3.4.8) C:\projects\opencv-python\opencv\modules\videoio\src\cap_images.cpp:246: 
error: (-5:Bad argument) CAP_IMAGES: can't find starting number (in the name of file):
 C:\Users\***\Desktop\繝輔か繝ｫ繝\逕ｻ蜒・bmp in function 'cv::icvExtractPattern'
```

となってどうやら文字コードUTF-8→Shift_JISに文字化けしているみたい。

# 最後に
　numpyかませる方法がよくわからない、こっちのほうが簡単って思ってまとめました。参考になれば幸いです。
　`cv2.VideoCapture`に関しては日本語が入っているときも普通に読み込めることがあって挙動が安定しなくて「？」という感じです。自分はpythonで実行したときには普通に読み込めたがpyinstallerでexe化したものを実行したらエラーが出ました。pyinstallerを実行する時にUnicodeDecodeErrorで'utf-8'がどうちゃらっていうエラーが出ててそれを[こちら](https://qiita.com/pocket_kyoto/items/80a1ac0e46819d90737f)のようにライブラリを書き換えるという力技をしたせいだと思ったので沼りました。この記事のようにすれば万事解決です。
　`cv2.VideoCapture`は日本語でも大丈夫なことがあるということについてなにか分かる人がいれば教えてくれれば幸いです。

# 参考
[1] Python OpenCV の cv2.imread 及び cv2.imwrite で日本語を含むファイルパスを取り扱う際の問題への対処について - Qiita, 2018, https://qiita.com/SKYS/items/cbde3775e2143cad7455, 
[2] Python - imwriteでのファイル名の文字化け｜teratail, 2020, https://teratail.com/questions/268813, 
[3] pythonスクリプトをexeに変換する（つまづきポイントまとめ） - Qiita, 2018, https://qiita.com/pocket_kyoto/items/80a1ac0e46819d90737f,