Python - cv2.getPerspectiveTransform() で与えるべきポイントを決定するにはどうすればよいですか?

okwaves2024-01-25  9

次の画像をワープして、画像の左側にある大きな壁の正面平行ビューを取得しようとしています。ただし、適用した場合に望ましい結果が得られる行列を取得するには、関数 cv2.getPerspectiveTransform() でどの点を指定する必要があるかを決定できません。

私が使用しているコードは次のとおりです:

import cv2
import numpy as np

circles = np.zeros((4,2),np.int)
counter = 0

def mousePoints(event,x,y,flags,params):
    global counter
    if event == cv2.EVENT_LBUTTONDOWN:

        circles[counter] = x,y
        counter = counter + 1
        print(circles)

img = cv2.imread("DSC_0273.JPG")

img = cv2.resize(img,(1500,1000))
q = 0
while True:

    if counter == 4:
        q = q+1

        height1,width1 = 1080,1920
        pts1 = np.float32([circles[0],circles[1],circles[2],circles[3]])
        width = np.sqrt((circles[1][0] - circles[0][0])**2 + (circles[1][1] - circles[0][1])**2)
        height =  np.sqrt((circles[2][1] - circles[0][1])**2 + (circles[2][0] - circles[0][0])**2)
        width = int(np.round(width))
        height = int(np.round(height))
        x1,y1 = circles[0]
    
        pts2 = np.float32([[x1,y1],[(x1+width),y1],[(x1+width),(y1+height)],[x1,(y1+height)]])
        matrix = cv2.getPerspectiveTransform(pts1,pts2)

        if q == 1:
            print(matrix.shape)
            print(matrix)
        imgOutput = cv2.warpPerspective(img,matrix,(width1,height1))
        cv2.imshow("Output Image ", imgOutput)


for x in range (0,4):
    cv2.circle(img,(circles[x][0],circles[x][1]),3,(0,255,0),cv2.FILLED)

cv2.imshow("Original Image ", img)
cv2.setMouseCallback("Original Image ", mousePoints)
cv2.waitKey(1)

基本的には、4 つのポイントをクリックすると、コードがワーピング マトリックスを見つけて、これらの 4 つのポイント内に囲まれた領域が長方形にマップされるようにします。つまり、指定した最初のポイントが同じピクセル位置にマップされ、他の4点は囲まれた部分が長方形になるように調整します。これを推定するために、同じ行列を画像全体に適用します。 点の集合(4つの点to マウスクリックで与えられます)私が試したのは次のとおりです: [[349, 445]、[396, 415]、[388, 596]、[338, 610]] 私が得た結果は次のとおりです。

1

コードを編集して質問し、試したポイントと得られた結果を示してください。

– UnaccountableMod にがっかり

2020 年 9 月 4 日 19:21

典型的には、異なる画像で一致するように設計されているため、シフト、サーフ、オーブなどの一致するキーポイントが指定されます。

– ミカ

2020 年 9 月 4 日 21:19

@barny 質問どおり、コード、使用したポイントのセット、得られた出力を追加しました。

– アダーシュ・スブラマニアン

2020 年 9 月 5 日 7:39

@Micka 申し訳ありませんが、何を明確に理解していませんあなたはそこを意味していました。もう少し詳しく教えていただけますか?

– アダーシュ・スブラマニアン

2020 年 9 月 5 日 7:39

コードのインデントを修正してください。現時点では実行されません。コードを自分で試したいとします。コードをファイルに貼り付けて実行できる必要があります。そのため、再現可能な最小限のサンプルが必要です。

– UnaccountableMod にがっかり

2020 年 9 月 5 日 7:50



------------------------

コードを動作させることができません。問題のコードが最小限の再現可能な例ではない場合、率直に言って非常に苦痛です。私は問題に興味があったので固執しただけです - あなたのコードがどのようになっているのか理解できません4 回のマウス クリックを収集してからパースペクティブを処理するはずですが、インデントがガベージになります。

実行のたびにポイントをクリックする必要がある場合の問題の 1 つは、比較可能な実行を取得するのが難しいことです。そのため、一度クリックを収集し、それらのクリックを使用して状況を確認しました。

私が遭遇した問題の 1 つは、一連のクリックです。私の知る限り、機能する変換を取得するための正しい順序は、左上、右上、左下、右下です。紛らわしいことに、質問に挙げた一連のポイントは間違っています。その順序で。正しいシーケンスに対応させるために、pts2 の最後の 2 つの値を交換する必要がありました。おそらく両方のスワップが相互に補い合っているのでしょう。機能しないコードを推測しようとして力尽きました。

