[E-03] camera_stickerapp_project


項目:猫ひげシールを作る



step1. シールの取得、作成


step2. 顔の検出&ランドマークの検出


dlibを利用して顔を囲むボックスの位置とランドマークの位置を探します.
# import module
import os
import cv2
import matplotlib.pyplot as plt
import numpy as np
import dlib
#얼굴 불러오기
my_image_path = os.getenv('HOME')+'/aiffel/camera_sticker/images/project_image.png' #원하는 이미지 경로를 지정한다
img_bgr = cv2.imread(my_image_path)    # OpenCV로 이미지를 불러온다(절대/상대 경로 모두 가능)

img_show = img_bgr.copy()      # 출력용 이미지를 따로 보관합니다

plt.imshow(img_bgr)
plt.show()
''' # RGB로 변경해보기
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) 
plt.imshow(img_rgb)
plt.show()'''
' # RGB로 변경해보기\nimg_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) \nplt.imshow(img_rgb)\nplt.show()'

外枠の検索

# detector, 얼굴찾기

detector_hog = dlib.get_frontal_face_detector()

# 변환
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
dlib_rects = detector_hog(img_rgb, 1)
print(dlib_rects)  # 사각형 좌표값
rectangles[[(194, 297) (656, 759)]]
for dlib_rect in dlib_rects:
    l = dlib_rect.left()
    t = dlib_rect.top()
    r = dlib_rect.right()
    b = dlib_rect.bottom()
    
    cv2.rectangle(img_show, (l,t), (r,b), (0,255,0), thickness=5, lineType=cv2.LINE_AA)

img_show_rgb = cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB)
plt.imshow(img_show_rgb)
plt.show()

ランドマークの場所を見つけます。

model_path = os.getenv('HOME')+'/aiffel/camera_sticker/models/shape_predictor_68_face_landmarks.dat'
landmark_predictor = dlib.shape_predictor(model_path)
print("done")
done
list_landmarks = []

# 얼굴 영역 박스 마다 face landmark를 찾아낸다
for dlib_rect in dlib_rects:
    points = landmark_predictor(img_rgb, dlib_rect) # rgb image, rectangle
    # 68개의 face landmark 좌표를 저장한다.
    
    list_points = list(map(lambda p: (p.x, p.y), points.parts()))
    
    list_landmarks.append(list_points)

print(len(list_landmarks[0]))
68
for landmark in list_landmarks:
    for point in landmark:
        cv2.circle(img_show, point, 5, (255, 0, 255), -1)
        # cv2.circle(img, center, radius, color, thickness=None, lineType=None, shift=None) -> img


img_show_rgb = cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB)                
plt.imshow(img_show_rgb)
plt.show()
## 코끝 랜드마크 찾기
for dlib_rect, landmark in zip(dlib_rects, list_landmarks):
    print (landmark[33]) # 코끝의 index는 33 #설명에는 코끝보다 좋은 곳이 있다고 했는데, 잘 모르겠다.
    x = landmark[33][0]
    y = (landmark[33][1] + landmark[30][1])//2
    w = h = dlib_rect.width()
    print ('(x,y) : (%d,%d)'%(x,y))
    print ('(w,h) : (%d,%d)'%(w,h))
(441, 572)
(x,y) : (441,546)
(w,h) : (463,463)

step3. マップの適用位置を確認する

## resize

sticker_path = os.getenv('HOME')+'/aiffel/camera_sticker/images/catfish.png'
img_sticker = cv2.imread(sticker_path) # 스티커 이미지를 불러옵니다
img_sticker = cv2.resize(img_sticker, (w,h)) # 스티커 resize
print (img_sticker.shape)
(463, 463, 3)
# refine 해줌.

refined_x = x - w//2 
refined_y = y - h//2
print ('(refined_x,refined_y) : (%d,%d)'%(refined_x, refined_y))
(refined_x,refined_y) : (210,315)
## 범위 밖의 스티커 이미지 영역을 잘라줌

if refined_x < 0: 
    img_sticker = img_sticker[:, -refined_x:]
    refined_x = 0
if refined_y < 0:
    img_sticker = img_sticker[-refined_y:, :]
    refined_y = 0
