python
cv2.inRangeは
cv.inRange(img, min, max)
のように呼びだすと画像imgに対して値がmin以上max以下の部分を白に、
それ以外を黒にするマスクを作ってくれる関数である。
カラー画像に対してはminとmaxは3次元のタプルやnp.arrayでもよく、BGRの各値が範囲内かどうかを調べてくれる、
色域抽出などの処理で便利な関数である。
地味なハマりどころとして「inRangeのmin, maxはimgがカラーのときはnp.arrayでもよいが、 グレースケールのときはintでなければならない」という仕様だ。 おそらくこれは、カラー画像のときはRGBを3次元のnp.arrayで表現できるようにしておくための仕様と思われる。 これによってカラー画像では動いていたコードが、たまたまグレー画像が来たときにエラーを出すということがある。
In [1]: import cv2 In [2]: import numpy as np # グレー画像を生成 In [4]: img = (np.random.rand(30, 20) * 255).astype(np.uint8) In [5]: img Out[5]: array([[171, 163, 88, 178, 67, 140, 58, 119, 1, 100, 43, 74, 87, 172, 204, 158, 120, 183, 16, 99], ... (中略) # min, maxがint、これは成功 In [6]: cv2.inRange(img, 100, 120) Out[6]: array([[ 0, 0, 0, 0, 0, 0, 0, 255, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0], # min, maxがnp.array、これは失敗 In [8]: cv2.inRange(img, img[0, 0], img[0, 0] + 3) --------------------------------------------------------------------------- error Traceback (most recent call last) Cell In[8], line 1 ----> 1 cv2.inRange(img, img[0, 0], img[0, 0] + 3) error: OpenCV(4.8.1) :-1: error: (-5:Bad argument) in function 'inRange' > Overload resolution failed: > - lowerb is not a numpy array, neither a scalar > - Expected Ptr<cv::UMat> for argument 'lowerb' # min, maxをintに変換してから渡せば成功 In [9]: cv2.inRange(img, img[0, 0].item(), img[0, 0].item() + 3) Out[9]: array([[255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0],
エラーメッセージは"lowerb is not a numpy array, neither a scalar"というわかりにくいものが、 np.arrayを渡しているにもかかわらず出てしまうのがまた厄介である。
なお、カラー画像の場合はnp.arrayを渡してもよいので、imgからインデックスで引っこ抜いた 色値をそのままinRangeに渡してよい。
In [10]: img = (np.random.rand(30, 20, 3) * 255).astype(np.uint8) In [11]: cv2.inRange(img, img[0, 0], img[0, 0] + 3) Out[11]: array([[255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],カラーとグレーで処理をわけるのは面倒なので、飛んでくる画像を全てカラーに変換してしまうのも一つの手である。
おすすめ記事
PyTorchのテンソルを画像にして保存するNumPy np.clip: arrayの値の範囲を指定した最大値・最小値に合わせて丸める
Python OpenCV/PillowでWebP画像を読み込む