目標
次のことを学ぶ:
- ピクセル値へのアクセスと変更
- 画像のプロパティへのアクセス
- 関心領域 (ROI) の設定
- 画像の分割と統合
本節のほぼすべての操作は、主にOpenCVよりもNumpyに関連している。OpenCVでより最適化されたコードを書くには、Numpyの十分な知識が必要である。
(ほとんどが1行程度のコードなので、例はPythonのターミナルで示す)
ピクセル値へのアクセスと変更
まずカラー画像を読み込む:
>>> import numpy as np
>>> import cv2 as cv
>>> assert img is not None, "file could not be read, check with os.path.exists()"
Mat imread(const String &filename, int flags=IMREAD_COLOR_BGR)
Loads an image from a file.
ピクセル値には、その行と列の座標を使ってアクセスできる。BGR画像の場合は、青・緑・赤の値の配列が返される。グレースケール画像の場合は、対応する輝度だけが返される。
>>> px = img[100,100]
>>> print( px )
[157 166 200]
>>> blue = img[100,100,0]
>>> print( blue )
157
ピクセル値も同じ方法で変更できる。
>>> img[100,100] = [255,255,255]
>>> print( img[100,100] )
[255 255 255]
警告
Numpyは高速な配列計算のために最適化されたライブラリである。したがって、各ピクセル値に単純にアクセスして変更するのは非常に遅く、推奨されない。
画像プロパティへのアクセス
画像プロパティには、行数、列数、チャンネル数、画像データの型、ピクセル数などが含まれる。
画像の形状(shape)は img.shape でアクセスする。行数・列数・チャンネル数(画像がカラーの場合)のタプルを返す:
>>> print( img.shape )
(342, 548, 3)
- 覚え書き
- 画像がグレースケールの場合、返されるタプルには行数と列数だけが含まれる。そのため、読み込んだ画像がグレースケールかカラーかを確認するのに適した方法である。
ピクセルの総数は img.size でアクセスする:
>>> print( img.size )
562248
画像のデータ型は `img.dtype` で取得する:
>>> print( img.dtype )
uint8
- 覚え書き
- OpenCV-Pythonコードのエラーの多くは無効なデータ型が原因であるため、img.dtype はデバッグ時に非常に重要である。
画像のROI
ときには、画像の特定の領域を扱う必要がある。画像中の目の検出では、まず画像全体に対して顔検出を行う。顔が得られたら、画像全体を探索する代わりに、顔の領域だけを選択してその内側で目を探索する。これは精度(目は常に顔の上にあるため :D )と性能(小さな領域内を探索するため)を向上させる。
ROIも同様にNumpyのインデックス指定で取得する。ここではボールを選択し、それを画像内の別の領域へコピーしている:
>>> ball = img[280:340, 330:390]
>>> img[273:333, 100:160] = ball
以下の結果を確認すること:
image
画像チャンネルの分割と統合
ときには、画像のB、G、Rチャンネルを個別に扱う必要がある。この場合、BGR画像をシングルチャンネルに分割する必要がある。別の場合には、これらの個々のチャンネルを結合してBGR画像を作成する必要があるかもしれない。これは次のように簡単に行える:
void split(const Mat &src, Mat *mvbegin)
Divides a multi-channel array into several single-channel arrays.
void merge(const Mat *mv, size_t count, OutputArray dst)
Creates one multi-channel array out of several single-channel ones.
または
赤いピクセルをすべてゼロに設定したいとする。その場合、まずチャンネルを分割する必要はない。Numpyのインデックス指定のほうが高速である:
警告
cv.split() は(処理時間の点で)コストの高い操作である。そのため、必要な場合にのみ使用すること。それ以外の場合はNumpyのインデックス指定を使うこと。
画像への境界の作成(パディング)
写真の額縁のように画像の周囲に境界を作成したい場合は、cv.copyMakeBorder() を使用できる。ただし、畳み込み演算やゼロパディングなどへの応用も多い。この関数は次の引数を取る:
- src - 入力画像
- top, bottom, left, right - 対応する方向のピクセル数で表した境界の幅
- borderType - Flag defining what kind of border to be added. It can be following types:
- value - 境界の種類が cv.BORDER_CONSTANT の場合の境界の色
理解を深めるために、これらすべての境界の種類を示すサンプルコードを以下に示す:
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
BLUE = [255,0,0]
assert img1 is not None, "file could not be read, check with os.path.exists()"
plt.subplot(231),plt.imshow(img1,'gray'),plt.title('ORIGINAL')
plt.subplot(232),plt.imshow(replicate,'gray'),plt.title('REPLICATE')
plt.subplot(233),plt.imshow(reflect,'gray'),plt.title('REFLECT')
plt.subplot(234),plt.imshow(reflect101,'gray'),plt.title('REFLECT_101')
plt.subplot(235),plt.imshow(wrap,'gray'),plt.title('WRAP')
plt.subplot(236),plt.imshow(constant,'gray'),plt.title('CONSTANT')
plt.show()
void copyMakeBorder(InputArray src, OutputArray dst, int top, int bottom, int left, int right, int borderType, const Scalar &value=Scalar())
Forms a border around an image.
以下の結果を確認すること。(画像はmatplotlibで表示している。そのため赤と青のチャンネルが入れ替わる):
image