この記事の主な内容は、OpenCV-Python チュートリアルのOpenCVの画像処理部分から来ています。この部分の主な内容は次のとおりです。
-
異なる色空間間で画像を変更する方法を学びます。ビデオ内のカラフルなオブジェクトを追跡する方法も学びましょう。
-
回転、平行移動など、さまざまな幾何学的変換を画像に適用する方法を学びます。
-
グローバルしきい値処理、適応しきい値処理、Otsu の 2 値化などを使用して、画像をバイナリ イメージに変換する方法を学びます。
-
画像をぼかしたり、カスタム カーネルを使用して画像をフィルターしたりする方法などを学びます。
-
浸食、拡張、開口、閉鎖などの形態変化を理解します。
-
画像のグラデーションやエッジなどを見つける方法を学びます。
-
Canny エッジ検出を使用してエッジを見つける方法を学びます。
-
画像ピラミッドと、それを画像ブレンドに使用する方法について学びます。
-
OpenCV の輪郭に関するすべて。
-
OpenCV のヒストグラムについてのすべて。
-
OpenCV では、フーリエ変換、コサイン変換など、さまざまな画像変換が行われます。
-
テンプレート マッチングを使用して画像内のオブジェクトを検索する方法を学びます。
-
画像内の線を検出する方法を学びます。
-
画像内の円を検出する方法を学びます。
-
ウォーターシェッドアルゴリズムを使用した画像セグメンテーション
流域セグメンテーション アルゴリズムを使用して画像をセグメント化する方法を学びます。
-
GrabCut アルゴリズムを使用したインタラクティブな前景抽出
GrabCut アルゴリズムを使用して前景を抽出する方法を学びます
目標
- 平行移動、回転、アフィン変換など、さまざまな幾何学的変換を画像に適用する方法を学びます。
- これらの関数が表示されます: cv.getPerspectiveTransform。
変身
OpenCV は、あらゆる種類の変換を実行できる2 つの変換関数cv.warpAffine と cv.warpPerspectiveを提供します。cv.warpAffine は 2x3 変換行列を受け取り、cv.warpPerspective はパラメータとして 3x3 変換行列を受け取ります。
ズーム
スケーリングは単に画像のサイズを変更するだけです。OpenCV には、この操作を完了するための関数cv.resize()があります。画像のサイズは手動で指定することも、倍率を指定することもできます。スケーリング時にさまざまな補間方法を使用できます。スケールダウンに推奨される補間メソッドはcv.INTER_AREA、スケールアップに推奨される補間メソッドは cv.INTER_CUBIC (低速) および cv.INTER_LINEARです。デフォルトでは、すべてのサイズ変更にcv.INTER_LINEAR補間メソッドが使用されます。次のように入力画像のサイズを変更できます。
def scaling():
cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
img = cv.imread(cv.samples.findFile('messi5.jpg'))
res = cv.resize(img, None, fx=2, fy=2, interpolation=cv.INTER_CUBIC)
cv.imshow('frame', res)
# OR
height, width = img.shape[:2]
res = cv.resize(img, (2 * width, 2 * height), interpolation=cv.INTER_CUBIC)
cv.waitKey()
cv.destroyAllWindows()
スケーリング操作は、複数の画像をパラメータとして受け取る多くの操作に適しています。操作の入力が複数の画像で、入力画像のサイズに一定の制限があり、実際の入力画像がこの制限を満たすことが難しい場合、スケーリングは、ターゲットを満たすように一部の画像のサイズを変更するのに役立ちます。操作の要件を入力します。例えば、複数の画像の加算操作、画像の水平結合、垂直結合などです。
パン
平行移動とは、オブジェクトの位置を移動することです。(x,y) 方向のオフセットがわかっていて、それを作成すると ( tx t_xt×、ty t_ytはい) の場合、次の変換行列を作成できます:
[ 1 0 tx 0 1 tx ] \begin{bmatrix} 1&0&t_x \\ 0&1&t_x \end{bmatrix}[1001t×t×]
dst ( x , y ) = src ( M 11 ∗ x + M 12 ∗ y + M 13 , M 21 ∗ x + M 22 ∗ y + M 23 ) = src ( 1 ∗ x + 0 ∗ y + tx , 0 ∗ x + 1 ∗ y + ty ) dst(x,y) = src(M_{11} ∗ x+M_{12}∗ y+M_{13}, M_{21}∗ x+M_{22}∗ y+ M_{23}) \\ = src(1*x+0*y+t_x, 0*x+1*y+t_y)d s t ( x ,y )=s r c ( M1 1∗ ×+M1 2∗ y+M1 3、M2 1∗ ×+M2 2∗ y+M2 3)=s r c ( 1 ∗ x+0 ∗ y+t×、0 ∗ ×+1 ∗ y+tはい)
これを np.float32 型の Numpy 配列に入れて、cv.warpAffine()関数に渡すことができます。次の (100,50) の移動例を参照してください。
def translation():
cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
img = cv.imread(cv.samples.findFile('messi5.jpg'))
rows, cols, _ = img.shape
M = np.float32([[1, 0, 100], [0, 1, 50]])
dst = cv.warpAffine(img, M, (cols, rows))
dst = cv.hconcat([img, dst])
cv.imshow('frame', dst)
cv.waitKey()
cv.destroyAllWindows()
cv.warpAffine()関数の 3 番目のパラメーターは出力イメージのサイズであり、その形式は(width, height)である必要があります。幅 = 列数、高さ = 行数であることを覚えておいてください。
表示される結果は次のようになります。
回転させる
画像を角度 θ だけ回転するには、次の変換行列を使用します。
M = [ cos θ − sin θ sin θ cos θ ] M = \begin{bmatrix} cosθ&−sinθ \\ sinθ&cosθ \end{bmatrix}M=[c o s θsinθ _ _ _− s i n θc o s θ]
ただし、OpenCV では、回転中心を調整できるスケーリングされた回転が提供されるため、好きな位置で回転できます。変更された変換行列は次のように与えられます。
[ α β ( 1 − α ) ⋅ 中心 。x − β ⋅ 中心 。y − β α β ⋅ 中心 。x + ( 1 − α ) ⋅ 中心 。y ] \begin{bmatrix} α&β&(1−α)⋅center.x−β⋅center.y \\ −β&α&β⋅center.x+(1−α)⋅center.y \end{bmatrix}[ある− bbある( 1 − a )⋅センター。_ _ _ _ _ x − β⋅センター。_ _ _ _ _ yb⋅センター。_ _ _ _ _ バツ+( 1 − a )⋅センター。_ _ _ _ _ はい]
その中に:
α = スケール ⋅ cos θ 、 β = スケール ⋅ sin θ α=scale ⋅cosθ 、 \\ β=scale ⋅sinθある=スケール_ _ _ _⋅c o s θ 、b=スケール_ _ _ _⋅サインθ _ _
この回転行列を取得するために、OpenCV は関数cv.getRotationMatrix2Dを提供します。以下の例を確認してください。これは、画像を中心に対して 120 度回転し、1.2 倍に拡大します。
def rotation():
cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
img = cv.imread(cv.samples.findFile('messi5.jpg'))
rows, cols, _ = img.shape
# cols-1 and rows-1 are the coordinate limits.
M = cv.getRotationMatrix2D(((cols - 1) / 2.0, (rows - 1) / 2.0), 120, 1.2)
dst = cv.warpAffine(img, M, (cols, rows))
dst = cv.hconcat([img, dst])
cv.imshow('frame', dst)
cv.waitKey()
cv.destroyAllWindows()
結果を見てみましょう:
放射線変換
アフィン変換では、元のイメージ内のすべての平行線が出力イメージでも平行のままになります。変換行列を見つけるには、入力画像内の 3 つの点と、出力画像内のそれらの対応する位置が必要です。cv.getAffineTransform は2x3 行列を作成し、これがcv.warpAffineに渡されます。
以下の例を確認すると、選択されたポイントも表示されます (緑色でマークされています)。
def affine_transformation():
img = np.zeros((512, 512, 3), np.uint8)
cv.rectangle(img, (0, 0), (512, 512), (255, 255, 255), -1)
cv.line(img, (0, 50), (512, 50), (0, 0, 0), 3)
cv.line(img, (0, 150), (512, 150), (0, 0, 0), 3)
cv.line(img, (0, 300), (512, 300), (0, 0, 0), 3)
cv.line(img, (0, 450), (512, 450), (0, 0, 0), 3)
cv.line(img, (100, 0), (100, 512), (0, 0, 0), 3)
cv.line(img, (256, 0), (256, 512), (0, 0, 0), 3)
cv.line(img, (412, 0), (412, 512), (0, 0, 0), 3)
cv.rectangle(img, (60, 170), (430, 400), (0, 0, 0), 3)
# img, center, radius, color, thickness=None
cv.circle(img, (60, 50), 8, (0, 255, 0), -1)
cv.circle(img, (280, 50), 8, (0, 255, 0), -1)
cv.circle(img, (60, 270), 8, (0, 255, 0), -1)
rows, cols, ch = img.shape
pts1 = np.float32([[50, 50], [200, 50], [50, 200]])
pts2 = np.float32([[10, 100], [200, 50], [100, 250]])
M = cv.getAffineTransform(pts1, pts2)
dst = cv.warpAffine(img, M, (cols, rows))
plt.subplot(121), plt.imshow(img), plt.title('Input')
plt.subplot(122), plt.imshow(dst), plt.title('Output')
plt.show()
if __name__ == "__main__":
affine_transformation()
次の結果が表示されます。
視点変換
透視変換には、3x3 の変換行列が必要です。変換後も線は直線のままです。この変換行列を見つけるには、入力画像上の 4 つの点と、出力画像上の対応する点が必要です。これら 4 つの点のうち、3 点は同一線上にあってはなりません。変換行列は、関数cv.getPerspectiveTransformを介して見つけることができます。次に、この 3x3 変換行列を使用してcv.warpPerspectiveを適用します。
次のコードを確認できます。
def perspective_transformation():
cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
img = cv.imread(cv.samples.findFile('sudoku.png'))
rows, cols, ch = img.shape
pts1 = np.float32([[70, 80], [490, 70], [30, 510], [515, 515]])
pts2 = np.float32([[0, 0], [515, 0], [0, 515], [515, 515]])
M = cv.getPerspectiveTransform(pts1, pts2)
dst = cv.warpPerspective(img, M, (515, 515))
cv.line(img, (0, int(rows / 2)), (cols, int(rows / 2)), (0, 255, 0), 3)
cv.line(img, (int(cols / 2), 0), (int(cols / 2), rows), (0, 255, 0), 3)
cv.circle(img, (70, 80), 8, (0, 255, 0), -1)
cv.circle(img, (490, 70), 8, (0, 255, 0), -1)
cv.circle(img, (30, 510), 8, (0, 255, 0), -1)
cv.circle(img, (515, 515), 8, (0, 255, 0), -1)
plt.subplot(121), plt.imshow(img), plt.title('Input')
cv.line(dst, (0, int(rows / 2)), (cols, int(rows / 2)), (0, 255, 0), 3)
cv.line(dst, (int(cols / 2), 0), (int(cols / 2), rows), (0, 255, 0), 3)
plt.subplot(122), plt.imshow(dst), plt.title('Output')
plt.show()
if __name__ == "__main__":
perspective_transformation()
最終的な結果は以下のようになります。
その他のリソース
- 「コンピューター ビジョン: アルゴリズムとアプリケーション」、リチャード シェリスキー
参考資料
終わり。