소스 뷰어
멀티 하네스의 전처리¶
- 멀티 하네스 (multi harness)를 찍은 영상을 입력 받아서 멀티 하네스 객체의 기울어진 각도를 인식하고 영상에서 객체가 가지런히 놓일 수 있도록 회전 보정을 한다. 여기서 멀티 하네스는 전자 제품 내부에서 다중의 전선들을 한 번에 연결하는 케이블 커넥터이다
import numpy as np, math , cv2
# 수행시간 체크 함수
stime = 0
def ck_time(mode = 0 , msg = ""):
global stime
if (mode ==0 ):
stime = time.perf_counter()
elif (mode==1):
etime = time.perf_counter()
elapsed = (etime - stime)
print("수행시간 = %.5f sec" % elapsed) # 초 단위 경과 시간
elif (mode == 2):
etime = time.perf_counter()
return (etime - stime)
elif (mode== 3 ):
etime = time.perf_counter()
elapsed = (etime - stime)
print("%s = %.5f sec" %(msg, elapsed)) # 초 단위 경과 시간
직선 누적 행렬 구성¶
def accumulate(image, rho, theta):
h, w = image.shape[:2]
rows, cols = (h + w) * 2 // rho, int( np.pi/theta) # 누적행렬 너비, 높이
accumulate = np.zeros((rows, cols), np.int32) # 직선 누적행렬
sin_cos = [(np.sin(t * theta), np.cos(t * theta)) for t in range(cols)] # 삼각 함수값 미리 저장
# pts = [(y, x) for x in range(w) for y in range(h) if image[y, x] > 0 ]
pts = np.where(image > 0)
polars = np.dot(sin_cos, pts).T # 행렬곱으로 허프 변환 수식 계산
polars = (polars/ rho + rows / 2) # 해상도 변경 및 위치 조정
for row in polars.astype(int):
for t, r in enumerate(row):
accumulate[r, t] += 1 # 좌표 누적
return accumulate
허프 누적 행렬의 지역 최대값 선정¶
def masking(accumulate, h, w, thresh):
rows, cols = accumulate.shape[:2]
dst = np.zeros(accumulate.shape, np.uint32)
for y in range(0, rows, h): # 누적행렬 조회
for x in range(0, cols, w):
roi = accumulate[y:y+h, x:x+w]
_ , max, _, (x0, y0) = cv2.minMaxLoc(roi)
dst[y+y0, x+x0] = max
return dst
임계값 이상인 누적값(직선) 선별¶
def select_lines(acc_dst, rho, theta, thresh):
rows = acc_dst.shape[0]
r, t = np.where(acc_dst>thresh) # 임계값 이상인 좌표(세로, 가로)
rhos = ((r - (rows / 2)) * rho) # rho 계산
radians = t * theta # theta 계산
value = acc_dst[r,t] # 임계값 이상인 누적값
idx = np.argsort(value)[::-1] # 누적값 기준 세로 방향 내림차순 정렬
lines = np.transpose([rhos, radians]) # ndarray 객체 생성후 전치
lines = lines[idx, :] # 누적값에 다른 정렬
return np.expand_dims(lines, axis=1)
허프 변환을 이용한 직선 검출¶
def houghLines(src, rho, theta, thresh):
acc_mat = accumulate(src, rho, theta) # 허프 누적 행렬 계산
acc_dst = masking(acc_mat, 7, 3, thresh) # 마스킹 처리 7행,3열
lines = select_lines(acc_dst, rho, theta, thresh) # 직선 가져오기
return lines
def draw_houghLines(src, lines, nline):
dst = cv2.cvtColor(src, cv2.COLOR_GRAY2BGR) # 컬러 영상 변환
min_length = min(len(lines), nline)
for i in range(min_length):
rho, radian = lines[i, 0, 0:2] # 수직거리 , 각도 - 3차원 행렬임
a, b = math.cos(radian), math.sin(radian)
pt = (a * rho, b * rho) # 검출 직선상의 한 좌표 계산
delta = (-1000 * b, 1000 * a) # 직선상의 이동 위치
pt1 = np.add(pt, delta).astype('int')
pt2 = np.subtract(pt, delta).astype('int')
cv2.line(dst, tuple(pt1), tuple(pt2), (0, 255, 0), 2, cv2.LINE_AA)
return dst
def detect_maxObject(img):
# 외곽선 검출 - Opnecv 4.0부터 반환값은 2개 원소 갖는 튜플
results = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if int(cv2.__version__[0]) >= 4: # Opnecv 4.0은 2원소 튜플 반환
contours = results[0]
else:
contours = results[1] # OpenCV 3.x은 3원소 튜플 반환
areas = [cv2.contourArea(c) for c in contours]
idx = np.argsort(areas)
max_rect = contours[idx[-1]]
rect = cv2.boundingRect(max_rect) # 외곽선을 모두 포함하는 사각형 반환
rect = np.add(rect, (-10, -10, 20, 20)) # 검출 객체 사각형 크기 확대
return rect
image = cv2.imread('img/harness.jpg', cv2.IMREAD_COLOR)
if image is None: raise Exception("영상 파일 읽기 에러")
rho, theta = 1, np.pi / 180 # 허프변환 거리간격, 각도간격
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 명암도 영상 변환
_, th_gray = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY) # 이진 영상 변환
kernel = np.ones((3, 3), np.uint8)
morph = cv2.erode(th_gray, kernel, iterations=2) # 침식 연산 - 2번 반복
x, y, w, h = detect_maxObject(np.copy(morph)) # 가장 큰 객체 검출
roi = th_gray[y:y+h, x:x+w]
canny = cv2.Canny(roi, 40, 100) # 캐니 에지 검출
lines = houghLines(canny, rho, theta, 50) # 허프 직선 검출
# lines = cv2.HoughLines(canny, rho, theta, 50) # OpenCV 함수
cv2.rectangle(morph, (x, y, w, h), 100, 2) # 큰 객체 사각형 표시
canny_line = draw_houghLines(canny, lines, 1) # 직선 표시
angle = (np.pi - lines[0, 0, 1]) * 180 / np.pi # 회전 각도 계산
h, w = image.shape[:2]
center = (w//2, h//2) # 입력 영상의 중심점
rot_map = cv2.getRotationMatrix2D(center, -angle, 1) # 반대방향 회전 행렬 계산
dst = cv2.warpAffine(image, rot_map, (w, h), cv2.INTER_LINEAR) # 역회전 수행
cv2.imshow("image", image)
cv2.imshow("morph", morph)
cv2.imshow("line_detect", canny_line)
cv2.resizeWindow("line_detect", 250, canny_line.shape[0])
cv2.imshow("dst", dst)
cv2.waitKey(0)