티스토리 뷰
또 등장하는 제 마우스 패드와 선물 받은 큐브를 통해서
가려진 타원을 어떻게 복원할지에 대해서 설명을 드리고자 합니다.
일반적으로 circle 과 기존의 ellipse fitting 으로는 아래 마우스 패드의 외각선을 추출해 진행해보면 이상한 결과를 얻을 수 있습니다. 물론,, 아래 마우스 패드의 외각선 정보에 이상한 그림이 겹쳐져 잘못된 정보를 포함해 버렸기 때문이죠 ^^
* fit ellipse using RANSAC (random sample consensu)
RANSAC(random sample consensu) 이란 ?
그림의 마우스 패드를 검출 하려고 보면 외각선 정보에 큐브 외각선이 겹쳐 나타나게 됩니다. 어떻게 하면 이 잘못된 정보 (outlier) 를 제외하고 피팅을 진행 할 수 있을까요? RANSAC은 데이터 집합 중에 일정확률을 가지고 샘플을 추려서 모델에 맞춰보고 해당 샘플 중에 가장 데이터 집합을 많이 대변하는 모델을 추려내는 작업이라고 생각할 수 있습니다.
* 말로 설명하려니 어렵네요. 아래 한번 RANSAC 에 대한 접근 방법을 보시면 바로 이해하실 수 있습니다.
모델 : 타원(rotated ellipse)
데이터 : 마우스 패드 외각선(Inlier) 와 큐브 외각선(outlier)이 포함된 외각선 집합
1. 일정 확률을 가지고 DATA 집합에서 임의 추출 진행
2. 추출진 sample 을 통해서 모델을 예측
3. 예측 모델에서 어느정도의 offset 범위 안에 들어오는 기존보다 클 경우, 해당 (x,y) 값들을 저장
4. 1 - 3 을 N 번 반복 수행
5. 최종 저장된 (X,Y) 값을 통해서 fitting 진행
import cv2
import os,re,sys
import numpy as np
def fit_rotated_ellipse_ransac(data,iter=30,sample_num=10,offset=80.0):
count_max = 0
effective_sample = None
for i in range(iter):
sample = np.random.choice(len(data), sample_num, replace=False)
xs = data[sample][:,0].reshape(-1,1)
ys = data[sample][:,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
# fitter a*x**2 + b*x*y + c*y**2 + d*x + e*y + f = 0
a = 1.0; b= P[0,0]; c= P[1,0]; d = P[2,0]; e= P[3,0]; f=P[4,0];
ellipse_model = lambda x,y : a*x**2 + b*x*y + c*y**2 + d*x + e*y + f
# threshold
ran_sample = np.array([[x,y] for (x,y) in data if np.abs(ellipse_model(x,y)) < offset ])
if(len(ran_sample) > count_max):
count_max = len(ran_sample)
effective_sample = ran_sample
return fit_rotated_ellipse(effective_sample)
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];
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))
ellipse_model = lambda x,y : a*x**2 + b*x*y + c*y**2 + d*x + e*y + f
error_sum = np.sum([ellipse_model(x,y) for x,y in data])
print('fitting error = %.3f' % (error_sum))
return (cx,cy,w,h,theta)
def main(img_path):
color_list = [(238,0,0),(0,252,124),(142,56,142),(10,20,0),(245,245,245)]
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)
color_th = cv2.cvtColor(th,cv2.COLOR_GRAY2RGB)
_, contours , _ = cv2.findContours(th,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
zeros = np.zeros(np.shape(th),dtype=np.uint8)
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):
# fit ellipse
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[2],2)
# fit ellipse using ransac
cx,cy,w,h,theta = fit_rotated_ellipse_ransac(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 |
[ 최소자승법 ] 원, 타원 측정 (7) | 2017.07.23 |
- Total
- Today
- Yesterday
- gPRC
- flask serving
- dct
- DW
- 네이버웹툰
- tensorflow serving
- Digital watermarking
- SvD
- Residual Block
- 캡처방지
- DWT-DCT
- backpropagation
- keras
- numpy
- implementation
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |