학습한 개념
-
cv2.imshow()
는 커널이 죽는다.
: https://stackoverflow.com/questions/43943333/cv2-imshow-crashes-kernel
: 찾아보니 로컬에서 실행해서 이미지를 보이려면 뭔가 또 해야한단다.
-
OpenCV의 cv2.imread()
는 이미지 색을 RGB가 아닌 BGR로 가져옴
: 이거 순서 cv2.cvtColor()
로 바꿔줘야 편함
: 혹은 B,G,R을 cv2.split()
으로 분리 및 할당 후 cv2.merge()
로 합쳐서 다시 할당해도 됨
-
obeject detection과 스티커 붙이기의 큰 그림
- bounding box 추출
- face landmark localization(bounding box를 이용함)
- landmark의 인덱스를 알아내서, 알고 싶은 부분의 인덱스를 활용해 좌표를 구한다
- 거기로부터 이미지를 붙일 좌표를 다시 구한다
- 좌표는 bounding box 대비 비율로 해야 다른 사진을 읽어와도 쓸 수 있다.
- 왜냐하면 사진마다 얼굴 크기(즉 카메라로부터의 거리 등)가 다를 것이니
- 즉, 스티커를 갖다붙일 때 절대값스럽게(예: x 픽셀 위) 하면 안 됨.
- 좌표 및 합성할 이미지 사이즈를 통해 그 부분만 슬라이싱 해오고
- np.where()조건식을 통해 갖다 붙일 부분만 붙이고, 나머지는 원본을 참조하게끔 한다.
-
HOG 알고리즘 face detector
-
이미지 피라미드
-
landmark 모델
-
zip()
은 인자값끼리 개수가 달라도 되나보다
t1 = [1,2,3]
t2 = ['a', 'b', 'c', 'd']
for num, let in zip(t1, t2):
print(num, let)
>>>
1 a
2 b
3 c
OpenCV()
는 crop 기능이 없단다.
: 그래서 그냥 ndarray 슬라이싱 기능으로 가져오기...
미니프로젝트
오늘의 목표 : 사진 속 얼굴을 찾고, 얼굴의 랜드마크를 이용해 적절한 위치에 콧수염 이미지를 합성한다.
- 이미지는 우리 첫째 아들 이미지를 썼다.
- 콧수염 이미지는 아이펠에서 제공받은 이미지이다.
- png 파일이고 콧수염 부분만 검정색, 나머지는 흰 바탕이다.
- 사이즈가 정방형이어서 콧수염 크기만큼 나중에 slicing으로 crop했다.
- object keypoint estimation(landmark 찾기)은 Dlib에서 제공하는 ibug 300-W 데이터셋을 pre-trained한 모델을 사용했다.(모델은 wget을 통해 다운로드 받았다)
- 그리고 문제 없이 이미지들을 로드하고, 예상한 곳에 콧수염 이미지를 넣어봤는데...
헤맨 점
-
dlib.get_frontal_face_detector(img file, 이미지 피라미드 upsamping 수)
- 여기서 upsampling 수를 늘려봤다. 1일 때는 좀 넓었고, 2일 때는 bounding box가 작아졌다.(얼굴이 살짝 기울어져 있었는데 밑의 턱라인이 살짝 잘렸다.)
: 다 담는 쪽으로 해야지 나중에 return으로 나온 좌표값을 잘 활용할 수 있지 않을까 싶다.
-
cv2.addWeighted(img1, w1, img2, w2, b)
함수 써서 블렌딩하기
- 블렌딩 방법론 정하기
1) 스티커 사진을 원본 이미지로 늘리기
- 보니까 사진 2개의 사이즈가 같아야 한다.
- 스티커 사진을 키우고, 여백을 백색으로 넣어볼까?
- 근데 다른 방법이 있단다.(그래서 실행 X)
2) 스티커 사진 사이즈에 맞게 원본 잘라오기
- 그리고 두 개를 blending 후 다시 원본에 덮어씌우려 했다(slicing으로)
- 근데 블렌딩이 잘 안된다.
- 내가 원한 건
cv2.addWeighted(img1, w1, img2, w2, b)
에서 w1
은 1
로 해서 원본은 전혀 변화가 없고, w2
는 img2
투명해지기 위해 0.7
정도로 되는 것인데,
- 그러니 사진이 새하얘진다.(gamma=b가 없었음)
3) gamma
값으로 새하얀 거 빼보기(-255
대입)
- 살짝 감이 잡혀
-255
에 영향을 안 받도록 img1
의 가중치를 2
로 해보자'라는 식으로
cv2.addWeighted(img1, 2, img2, 0.5, -255)
를 했더니
- 좀 괜찮다.
- 근데 살짝 보니 원본 사진의 색감이 달라졌다.(*2배 한 게 아닌 것 같다.)
4) img1 미리 영향 갈 수만큼 더해보자
- 결국 수식이라는 감이 잡혔다.
cv2.addWeighted(img1 + (255/10*6), 1, img2, 0.6, -(255/10*6))
로 했는데
- 아예 오류가 나버린다.(
Bad arguments
란다.)
- 찾아보니 이미지 값은 0~255만 가질 수 있다고 하는데, 아마 저런 수식이 어떤 곳에서 음수 혹은 255 이상의 양수가 나와서 그런 것 같다.
5) 일단 마무리
- 시간이 다 되어 일단 마무리를 하기 위해 다른 사람들이 한 아래의 코드로 했다
cv2.addWeighted(img1, 0.5, img2, 0.5, 0)
- 이 후에 이 부분을 원본 이미지에 slicing으로 덮어씌웠는데(
np.where(스티커이미지==255, 원본 슬라이싱 부분, 스티커이미지)
) 사용
- 사실 이 부분도 slicing된 부분의 원본 이미지 투명도가 낮아지기 때문에 궁극적으로 내가 원한 코드는 아닌 것 같다.
6) 이후에 고민해본 것들
- 찾아 보니 음수, 255 이상의 양수가 되는 값들은 다른 곳에 저장되게 할 수 있단다.
- 그렇게 저장하고 나중에 빼주거나 더해주면 어떨까...?
- 근데 이것도 결국 cv2 함수 내부에서는 해주는 게 불가능할 것 같다.(예를 들어 img2 의 하얀 배경을 지워주기 위해 일정 값을 (-)해야 하는데, 이러면 img2 내부의 작은 수가 있는 픽셀은 어쩔 수 없이 음수로 되어버린다. 그러면 다시 또 오류가 날 것이다)
- 그래서 차라리 이럴바엔 그냥
cv2
함수 밖에서 numpy array
로 값을 자체적으로 계산하는 게 더 낫겠다 싶었다...
더 공부할 것
- object detection 기술...
- HOG(Histogram of Oriented Gradients)의 원리
- SVM의 원리
- feature descriptor의 개념...
- 이거 좀 중요한 것 같다...