# 길어서 복잡해 보이지만 img_show[from:to] 형식입니다
sticker_area = img_show[refined_y:refined_y+img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]]

img_show[refined_y:refined_y+img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]] = \
   np.where(img_sticker==255, sticker_area,img_sticker).astype(np.uint8)
print("Done")
Done
plt.imshow(cv2.cvtColor(img_show, cv2.COLOR_BGR2RGB))
plt.show()

step4. マップの適用


1) np.whereアプリケーションの使用

## bgr이미지에서 사용해서, landmark와 bounding box 없이 스티커만 출력
img_del = img_bgr.copy() # 2) 과제를 위해 미리 복사함.

sticker_area = img_bgr[refined_y:refined_y +img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]]

img_bgr[refined_y:refined_y +img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]] = \
    np.where(img_sticker==255, sticker_area,img_sticker).astype(np.uint8)
plt.imshow(cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB))
plt.show()
## was hard: imshow할떄 이미 한 번 적용되어버린 건 지워지지 않고 떠버림 ㅠㅠ

2)自然に見えるようにcv 2。addWeighted()を参照

# 사용할 이미지 체크
plt.imshow(img_del)
<matplotlib.image.AxesImage at 0x7f9998259fd0>
# addWeight로 합성하기

img_crop = img_del[refined_y:refined_y +img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]]
#img_sticker와 size가 동일하게 잘라줌

img_sticker_select = img_sticker[:,:] = \
    np.where(img_sticker==255, sticker_area,img_sticker).astype(np.uint8)

img_weighted = cv2.addWeighted(img_crop, 0.4, img_sticker_select, 0.6, 0.0) # 가중치를 부여함

plt.imshow(img_weighted)
plt.show()

img_del[refined_y:refined_y +img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]] = img_weighted

plt.imshow(cv2.cvtColor(img_del, cv2.COLOR_BGR2RGB))
plt.show()

step5. 質問の参照


(1)異なる角度から自撮り写真を撮り,シールを繰り返し使用する.


1.角度の違う顔

# 얼굴 불러오기- up, under, side

my_image_path = os.getenv('HOME')+'/aiffel/camera_sticker/images/up.png' #원하는 이미지 경로를 지정한다
img_up = cv2.imread(my_image_path)    # OpenCV로 이미지를 불러온다(절대/상대 경로 모두 가능)

my_image_path = os.getenv('HOME')+'/aiffel/camera_sticker/images/under.png'
img_under = cv2.imread(my_image_path)

my_image_path = os.getenv('HOME')+'/aiffel/camera_sticker/images/side.png'
img_side = cv2.imread(my_image_path)

 # 출력용 이미지를 따로 보관합니다
img_show_up = img_up.copy()     
img_show_under = img_under.copy()
img_show_side = img_side.copy()

# check

plt.figure(figsize=(20,20))
plt.subplot(1,3,1)
plt.imshow(img_show_up)

plt.subplot(1,3,2)
plt.imshow(img_show_under)

plt.subplot(1,3,3)
plt.imshow(img_show_side)

plt.show()

前に学習した内容に基づいて関数を作成します。

def draw_box(img_input):
    global l,t,r,b, dlib_rects
    #detector load
    detector_hog = dlib.get_frontal_face_detector()
    
    #변환
    img_re = cv2.cvtColor(img_input, cv2.COLOR_BGR2RGB)
    dlib_rects = detector_hog(img_re, 1)
    
    for dlib_rect in dlib_rects:
        l = dlib_rect.left()
        t = dlib_rect.top()
        r = dlib_rect.right()
        b = dlib_rect.bottom()
    
    
    
    # draw
    cv2.rectangle(img_input, (l,t), (r,b), (0,255,0), thickness=20, lineType=cv2.LINE_AA)
    print("bounding box is drawed")
    print(dlib_rects)
    return img_input


