Skip to content

Instantly share code, notes, and snippets.

@Mooan
Last active September 17, 2020 14:36
Show Gist options
  • Save Mooan/872e7cdf5738b0b840b9 to your computer and use it in GitHub Desktop.
Save Mooan/872e7cdf5738b0b840b9 to your computer and use it in GitHub Desktop.

顔画像から地獄のミサワ顔を生成する.

OpenCVの勉強のため、題記のとおり課題設定し、取り組んでみた記録.

以下のステップで地獄のミサワ顔を作る.

  1. 入力画像から顔を検出し、抽出する.
  2. 1.で抽出した画像を切り出す.
  3. 2.で切り出した画像を、顔を中心部に寄せる感じで画像処理で変形する
  4. 3.の画像変換結果を、入力画像に合成する.
# TODO: files
!ls -1
face_detect_cv_test.ipynb
haarcascade_frontalface_alt.xml
images.jpeg
lena_std.tif
shape_predictor_68_face_landmarks.dat
test.py

OpenCVによる顔検出

初めに、以下を参考に、OpenCVで顔検出してみる. http://qiita.com/donksite/items/f500d301bc192efcec70

顔領域だけ、ピンポイントに取り出すことができなかった.

顔領域だけ、ピンポイントで取り出すために、以下の方法を考えた.

  • a.) 肌色を検出し、肌色部分だけ抽出する.
  • b.) 顔のパーツ(目、鼻、口、など)を検出し、それらの座標を使って絞り込む
%matplotlib inline
import cv2 # opencv
import matplotlib.pyplot as plt # matplotlibの描画系

fn_img = "lena_std.tif" # レナさんの画像ファイル名
img = cv2.imread(fn_img) # レナさんの画像を読み込む

img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # グレースケール化

# cascade の学習結果は https://github.com/Itseez/opencv/tree/master/data/haarcascades から落とせる
cascade_path = "haarcascade_frontalface_alt.xml"
cascade = cv2.CascadeClassifier(cascade_path) # カスケード分類器を作成
facerect = cascade.detectMultiScale(img_gray, scaleFactor=1.1, minNeighbors=1, minSize=(10,10)) # 顔認識

img_result = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 出力結果用にコピー & RGB化
rect_color = (0, 255, 0) # 矩形の色 (B=0, G=255, R=0)
if len(facerect) > 0:
    for rect in facerect:
        # 検出範囲を矩形で囲む
        cv2.rectangle(img_result, tuple(rect[0:2]), tuple(rect[0:2] + rect[2:4]), rect_color, thickness=2)

plt.imshow(img_result)
<matplotlib.image.AxesImage at 0x7f54e64c1b50>

output_3_1

dlibによる顔検出、及び、顔パーツの検出

dlibには、顔パーツの検出機能があるようだ.

https://matthewearl.github.io/2015/07/28/switching-eds-with-python/

dlibによる顔検出

まずは、顔検出する.

%matplotlib inline
import cv2
import dlib
import numpy as np
import matplotlib.pyplot as plt

fn_img = "lena_std.tif" # input image (BGR)
#fn_img = "images.jpeg"
img = cv2.imread(fn_img)

# Initialize dlib detector and predictor
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")

# Conduct face detection
rects = detector(img, 1)
if len(rects) < 1:
    print "face didn't detected"
else:
    print "face detected: %d" % ( len(rects))
face detected: 1

顔パーツの検出

# image to be output
tmp_img = img.copy()
    
# facelandmark vector
vec = np.matrix([[p.x, p.y] for p in predictor(img, rects[0]).parts()])
    
highlights = [ 5, 10, 17, 26, ]

for i in range(0, len(vec)-1):
    center = (vec[i,0], vec[i,1])
    radius = 5
    if i in highlights:
        color = (0, 0,255) # Red
    else:
        color = (0,0,0) # black
    cv2.circle(tmp_img, center, radius, color, thickness=-1 )
    
plt.imshow(cv2.cvtColor(tmp_img, cv2.COLOR_BGR2RGB))
    
<matplotlib.image.AxesImage at 0x7f54d0617210>

output_7_1

目、鼻、口を包含する4点をpointsとする.
pointsを包含する正方形の4点を、(x1, y1), (x2, y1), (x1, y2), (x2, y2)を導出する.

    points   = np.array([ [vec[i,0], vec[i,1]] for i in highlights ])
    for v in points:
        print "points: [%d, %d]" % ( v[0], v[1])
        
