画像処理002_OpenCVによる画像の幾何変換

この記事の主な内容は、OpenCV-Python チュートリアルのOpenCVの画像処理部分から来ています。この部分の主な内容は次のとおりです。

目標

  • 平行移動、回転、アフィン変換など、さまざまな幾何学的変換を画像に適用する方法を学びます。
  • これらの関数が表示されます: 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 2y+M1 3M2 1×+M2 2y+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()

最終的な結果は以下のようになります。

画像

その他のリソース

  1. 「コンピューター ビジョン: アルゴリズムとアプリケーション」、リチャード シェリスキー

参考資料

画像の幾何学的変換

Markdown でよく使用される LaTex の数式

マークダウン数式の構文

Cmd Markdown 式ガイド

終わり。

おすすめ

転載: blog.csdn.net/tq08g2z/article/details/123930589