ポイントは比較的近くにあります。ポイントがより広範囲に分散していると、より良い結果が得られると思います。 「より良い」ことに注意してください。 「完璧」ではありません。 - 「完璧な」ものはないと思います。あなたがやろうとしていることは、遠近法の歪みが非常に大きく、WarpPerspective が画像のさまざまな深度に対して魔法のように再投影できないためです。

クリックの位置が入力または出力で表示されないため、出力と入力を比較することも困難です。

もう 1 つよくわからないのは、私が作成した寺院のソース画像のサイズ変更です。幅1500×高さ1000の場合はownloaded。これは無視します。

ここまで述べてきましたが、あなたが得ているものは、あなたが得るべきものとほぼ同じだと思います。はい、非常に歪んでいますが、ワープ遠近法は単純な 2D 操作であり、レンズの歪みを考慮して再投影を行うとは主張しません。

これは、4 つの固定点を使用するコードの簡略化したバージョンです。出力画像に表示されるように、変換前にソース画像にそれらをオーバーレイしました。入力画像にグリッド線をオーバーレイして、何を確認することもできます。これらは出力画像のようになります。はい、出力には 4 つの緑色の点が含まれていますが、右側の 2 つは変換により非常に小さくなっています。たとえば、アーチの先端がほぼ揃っていることを確認できます。明らかにアーチ内の 3D コンテンツが揃っています。奇妙に見えますが、先ほども言ったように、魔法の杖はありません。

import cv2
import numpy as np

circles = np.zeros((4,2),np.int)

circles = [(349, 473), (903, 158),(336, 713), (918, 758) ]

img = cv2.imread("temple.JPG")

for x in range (0,4):
    cv2.circle(img,(circles[x][0],circles[x][1]),3,(0,255,0),cv2.FILLED)

cv2.imshow("Original Image ", img)

cv2.waitKey(-1)
    
height1,width1 = 1080,1920

pts1 = np.float32([circles[0],circles[1],circles[2],circles[3]])
width = np.sqrt((circles[1][0] - circles[0][0])**2 + (circles[1][1] - circles[0][1])**2)
height =  np.sqrt((circles[2][1] - circles[0][1])**2 + (circles[2][0] - circles[0][0])**2)
width = int(np.round(width))
height = int(np.round(height))
x1,y1 = circles[0]

print( f"{x1=} {y1=} {width=} {height=}")

# NOTE the third and fourth values are swapped from the original code
pts2 = np.float32([[x1,y1],[(x1+width),y1],[x1,(y1+height)],[(x1+width),(y1+height)]])
matrix = cv2.getPerspectiveTransform(pts1,pts2)

print(matrix.shape)
print(matrix)

imgOutput = cv2.warpPerspective(img,matrix,(width1,height1))
cv2.imshow("Output Image ", imgOutput)

cv2.waitKey(-1)

入力ポイントがオーバーレイされた入力画像:

出力画像の切り取られた部分:

4

コード内のインデントがめちゃくちゃだったことをお詫び申し上げます。私の注意を逸してしまいました。

– アダーシュ・スブラマニアン

2020 年 9 月 5 日 10:22

全体を通して使用した順序は、左上、右上、右下、左下です。それが混乱の原因だと思います。コードはエラーなく動作しますが、混乱していて、望ましい結果が得られる 4 つのポイントを明確に指定できませんでした。

– アダーシュ・スブラマニアン

2020 年 9 月 5 日 16:55

また、4 つの近いポイントではなく、より多くの広がりのあるポイントを与えるようアドバイスされました。どうしてこれなの?壁は真っ直ぐな剛体です。したがって、次の変換行列を取得すると、壁内の小さな部分を壁全体に適用すると、技術的には壁全体が正しく歪むはずですよね?

– アダーシュ・スブラマニアン

2020 年 9 月 5 日 16:57

1

ポイントがより分散されると、精度が向上します。コンセプトは、建物内の水平および垂直の直線上の点を特定することです。したがって、建物の線を使用して、修正したい面に垂直にまっすぐ見たときに、線がどのように長方形を形成するかを想像してみてください。透視変換ではすべての直線が維持されることに注意してください。 N通常、接地面は直線を形成します。したがって、建物と地面が交差する場所が良い場所になります。ただし、画像には広角レンズによる樽型の歪みがあります。そのため、地面は少し曲がっています。

– fmw42

2020 年 9 月 5 日 17:14

総合生活情報サイト - OKWAVES
総合生活情報サイト - OKWAVES
生活総合情報サイトokwaves(オールアバウト)。その道のプロ(専門家)が、日常生活をより豊かに快適にするノウハウから業界の最新動向、読み物コラムまで、多彩なコンテンツを発信。