OpenCVとmatplotlib.pyplotの競合について

`OpenCV(Python)`で処理した画像の画素値ヒストグラムを`matplotlib.pyplot`しようとしてハマったのでメモしておきます。

# 環境

項目|バージョン
---|---
OS|MacOSX Sierra
Python|3.6.2(pyenv-virtualenv)
matplotlibのbackend|TkAgg([pyenvとvirtualenvで環境構築した時にmatplotlib.pyplotが使えなかった時の対処法](https://qiita.com/Kodaira_/items/1a3b801c7a5a41c9ce49)を参考に設定させていただきました)

```
$ pip freeze -l
cycler==0.10.0
matplotlib==2.1.2
numpy==1.13.3
opencv-python==3.3.0.10
pyparsing==2.2.0
python-dateutil==2.6.1
pytz==2018.3
six==1.11.0
```

# やりたかったこと

+ 現在カメラに写っている画像をプレビューしながら、画素値のヒストグラムをリアルタイムに描画する

だいぶ端折っていまいまいましたが、当初はだいたい下のようなコードを書きました。

```.python
# coding: UTF-8
import cv2
import numpy as np
import matplotlib
from matplotlib import pyplot as plt

def run():
    cv2.namedWindow(self.srcWindowName)  # 加工前の映像表示ウィンドウ生成
    cv2.namedWindow(self.dstWindowName)  # 加工後の映像表示ウィンドウ生成
    cap = cv2.VideoCapture(0)
    while True:
        ret, src = cap.read()
        if ret:
            dst = convert(src)  # 加工
            cv2.imshow(self.srcWindowName, src)
            cv2.imshow(self.dstWindowName, dst)

            # ヒストグラム
            img_hist = cv2.cvtColor(dst, cv2.COLOR_BGR2GRAY)
            hist = cv2.calcHist([img_hist],[0],None,[256],[0,256])
            plt.plot(hist)
            plt.xlim([0,256])
            plt.ylim([0,10000])
            plt.pause(.01)
            plt.cla()  # 現在描写されているグラフを消去

        key = cv2.waitKey(1)
        if key == 27:  # ESCキー押されたら終了
            break

    cap.release()
    cv2.destroyAllWindows()
```

# エラー

上記のコードを実行すると、以下のようなエラーが発生しました。

```
2018-02-21 17:42:24.083 python[11468:1380593] -[QNSApplication _setup:]: unrecognized selector sent to instance 0x7feff8ecedb0
2018-02-21 17:42:24.086 python[11468:1380593] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[QNSApplication _setup:]: unrecognized selector sent to instance 0x7feff8ecedb0'
*** First throw call stack:
(
        0   CoreFoundation                      0x00007fffbd5122cb __exceptionPreprocess + 171
        1   libobjc.A.dylib                     0x00007fffd232948d objc_exception_throw + 48
        2   CoreFoundation                      0x00007fffbd593f04 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
        3   CoreFoundation                      0x00007fffbd484755 ___forwarding___ + 1061
        4   CoreFoundation                      0x00007fffbd4842a8 _CF_forwarding_prep_0 + 120
        5   Tk                                  0x0000000116b19c02 TkpInit + 471
        6   Tk                                  0x0000000116a952a9 Tk_Init + 1794
        7   _tkinter.cpython-36m-darwin.so      0x000000011696edd4 Tcl_AppInit + 84
        8   _tkinter.cpython-36m-darwin.so      0x0000000116969c4f _tkinter_create + 1071
        9   python                              0x000000010d53ef46 _PyCFunction_FastCallDict + 166
        10  python                              0x000000010d5c764e call_function + 478
        11  python                              0x000000010d5c06b3 _PyEval_EvalFrameDefault + 4851
        12  python                              0x000000010d5c8126 _PyEval_EvalCodeWithName + 2566
        13  python                              0x000000010d5c8bc6 _PyFunction_FastCallDict + 662
        14  python                              0x000000010d4f5eba _PyObject_FastCallDict + 346
        15  python                              0x000000010d4f5fec _PyObject_Call_Prepend + 156
        16  python                              0x000000010d4f5c15 PyObject_Call + 101
        17  python                              0x000000010d5573ee slot_tp_init + 158
        18  python                              0x000000010d5536a9 type_call + 313
        19  python                              0x000000010d4f5e84 _PyObject_FastCallDict + 292
        20  python                              0x000000010d4f62d5 _PyObject_FastCallKeywords + 197
        21  python                              0x000000010d5c7622 call_function + 434
        22  python                              0x000000010d5c073d _PyEval_EvalFrameDefault + 4989
        23  python                              0x000000010d5c8878 fast_function + 568
        24  python                              0x000000010d5c7629 call_function + 441
        25  python                              0x000000010d5c06b3 _PyEval_EvalFrameDefault + 4851
        26  python                              0x000000010d5c8126 _PyEval_EvalCodeWithName + 2566
        27  python                              0x000000010d5c8bc6 _PyFunction_FastCallDict + 662
        28  python                              0x000000010d4f5eba _PyObject_FastCallDict + 346
        29  python                              0x000000010d4f5fec _PyObject_Call_Prepend + 156
        30  python                              0x000000010d4f5c15 PyObject_Call + 101
        31  python                              0x000000010d5c0a07 _PyEval_EvalFrameDefault + 5703
        32  python                              0x000000010d5c8126 _PyEval_EvalCodeWithName + 2566
        33  python                              0x000000010d5c8919 fast_function + 729
        34  python                              0x000000010d5c7629 call_function + 441
        35  python                              0x000000010d5c06b3 _PyEval_EvalFrameDefault + 4851
        36  python                              0x000000010d5c8878 fast_function + 568
        37  python                              0x000000010d5c7629 call_function + 441
        38  python                              0x000000010d5c06b3 _PyEval_EvalFrameDefault + 4851
        39  python                              0x000000010d5c8126 _PyEval_EvalCodeWithName + 2566
        40  python                              0x000000010d5c8919 fast_function + 729
        41  python                              0x000000010d5c7629 call_function + 441
        42  python                              0x000000010d5c06b3 _PyEval_EvalFrameDefault + 4851
        43  python                              0x000000010d5c8126 _PyEval_EvalCodeWithName + 2566
        44  python                              0x000000010d5c8919 fast_function + 729
        45  python                              0x000000010d5c7629 call_function + 441
        46  python                              0x000000010d5c06b3 _PyEval_EvalFrameDefault + 4851
        47  python                              0x000000010d5c8878 fast_function + 568
        48  python                              0x000000010d5c7629 call_function + 441
        49  python                              0x000000010d5c06b3 _PyEval_EvalFrameDefault + 4851
        50  python                              0x000000010d5c8878 fast_function + 568
        51  python                              0x000000010d5c7629 call_function + 441
        52  python                              0x000000010d5c06b3 _PyEval_EvalFrameDefault + 4851
        53  python                              0x000000010d5c8126 _PyEval_EvalCodeWithName + 2566
        54  python                              0x000000010d5bf330 PyEval_EvalCode + 48
        55  python                              0x000000010d5f3339 PyRun_FileExFlags + 185
        56  python                              0x000000010d5f286d PyRun_SimpleFileExFlags + 285
        57  python                              0x000000010d60c1b3 Py_Main + 3427
        58  python                              0x000000010d4ea7d8 main + 248
        59  libdyld.dylib                       0x00007fffd2c0f235 start + 1
        60  ???                                 0x0000000000000002 0x0 + 2
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Abort trap: 6
```

# OpenCVとmatplotlib.pyplotが競合していた

どうやらOpenCVで生成したウィンドウがある状態で、pyplotの描画を行おうとすると、上記のエラーが発生してしまうようです。

上記のエラーでググるとstackoverflowなどでは、「明示的に`TkAgg`を使う宣言をする」という解決策が大半だったのですが、原因は別のところにありました。

結果的に`cv2.namedWindow(self.srcWindowName)`や`cv2.imshow(self.srcWindowName, src)`などのプレビュー描画処理をコメントアウトすることで、ヒストグラムは正常に表示されるようになりました。

プレビューしながら・・・という機能を作るためには、別プロセスでプレビュー機能を立ち上げたほうが良いかもしれません。