OpenCVでは日本語のパスに対応していない!?
cv2.VideoCapture
を普通に実行していると普通に動いたが、pyinstallerでexe化すると文字化けしてエラーが出てしまった。(違いがよくわからないが)調べてみるとOpenCVではANSI文字列以外、つまり日本語のパスに対応していない模様。なのでcv2.imread
, cv2.imwrite
, cv2.VideoCapture
等で日本語を含むパスを指定するには工夫が必要。
こちらではnumpyを噛ませて解決してましたが、なんか難しそうだしcv2.VideoCapture
でのやり方もよくわからない。こちらでファイルをリネームすればいいというのを採用。
解決方法
対象ファイルのディレクトリに移動して、一時的に対象ファイルの名前を変更して処理する。
コード例(cv2.imread
)
- 対象ファイルがあるディレクトリに移動
- 対象ファイルの名前を変更
- 対象ファイルを読み取る
- 対象ファイルの名前を戻す
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
で画像を保存するときは
- 保存するディレクトリに移動
- 対象ファイルを保存
- 対象ファイルの名前を戻す
とするとよい
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()
としないと名前を変えることはできないので、処理が終わった後に名前を戻す。それにより関数化ができない。
- 対象ファイルがあるディレクトリに移動
- 対象ファイルの名前を変更
- 対象ファイルを読み取る
- 対象ファイルの名前を戻す
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
を日本語を含むパスを指定した場合の挙動を確認
import cv2
path = "C:/Users/sd18080/Desktop/フォルダ/画像.bmp"
img = cv2.imread(path)
print(img)
結果
None
エラーすら表示されない…!
OpenCVではそもそも存在しないファイルを参照しようとしてもエラーが出ないよう。
試しに上記をpath="aaaaa"
と適当に存在しないファイルを設定した場合も同様の結果となった。
cv2.VideoCapture
のエラー挙動
cv2.VideoCapture
は存在しないファイルを指定するとエラーが表示される
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'がどうちゃらっていうエラーが出ててそれをこちらのようにライブラリを書き換えるという力技をしたせいだと思ったので沼りました。この記事のようにすれば万事解決です。
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,