points: [237, 374]
points: [312, 376]
points: [243, 251]
points: [354, 252]
    x1 = max(points[:,0])
    x2 = min(points[:,0])
    y1 = max(points[:,1])
    y2 = min(points[:,1])
    print "x1=%d x2=%d y1=%d y2=%d" % (x1, x2, y1, y2 )
x1=354 x2=237 y1=376 y2=251

ミサワ変換

こんな顔を作りたい.

例

まず、顔から目、鼻、口のみを切り出す.
切り出した顔を縮小し、元の顔に合成する.

# face mask
mask_image = np.zeros(img.shape, np.uint8)
points = cv2.convexHull(points)
cv2.fillConvexPoly(mask_image, points, color=(255,255,255))

# crop face
face_area = cv2.bitwise_and(img, mask_image)
plt.imshow(cv2.cvtColor(face_area, cv2.COLOR_BGR2RGB))
<matplotlib.image.AxesImage at 0x7f54d0272f50>

output_12_1

# remove face
non_face_area = img.copy()
cv2.fillConvexPoly(non_face_area, points, color=(0,0,0))
plt.imshow(cv2.cvtColor(non_face_area, cv2.COLOR_BGR2RGB))
<matplotlib.image.AxesImage at 0x7f54d01ed750>

output_13_1

# make it small
scale = 0.6 # < 1.0
w_base = x1-x2
h_base = y1-y2
w = np.int((x1-x2) * scale)
h = np.int((y1-y2) * scale)
w_offset = ( w_base - w ) / 2
h_offset = ( h_base - h ) / 2

face_area_misawa = np.zeros(face_area.shape, np.uint8)
face_area_misawa[(y2+h_offset):(y2+h+h_offset), (x2+w_offset):(x2+w+w_offset)] = cv2.resize(face_area[y2:y1, x2:x1], (w,h))

plt.imshow(cv2.cvtColor(face_area_misawa, cv2.COLOR_BGR2RGB))
<matplotlib.image.AxesImage at 0x7f54d0137090>

output_14_1

mixed_image = non_face_area + face_area_misawa
plt.imshow(cv2.cvtColor(mixed_image, cv2.COLOR_BGR2RGB))
<matplotlib.image.AxesImage at 0x7f54d0054f90>

output_15_1

その際に、縮小して元の画像から情報が欠損した部分に、周りの色と同じになるように補間をかける.

画像の欠損部分マスクと元画像の論理和をから、欠損部分を埋める画像を作り(intp_mask), 上記合成画像とさらに論理和をとった. (まろやかさを出すためにGaussianBlurもかけた)

ret, tmp  = cv2.threshold(mixed_image, 1, 255, cv2.THRESH_BINARY)
intp_mask = cv2.split(cv2.bitwise_not(tmp))[0] 
blured_img = cv2.GaussianBlur(img, (51, 51), 5, img, 5)
intp_img  = cv2.bitwise_and(blured_img, blured_img, mask=intp_mask)

plt.imshow(cv2.cvtColor(intp_img, cv2.COLOR_BGR2RGB))
<matplotlib.image.AxesImage at 0x7f54c85bb650>

output_17_1

mixed_image2 = intp_img + mixed_image
plt.imshow(cv2.cvtColor(mixed_image2, cv2.COLOR_BGR2RGB))
<matplotlib.image.AxesImage at 0x7f54c8599090>

output_18_1

... まろやかさがたりない?

tmp_img = cv2.GaussianBlur(mixed_image2, (5, 5), 3, img, 3)
plt.imshow(cv2.cvtColor(tmp_img, cv2.COLOR_BGR2RGB))
<matplotlib.image.AxesImage at 0x7f54c8494d10>

output_19_1

結論と今後の課題

  • dlibを使った顔画像の検出はうまくできた.
    • 顔の特徴点からミサワ変換の範囲指定まではいい感じ
      • OpenCVで同様のことはできないのか?
  • 画像処理で、縮小前後の画像の合成がうまくいかない.
    • OpenGL使ってポリゴンに顔を写像して、特定範囲のみ縮小してみる.
@Mooan
Copy link
Author

Mooan commented Mar 22, 2016

output_19_1
output_18_1
output_17_1
output_15_1
output_14_1
output_13_1
output_12_1
Uploading output_7_1.png…
output_3_1

@Mooan
Copy link
Author

Mooan commented Mar 22, 2016

output_7_1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment