소스 뷰어

멀티 하네스의 전처리

  • 멀티 하네스 (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)
-1

result