Python の OpenCV による画像認識で cv::CascadeClassifier::detectMultiScale のエラーに悩まされた話

# 概要
Python で OpenCV を用いた画像認識を行う際，分類器のパスを正しく指定しているにもかかわらず `cv::CascadeClassifier::detectMultiScale` に関するエラーが出る場合は，分類器のパスに全角文字が含まれていないかを確認する．

# 詳細
[こちら](https://oliversi.com/2019/01/13/python-opencv-face/)の記事を参考に，`facerecognition.py` を次の通り作成した．

```py:facerecognition.py
#! Python 3.7
# OpenCVを用いた物体認識

imgPath = 'img.jpg'             # 物体認識させる画像の相対パス
outputName = 'img_cascade.jpg'  # 物体認識後の出力画像名

print('ただいま実行中です。ひと口チョコでも食べながらお待ちください。')
import cv2, os

cascade = cv2.CascadeClassifier('C:/Users/ユーザー名/AppData/Local/Programs/Python/Python37/Lib/site-packages/cv2/data/haarcascade_frontalface_default.xml')
img = cv2.imread(imgPath)                                   # 処理する画像の指定
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)                # 画像のグレースケール化
rects = cascade.detectMultiScale(gray, 1.1, 3, 0, (20,20))  # 物体認識

# 物体認識した部分を四角囲み
if len(rects) > 0:
    for rect in rects:
        cv2.rectangle(img, tuple(rect[0:2]), tuple(rect[0:2]+rect[2:4]), (0, 0, 255), thickness=2)
else:
    print("検出なし")

# 物体認識が済んだ画像の出力
cv2.imwrite(outputName, img)
print('実行が完了しました。結果を ' + outputName + ' に出力しました。')
```
しかし，これを実行すると次のようなエラーを吐いた．

```
Traceback (most recent call last):
  File "facerecognition.py", line 13, in <module>
    rects = cascade.detectMultiScale(gray, 1.1, 3, 0, (20,20))  # 物体認識
cv2.error: OpenCV(4.1.1) C:\projects\opencv-python\opencv\modules\objdetect\src\cascadedetect.cpp:1689: error: (-215:Assertion failed) !empty() in function 'cv::CascadeClassifier::detectMultiScale'
```
エラーメッセージで検索してみると，[分類器の場所をフルパスで指定しなさい](https://stackoverflow.com/questions/54120605/python-opencv-empty-in-function-cvcascadeclassifierdetectmultiscale)とか，[そもそも分類器のパス指定が間違っている](https://qiita.com/K_M95/items/f1a3e7c47800adb94095)などの原因が出てくるが，いずれにも該当せず困った．

最終的に[こちら](http://shuzo-kino.hateblo.jp/entry/2018/01/03/234834)のブログ記事にたどり着き，`facerecognition.py` と同じディレクトリに作成した `data` フォルダに分類器を配置し，それにあわせて変数 `cascade` で指定しているパスを直したら，エラーは解消された．

```py:facerecognition.pyの10行目
cascade = cv2.CascadeClassifier('data/haarcascade_frontalface_default.xml')
```

# 原因は何か？
最初に指定したフルパスの中には全角文字（筆者の場合「ユーザー名」がそうだった）が含まれていた．それが原因かどうかを調べるため，分類器を配置したフォルダ名を全角文字を含むもの `あいうえお` に変更して再度試してみる．

```py:facerecognition.pyの10行目
cascade = cv2.CascadeClassifier('あいうえお/haarcascade_frontalface_default.xml')
```
すると，先ほどと全く同じエラーメッセージが出た．

```
Traceback (most recent call last):
  File "facerecognition.py", line 13, in <module>
    rects = cascade.detectMultiScale(gray, 1.1, 3, 0, (20,20))  # 物体認識
cv2.error: OpenCV(4.1.1) C:\projects\opencv-python\opencv\modules\objdetect\src\cascadedetect.cpp:1689: error: (-215:Assertion failed) !empty() in function 'cv::CascadeClassifier::detectMultiScale'
```
エラーの原因は分類器のパスに全角文字が含まれていたことで間違いなさそうだ．

# 参考
- [Python: OpenCVを使用して顔検出をする – バイオインフォマティクスの備忘録](https://oliversi.com/2019/01/13/python-opencv-face/)
- [c - Python OpenCV - "!empty() in function 'cv::CascadeClassifier::detectMultiScale'" - Stack Overflow](https://stackoverflow.com/questions/54120605/python-opencv-empty-in-function-cvcascadeclassifierdetectmultiscale)
- [OpenCVで顔認識をしてみた - Qiita](https://qiita.com/K_M95/items/f1a3e7c47800adb94095)
- [OpenCVの顔画像追跡サンプルで"Assertion failed (!empty()) in detectMultiScale,"とかいうエラーがでたら……学習ファイルの読み込みができていないかも - Bye Bye Moore](http://shuzo-kino.hateblo.jp/entry/2018/01/03/234834)