속도를 산출하는 과정에서 핀홀 카메라에서 기준선을 그리려 보니 원근에 의해 정확하게 차량의 이동 방향과 수평되는 선을 그리기가 어려워 bird-eye view를 통해 선을 그린 후 해당 선을 다시 원본 영상에 투영하려 했다

 

먼저 bird-eye view를 만들기 위해 opencv에서 제공해주는 getPerspectiveTransform을 이용하였다 

 

실제 적용은 도로에서 실행했지만 보기 좋은 예시 그림을 가지고 포스트를 올린다.

 

1. 원본사진에서 bird-eye view로 볼 부분의 4점을 선택한다. 

source_coord = np.float32( mouse_events.points )
H = max(mouse_events.points[2][0],mouse_events.points[3][0]) - min(mouse_events.points[0][0],mouse_events.points[1][0])
W = max(mouse_events.points[0][1], mouse_events.points[1][1], mouse_events.points[2][1], mouse_events.points[3][1] ) - min(mouse_events.points[0][1], mouse_events.points[1][1], mouse_events.points[2][1], mouse_events.points[3][1] )
destination_coord = np.float32( [ [0,0], [0,H], [W,0], [W,H] ] )
M = cv2.getPerspectiveTransform(source_coord, destination_coord)

2. opencv에서 제공해주는 cv2.getPerspectiveTransform과 cv2.warpPerspective를 이용하면 쉽게 계산결과를 볼 수 있다 

( getPerspectiveTransform에서는 순서대로 선정보를 입력해줘야한다 - 좌상, 좌하, 우상, 우하 )

M = cv2.getPerspectiveTransform(coord1, coord2) 

transformed = cv2.warpPerspective(image, M, (W, H))

transformed = cv2.warpPerspective(frame, M, (W,H))

계산결과가 잘 나왔다 

 

3. 계산 결과에 선을 그린다 

4. 이제 원본 이미지에 해당 선을 다시 그려야하는데 아무것도 모른상태로 함수를 이용하여 변환했기 때문에 역변환을 제공해주는 함수가 있는가 찾아봤지만 찾지 못했다 그래서 공식을 이용해서 역변환 하기로 하였다 

 

시점변환(perspective transform)의 공식은 다음과 같다 

여기서 우리가 알고 있는 것은 x', y', x, y 이다 x'과 y'은 변경시킬 좌표이고 x, y 는 원본에서의 좌표이다 

 

x',y'은 [0,0, 0,H, W,0, W,H] 가 되고 ( W : 넓이, H : 높이) 

x,y 는 마우스를 통해 찍은 점의 위치 대략적으로 [100,30 0,400 300,100 330,450] 이라고 하겠다 

 

getPerspectiveTransform을 이용하여 바꿀 위치들을 지정해주면(x', y') 위의 공식에서 h들에 대한 값이 return 된다  (이후부터 h들을 H라고 하겠다 )

그렇다면 [wx' wy' w] = H@[x y 1] 이 되고 우리가 구한 것은 w와 x' y'이 되었다 

이제 우리는 x' y' 이 있다면 역변환을 구할 수 있을것이다 

여기서 삽질을 정말 오래 했는데 w 를 보지 못하고 계속 [x' y' 1] 만을 이용하여 왜 생각대로 안되지 라고 한 3시간 찾아보았다 

어쨋든.... 

 

우리가 구할것은 이제 x' y' w 를 알고 있을 때 x y 를 구하는 것이다 

우리가 알고 있는 것은 x' y' w H 이니 이리저리 나누고 곱하고 해보자 

 

warpPerspective를 통해 나오는 값은 wx' wy' w 이다. 그러므로 원본 을 구하기 위해서는 w 를 모든 항에 나눠 x' y' 을 구해야 한다 그럼 끝나는 건데 w 를 생략해서 계속 고민하고 있었다니....

 

역행렬을 구할 때는 numpy 에 linalg.pinv 또는 linalg.inv를 이용하면 된다 pinv는 persuade inverse이다

첫번쨰 : 원본사진 두번째 : 변환 후 사진 세번째 : 역변환 사진

line_coord = np.float32(mouse_events2.points)
One = np.squeeze(INV_M @ np.expand_dims(np.append(line_coord[0], 1), axis=1))
Two = np.squeeze(INV_M @ np.expand_dims(np.append(line_coord[1], 1), axis=1))
One = One[:2] / One[2]
Two = Two[:2] / Two[2]

선이 잘 그려졌다! 

 

해당 코드는 깃헙에 업로드 되어있다. 

https://github.com/dldidfh/tistory_code/tree/master/%EC%9B%90%EA%B7%BC%ED%88%AC%EC%98%81

 

GitHub - dldidfh/tistory_code

Contribute to dldidfh/tistory_code development by creating an account on GitHub.

github.com

 

+ Recent posts