こしあん
2022-12-05

OpenCVで「 (-5:Bad argument) in function ‘rectangle’」と怒られた


11.3k{icon} {views}

OpenCVでcv2.rectangleを実行したところ、「-1: error: (-5:Bad argument) in function ‘rectangle’ > Overload resolution failed: > – Layout of the output array img is incompatible with cv::Mat」というエラーが表示されてしまいました。その対処方法を見ていきます。

問題

割りとハマったデバッグ。cv2.rectangle使って、

cv2.rectangle(img, (20, 20), (50, 50), [255, 255, 0], 1)

こんな感じで書いてたら、

  File "hogehoge.py", line 10, in main
    cv2.rectangle(img, (20, 20), (50, 50), [255, 255, 0], 1)
cv2.error: OpenCV(4.6.0) :-1: error: (-5:Bad argument) in function 'rectangle'
> Overload resolution failed:
>  - Layout of the output array img is incompatible with cv::Mat
>  - Expected Ptr<cv::UMat> for argument 'img'
>  - Layout of the output array img is incompatible with cv::Mat
>  - Expected Ptr<cv::UMat> for argument 'img'

と怒られた。

原因

理由を探していたら、BGR→RGBの変換に問題があり、

    img = np.ones((100, 100, 3), dtype=np.uint8)
    img = img[:, :, ::-1] # ここがだめ
    cv2.rectangle(img, (20, 20), (50, 50), [255, 255, 0], 1)

スライスでRGB→BGRを変換してしまうと、先程のようなエラーが出る。

対策

.copy()を作ることでエラーは消えます

    img = np.ones((100, 100, 3), dtype=np.uint8)
    img = img[:, :, ::-1].copy() # copyを追加
    cv2.rectangle(img, (20, 20), (50, 50), [255, 255, 0], 1)

この理由は、reshape自体がメモリのコピーをせず、viewを作るだけだからだと思われます。

It is not always possible to change the shape of an array without copying the data. If you want an error to be raised when the data is copied, you should assign the new shape to the shape attribute of the array:

https://numpy.org/doc/stable/reference/generated/numpy.reshape.html

また、スライスでRGB→BGRを変換せず、OpenCVの関数で変換した場合はエラーは出ません

    img = np.ones((100, 100, 3), dtype=np.uint8)
    img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
    cv2.rectangle(img, (20, 20), (50, 50), [255, 255, 0], 1)

パフォーマンス比較

以下の2つのパターンで速度比較してみます。

  • スライスでBGR→RGBを変換する+copy()する
  • OpenCVの関数で変換する

ほぼ変わらないようなイメージもありますが、前者はコピーを挟んでいるので遅くなりそうなイメージもあります。やってみましょう。

def case_a():
    img = np.random.uniform(low=0, high=256, size=(1080, 1920, 3)).astype(np.uint8)
    start_time = time.time()
    for i in range(1000):
        x = img[:, :, ::-1].copy()
    print("case a")
    print(time.time()-start_time)

def case_b():
    img = np.random.uniform(low=0, high=256, size=(1080, 1920, 3)).astype(np.uint8)
    start_time = time.time()
    for i in range(1000):
        x = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
    print("case b")
    print(time.time()-start_time)

3回実行してみたところ、

  • case_aは7.61秒、7.50秒、7.64秒
  • case_bは1.37秒、1.31秒、1.31秒

でした。結構変わりますね(多分メモリコピーが遅いのかと)。

結論

BGR→RGBの変換はOpenCVの関数でやったほうがよさそう



Shikoan's ML Blogの中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内

技術書コーナー

北海道の駅巡りコーナー


Add a Comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です