def draw_landmark(img_input):
    global points, list_landmarks, dlib_rects
    list_landmarks = []
    
    # 얼굴 영역 박스 마다 face landmark를 찾아낸다
    for dlib_rect in dlib_rects:
        points = landmark_predictor(img_input, dlib_rect) # rgb image, rectangle
        # 68개의 face landmark 좌표를 저장한다.
    
    list_points = list(map(lambda p: (p.x, p.y), points.parts()))
    
    list_landmarks.append(list_points)
    
    for landmark in list_landmarks:
        for point in landmark:
            cv2.circle(img_input, point, 5, (255, 0, 255), -1)
            # cv2.circle(img, center, radius, color, thickness=None, lineType=None, shift=None) -> img
    
    print("Drawing Finished")
    return None


def find_landmark(number):
    global x,y,w,h
    
    for dlib_rect, landmark in zip(dlib_rects, list_landmarks):
        print (landmark[number]) # 코끝의 index는 33 #설명에는 코끝보다 좋은 곳이 있다고 했는데, 잘 모르겠다.
        x = landmark[number][0]
        y = landmark[number][1]
        w = h = dlib_rect.width()
    return None


def get_sticker_area(img_input): #img_sticker가 있는 상태에서 진행
    global refined_x, refined_y, sticker_with_area, img_sticker
    
    sticker_path = os.getenv('HOME')+'/aiffel/camera_sticker/images/catfish.png'
    img_sticker = cv2.imread(sticker_path) # 스티커 이미지를 불러옵니다
    img_sticker = cv2.resize(img_sticker, (w,h)) # 스티커 resize
    
    refined_x = x - w//2 
    refined_y = y - h//2
    
    if refined_x < 0: 
        img_sticker = img_sticker[:, -refined_x:]
        refined_x = 0
    if refined_y < 0:
        img_sticker = img_sticker[-refined_y:, :]
        refined_y = 0
    
    sticker_with_area = img_input[refined_y:refined_y+img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]]

    return None

def transparent_pic(img_input):
    
    img_cropped = img_input[refined_y:refined_y +img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]]
    
    img_with_sticker_select = img_sticker[:,:] = \
        np.where(img_sticker==255, sticker_with_area,img_sticker).astype(np.uint8)
    
    img_with_weight = cv2.addWeighted(img_cropped, 0.4, img_with_sticker_select, 0.6, 0.0) # 가중치를 부여함
    
    img_input[refined_y:refined_y +img_sticker.shape[0], refined_x:refined_x+img_sticker.shape[1]] = \
        img_with_weight

    return img_input

def do_all(img_input, number):
    draw_box(img_input)
    draw_landmark(img_input)
    find_landmark(number)
    get_sticker_area(img_input)
    transparent_pic(img_input)
img_show_up = img_up.copy()     

do_all(img_show_up,33)

plt.imshow(cv2.cvtColor(img_show_up, cv2.COLOR_BGR2RGB))
plt.show()

bounding box is drawed
rectangles[]
Drawing Finished
img_show_under = img_under.copy()

do_all(img_show_under,33)


plt.imshow(cv2.cvtColor(img_show_under, cv2.COLOR_BGR2RGB))
plt.show()
bounding box is drawed
rectangles[[(171, 357) (726, 911)]]
Drawing Finished
(454, 662)
img_show_side = img_side.copy()

do_all(img_show_side,33)

plt.imshow(cv2.cvtColor(img_show_side, cv2.COLOR_BGR2RGB))
plt.show()
bounding box is drawed
rectangles[[(245, 297) (707, 759)]]
Drawing Finished
(455, 578)

ステップ5-(1)レビュー


角度を変えると、認識が悪いことがわかります.
upの場合は顔も認識できず、ランドマークも見つからない.
しかしunder、sideならboundingbox、ランドマーク、シールは正常に動作します.
原図の写真の合成は正常で、座標値がよく与えられて、印刷されました.

(2)問題点をできるだけ詳細に記録する.あなたが考えている問題を解決する方法は必ず存在します!


今日作成されたシールアプリでは、以下の点を考慮できます.

顔の角度によってシールはどのように変化しますか?


1)3 D形状の回転


正面撮影時の道標値が固定されていると仮定して、彼とどれだけ違うかを比較して回転できると思います.
左右の回転、上下の回転はすべて体現しなければならない.
人の顔が対称であることを創意とする.

