Language - Python(Opencv)

Python Opencv - #31, 모멘트 객체 검출

KimTory 2021. 11. 23. 22:21

모멘트 기반 객체 검출

 모멘트(Moments)는 영상의 형태를 표현하는 일련의 실수 값입니다.

 모멘트 기반 객체 검출은 두 개의 외곽선, 그레이스케일 영상을 이용하여 모양을 비교하는 방법입니다.

 기본적으로 회전, 크기 변환, 대칭, 이동 강력하며 객체에 변형이 생긴 경우에는 성능이 떨어집니다.

 

 특정 함수 집합과의 상관 관계(correlation) 형태로 계산합니다.

 

 

 f(x,y)는 입력영상을 의미합니다.

 xp,yq는 이미 정의되어 있는 다항식 함수입니다.

 이 두개를 correlation 계산을 해서 실수값 m을 계산합니다.

 p와 q를 변경하면서 많개는 몇십개까지 추출하여 영상의 모양 정보를 표현하는 방법입니다.

 

 x와 y 함수를 어떻게 정의하냐에 따라 모멘트를 여러가지 형태로 정의할 수 있습니다.

 

 OpenCV에서는 Geomertic moments, Central moments, Normalized central moments 3가지 기능을 제공합니다.

 

 Geometric moments 는 위의 식 대로 계산한 것입니다.

 Central moments는 Geometric moments를 약간 변경해서 객체의 위치가 변경되어도 동일한 값의 특징 벡터를 추출하는 방법입니다.

 Normalized central moments는 Central moments를 정규화한것 입니다.

 

 객관적으로 보았을 때 이 3개의 성능이 좋은 편은 아닙니다.

 성능이 아주 좋진 않지만 나름대로 원하는 객체를 찾는데 사용할 수 있습니다.

 이외에도 Hu의 7개 불변 모멘트 기법도 있습니다.

 

Hu의 7개 불변 모멘트 - Hu's seven invariant moments

 Hu의 7개 불변 모멘트는 Normalized central moments의 수식을 조합해서 어떤 객체가크기 변경, 회전, 대칭, 이동하더라도 모멘트 값은 유지됩니다.

 이 방법을 이용하면 어떤 객체가 회전, 이동, 변경 되어도 모멘트는 변화가 없습니다.

 객체와 객체의 모양을 비교할 때 이 방법을 이용하면 원하는 객체를 찾을 수 있습니다.

 

1. 모양 비교 함수 - cv2.matchShapes

 내부적으로 휴의 7개 불변 모멘트를 계산해서 두 객체의 모양을 비교한 뒤에 distance 값을 반환합니다.

 휴의 7개 불변 모멘트를 이용하여 두 외곽선 또는 영상의 모양을 비교합니다.

 따라서 크기, 회전, 이동, 대칭 변환에 강합니다.

 

 하지만 투시변환이나 warp를 한 객체는 좋은 결과를 나타내지 않습니다.

 

cv2.matchShapes(contour1, contour2, method, parameter) -> retval

• contour1: 첫 번째 외곽선 또는 그레이스케일 영상 - findcontour로 검출한 외곽선 정보를 입력해도 됩니다.

• contour2: 두 번째 외곽선 또는 그레이스케일 영상 - findcontour로 검출한 외곽선 정보를 입력해도 됩니다.

• method: 비교 방법 지정. cv2.CONTOURS_MATCH_I1, cv2.CONTOURS_MATCH_I2, cv2.CONTOURS_MATCH_I3 중 하나 사용.

• parameter: 사용되지 않음. 0 지정.

• retval: 두 외곽선 또는 그레이스케일 영상 사이의 거리(distance)

method 인자

 

 

 3번째 방법이 정규화를 이용하므로 효과가 가장 좋다고 알려져 있습니다.

 3번을 주로 사용합니다.

 mi는 1부터 7까지의 휴 모멘트 값을 의미합니다.

 a,b는 a객체, b객체를 의미합니다.

 

 두번째 방법은 m1부터 m7까지의 차이값의 절대값을 반환하는 방법입니다.

 

retval

 반환 값은 어떤 두 객체가 비슷하다고 판단하면 작은 값, 다르다고 판단하면 큰 값을 반환합니다.

 

2. 모멘트 기반 객체 검출 예제 코드

 

 객체가 검은색, 배경이 흰색이라 findcontour을 이용할 때 반전해서 이용해야 합니다.
 symbols 에서 spades를 찾아내는 것이 목적입니다.

# 영상 불러오기
# 2장의 영상을 사용합니다.
# 객체가 검은색, 배경이 흰색이라 findcontour을 이용할 때 반전해서 이용해야 합니다.
# symbols 에서 spades를 찾아내는 것이 목적입니다.
obj = cv2.imread('spades.png', cv2.IMREAD_GRAYSCALE) # 찾고 싶은거
src = cv2.imread('symbols.png', cv2.IMREAD_GRAYSCALE) # 대상

if src is None or obj is None:
    print('Image load failed!')
    sys.exit()

# 객체 영상 외곽선 검출
# 이진화, 반전
_, obj_bin = cv2.threshold(obj, 128, 255, cv2.THRESH_BINARY_INV)

# 외곽선 검출, EXTERNAL은 바깥 윤곽선, APPROX_NONE는 모든 외곽선 좌표 구하기
obj_contours, _ = cv2.findContours(obj_bon, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

# 외곽선 정보를 obj_pts에 저장 (객체 1개)
obj_pts = obj_contours[0]

# 입력 영상 분석
# src 영상에서 객체가 검은색이므로 반전해서 이진화
_, src_bin = cv2.threshold(src, 128, 255, cv2.THRESH_BINARY_INV)

# 모든 정보를 저장
contours, _ = cv2.findContours(src_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

# 결과 영상
dst = cv2.cvtColor(src, cv2.COLOR_GRAY2BGR)

# 입력 영상의 모든 객체 영역에 대해서 contours에 있는 각각의 점들의 집합을 검출
for pts in coutours:
    if cv2.contourArea(pts) < 1000: # 영역 계산, 노이즈 제거
        continue
        
    # 외곽선 사각형 좌표 받아서 그리기
    rc = cv2.boundingRect(pts) # 외곽선 좌표 입력하면 사각형 좌표 반환
    cv2.rectangle(dst, rc, (255, 0, 0), 1)
    
    # 두 개의 외곽선 정보를 비교해서 모양을 비교하는 용도로 사용
    # 이미 변형이 생긴 모양을 비교할 때는 동작이 잘 안됌
    # 기본적으로 회전, 크기 변환, 대칭, 이동
    dist = cv2.matchSapes(obj_pts, pts, cv2.CONTOURS_MATCH_I3, 0)
    
    # distance 값을 화면에 출력
    # spades와 같은 모양은 0.1보다 작은값
    cv2.putText(dst, str(round(dist, 4)), (rc[0], re[1] - 3),
    			cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 0), 1, cv2.LINE_AA)
    
    # 0.1 보다 작은 건 빨간생으로 사각형
    if dist < 0.1:
        cv2.rectangle(dst, rc, (0, 0, 255), 2)

cv2.imshow('obj', obj)
cv2.imshow('dst', dst)
cv2.waitKey(0)