728x90
반응형
SMALL
1. 레이블링(labeling)
- 이진화, 모폴로지를 수행하면 객체와 배경 영역을 구분할 수 있게됨
- 객체 단위 분석을 통해 각 객체를 분할하여 특징을 분석하고 객체의 위치, 크기 정보, 모양 분석, ROI 추출 등이 가능함
- 서로 연결되어 있는 객체 픽셀에 고유번호를 할당하여 영역 기반 모양 분석, 레이블맵, 바운딩 박스, 픽셀 개수, 무게중심, 좌표 등을 반환할 수 있게 함
- cv2.connectedComponents(영상, 레이블 맵)
- 레이블 맵: 픽셀 연결 관계(4방향 연결, 8방향 연결..)
- return: 객체 개수, 레이블 맵 행렬
- cv2.connectedComponentsWithStats(영상, 레이블맵)
- return: 객체 개수, 레이블 맵 행렬, (객체 위치, 가로세로길이, 면적 등 행렬, 무게중심 정보)
import cv2
img = cv2.imread('keyboard.bmp', cv2.IMREAD_GRAYSCALE)
# 이진화 - 글씨가 또렷하게 됨
_, img_bin = cv2.threshold(img, 0, 255, cv2.THRESH_OTSU) # 자동 이진화
dst = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
# 레이블링
cnt, labels, stats, centroids = cv2.connectedComponentsWithStats(img_bin)
print(cnt)
print(labels)
print(stats)
print(centroids)
for i in range(1, cnt):
(x, y, w, h, area) = stats[i]
if area < 20: # 노이즈의 면적이 20px미만으로 작다고 생각
continue
cv2.rectangle(dst, (x, y, w, h), (0, 255, 255))
cv2.imshow('img', img)
cv2.imshow('img_bin', img_bin)
cv2.imshow('dst', dst)
cv2.waitKey()
2. 객체의 외곽선 검출
- 레이블링과 함께 영상에서 객체의 정보를 검출하는 방법 중 하나
- 이진화된 영상에서 검출되며 배경 영역과 닿아 있는 픽셀을 찾아 외곽선으로 인식
- 외곽선은 객체 외부뿐 아니라 내부에도 생길 수 있음
2-1. 외곽선 검출하기
- 외곽선이 각각 인덱스값을 가짐
- cv2.findContours(영상, 검출모드, 외곽선 좌표 근사화 방법) -> 리턴 외곽선 좌표 정보
- 검출모드
- RETR_EXTERNAL: 객체 외부 외곽선만 검출
- RETR_LIST: 객체 외부, 내부 외곽선 모두 검출
- RETR_CCOMP: 모든 외곽선 검출, 2단계 계층 구조를 구성
- RETR_TREE: 모든 외곽선 검출, 전체 계층 구조 구성
- 외곽선 좌표 근사화 방법
- CHAIN_APPROX_NONE: 모든 외곽선 좌표를 저장
- CHAIN_APPROX_SIMPLE: 외곽선 중에서 수평, 수직, 대각선 성분은 끝 점만 저장
2-2. 외곽선 그리기
- cv2.drawContours(영상, 외곽선 좌표 정보, 외곽선 인덱스, 색상, 두께)
- 외곽선 인덱스: -1을 지정하면 모든 외곽선을 그림
import cv2
import random
img = cv2.imread('contours.bmp', cv2.IMREAD_GRAYSCALE)
contours, _ = cv2.findContours(img, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
dst = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) # 랜덤한 색상을 뽑아주기 위해
cv2.drawContours(dst, contours, -1, color, 2) # -1: 모든 외곽선 검출(내부, 외부)
cv2.imshow('img', img)
cv2.imshow('dst', dst)
cv2.waitKey()
✅ 외곽선만 따로 추출하기
import cv2
import random
import numpy as np
img = cv2.imread('milkdrop.bmp', cv2.IMREAD_GRAYSCALE)
_, img_bin = cv2.threshold(img, 0, 255, cv2.THRESH_OTSU)
contours, _ = cv2.findContours(img_bin, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
# dst = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
h, w = img.shape[:2]
dst = np.zeros((h, w, 3), np.uint8)
for i in range(len(contours)):
color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) # 랜덤한 색상을 뽑아주기 위해
cv2.drawContours(dst, contours, i, color, 2) # i 개수만큼 그림
cv2.imshow('img', img)
cv2.imshow('img_bin', img_bin)
cv2.imshow('dst', dst)
cv2.waitKey()
2-3. 외곽선 길이 구하기
- cv2.arclength(외곽선 좌표, 폐곡선 여부)
2-4. 면적 구하기
- cv2.contourArea(외곽선 좌표, False)
2-5. 바운딩 박스 구하기
- cv2.boundingRect(외곽선 좌표)
2-6. 외곽선 근사화
- 검출한 윤곽선 정보를 분석하여 정점수가 적은 윤곽선 또는 다각형으로 표현할 수 있게 만드는 것, 다각형의 좌표를 반환
- cv2.approxPolyDP(외곽선 좌표, 근사화 정밀도 조절, 폐곡선 여부)
- 근사화 정밀도 조절: 입력 컨투어와 근사화된 컨투어 사이의 최대 거리. 값이 작을수록 다각형이 정확해지고, 꼭지점의 수가 늘어남
- cv2.isContourConvex()
- contour에 오목한 부분이 있는지 체크(있으면 True, 없으면 False를 반환, 하나라도 볼록한 부분이 있으면 False로 나옴)
- cv2.convexHull()
- contour에 있는 오목한 부분을 제거
✅ 아래 그림을 사각형, 삼각형, 원형, 알 수 없음으로 나누는 프로그램을 작성하여보자.
import cv2
import math
def setLabel(img, pts, label):
(x, y, w, h) = cv2.boundingRect(pts) # 좌표들을 통해 사각형을 입힘
pt1 = (x, y)
pt2 = (x+w, y+h)
cv2.rectangle(img, pt1, pt2, (125,125,30),2)
# cv2.drawContours(img, contours, -1, 120, 2)
# img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
cv2.putText(img, label, pt1, cv2.FONT_HERSHEY_PLAIN, 1, (0, 0, 255))
img = cv2.imread('polygon.bmp')
gray = cv2.cvtColor(img, cv2.COLOR_BGRA2GRAY)
# cv2.THRESH_BINARY_INV: 외부 외곽선1개만 잡히는걸 반대로 안에 도형만 검출하게 하기 위해
_, img_bin = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
contours, _ = cv2.findContours(img_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# print(contours) # 좌표정보
for pts in contours:
# 면적을 비교해서 잡음(노이즈)는 객체로 잡지 않게 하기 위해
if cv2.contourArea(pts) < 400:
continue
# 외곽선 길이를 구한 값에 0.02를 곱해 값을 줄임(근사화 정밀도를 낮춤(다각형의 꼭짓점수를 찾기위해 조절함))
approx = cv2.approxPolyDP(pts, cv2.arcLength(pts, True) * 0.02, True)
# print(approx)
num_pts = len(approx)
# print(num_pts)
if num_pts == 3:
setLabel(img, pts, 'Triangle')
elif num_pts == 4:
setLabel(img, pts, 'Rectangle')
else:
length = cv2.arcLength(pts, True)
area = cv2.contourArea(pts)
# 둥굴기 구하기 공식
# 4.: 상수는 아무값이나 줘도 됨. 0 ~ 1사이의 값으로 임계값을 계산할때는 4를 곱해주는 것이 좋음
ratio = 4. * math.pi * area / (length * length)
if ratio > 0.85: # 1에 가까울수록 원형, 0.8이상정도 되면 원이라고 봄
setLabel(img, pts, 'Circle')
elif ratio < 0.7:
setLabel(img, pts, 'NoName')
else:
setLabel(img, pts, 'Ellipse')
cv2.imshow('img', img)
cv2.imshow('img_bin', img_bin)
cv2.imshow('gray', gray)
cv2.waitKey()
✅ 외곽선 근사화하기(볼록, 오목한 부분)
import cv2
img = cv2.imread('hat.png')
cpy = img.copy()
gray = cv2.cvtColor(cpy, cv2.COLOR_BGRA2GRAY)
_, thr = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)
contour, _ = cv2.findContours(thr, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# print(contour[1])
cnt = contour[0]
cv2.drawContours(img, [cnt], -1, (255, 0, 0), 2)
check = cv2.isContourConvex(cnt)
print(check) # False(하나라도 볼록한 부분이 있으면 False)
>>> False
if not check:
hull = cv2.convexHull(cnt)
cv2.drawContours(cpy, [hull], -1, (0, 255, 0), 2)
cv2.imshow('hull', cpy)
cv2.imshow('contour', img)
# cv2.imshow('img', img)
cv2.waitKey()
728x90
반응형
LIST
'Python > Computer Vision' 카테고리의 다른 글
[파이썬, Python] Computer Vision - 분류(Classification), CNN과 CNN의 변천 (0) | 2023.09.04 |
---|---|
[파이썬, Python] 컴퓨터 비전(Computer Vision)의 데이터셋 (0) | 2023.09.04 |
[파이썬, Python] OpenCV - 모폴로지 처리, 모폴로지 연산 (0) | 2023.09.02 |
[파이썬, Python] OpenCV - 영상의 필터링, 블러링, 엣지(Edge)검출 (1) | 2023.09.01 |
[파이썬, Python] OpenCV - 영상의 변환 (0) | 2023.08.29 |