以前の記事でハフ変換を使って画像から直線を求めました。
直線を出したら交点も求めたくなるのではないか、という気がするので今回はタイトル通りに2つの直線の交点を計算する方法についての備忘録です。
では始めます。
1:交点の計算方法
詳しい考え方は参考資料の以下のページ様に解説があるのでそちらを確認してください。
この方法をpythonで書くと以下のようになります。
# 線分ABと線分CDの交点を求める関数 def _calc_cross_point(pointA, pointB, pointC, pointD): cross_point = (0,0) bunbo = (pointB[0] - pointA[0]) * (pointD[1] - pointC[1]) - (pointB[1] - pointA[1]) * (pointD[0] - pointC[0]) # 直線が平行な場合 if (bunbo == 0): return False, cross_point vectorAC = ((pointC[0] - pointA[0]), (pointC[1] - pointA[1])) r = ((pointD[1] - pointC[1]) * vectorAC[0] - (pointD[0] - pointC[0]) * vectorAC[1]) / bunbo s = ((pointB[1] - pointA[1]) * vectorAC[0] - (pointB[0] - pointA[0]) * vectorAC[1]) / bunbo # rを使った計算の場合 distance = ((pointB[0] - pointA[0]) * r, (pointB[1] - pointA[1]) * r) cross_point = (int(pointA[0] + distance[0]), int(pointA[1] + distance[1])) # sを使った計算の場合 # distance = ((pointD[0] - pointC[0]) * s, (pointD[1] - pointC[1]) * s) # cross_point = (int(pointC[0] + distance[0]), int(pointC[1] + distance[1])) return True, cross_point
2:実際に交点を計算してみる
計算方法がわかったので前回作成したハフ変換と組み合わせて交点を計算してみます。
・houghLinesCross.py
import numpy as np import cv2 IMAGE_PATH = "./test.jpg" # 読み込む画像 def main(): image = cv2.imread(IMAGE_PATH) # 画像読み込み image2 = cv2.imread(IMAGE_PATH) # 画像読み込み gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) # グレースケール化 outLineImage = cv2.Canny(gray, 220, 250, apertureSize = 3) # 輪郭線抽出 cv2.imwrite("./outLine.png", outLineImage) # ファイル保存 houghList = hough_lines(image, outLineImage) # ハフ変換による直線抽出 cv2.imwrite("./result_hough.png", image) # ファイル保存 draw_cross_points(image, houghList) # 直線リストから交点を描画 cv2.imwrite("./result_hough_cross.png", image) # ファイル保存 houghPList = hough_lines_p(image2, outLineImage) # 確率的ハフ変換による直線抽出 cv2.imwrite("./result_houghP.png", image2) # ファイル保存 draw_cross_points(image2, houghPList) # 直線リストから交点を描画 cv2.imwrite("./result_houghP_cross.png", image2) # ファイル保存 # ハフ変換で直線を抽出する関数 def hough_lines(image, outLineImage): lineList = [] lines = cv2.HoughLines(outLineImage, rho=1, theta=np.pi/180, threshold=200) # ハフ変換で直線抽出 print("hough_lines: ", len(lines)) for line in lines: rho, theta = line[0] a = np.cos(theta) b = np.sin(theta) x0 = a*rho y0 = b*rho x1 = int(x0 + 1000*(-b)) y1 = int(y0 + 1000*(a)) x2 = int(x0 - 1000*(-b)) y2 = int(y0 - 1000*(a)) lineList.append((x1, y1, x2, y2)) cv2.line(image,(x1,y1),(x2,y2),(0,0,255),2) # 赤色で直線を引く return lineList # 確率的ハフ変換で直線を抽出する関数 def hough_lines_p(image, outLineImage): lineList = [] # 確率的ハフ変換で直線を抽出 lines = cv2.HoughLinesP(outLineImage, rho=1, theta=np.pi/180, threshold=200, minLineLength=100, maxLineGap=100) print("hough_lines_p: ", len(lines)) for line in lines: x1, y1, x2, y2 = line[0] lineList.append((x1, y1, x2, y2)) cv2.line(image,(x1,y1),(x2,y2),(0,255,0),2) # 緑色で直線を引く return lineList # 交点を描画する関数 def draw_cross_points(image, lineList): size = len(lineList) cnt = 0 for i in range(size-1): for j in range(i+1, size): pointA = (lineList[i][0], lineList[i][1]) pointB = (lineList[i][2], lineList[i][3]) pointC = (lineList[j][0], lineList[j][1]) pointD = (lineList[j][2], lineList[j][3]) ret, cross_point = calc_cross_point(pointA, pointB, pointC, pointD) # 交点を計算 if ret: # 交点が取得できた場合でも画像の範囲外のものは除外 if (cross_point[0] >= 0) and (cross_point[0] <= image.shape[1]) and (cross_point[1] >= 0) and (cross_point[1] <= image.shape[0]) : cv2.circle(image, (cross_point[0],cross_point[1]), 2, (255,0,0), 3) # 交点を青色で描画 cnt = cnt + 1 print("draw_cross_points:", cnt) # 線分ABと線分CDの交点を求める関数 def calc_cross_point(pointA, pointB, pointC, pointD): cross_points = (0,0) bunbo = (pointB[0] - pointA[0]) * (pointD[1] - pointC[1]) - (pointB[1] - pointA[1]) * (pointD[0] - pointC[0]) # 直線が平行な場合 if (bunbo == 0): return False, cross_points vectorAC = ((pointC[0] - pointA[0]), (pointC[1] - pointA[1])) r = ((pointD[1] - pointC[1]) * vectorAC[0] - (pointD[0] - pointC[0]) * vectorAC[1]) / bunbo s = ((pointB[1] - pointA[1]) * vectorAC[0] - (pointB[0] - pointA[0]) * vectorAC[1]) / bunbo # 線分AB、線分AC上に存在しない場合 if (r <= 0) or (1 <= r) or (s <= 0) or (1 <= s): return False, cross_points # rを使った計算の場合 distance = ((pointB[0] - pointA[0]) * r, (pointB[1] - pointA[1]) * r) cross_points = (int(pointA[0] + distance[0]), int(pointA[1] + distance[1])) # sを使った計算の場合 # distance = ((pointD[0] - pointC[0]) * s, (pointD[1] - pointC[1]) * s) # cross_points = (int(pointC[0] + distance[0]), int(pointC[1] + distance[1])) return True, cross_points if __name__ == '__main__': main()
上記を実行した結果は以下のようになります。
・元画像
・結果(左:ハフ変換 右:確率的ハフ変換)
交点に青色の点がプロットされているのがわかるかと思います。
以上が2つの直線の交点を計算する方法です。
カードの直線を検出してその角の座標を取得したい場合に使うといいかもしれません。
また今回作成したソースコードは以下に公開しておきます。
・参考資料