Computer vision 7 (모폴로지 연산과 객체 검출)
모폴로지 연산과 객체 검출
1. 모폴로지 연산
침식과 팽창
import cv2
import numpy as np
src = cv2.imread('./data/milkdrop.bmp', cv2.IMREAD_GRAYSCALE)
thresh, src_bin = cv2.threshold(src, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
print(thresh)
erode = cv2.erode(src_bin, None)
dilate = cv2.dilate(src_bin, None)
cv2.imshow('src', src)
cv2.imshow('src_bin', src_bin)
cv2.imshow('erode', erode)
cv2.imshow('dilate', dilate)
cv2.waitKey()
cv2.destroyAllWindows()
154.0
열기와 닫기
src = cv2.imread('./data/milkdrop.bmp', cv2.IMREAD_GRAYSCALE)
thresh, src_bin = cv2.threshold(src, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
print(thresh)
# cv2.morphologyEx(원본영상, 연산방법, 구조물, 반복횟수)
# 열기 : 침식 -> 팽창
opening = cv2.morphologyEx(src_bin, cv2.MORPH_OPEN, None, iterations=1)
# 닫기 : 팽창 -> 침식
closing = cv2.morphologyEx(src_bin, cv2.MORPH_CLOSE, None, iterations=1)
cv2.imshow('src', src)
cv2.imshow('src_bin', src_bin)
cv2.imshow('opening', opening)
cv2.imshow('closing', closing)
cv2.waitKey()
cv2.destroyAllWindows()
154.0
2. 레이블링과 외곽선 검출
레이블링 기본
src = np.array([[0, 0, 1, 1, 0, 0, 0, 0],
[1, 1, 1, 1, 0, 0, 1, 0],
[1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 1, 0],
[0, 0, 0, 1, 1, 1, 1, 0],
[0, 0, 0, 1, 0, 0, 1, 0],
[0, 0, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0]]).astype(np.uint8)
src = src * 255
cnt, labels = cv2.connectedComponents(src)
cnt # 4개의 그룹(3개 객체 + 1개 배경)
4
labels
array([[0, 0, 1, 1, 0, 0, 0, 0],
[1, 1, 1, 1, 0, 0, 2, 0],
[1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 3, 3, 0],
[0, 0, 0, 3, 3, 3, 3, 0],
[0, 0, 0, 3, 0, 0, 3, 0],
[0, 0, 3, 3, 3, 3, 3, 0],
[0, 0, 0, 0, 0, 0, 0, 0]], dtype=int32)
src = cv2.imread('./data/circles.jpg', cv2.IMREAD_GRAYSCALE)
thresh, src_bin = cv2.threshold(src, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
# todo 객체별로 색깔 다르게 해서 보여주기
cnt, labels = cv2.connectedComponents(src_bin)
dst = cv2.cvtColor(src, cv2.COLOR_GRAY2BGR) # 객체별로 색깔을 다르게 표시할 3차원 도화지 준비
dst[labels==0] = (0, 255, 255) # 배경을 노란색을 설정
dst[labels==1] = (0, 0, 255) # 1번객체를 빨간색으로 설정
dst[labels==2] = (255, 0, 255) # 2번객체를 보라색으로 설정
dst[labels==3] = (0, 255, 0) #3번 객체를 초록색으로 설정
cv2.imshow('src', src)
cv2.imshow('src_bin', src_bin)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
레이블링 응용
src = cv2.imread('./data/circles.jpg', cv2.IMREAD_GRAYSCALE)
thresh, src_bin = cv2.threshold(src, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
cnt, labels, stats, centroids = cv2.connectedComponentsWithStats(src_bin)
dst = cv2.cvtColor(src, cv2.COLOR_GRAY2BGR) # 객체별로 색깔을 다르게 표시할 3차원 도화지 준비
for i in range(1, cnt): # 객체 1, 2, 3 (배경 제외)
b = np.random.randint(0, 255)
g = np.random.randint(0, 255)
r = np.random.randint(0, 255)
dst[labels==i] = (b, g, r)
# boungding box (from stats) --> rectangle 그리기
x, y, width, height, area = stats[i]
cv2.rectangle(dst, (x, y), (x+width, y+height), (0, 0, 255))
# center (from centroids) --> circle 그리기
cx, cy = centroids[i]
cv2.circle(dst, (int(cx), int(cy)), 5, (255, 0, 0), -1)
cv2.imshow('src', src)
cv2.imshow('src_bin', src_bin)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
src = cv2.imread('./data/keyboard.bmp')
dst = src.copy()
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
thresh, src_bin = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
cnt, labels, stats, centroids = cv2.connectedComponentsWithStats(src_bin)
for i in range(1, cnt): # 객체 1~37 (배경 제외)
# boungding box (from stats) --> rectangle 그리기
x, y, width, height, area = stats[i]
if area > 20:
cv2.rectangle(dst, (x, y), (x+width, y+height), (0, 255, 255))
cv2.imshow('src', src)
cv2.imshow('src_bin', src_bin)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
외곽선 검출과 그리기
src = cv2.imread('./data/contours.bmp')
dst = src.copy()
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
# thresh, src_bin = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(gray, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
for i in range(len(contours)): # 9 iterations
b = np.random.randint(0, 255)
g = np.random.randint(0, 255)
r = np.random.randint(0, 255)
cv2.drawContours(dst, contours, i, (b, g, r), 2)
cv2.imshow('src', src)
cv2.imshow('gray', gray)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
실습 thumbs_up_down.jpg의 손 외곽선 그리기
src = cv2.imread('./data/thumbs_up_down.jpg')
dst = src.copy()
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
thresh, gray_bin = cv2.threshold(gray, 220, 255, cv2.THRESH_BINARY_INV)
print(thresh)
contours, hierarchy = cv2.findContours(gray_bin, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
cv2.drawContours(dst, contours, -1, (0, 0, 255), 3, cv2.LINE_AA)
cv2.imshow('src', src)
cv2.imshow('gray', gray)
cv2.imshow('gray_bin', gray_bin)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
220.0
# 오른손 왼손을 둘러싸는 Bounding Box 그리기
right_hand = contours[0] # 오른손을 둘러싸는 점의 좌표들
left_hand = contours[1] # 왼손을 둘러싸는 점의 좌표들
x1, y1, width1, height1 = cv2.boundingRect(right_hand)
x2, y2, width2, height2 = cv2.boundingRect(left_hand)
dst = src.copy()
cv2.rectangle(dst, (x1, y1), (x1+width1, y1+height1), (0, 255, 255), 3)
cv2.rectangle(dst, (x2, y2), (x2+width2, y2+height2), (0, 0, 255), 3)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
3.객체 검출
캐스케이드 분류기와 얼굴 검출
- https://docs.opencv.org/4.6.0/db/d28/tutorial_cascade_classifier.html
- https://towardsdatascience.com/viola-jones-algorithm-and-haar-cascade-classifier-ee3bfb19f7d8
얼굴 검출
image = cv2.imread('./data/lena.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
face_cascade = cv2.CascadeClassifier('./data/haarcascade_frontalface_default.xml')
faces = face_cascade.detectMultiScale(gray)
for face in faces:
x, y, w, h = face
cv2.rectangle(image, (x, y), (x+w, y+h), (0, 0, 255), 2)
cv2.imshow('image', image)
cv2.waitKey()
cv2.destroyAllWindows()
눈 검출
image = cv2.imread('./data/kids2.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
face_cascade = cv2.CascadeClassifier('./data/haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier('./data/haarcascade_eye.xml')
faces = face_cascade.detectMultiScale(gray)
for face in faces:
x, y, w, h = face
cv2.rectangle(image, (x, y), (x+w, y+h), (0, 0, 255), 2)
face_rect = image[y:y+h, x:x+w]
eyes = eye_cascade.detectMultiScale(face_rect)
for eye in eyes:
x2, y2, w2, h2 = eye
cv2.rectangle(face_rect, (x2, y2), (x2+w2, y2+h2), (255, 0, 0), 2)
cv2.imshow('image', image)
cv2.waitKey()
cv2.destroyAllWindows()
# detecMultiScale() 의 중요 파라미터들
# scaleFactor : 검색 윈도우 확대 비율(default = 1.1)
# minNeighbors : 검출 영역으로 선택하기 위한 최소 검출 횟수 (default=3)
# minSize : 검출할 객체의 최소 크기 (예, (100, 100))
# maxSize : 검출할 객체의 최대 크기
# scaleFactor가 detect시 미치는 영향
# https://answers.opencv.org/question/10654/how-does-the-parameter-scalefactor-in-detectmultiscale-affect-face-detection/
실습 : multi face 검출
image = cv2.imread('./data/multi_faces.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
face_cascade = cv2.CascadeClassifier('./data/haarcascade_frontalface_default.xml')
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.3, minNeighbors=8)
#faces = face_cascade.detectMultiScale(gray, minSize=(90, 90), maxSize=(100, 100))
#faces = face_cascade.detectMultiScale(gray, minNeighbors=15)
for face in faces:
x, y, w, h = face
cv2.rectangle(image, (x, y), (x+w, y+h), (0, 0, 255), 2)
cv2.imshow('image', image)
cv2.waitKey()
cv2.destroyAllWindows()
실습 : Camera Device로부터 얻은 frame에 대해 얼굴(눈) 검출하기
import sys
face_cascade = cv2.CascadeClassifier('./data/haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier('./data/haarcascade_eye.xml')
cap = cv2.VideoCapture(0) # 0: 메인 카메라
if not cap.isOpened():
print("camera open failed")
sys.exit()
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
fps = cap.get(cv2.CAP_PROP_FPS) # frame per second
delay = round(1000/30)
print(width, height, fps, delay)
while True:
ret, frame = cap.read() # frame : 이미지 한장 (shape : height x width x channel)
if not ret:
print("frame read error")
break
# detect face and eyes
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, minNeighbors=10)
for face in faces:
x1, y1, w1, h1 = face
cv2.rectangle(frame, (x1, y1), (x1+w1, y1+h1), (0, 0, 255), 2)
face_rect = frame[y1:y1+h1, x1:x1+w1]
eyes = eye_cascade.detectMultiScale(face_rect, minNeighbors=10)
for eye in eyes:
x2, y2, w2, h2 = eye
center = int(x2+w2/2), int(y2+h2/2)
cv2.circle(face_rect, center, int(w2/2), (255, 0, 0), 2)
cv2.imshow('camera', frame) # 재생
key = cv2.waitKey(delay) # delay(ms) 기다리기(sleep 효과)
if key == 27: # 27 : Esc key, 종료조건
break
if cap.isOpened(): # True
print('cap release!!')
cap.release()
cv2.destroyAllWindows()
640.0 480.0 30.0 33
cap release!!
실습 : Blurring을 이용하여 얼굴만 가리기
import sys
face_cascade = cv2.CascadeClassifier('./data/haarcascade_frontalface_default.xml')
cap = cv2.VideoCapture(0) # 0: 메인 카메라
if not cap.isOpened():
print("camera open failed")
sys.exit()
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
fps = cap.get(cv2.CAP_PROP_FPS) # frame per second
delay = round(1000/30)
print(width, height, fps, delay)
while True:
ret, frame = cap.read() # frame : 이미지 한장 (shape : height x width x channel)
if not ret:
print("frame read error")
break
# detect face and eyes
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, minNeighbors=10)
for face in faces:
x1, y1, w1, h1 = face
cv2.rectangle(frame, (x1, y1), (x1+w1, y1+h1), (0, 0, 255), 2)
face_rect = frame[y1:y1+h1, x1:x1+w1]
kernel_size = 50
frame[y1:y1+h1, x1:x1+w1] = cv2.blur(face_rect, (kernel_size, kernel_size))
cv2.imshow('camera', frame) # 재생
key = cv2.waitKey(delay) # delay(ms) 기다리기(sleep 효과)
if key == 27: # 27 : Esc key, 종료조건
break
if cap.isOpened(): # True
print('cap release!!')
cap.release()
cv2.destroyAllWindows()
640.0 480.0 30.0 33
cap release!!
참고 numpy stack
- https://numpy.org/devdocs/reference/generated/numpy.dstack.html
np.dstack()
a = np.array([[1],[2],[3]])
b = np.array([[2],[3],[4]])
c = np.array([[3],[4],[5]])
print(a.shape, b.shape, c.shape)
d = np.dstack((a,b,c))
print(d.shape)
(3, 1) (3, 1) (3, 1)
(3, 1, 3)
np.hstack()
a = np.array([[1, 1],[2, 2],[3, 3]])
b = np.array([[4, 4],[5, 5],[6, 5]])
print(a.shape, b.shape)
c = np.hstack((a,b))
print(c.shape)
(3, 2) (3, 2)
(3, 4)
np.vstack()
a = np.array([[1], [2], [3]])
b = np.array([[4], [5], [6]])
print(a.shape, b.shape)
c = np.vstack((a,b))
print(c.shape)
(3, 1) (3, 1)
(6, 1)
댓글남기기