티스토리 뷰
* 이글은 다크프로그래머 님의 - [최소자승법의 이해] 를 읽고 필요한 부분을 정리 및 제가 아는 내용을 추가하였습니다.
http://darkpgmr.tistory.com/56
** Least Square Method, fit ellipse, fit circle, python opencv
가끔 영상을 보면서 원과 타원에 대해서 어떠한 정보를 얻어야 될 때가 있습니다.
외각선 정보보다, 몇개의 파라미터 값(중심, 사이즈 ...)만 알면 표현이 훨씬 간단할 뿐만 아니라 각 물체(원, 타원, 사각) 간의 관계 표현할 때 이만큼 좋은 정보가 없습니다.
그럼 세부적으로 중심, 지름, 장축, 단축 등 이런 정보들은 어떻게 얻을 수 있을 까요?
이럴 때 많이 사용하는 방식이 피팅(fitting)을 사용해서 원의 방정식 또는 타원의 방정식에 얻어진 외각선( x,y 위치 정보)에 데이터가 얼마나 잘 맞는지 넣어보면서 파라미터(장축,단축, 중심) 를 찾는 방향으로 알 수 있습니다.
영상내 어떤 둥그런 물체(object) 를 필터링 해서 크기가 얼마인지 알고 싶을 때,
제가 아끼는 마우스패드가 쇼파 위에 이쁘게 놓여있네요, 이럴 때 이 마우스 패드의 중심과 크기를 알려고 하면 간단히 opencv 의 fitEllipse 함수를 사용해도 큰 문제는 없지만, 기본 개념부터 구현해 보았습니다.
□원의 방정식
답을 찾아가는 방법으로 pesudo inverse 방식을 쓰려고 양함수( f(x,y) = z) 모델을 이용하여 수식을 정리하면 아래와 같이 a,b,c 라는 파라미터를 구할 수 있습니다.
$$ \begin{matrix} { (x-x_1)^2 } + { (y-y_1)^2 } &=& r^2 \\ \\ \Rightarrow x^2+y^2 -2ax - 2by + a^2 + b^2 - r^2 &=& 0 \\ \\ \Rightarrow x^2+y^2 -2ax - 2by + c = 0 &,& c = a^2 + b^2 - r^2 \\ \\ \Rightarrow -2ax - 2by + c &=& -x^2-y^2 \end{matrix} $$
$$ \begin{matrix} \begin{bmatrix} -2x_1& -2y_1& 1\\ \vdots& \vdots&\vdots \\ -2x_n& -2y_n& 1 \end{bmatrix} \begin{bmatrix} a \\ b \\ c \end{bmatrix} = \begin{bmatrix} -x_1^2-y_1^2 \\ \vdots \\ -x_n^2-y_n^2 \end{bmatrix} \\ \\ J =\begin{bmatrix} -2x_1& -2y_1& 1 \\ \vdots& \vdots&\vdots \\ -2x_n& -2y_n& 1 \end{bmatrix}, X = \begin{bmatrix} a \\ b \\ c \end{bmatrix}, Y = \begin{bmatrix} -x_1^2-y_1^2 \\ \vdots \\ -x_n^2-y_n^2 \end{bmatrix} \\ \\ J X = Y \\ X = {(J^T J)}^{-1} J^T Y \\ \\ \end{matrix} $$
□ 타원의 방정식
중심이 0,0 위치에서의 타원의 방정식
$$ \Large \begin{matrix}{ x^2 \over w^2 } + { y^2 \over h^2 } = 1 \end{matrix} $$
원은 회전된 경우(rotated) 문제 없지만, 타원의 경우는 중요하기 때문엥 이를 방정식에 반영 해주어야 합니다.
접근 방법을
1. roatated ellipse (회전이 되어 있는 타원)
2. rotated ellipse 의 회전 각도를 반대로 적용하여 타원의 방정식 폼에 맞게 수정
3. 타원의 중심이 이동 할 수 있음으로 타원의 방정식에 반영
CCW 로 회전이 되어 있다는 가정하에 복원을 위해 CW 방향으로 회전 변환을 적용하겠습니다.
$$ \begin{matrix} \begin{bmatrix} x' \\ y' \end{bmatrix} = \begin{bmatrix} cos(\theta) & sin(\theta) \\ -sin(\theta) & cos(\theta) \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} \\ \\ \Rightarrow x' = cos(\theta)x + sin(\theta)y \\ \Rightarrow y' = -sin(\theta)x + cos(\theta)y \end{matrix} $$
최종적으로 x1,y1 의 중심에 회전이 반영된 방정식입니다.
$$ { (x − x_1 )^2 \over a^2 } + { (y − y_1 )^2 \over b^2} = 1 \\ { [cos(\theta)x + sin(\theta)y − x_1 ]^2 \over a^2 } + { [−sin(\theta)x + cos(\theta)y −y_1]^2 \over b^2} = 1 $$
위 수식을 풀어서 계수를 정리하면 다음과 같습니다. 여기에 간단하게 회전각도에 대해서만 유도를 해보면
( 추가된 내용)
$$ x^2[b^2cos^2(\theta) + a^2sin^2(\theta)] + xy[2b^2sin(\theta)cos(\theta) − 2a^2sin(\theta)cos(\theta)] +y^2[b^2sin^2(\theta)+a^2cos^2(\theta)] +\cdots = a^2b^2 \\ \\ c_1 x^2 + c_2 xy +c_3 y^2 +c_4 x + c_5 y + c6 = 0 $$
$$ \begin{array}{l} { c2 \over (c1 − c3) } = { 2b^2sin\theta cos\theta − 2a^2sin\theta cos\theta \over b^2cos^2\theta + a^2sin^2\theta − b^2cos^2\theta − a^2sin^2\theta} = {2sin\theta cos\theta(b^2 − a^2) \over(cos^2\theta − sin^2\theta)(b^2 − a^2)} \\ = { 2sin\theta cos\theta \over cos^2\theta \; sin^2\theta} = { sin2\theta \over cos 2\theta } = tan2\theta \\ \therefore \theta = {1 \over 2} tan^{−1}({c2 \over {c1 − c3}}) \end{array}$$
아래 코드에 시간이 되시는 분들은 한번 생각해서 유도 해보시면 큰 도움이 되실 것 같아요.
$$ \begin{matrix} ax^2 + bxy + cy^2 + dx + ey + f &=& 0 \\ \\ \Rightarrow x^2 + b'xy + c'y^2 + d'x + e'y + f' &=& 0 \end{matrix} \tag{ Dividing by a} $$
$$\begin{matrix} \begin{bmatrix} x_1y_1& {y_1}^2 & x_1 & y_1 & 1 \\ \\ \vdots & \vdots & \vdots & \vdots & \vdots \\ x_ny_n& {y_n}^2 & x_n & y_n & 1 \end{bmatrix} \begin{bmatrix} b'\\ c'\\ d'\\ e'\\ f' \end{bmatrix} &=& \begin{bmatrix} - {x_1}^2\\ \vdots \\ -{x_n}^2 \end{bmatrix} \\ \end{matrix} \tag{ explicit function form, y=f(x,y)} $$
$$ \begin{matrix} J X &=& Y \\ X &=& {(J^T J)}^{-1} J^T Y \end{matrix} \tag{Pesudo inver }$$
$$ \Large \begin{matrix} \theta = { 1 \over 2 } \tan^{-1}( { b \over a-c }) \\ c_x = { 2cd - be \over b^2 - 4ac } \\ c_y = { 2ae- bd \over b^2 - 4ac } \\ w = \sqrt{ {ac_x^2 + bc_xc_y+ cc_y^2 -f \over a \cos^2\theta + b\cos\theta\sin\theta + c\sin^2\theta } } \\ h = \sqrt{ {ac_x^2 + bc_xc_y+ cc_y^2 -f \over a \sin^2\theta - b\cos\theta\sin\theta + c\cos^2\theta } } \end{matrix} $$
노랑색의 경우 원으로 피팅한 결과 입니다. 당연히 LSM(Least Square Method) 방식으로 맞추다 보니 전체의 모양이 반영이 안되었네요, 파랑색의 경우는 타원의 피팅한 결과 입니다. fitEllipse 함수와 거의 유사하게 나오는 것을 확인 하 실 수 있습니다.
#dependency opencv-python 3.2
#python version 3.6
import cv2
import os,re,sys
import numpy as np
def fit_circle(data):
#data [[x1,y],[x2,y2]...]
xs = data[:,0].reshape(-1,1)
ys = data[:,1].reshape(-1,1)
J = np.mat(np.hstack((-2 * xs,-2 * ys,np.ones_like(xs,dtype=np.float))))
Y = np.mat(-xs ** 2 - ys ** 2)
X = (J.T * J).I * J.T * Y
cx = X[0,0]
cy = X[1,0]
c = X[2,0]
r = np.sqrt(cx ** 2 + cy ** 2 - c)
return (cx,cy,r)
def fit_rotated_ellipse(data):
xs = data[:,0].reshape(-1,1)
ys = data[:,1].reshape(-1,1)
J = np.mat( np.hstack((xs*ys,ys**2,xs, ys, np.ones_like(xs,dtype=np.float))) )
Y = np.mat(-1*xs**2)
P= (J.T * J).I * J.T * Y
a = 1.0; b= P[0,0]; c= P[1,0]; d = P[2,0]; e= P[3,0]; f=P[4,0];
# To do implementation
#a,b,c,d,e,f 를 통해 theta, 중심(cx,cy) , 장축(major), 단축(minor) 등을 뽑아 낼 수 있어요
theta = 0.5* np.arctan(b/(a-c))
cx = (2*c*d - b*e)/(b**2-4*a*c)
cy = (2*a*e - b*d)/(b**2-4*a*c)
cu = a*cx**2 + b*cx*cy + c*cy**2 -f
w= np.sqrt(cu/(a*np.cos(theta)**2 + b* np.cos(theta)*np.sin(theta) + c*np.sin(theta)**2))
h= np.sqrt(cu/(a*np.sin(theta)**2 - b* np.cos(theta)*np.sin(theta) + c*np.cos(theta)**2))
return (cx,cy,w,h,theta)
def main(img_path):
color_list = [(238,0,0),(0,252,124),(142,56,142)]
src = cv2.imread(img_path, cv2.IMREAD_COLOR)
gray = cv2.cvtColor(src,cv2.COLOR_RGB2GRAY)
retv, th = cv2.threshold(gray,0,255,cv2.THRESH_BINARY + cv2.THRESH_OTSU)
_, contours , _ = cv2.findContours(th,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
for con in contours:
approx = cv2.approxPolyDP(con, 0.01 * cv2.arcLength(con,True),True)
area = cv2.contourArea(con)
if(len(approx) > 10 and area > 30000):
a,b,r = fit_circle(con.reshape(-1,2))
#cv2.drawContours(src,[con],0,color_list[2],2)
cv2.circle(src,(int(a),int(b)),int(r),color_list[1])
cx,cy,w,h,theta = fit_rotated_ellipse(con.reshape(-1,2))
cv2.ellipse(src,(int(cx),int(cy)),(int(w),int(h)),theta*180.0/np.pi,0.0,360.0,color_list[0],2)
cv2.imwrite('out.jpg',src)
if __name__ == '__main__':
if(len(sys.argv) != 2):
print('usage : {0} < image_abs_path >'.format(sys.argv[0]))
exit(0)
main(sys.argv[1])
'영상처리' 카테고리의 다른 글
Digital watermarking (네이버웹툰 캡처 추적) #1 (0) | 2021.09.11 |
---|---|
Minimum Volume Enclosing Ellipsoid (0) | 2020.10.23 |
[ Lagrange multiplier ] 두 타원 간의 최소거리 (0) | 2017.11.24 |
[ RANSAC ] 타원 피팅 (fit ellipse using ransac) (1) | 2017.08.03 |
- Total
- Today
- Yesterday
- keras
- 네이버웹툰
- DW
- backpropagation
- SvD
- Residual Block
- Digital watermarking
- 캡처방지
- flask serving
- tensorflow serving
- numpy
- dct
- DWT-DCT
- implementation
- gPRC
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |