web-dev-qa-db-ja.com

python)を使用したOpenCVでの遠近法の修正

4方向すべてに傾いている傾斜した長方形(クレジットカード)の遠近法補正を行おうとしています。その四隅とそれぞれの傾斜角度はわかりましたが、投影する必要のある座標の正確な位置を見つけることができません。 cv2.getPerspectiveTransformを使用して変換を行っています。

実際のカード(傾斜していないカード)のアスペクト比がありますが、元のアスペクト比が維持されるような座標が必要です。バウンディング長方形を使用してみましたが、これによりカードのサイズが大きくなります。

どんな助けでもいただければ幸いです。test image

Bounding Rectangle

15
Anuradha

これがあなたが従う必要がある方法です...

簡単にするために、画像のサイズを小さくしました。

enter image description here

  • ソース画像の四角形の頂点を計算します。ここでは手動で調べます。エッジ検出、ハフラインなどを選択できます。
  Q1=manual calculation;
  Q2=manual calculation;
  Q3=manual calculation;
  Q4=manual calculation;
  • アスペクト比を維持して宛先画像の四角形の頂点を計算します。ここでは、ソースの四角形の頂点の上からカードの幅を取得し、アスペクト比を掛けて高さを計算できます。
   // compute the size of the card by keeping aspect ratio.
    double ratio=1.6;
    double cardH=sqrt((Q3.x-Q2.x)*(Q3.x-Q2.x)+(Q3.y-Q2.y)*(Q3.y-Q2.y)); //Or you can give your own height
    double cardW=ratio*cardH;
    Rect R(Q1.x,Q1.y,cardW,cardH);
  • これで、ソースと宛先の四角形の頂点が得られ、warpPerspectiveを適用します。

enter image description here

以下のC++コードを参照できます。

   //Compute quad point for Edge
    Point Q1=Point2f(90,11);
    Point Q2=Point2f(596,135);
    Point Q3=Point2f(632,452);
    Point Q4=Point2f(90,513);

    // compute the size of the card by keeping aspect ratio.
    double ratio=1.6;
    double cardH=sqrt((Q3.x-Q2.x)*(Q3.x-Q2.x)+(Q3.y-Q2.y)*(Q3.y-Q2.y));//Or you can give your own height
    double cardW=ratio*cardH;
    Rect R(Q1.x,Q1.y,cardW,cardH);

    Point R1=Point2f(R.x,R.y);
    Point R2=Point2f(R.x+R.width,R.y);
    Point R3=Point2f(Point2f(R.x+R.width,R.y+R.height));
    Point R4=Point2f(Point2f(R.x,R.y+R.height));

    std::vector<Point2f> quad_pts;
    std::vector<Point2f> squre_pts;

    quad_pts.Push_back(Q1);
    quad_pts.Push_back(Q2);
    quad_pts.Push_back(Q3);
    quad_pts.Push_back(Q4);

    squre_pts.Push_back(R1);
    squre_pts.Push_back(R2);
    squre_pts.Push_back(R3);
    squre_pts.Push_back(R4);


    Mat transmtx = getPerspectiveTransform(quad_pts,squre_pts);
    int offsetSize=150;
    Mat transformed = Mat::zeros(R.height+offsetSize, R.width+offsetSize, CV_8UC3);
    warpPerspective(src, transformed, transmtx, transformed.size());

    //rectangle(src, R, Scalar(0,255,0),1,8,0);

    line(src,Q1,Q2, Scalar(0,0,255),1,CV_AA,0);
    line(src,Q2,Q3, Scalar(0,0,255),1,CV_AA,0);
    line(src,Q3,Q4, Scalar(0,0,255),1,CV_AA,0);
    line(src,Q4,Q1, Scalar(0,0,255),1,CV_AA,0);

    imshow("quadrilateral", transformed);
    imshow("src",src);
    waitKey();
10
Haris

@Harisから提供された回答をPythonで書いています。

import cv2
import math
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('test.jpg')
rows,cols,ch = img.shape

pts1 = np.float32([[360,50],[2122,470],[2264, 1616],[328,1820]])

ratio=1.6
cardH=math.sqrt((pts1[2][0]-pts1[1][0])*(pts1[2][0]-pts1[1][0])+(pts1[2][1]-pts1[1][1])*(pts1[2][1]-pts1[1][1]))
cardW=ratio*cardH;
pts2 = np.float32([[pts1[0][0],pts1[0][1]], [pts1[0][0]+cardW, pts1[0][1]], [pts1[0][0]+cardW, pts1[0][1]+cardH], [pts1[0][0], pts1[0][1]+cardH]])

M = cv2.getPerspectiveTransform(pts1,pts2)

offsetSize=500
transformed = np.zeros((int(cardW+offsetSize), int(cardH+offsetSize)), dtype=np.uint8);
dst = cv2.warpPerspective(img, M, transformed.shape)

plt.subplot(121),plt.imshow(img),plt.title('Input')
plt.subplot(122),plt.imshow(dst),plt.title('Output')
plt.show()

enter image description here

1
Vedanshu

enter image description here 私ははるかに簡単なより良い解決策を持っています:-元の画像の赤い長方形と長方形の角の点はソース点です
-ソースポイントと宛先ポイントを引数として取り、図に示すように任意の画像を宛先画像に変換する変換行列を返すcv2.getPerspectiveTransform(src, dst)を使用します-この変換行列をcv2.warpPerspective()
-ご覧のとおり、結果は優れています。画像の非常に素晴らしい鳥瞰図が得られます

import cv2
import matplotlib.pyplot as plt
import numpy as np


def unwarp(img, src, dst, testing):
    h, w = img.shape[:2]
    # use cv2.getPerspectiveTransform() to get M, the transform matrix, and Minv, the inverse
    M = cv2.getPerspectiveTransform(src, dst)
    # use cv2.warpPerspective() to warp your image to a top-down view
    warped = cv2.warpPerspective(img, M, (w, h), flags=cv2.INTER_LINEAR)

    if testing:
        f, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 10))
        f.subplots_adjust(hspace=.2, wspace=.05)
        ax1.imshow(img)
        x = [src[0][0], src[2][0], src[3][0], src[1][0], src[0][0]]
        y = [src[0][1], src[2][1], src[3][1], src[1][1], src[0][1]]
        ax1.plot(x, y, color='red', alpha=0.4, linewidth=3, solid_capstyle='round', zorder=2)
        ax1.set_ylim([h, 0])
        ax1.set_xlim([0, w])
        ax1.set_title('Original Image', fontsize=30)
        ax2.imshow(cv2.flip(warped, 1))
        ax2.set_title('Unwarped Image', fontsize=30)
        plt.show()
    else:
        return warped, M


im = cv2.imread("so.JPG")

w, h = im.shape[0], im.shape[1]
# We will first manually select the source points 
# we will select the destination point which will map the source points in
# original image to destination points in unwarped image
src = np.float32([(20,     1),
                  (540,  130),
                  (20,    520),
                  (570,  450)])

dst = np.float32([(600, 0),
                  (0, 0),
                  (600, 531),
                  (0, 531)])

unwarp(im, src, dst, True)

cv2.imshow("so", im)
cv2.waitKey(0)[![enter image description here][1]][1]
cv2.destroyAllWindows()
1
Jai