OpenCVとNumPy

Pythonで画像処理を実装する時に様々なライブラリがありますね。ネット上のサンプルコードなどを見ていると、それぞれの仕様を確認せずに混在して使っている人も多いように見えます。動けば良いという事であればそれまでなのですが、トラブルによる無駄な時間を削減したいという事であれば、あえて使用するライブラリを絞り込んで、その仕様を十分に理解するのも良いと考えています。(急がば廻れ的な考えです)

そこで私は基本的にOpenCVとNumPyだけでコーディングするようにしています。もちろん時と場合によってはscikit-imageやPillowも使いますが、基本的にこの2つのライブラリの経験値を重点的に上げる事で品質向上と全体工期の短縮を図っている感じですね。

さて、native版のOpenCVでは画像データの保持にcvMatクラスを使っています。これは名前の通り行列を扱うクラスですが、2次元画像は2次元行列と同じデータ構造で表現できますね。NumPyも同様です。こちらはN次元行列を扱う代数計算用のライブラリですが、多次元データの扱いやすさから画像データを格納するデータ形式としても優れていると思います。これらの理由によると思われますがPython版のOpenCVは、画像データをNumPyオブジェクトとして扱っているのでしょう。

しかし、ここで一つ大きな問題が。

OpenCVとNumPyで次元表現が何故か逆なのです。(まぁ理由は想像つきますが割愛します。行列をどれだけ重視したかという感じでしょう)

元画像をOpenCVのresize関数で縦横半分 にしたいとします。この時、リサイズ後の画像サイズをcv2.resize関数のdsize引数に指定しますが、縦横の指定順がNumPyと逆になります。

import numpy as np
import cv2

src = cv2.imread('figure1.png')
h, w, _ = src.shape

# これは誤り
dst_shape = (h // 2, w // 2)
dst = cv2.resize(src, dsize=dst_shape)
cv2.imwrite('figure2.png', dst)

# こちらが正しい
dst_shape = (w // 2, h // 2)
dst = cv2.resize(src, dsize=dst_shape)
cv2.imwrite('figure3.png', dst)
元画像
dsize = (w // 2, h // 2)
dsize = (h // 2, w // 2)

Pythonで画像処理を実装し始めた当初に何度かハマったのがこの問題で、特に像をリサイズする際に問題になりました。しかも今でも油断すると間違える…。特に画像サイズ情報をtupleで保持している時などに、それをそのまま使ってしまってハマることが多い気がします。

この一件以来、自分の中では次元表現は全てNumPy形式で統一するようにしてOpenCV関数呼び出し時に変換するようにしました。

Author: kan
初めてプログラムらしきものを作ったのは幼稚園の時。それから約40年経ち、現在はデジタル回路設計から信号処理、機械学習まで幅広い経験を活かしてシステムアーキテクトとして活動中。超並列処理、デジタル回路とソフトウェアのバランス設計が得意分野。 Linux/Mac/Windows使い。 C/C++を主要言語として、Unity、Qtなどのフレームワーク興味あり。UI/UXデザイン、STL拡張など。 音声処理、画像処理、技術コンサルは仕事でも請け負います。 一般ソフトウェア開発プロセス、医療機器ソフトウェア開発プロセス作成も進行中。

コメントを残す

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

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください