@23_사진에서 얼굴 찾아 모자이크 하기
- 드디어 OpenCV 를 써 본다.
- Python에서 비주얼 한 프로그램을 만드는 것.
- 뭐 별게 있겠냐마는 왠지 모르게 비주얼 프로그램에 대한 두려움(?) 같은게 있었다.
- 써 보니 쉽네. 예전에 맬랩(MatLab) 썼던 느낌이랄까?
1. 얼굴이 여러 개 나온 사진에서 얼굴과 눈을 찾아 표시하기
2. 얼굴을 모자이크 처리하기
1. 이미지에서 얼굴과 눈 찾기
import numpy as np
import cv2
# 얼굴 찾는 함수
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
# 눈 찾는 함수
eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_eye.xml')
# 한글이 있는 경로를 못 읽어와서 numpy 사용
ff = np.fromfile(r'기초프로그램\23_mosaic\sampleImg.jpg', np.uint8)
# np로 읽어온 파일에서 이미지를 변환하지 않고 가져오기
img = cv2.imdecode(ff, cv2.IMREAD_UNCHANGED)
# 리사이징인데, fx, fy (변환율 가로,세로)가 1.0 이므로 그대로 가져옴)
# 보간방법은 INTER_LINEAR(옆에 있는 픽셀값 그대로 가져오는 것)
img = cv2.resize(img, dsize=(0,0), fx=1.0, fy=1.0, interpolation=cv2.INTER_LINEAR)
# 회색조로 변환 - BGR은 Blue, Green, Red (RGB가 아니라 BGR)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 얼굴 여러개 찾기
faces = face_cascade.detectMultiScale(gray, 1.2,5)
# 찾은 얼굴의 범위가 나옴(좌상단 좌표와 크기(w, h))
for (x,y,w,h) in faces:
# 얼굴범위를 색상값 BGR(255, 0, 0) = Blue, 두께 2, 네모칸 그림
# cv는 왜 RGB가 아니라 BGR로 하는지 모르겠네요 ...
cv2.rectangle(img, (x,y), (x+w, y+h), (255,0,0), 2)
#roi : Region Of Interest (관심영역)
# 그레이 스케일로 바꾼 이미지의 관심영역 (얼굴 1개)
roi_gray = gray[y:y+h, x:x+w]
# 원래 이미지의 관심영역 (얼굴 1개)
roi_color = img[y:y+h, x:x+w]
# 그레이 이미지 관심영역(현재 선택된 얼굴) 에서 눈 찾기
eyes = eye_cascade.detectMultiScale(roi_gray)
for(ex, ey, ew, eh) in eyes:
# 찾은 위치 그래로 원래 이미지에 초록색 사각형으로 표시하기
cv2.rectangle(roi_color, (ex, ey), (ex+ew, ey+eh), (0,255,0), 2)
# 윈도우 창을 열어 이미지를 보여준다. 창 이름은 'face find'
cv2.imshow('face find', img)
# 키 입력을 기다렸다가
cv2.waitKey(0)
# 키 입력 받으면 모든 윈도우 닫기
cv2.destroyAllWindows()
결과.
2. 얼굴 하나하나 찾아서 모자이크 처리하기
- 모자이크 처리방법
1> 얼굴 이미지를 찾아서
2> 이미지를 우선 축소시킨다 - 이 때 이미 픽섹들이 날아간다.
3> 축소되었던 이미지를 원래 크기로 키운다 - 이 때 비어있는 픽셀들을 선택한 보간법에 따라 채운다.
import numpy as np
import cv2
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_eye.xml')
ff = np.fromfile(r'기초프로그램\23_mosaic\sampleImg.jpg', np.uint8)
img = cv2.imdecode(ff, cv2.IMREAD_UNCHANGED)
img = cv2.resize(img, dsize=(0,0), fx=1.0, fy=1.0, interpolation=cv2.INTER_LINEAR)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 얼굴 여러개 찾기
faces = face_cascade.detectMultiScale(gray, 1.2,5)
# 얼굴 축소 비율
fx = 0.05
fy = 0.05
for (x,y,w,h) in faces:
print(f'얼굴가로 : {w}px\t축소가로(w*fx 반올림) : {round(w*fx)}px\n얼굴세로 : {h}px\t축소세로(h*fy 반올림) : {round(h*fy)}px\n\n')
# 현재 선택된 얼굴
face_img = img[y:y+h, x:x+w]
# 현재 찾은 얼굴 보여주기
cv2.imshow('Original Face', face_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 얼굴 축소시켜 보여주기
face_img = cv2.resize(face_img, dsize=(0,0), fx=fx, fy=fy)
cv2.imshow('Reduced Face', face_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 원래 크기로 돌리면서 모자란 픽셀들은 이웃보간법(옆에 있는 색상으로 채우기)으로 채우기
face_img = cv2.resize(face_img, (w,h), interpolation = cv2.INTER_AREA)
cv2.imshow('Enlarged Face (Mosaic effect)', face_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 원래 이미지를 모자이크된 이미지로 덮어쓰기
img[y:y+h, x:x+w] = face_img
# 모자이크 처리된 전체 사진 표시
cv2.imshow('face find', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
결과.
- 가족은 4명인데, 목 부분이 두 개 잡혀서 모두 열굴이 6개라고 나옴
- 축소가로, 축소세로의 픽셀값이 확대된 이미지에서의 사각형 개수라고 보면 된다.
-- 왜냐하면 같은 색깔로 보간되었으므로
(원래 이미지에서 얼굴 찾음 : 264x264 픽셀)
(축소한 이미지 - 13x13 픽셀)
(INTER_AREA 보간법으로 다시 확대 - 264x264 픽셀)
(이때 네모로 보이는 칸의 개수를 세어 보면 가로 13개, 세로 13개임)
(축소한 이미지의 픽셀 13x13 을 그대로 확대했다고 보면 됨)
최종 결과.
예전에 이미지프로세싱 과목에서 MATLAB으로 여러가지 이미지변환을 했었던 기억이 있는데
코드에서 자세한 설명이 없는 부분에 대해
그 때 기억이 남아있어서 그런지 얼추 이해하고 찾아볼 수 있었다.
역시 공부는 해서 없어지는게 아니다.
머릿속 어딘가 꾸겨져 있을 뿐...
꺼내서 펴 봐야지 ㅎ
최근댓글