画像から,27と29を基準とした左右,上下に展開した(36,45),(28,30)を見つけることができる.
なので、写真の方向が正面なら27と29を基準に
abs(座標値36)-(座標値27)=abs(座標値27)-(座標値45)
abs(座標値28)-(座標値29)=abs(座標値29)-(座標値30)
そうでしょう.
したがって,回転が発生するとこれらの計算値の間に差が生じ,そこで回転量を判断することができる.
その回転量を基準にシール画像を回転させると、角度に応じて反応するアプリを作ることができます.
どうやって実現するかいろいろ考えましたが、成功しませんでした.

2)2 D形状の回転


正面の場合、45度、90度の回転しか行わない場合があります.
これは後で説明します
from scipy import ndimage


my_image_path = os.getenv('HOME')+'/aiffel/camera_sticker/images/project_image.png' #원하는 이미지 경로를 지정한다
img_bgr = cv2.imread(my_image_path)    # OpenCV로 이미지를 불러온다(절대/상대 경로 모두 가능)
img_show = img_bgr.copy()      # 출력용 이미지를 따로 보관합니다

rotated_315 = ndimage.rotate(img_bgr, 45)    
plt.imshow(rotated_315)
plt.show()

rotated_90 = ndimage.rotate(img_bgr, 90)    
plt.imshow(rotated_90)
plt.show()



do_all(rotated_315,33)
plt.imshow(cv2.cvtColor(rotated_315, cv2.COLOR_BGR2RGB))
plt.show()

do_all(rotated_90,33)
plt.imshow(cv2.cvtColor(rotated_90, cv2.COLOR_BGR2RGB))
plt.show()

bounding box is drawed
rectangles[]
Drawing Finished
bounding box is drawed
rectangles[]
Drawing Finished

運転速度は重要ですか?


大切だと思います.ユーザーの便宜のために作られたカメラアプリであり、各種フィルターを確認する必要があり、良い写真を撮るために角度を見回すと、フィルターはすぐに反応しなければならない.このようにしてこそ、ユーザーは便利になり、アプリケーションに滞在する時間が長くなります.

シールアプリを作る精度はどれくらい重要ですか?


精度は実行速度ほど重要ではないと思います.カメラソフトを持って冗談を言っていると、思わぬところが認識されるのが気になり、逆にもっと面白い場合があります.
一度エラーが発生しても、カメラをまたくるくる回すと元の状態に戻るので大丈夫だと思います.

画像輝度の影響


光の強度によって画素値が変化することを認識した.つまり、同じ物体を撮影しても、光が弱い状態で撮影すると、画素の値が全体的に小さくなります.同じ画像が暗いことを理由に他の物体に分類することはできないので,各ベクトルの要素の和を1にするnormalizatonプロセスを実行する.
したがって、画像の輝度が低い場合は、輝度が高い場合に比べて認識できないようである.

撮影距離の影響


遠くで撮った画像では、顔の線や特徴がよく見つからないようです.

振り返る

  • 今回のプロジェクトの難点、
  • 画像の形状を綴るのは難しい.また、boundingboxとランドマーク、そしてstickerは、理解していないので削除できないので、間違いだと思って迷いました.
  • 画像を回転させると認識できない点も解決しようとしたが、多くの時間をかけて断念した.
  • プロジェクトを行ったときに発見された点またはまだ不明な点.

  • boundingbox、ランドマーク、stickerが削除できないのは、어떻게 이미지를 그리는가の動作原理が分からないからです.普段使っているカメラソフトにシールが貼られると思っていたのですが、画素値が変わるのは初めて知りました.

  • どのように3 D回転に伴ってマップ写真を変えるか、回転した後にどのように人の顔を認識するかはまだぼやけている.
  • ルーブルリック評価指標に合わせて試したもの.
  • はグーグルゲームをたくさんしました.1、2号は比較的容易に実現でき、
  • googlingにより
  • 検出器hogの検出が画像輝度の影響を受けることが分かった.
  • ルーブルリック評価の関連指標に達しなければ、原因に関する推定.
  • の3つの評価項目では、達成できない可能性があります.様々な変数の影響の程度を深く分析していないようだ.
  • 自己承諾
  • 前回は質問の習慣を身につけることを決意しました.今までその習慣を身につけていない.
  • ブログを書くだけの習慣が身についたようです!