Python Opencv - #28, 레이블링
▶ 객체 단위 분석
객체 단위 분석은 객체를 분할하여 특징을 분석하는 것을 의미합니다.
영상이 입력되었을 때 객체와 배경이 분리될 수 있다고 가정하며 이진화를 통해 객체와 배경을 분리합니다.
각각의 객체의 모양과 크기를 분석해서 내가 원하는 객체가 어디에 이쓰지 확인하고 싶을 때 객체단위 분석이
필요합니다. 객체 위치 및 크기 정보, ROI 추출, 모양 분석 등을 할 수 있습니다.
객체 단위 분석 방법은 레이블링과 외곽선 검출이 있습니다.
→ 레이블링 - Labeling
레이블링은 객체 구역을 영역 단위로 분석하는 것입니다.
서로 연결되어 있는 객체 픽셀에 고유한 번호를 지정하는 작업입니다.(레이블맵)
일반적으로 이진 영상에서 수행합니다.
레이블링 속도가 외곽선 검출보다 빨라서 더 효율적입니다.
레이블링을 하기 위해서는 연결성을 정의해야 합니다.
(1) 4-이웃 연결 관계(4-neighbor connectivity)
4-이웃 연결 관계는 어떤 객체가 연결되어 있을 때 두 개 픽셀이 상하좌우 관계로 연결되었을 때를 의미합니다.
4-이웃 연결 관계
(2) 8-이웃 연결 관계(8-neighbor connectivity)
8-이웃 연결 관계는 상하좌우에 대각선도 포함하여 연결되어 있을 때를 의미합니다.
OpenCV는 보통 8-이웃 연결 관계를 사용합니다.
8-이웃 연결 관계
→ 레이블링 알고리즘의 입력과 출력
0은 배경 1이상은 객체로 판단합니다.
객체들이 서로 연결되어 있으면 같은 번호를 지정합니다.
같은 객체에 번호가 지정된 것을 레이블 맵이라고 합니다.
레이블 맵은 정수형 행렬입니다.
→ 레이블링 함수 - cv2.connectedComponents
레이블링 함수를 이용하여 레이블맵을 생성할 수 있습니다.
cv2.connectedComponents(image, labels=None, connectivity=None, ltype=None) -> retval, labels
• image: 8비트 1채널 영상
• labels: 레이블 맵 행렬. 입력 영상과 같은 크기. numpy.ndarray.
• connectivity: 4 또는 8. 기본값은 8.
• ltype: labels 타입. cv2.CV_32S 또는 cv2.CV_16S. 기본값은 cv2.CV_32S.
• retval: 객체 개수. N을 반환하면 [0, N-1]의 레이블이 존재 하며, 0은 배경을 의미. (실제 흰색 객체 개수는 N-1개)
출력값이 두개 있습니다.
retval은 객체 갯수 + 1 (배경 포함)을 반환하고
labels는 레이블맵 행렬을 반환합니다.
→ 객체 정보를 함께 반환하는 레이블링 함수 - cv2.connectedComponentsWithStats
이 함수를 많이 이용합니다.
객체의 크기와 중심위치도 함께 반환합니다.
cv2.connectedComponentsWithStats(image, labels=None, stats=None, centroids=None, connectivity=None, ltype=None) -> retval, labels, stats, centroids
• image: 8비트 1채널 영상
• labels: 레이블 맵 행렬. 입력 영상과 같은 크기. numpy.ndarray.
• stats: 각 객체의 바운딩 박스, 픽셀 개수 정보를 담은 행렬. numpy.ndarray. shape=(N, 5), dtype=numpy.int32.
• centroids: 각 객체의 무게 중심 위치 정보를 담은 행렬 numpy.ndarray. shape=(N, 2), dtype=numpy.float64.
• ltype: labels 행렬 타입. cv2.CV_32S 또는 cv2.CV_16S. 기본값은 cv2.CV_32S
반환값으로 retval, labels, stats, centroids 를 반환합니다.
retval : 객체 수 + 1 (배경 포함)
labels : 객체에 번호가 지정된 레이블 맵
stats : N행 5열, N은 객체 수 + 1이며 각각의 행은 번호가 지정된 객체를 의미, 5열에는 x, y, width, height, area 순으로 정보가 담겨 있습니다. x,y 는 좌측 상단 좌표를 의미하며 area는 면적, 픽셀의 수를 의미합니다.
centroids : N행 2열, 2열에는 x,y 무게 중심 좌표가 입력되어 있습니다. 무게 중심 좌표는 픽셀의 x 좌표를 다 더해서 갯수로 나눈 값입니다. y좌표도 동일합니다.
→ 키보드 영상에서 문자 영역 분할 예제
src = cv2.imread('keyboard.bmp', cv2.IMREAD_GRAYSCALE)
if src is None:
print('Image load failed!')
sys.exit()
_, src_bin = cv2.threshold(src, 0, 255, cv2.THRESH_OTSU)
# count, labels number, label 상태, 검출 label 무게 중심(xy 좌표)
cnt, labels, stats, centroids = cv2.connectedComponentsWithStats(src_bin)
# labeling을 하기 위해 gray scale 에서 color scale로 변환해야 됨
dst = cv2.cvtColor(src, cv2.COLOR_GRAY2BGR)
# 범위를 1부터 시작한 이유는 배경을 제외
for i in range(1, cnt): # 각각의 객체 정보에 들어가기 위해 반복문.
(x, y, w, h, area) = stats[i]
# 노이즈 제거
if area < 20:
continue
cv2.rectangle(dst, (x, y, w, h), (0, 255, 255))
cv2.imshow('src', src)
cv2.imshow('src_bin', src_bin)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()