OpenCV - 이미지 Masking, RGB 색상 조작, Object Detection(Haar 방식)을 이용하 이미지/동영상 얼굴&눈 인식
Image Masking
-
원본 Image
-
Source
import cv2 import numpy as np img = cv2.imread('./pengsu.jpg') # 이미지 사이즈 살펴보기 print('width: ', img.shape[1]) # 640 print('height: ', img.shape[0]) # 405 print('channels: ', img.shape[2]) # 3 # 이미지 중심점 추출 (height, width) = img.shape[:2] center = (width // 2, height // 2) cv2.imshow('nomadProgrammer', img) # 이미지 사이즈(가로/세로)와 동일한 사이즈의 0으로 된 array 생성 mask = np.zeros(img.shape[:2], dtype="uint8") # 지난 포스팅에서 배운 cv2.circle을 이용해 mask의 중심점을 기준으로 크기 250, 색상(255,255,255), 전체(-1)을 생성 cv2.circle(mask, center, 250, (255, 255, 255), -1) cv2.imshow("mask", mask) # and비트연산(cv2.bitwise_and)을 이용해 img에 mask를 씌움 masked = cv2.bitwise_and(img, img, mask = mask) # bitwise (and, or, not, xor) cv2.imshow('nomadProgrammer with mask', masked) cv2.waitKey(0) cv2.destroyAllWindows()
-
결과 Image
Image RGB 다루기
-
RGB 색상별 분해(각 색상이 관여된 부분이 없다면 검정색으로 출력)
(Blue, Green, Red) = cv2.split(img) cv2.imshow('Red Channel ', Red) cv2.imshow('Green Channel ', Green) cv2.imshow('Blue Channel ', Blue) cv2.waitKey(0)
-
Red Channel
-
Green Channel
-
Blue Channel
-
-
원본 Image를 RGB 색상으로 나누기
zeros = np.zeros(img.shape[:2], dtype="uint8") cv2.imshow('Red', cv2.merge([zeros, zeros, Red])) cv2.imshow('Green', cv2.merge([zeros, Green, zeros])) cv2.imshow('Blue', cv2.merge([Blue, zeros, zeros])) cv2.waitKey(0) cv2.destroyAllWindows()
-
cv2.merge를 이용해 RGB 색상 값을 합쳐 원본처럼 만들기
BGR = cv2.merge([Blue, Green, Red]) cv2.imshow('blue, green adn red', BGR) cv2.waitKey(0) cv2.destroyAllWindows() # 결과는 원본과 동일하므로 생략
Face Detection
face detection의 방법은 두 가지로 나뉨
-
Object detection
-
deep neural network
1. Object detection
- cascade classifier : Haar 방식(cnn과는 다른 접근 방식)
- 장점 : 빠르고 가볍다
- 단점 : CNN보다 정확도가 떨어진다
-
공통점 : CNN과 같이 구역을 나누어 실행한다
-
축구선수 Image를 이용한 실습
-
원본 Image
import cv2 import numpy as np def detectAndDisplay(frame): # 정확도를 높이기 위해 그레이 스케일로 변환하여 detection 시작 frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # cv2.equalizeHist() : 히스토그램 평활화 # 픽셀값들의 누적분포함수를 이용해 어두운 부분을 밝게, 밝은 부분을 어둡게하여 선명한 이미지를 얻음 frame_gray = cv2.equalizeHist(frame_gray) # -- Detect Faces # detectMultiScale() : 크기가 다른 object를 검출하는 함수(하단의 xml 파일(트레이닝 데이터)을 불러와 cv2.CascadeClassifier()(다단계 분류)를 통해 어떤 object를 검출할 지 정함) # 해당 실습에서는 얼굴과 눈을 검출 # 얼굴 검출 faces = face_cascade.detectMultiScale(frame_gray) # (x값, y값) : 검출된 얼굴의 좌상단 위치 / w : 가로크기, h : 세로크기 for (x, y, w, h) in faces: center = (x + w//2, y + h//2) # 얼굴위치 사각형으로 표시 frame = cv2.rectangle(frame, (x,y), (x+w, y+h), (0, 255,0), 4) # 얼굴 위치 좌표 추출 faceROI = frame_gray[y:y+h, x:x+w] # 눈 검출 # -- In each face, detect eyes eyes = eyes_cascade.detectMultiScale(faceROI) for (x2, y2, w2, h2) in eyes: eye_center = (x + x2 + w2//2, y + y2 + h2//2) radius = int(round((w2 + h2)*0.25)) # 눈위치 원으로 표시 frame = cv2.circle(frame, eye_center, radius, (255,0.0), 4) cv2.imshow('capture - face detection', frame) img = cv2.imread('./soccer.jpg') # Haar cascade 트레이닝 데이터 face_cascade_name = './opencv_dnn/data/haarcascades_cuda/haarcascade_frontalface_alt.xml' eyes_cascade_name = './opencv_dnn/data/haarcascades_cuda/haarcascade_eye_tree_eyeglasses.xml' # 다단계 분류 face_cascade = cv2.CascadeClassifier() eyes_cascade = cv2.CascadeClassifier() # -- 1. Load the cascades if not face_cascade.load(cv2.samples.findFile(face_cascade_name)): print('--(!)Error loading face cascade') exit(0) if not eyes_cascade.load(cv2.samples.findFile(eyes_cascade_name)): print('--(!)Error loading eyes cascade') exit(0) detectAndDisplay(img) cv2.waitKey(0) cv2.destroyAllWindows()
-
결과 출력 : 얼굴은 다 잘 잡으나 눈은 전혀 잡지 못함
-
cv2.equalizeHist() 참고 Site
-
-
GUI를 이용한 Haar cascade 이미지의 얼굴/눈 검출
import cv2 import numpy as np # tkinter gui Modele from tkinter import * from tkinter import filedialog # Image handling Module # conda install -c conda-forge pillow / pip install Pillow # 이미지 포맷 변환, 크기 변형, 회줜, transform, filtering등의 프로세싱 작업 가능 from PIL import Image from PIL import ImageTk face_cascade_name = './opencv_dnn/data/haarcascades_cuda/haarcascade_frontalface_alt.xml' eyes_cascade_name = './opencv_dnn/data/haarcascades_cuda/haarcascade_eye_tree_eyeglasses.xml' face_cascade = cv2.CascadeClassifier() eyes_cascade = cv2.CascadeClassifier() file_name = './soccer.jpg' title_name = 'Haar cascade object detection' frame_width = 500 # 사이즈를 크게하면 인식하지 못하던 것을 인식하는 경우도 있음(우선 500 기준으로 정함)
- tkinter 강의 Site
# 파일 선택 함수 정의 def selectFile(): # 파일 경로 선택 함수(filedialog.askopenfilename()) # initaldir : 기본 경로 / filetypes : 읽어 올 파일 type 정의 file_name = filedialog.askopenfilename(initialdir = "./image",title = "Select file",filetypes = (("jpeg files","*.jpg"),("all files","*.*"))) # Image read read_image = cv2.imread(file_name) (height, width) = read_image.shape[:2] # sizeSpin : 하단의 Spinbox 참조 frameSize = int(sizeSpin.get()) ratio = frameSize / width dimension = (frameSize, int(height * ratio)) read_image = cv2.resize(read_image, dimension, interpolation = cv2.INTER_AREA) image = cv2.cvtColor(read_image, cv2.COLOR_BGR2RGB) image = Image.fromarray(image) imgtk = ImageTk.PhotoImage(image=image) detectAndDisplay(read_image) # 재귀함수 방식 호출
- tkinter filedialog.askopenfilename()
def detectAndDisplay(frame): # 그레이 스케일로 변환 frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) frame_gray = cv2.equalizeHist(frame_gray) # -- Detect Faces faces = face_cascade.detectMultiScale(frame_gray) for (x, y, w, h) in faces: center = (x + w//2, y + h//2) frame = cv2.rectangle(frame, (x,y), (x+w, y+h), (0, 255,0), 4) faceROI = frame_gray[y:y+h, x:x+w] # -- In each face, detect eyes eyes = eyes_cascade.detectMultiScale(faceROI) for (x2, y2, w2, h2) in eyes: eye_center = (x + x2 + w2//2, y + y2 + h2//2) radius = int(round((w2 + h2)*0.25)) frame = cv2.circle(frame, eye_center, radius, (255,0.0), 4) # cv2.imshow('capture - face detection', frame) # tkinter의 색상체계는 opencv와 달리 RGB 패턴(opencv는 BGR 패턴)이므로 변환 image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # numpy배열을 image 객체로 변환 image = Image.fromarray(image) # Tk inter 위에 그림을 보여주기 위해 tkinter와 호환되는 객체로 변환 imgtk = ImageTk.PhotoImage(image=image) # 이미지 갱신을 위해 image 매개변수 설정 detection.config(image=imgtk) # 가비지 컬렉터(garbage collector)가 이미지를 삭제하지 않도록 image 매개변수에 imgtk를 한번 더 등록 detection.image = imgtk
- tkinter opencv 적용하기 참고 Site
# main main = Tk() main.title(title_name) main.geometry() read_image = cv2.imread('./soccer.jpg') (height, width) = read_image.shape[:2] ratio = frame_width / width dimension = (frame_width, int(height * ratio)) # 이전 포스팅에서 배운 cv2.resize() 함수를 이용해 Image 크기 조절 read_image = cv2.resize(read_image, dimension, interpolation=cv2.INTER_AREA) # -- 1. Load the cascades if not face_cascade.load(cv2.samples.findFile(face_cascade_name)): print('--(!)Error loading face cascade') exit(0) if not eyes_cascade.load(cv2.samples.findFile(eyes_cascade_name)): print('--(!)Error loading eyes cascade') exit(0) # Label : 이미지나 도표, 그림 등에 사용되는 주석문 생성 label=Label(main, text=title_name) label.config(font=("Courier", 18)) # grid : 셀 단위 배치 # (row/column:해당 구역으로 위젯 이동 / rowspan/columspan:현재 배치된 구역에서 위치 조정 # / sticky:현재 배치된 구역 안에서 특정 위치로 이동) label.grid(row=0,column=0,columnspan=4) sizeLabel=Label(main, text='Frame Width : ') sizeLabel.grid(row=1,column=0) sizeVal = IntVar(value=frame_width) # Spinbox() : 수치를 조정하고 입력받는 '수치 조정 기입창' # textvariable : 라벨에 표시할 문자열을 가져올 변수 # from_ : 기입창의 최소값 / to : 기입창의 최대값 / increment : 기입창의 수치 간격 # justify : 수치 조정 기입창의 문자열이 여러 줄 일 경우 정렬 방법 sizeSpin = Spinbox(main, textvariable=sizeVal,from_=0, to=2000, increment=100, justify=RIGHT) sizeSpin.grid(row=1, column=1) # Button : 메소드 또는 함수 등을 실행시키기 위한 단추 Button(main,text="File Select", height=2,command=lambda:selectFile()).grid(row=1, column=2, columnspan=2, sticky=(W, E)) detection=Label(main, image=imgtk) detection.grid(row=2,column=0,columnspan=4) detectAndDisplay(read_image) # mainloop() : 사용자의 입력을 계속 기다리게 하는 목적으로 프로그램 흐름 마지막단에 위치 main.mainloop()
-
동영상 Object detection
- 이미지와 크게 다르진 않으나 동영상이기에
cv2.VideoCapture()
로 비디오를 stream한다.
import cv2 import numpy as np face_cascade_name = '../opencv_dnn/data/haarcascades/haarcascade_frontalface_alt.xml' eyes_cascade_name = '../opencv_dnn/data/haarcascades/haarcascade_eye_tree_eyeglasses.xml' file_name = 'video/india.mp4' def detectAndDisplay(frame): frame = cv2.resize(frame, dsize=(1280, 960), interpolation=cv2.INTER_AREA) frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) frame_gray = cv2.equalizeHist(frame_gray) #-- Detect faces faces = face_cascade.detectMultiScale(frame_gray) for (x,y,w,h) in faces: center = (x + w//2, y + h//2) frame = cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 4) faceROI = frame_gray[y:y+h,x:x+w] #-- In each face, detect eyes eyes = eyes_cascade.detectMultiScale(faceROI) for (x2,y2,w2,h2) in eyes: eye_center = (x + x2 + w2//2, y + y2 + h2//2) radius = int(round((w2 + h2)*0.25)) frame = cv2.circle(frame, eye_center, radius, (255, 0, 0 ), 4) cv2.imshow('Capture - Face detection', frame) face_cascade = cv2.CascadeClassifier() eyes_cascade = cv2.CascadeClassifier() #-- 1. Load the cascades if not face_cascade.load(cv2.samples.findFile(face_cascade_name)): print('--(!)Error loading face cascade') exit(0) if not eyes_cascade.load(cv2.samples.findFile(eyes_cascade_name)): print('--(!)Error loading eyes cascade') exit(0) #-- 2. Read the video stream cap = cv2.VideoCapture(file_name) if not cap.isOpened: print('--(!)Error opening video capture') exit(0) while True: ret, frame = cap.read() if frame is None: print('--(!) No captured frame -- Break!') break detectAndDisplay(frame) # Hit 'q' on the keyboard to quit! if cv2.waitKey(1) & 0xFF == ord('q'): break cv2.destroyAllWindows()
- 얼굴을 잘 못잡는 경우가 많다…
- 이미지와 크게 다르진 않으나 동영상이기에
결론 : Haar 방식은 이미지의 해상도와 크기가 detection에 영향을 줌
March 23, 2020 에 작성
Tags:
python