質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.50%

pythonを用いたモーフィング

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,278

score 3

コード

 前提・実現したいこと

pythonでのモーフィング

発生している問題・エラーメッセージ

[INFO] loading facial landmark predictor...
C:\Users\Owner\OneDrive\Documents
Traceback (most recent call last):
File "C:\Users\Owner\OneDrive\Documents\module1101.py", line 64, in <module>
points = Face_landmarks(filename)
File "C:\Users\Owner\OneDrive\Documents\module1101.py", line 29, in Face_landmarks
size = image.shape
AttributeError: 'NoneType' object has no attribute 'shape'

該当のソースコード

import argparse
import cv2
import numpy as np
import random
import os
import imutils
import dlib


def Face_landmarks(image_path):
  print("[INFO] loading facial landmark predictor...")
  detector = dlib.get_frontal_face_detector()

  path = os.getcwd()
  print(path)

  predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
  image = cv2.imread(image_path)
  size = image.shape
  gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
  rects = detector(gray, 0)
  if len(rects) > 2:
    print("[ERR] too many faces fount...")
    # print("[Error] {} faces found...".format(len(rect)))
    sys.exit(1)
  if len(rects) < 1:
    print("[ERR] face not found...")
    # print("[Error] face not found...".format(len(rect))
    sys.exit(1)
  for rect in rects:
    (bX, bY, bW, bH) = face_utils.rect_to_bb(rect)
    print("[INFO] face frame {}".format(bX, bY, bW, bH))
    shape = predictor(gray, rect)
    shape = face_utils.shape_to_np(shape)
    points = shape.tolist()
    # (0,0),(x,0),(0,y),(x,y)
    points.append([0, 0])
    points.append([int(size[1]-1), 0])
    points.append([0, int(size[0]-1)])
    points.append([int(size[1]-1), int(size[0]-1)])
    # (x/2,0),(0,y/2),(x/2,y),(x,y/2)
    points.append([int(size[1]/2), 0])
    points.append([0, int(size[0]/2)])
    points.append([int(size[1]/2), int(size[0]-1)])
    points.append([int(size[1]-1), int(size[0]/2)])
  cv2.destroyAllWindows()
  return points


if __name__ == '__main__':
    filename = 'image/MarilynMonroe.jpg'
    name,ext = os.path.splitext(filename)
    img = cv2.imread(filename)
    points = Face_landmarks(failname)
    stringToWrite = ''
    for (i, (x, y)) in enumerate(points):
        print(x, y)
        stringToWrite += str(x) + " " + str(y) + "n"
        cv2.circle(img, (x, y), 1, (0, 0, 255), -1)
        cv2.putText(img, str(i + 1), (x - 10, y - 10),
            cv2.FONT_HERSHEY_SIMPLEX, 0.35, (0, 0, 255), 1)
    cv2.imwrite('%s-points.jpg' %name,img)


def Face_delaunay(rect,points1 ,points2 ,alpha ):
    points = []
    for i in range(0, len(points1)):
        x = ( 1 - alpha ) * points1[i][0] + alpha * points2[i][0]
        y = ( 1 - alpha ) * points1[i][1] + alpha * points2[i][1]
        points.append((x,y))
    triangles, delaunay = calculateDelaunayTriangles(rect, points)
    cv2.destroyAllWindows()
    return triangles, delaunay
def calculateDelaunayTriangles(rect, points):

    subdiv = cv2.Subdiv2D(rect)
    for p in points:
        subdiv.insert(p)

    triangleList = subdiv.getTriangleList()

    delaunayTri = []

    pt = []

    for t in triangleList:
        pt.append((t[0], t[1]))
        pt.append((t[2], t[3]))
        pt.append((t[4], t[5]))

        pt1 = (t[0], t[1])
        pt2 = (t[2], t[3])
        pt3 = (t[4], t[5])

        if rectContains(rect, pt1) and rectContains(rect, pt2) and rectContains(rect, pt3):
            ind = []
            for j in range(0, 3):
                for k in range(0, len(points)):
                    if(abs(pt[j][0] - points[k][0]) < 1.0 and abs(pt[j][1] - points[k][1]) < 1.0):
                        ind.append(k)
            if len(ind) == 3:
                delaunayTri.append((ind[0], ind[1], ind[2]))

        pt = []

    return triangleList,delaunayTri
def rectContains(rect, point) :
    if point[0] < rect[0] :
        return False
    elif point[1] < rect[1] :
        return False
    elif point[0] > rect[0] + rect[2] :
        return False
    elif point[1] > rect[1] + rect[3] :
        return False
    return True


def Face_morph(img1, img2, img, tri1, tri2, tri, alpha) :
    """モーフィング画像作成
    Args:
        img1 : 画像1
        img2 : 画像2
        img  : 画像1,2のモーフィング画像(Output用画像)
        tri1 : 画像1の三角形
        tri2 : 画像2の三角形
        tri  : 画像1,2の間の三角形
        alpha: 重み
    """

    # 各三角形の座標を含む最小の矩形領域 (バウンディングボックス)を取得
    # (左上のx座標, 左上のy座標, 幅, 高さ)
    r1 = cv2.boundingRect(np.float32([tri1]))
    r2 = cv2.boundingRect(np.float32([tri2]))
    r = cv2.boundingRect(np.float32([tri]))
    # バウンディングボックスを左上を原点(0, 0)とした座標に変換
    t1Rect = []
    t2Rect = []
    tRect = []
    for i in range(0, 3):
        tRect.append(((tri[i][0] - r[0]),(tri[i][1] - r[1])))
        t1Rect.append(((tri1[i][0] - r1[0]),(tri1[i][1] - r1[1])))
        t2Rect.append(((tri2[i][0] - r2[0]),(tri2[i][1] - r2[1])))
    # 三角形のマスクを生成
    # 三角形の領域のピクセル値は1で、残りの領域のピクセル値は0になる
    mask = np.zeros((r[3], r[2], 3), dtype = np.float32)
    cv2.fillConvexPoly(mask, np.int32(tRect), (1.0, 1.0, 1.0), 16, 0)
    # アフィン変換の入力画像を用意
    img1Rect = img1[r1[1]:r1[1] + r1[3], r1[0]:r1[0] + r1[2]]
    img2Rect = img2[r2[1]:r2[1] + r2[3], r2[0]:r2[0] + r2[2]]
    # アフィン変換の変換行列を生成
    warpMat1 = cv2.getAffineTransform( np.float32(t1Rect), np.float32(tRect) )
    warpMat2 = cv2.getAffineTransform( np.float32(t2Rect), np.float32(tRect) )
    size = (r[2], r[3])
    # アフィン変換の実行
    # 1.src:入力画像、2.M:変換行列、3.dsize:出力画像のサイズ、4.flags:変換方法、5.borderMode:境界の対処方法
    warpImage1 = cv2.warpAffine( img1Rect, warpMat1, (size[0], size[1]), flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101 )
    warpImage2 = cv2.warpAffine( img2Rect, warpMat2, (size[0], size[1]), flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101 )
    # 2つの画像に重みを付けて、三角形の最終的なピクセル値を見つける
    imgRect = (1.0 - alpha) * warpImage1 + alpha * warpImage2
    # マスクと投影結果を使用して論理AND演算を実行し、
    # 三角形領域の投影されたピクセル値を取得しOutput用画像にコピー
    img[r[1]:r[1]+r[3], r[0]:r[0]+r[2]] = img[r[1]:r[1]+r[3], r[0]:r[0]+r[2]] * ( 1 - mask ) + imgRect * mask


    def betweenPoints(point1, point2, alpha) :
        points = []
    for i in range(0, len(points1)):
        x = ( 1 - alpha ) * points1[i][0] + alpha * points2[i][0]
        y = ( 1 - alpha ) * points1[i][1] + alpha * points2[i][1]
        points.append((x,y))
    return points
if __name__ == '__main__' :
    # モーフィングする画像取得
    filename1 = 'image/AudreyHepburn.jpg'
    filename2 = 'image/MarilynMonroe.jpg'
    img1 = cv2.imread(filename1)
    img2 = cv2.imread(filename2)

    # 画像をfloat型に変換
    img1 = np.float32(img1)
    img2 = np.float32(img2)
    # 長方形を取得
    size = img1.shape
    rect = (0, 0, size[1], size[0])
    # 顔の特徴点を取得
    points1 = face_landmarks.Face_landmarks(filename1)
    points2 = face_landmarks.Face_landmarks(filename2)
    # 1~99%割合を変えてモーフィング
    for cnt in range(1, 100):
        alpha = cnt * 0.01

        # 画像1,2の特徴点の間を取得
        points = betweenPoints(points1,points2,alpha)
        # ドロネーの三角形(座標配列とpoints要素番号)を取得
        triangles, delaunay = face_delaunay.Face_delaunay(rect,points)
        # モーフィング画像初期化
        imgMorph = np.zeros(img1.shape, dtype = img1.dtype)
        # ドロネー三角形の配列要素番号を読込
        for (i, (x, y, z)) in enumerate(delaunay):
            # ドロネー三角形のピクセル位置を取得
            tri1 = [points1[x], points1[y], points1[z]]
            tri2 = [points2[x], points2[y], points2[z]]
            tri = [points[x], points[y], points[z]]
            # モーフィング画像を作成
            Face_morph(img1, img2, imgMorph, tri1, tri2, tri, alpha)
        # モーフィング画像をint型に変換し出力
        imgMorph = np.uint8(imgMorph)
        cv2.imwrite('output/picture-%s.jpg' % str(cnt).zfill(3),imgMorph)

試したこと

補足情報(FW/ツールのバージョンなど)

こちらを参考にさせていただきました。

https://flat-kids.net/2020/06/30/%E9%A1%94%E3%83%A2%E3%83%BC%E3%83%95%E3%82%A3%E3%83%B3%E3%82%B01-%E9%A1%94%E3%81%AE%E7%89%B9%E5%BE%B4%E7%82%B9%E3%81%AE%E6%A4%9C%E5%87%BA/

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • toast-uz

    2020/11/01 09:59

    コードが正しく表示されていません。「コードの挿入」ボタンを使ってください。

    キャンセル

  • 1T2R3M4

    2020/11/01 10:12

    failnameはどこで定義されてますか。

    キャンセル

  • y_waiwai

    2020/11/01 11:32

    このままではコードが読めないので、質問を編集し、<code>ボタンを押し、出てくる’’’の枠の中にコードを貼り付けてください

    キャンセル

  • fourteenlength

    2020/11/01 13:10 編集

    たぶんおかしい箇所が10か所以上あります。相当慣れた人が書いたコードを質問者さんがニコイチしたのか不可解な部分がたくさんあります。

    キャンセル

回答 1

checkベストアンサー

0

GMH
35%
65%
FEA
※画像はwikiより。共にプログラマー界のスーパーおかあちゃんです。

コメントにも書きましたが、細かいエラーがたくさんありましたので個別には書きません。コメントと行間を察する温かい気持ちでコードを読んでください。

なお、fourteenlengthは「技量を証明したくて、むしゃくしゃしてやった、後悔はしていない」などと意味不明なことを供述しており…

import argparse

import cv2
import numpy as np
import random
import os
import imutils
import dlib

import sys # <--追加
from imutils import face_utils# <--追加
def Face_landmarks(image_path):
    print("[INFO] loading facial landmark predictor...")
    detector = dlib.get_frontal_face_detector()

    path = os.path.split(__file__)[0]+"\\" # <--デバグ用に修正
    print(path)

    predictor = dlib.shape_predictor(path + "shape_predictor_68_face_landmarks.dat")    # <--デバグ用に修正

    image = cv2.imread(path + image_path)# <--デバグ用に修正

    --追加
        # 画像が読めないとNoneになるため
        print("File loading error @Face_landmarks(image_path)")
        sys.exit()

    size = image.shape
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    rects = detector(gray, 0)
    if len(rects) > 2:
        print("[ERR] too many faces fount...")

        print("[Error] {} faces found...".format(len(rect)))
        sys.exit(1)

    if len(rects) < 1:
        print("[ERR] face not found...")

        print("[Error] face not found...".format(len(rect)))
        sys.exit(1)

    for rect in rects:
        (bX, bY, bW, bH) = face_utils.rect_to_bb(rect)

        print("[INFO] face frame {}".format(bX, bY, bW, bH))
        shape = predictor(gray, rect)

        shape = face_utils.shape_to_np(shape)

        points = shape.tolist()

        # (0,0),(x,0),(0,y),(x,y)
        points.append([0, 0])
        points.append([int(size[1]-1), 0])
        points.append([0, int(size[0]-1)])
        points.append([int(size[1]-1), int(size[0]-1)])

        # (x/2,0),(0,y/2),(x/2,y),(x,y/2)
        points.append([int(size[1]/2), 0])
        points.append([0, int(size[0]/2)])
        points.append([int(size[1]/2), int(size[0]-1)])
        points.append([int(size[1]-1), int(size[0]/2)])
        cv2.destroyAllWindows()
        # return powints
        return points #  <-- 修正

# if name == 'main': <-- 修正
if __name__ == '__main__':
    filename = 'image/Grace_Murray_Hopper.jpg'

    name,ext = os.path.splitext(filename)

    path = os.path.split(__file__)[0]+"\\" # <--デバグ用に
    img = cv2.imread(path + filename)# <--デバグ用に修正

    if img is None:# <--追加
        # 画像が読めないとNoneになるため
        print("File loading error @ __name__ == '__main__'")
        sys.exit()

    points = Face_landmarks(filename) # failname --> filename
    stringToWrite = ''

    for (i, (x, y)) in enumerate(points):
        print(x, y)
        stringToWrite += str(x) + " " + str(y) + "n"
        cv2.circle(img, (x, y), 1, (0, 0, 255), -1)
        cv2.putText(img, str(i + 1), (x - 10, y - 10),
        cv2.FONT_HERSHEY_SIMPLEX, 0.35, (0, 0, 255), 1)

    cv2.imwrite('%s-points.jpg' %name,img)

def Face_delaunay(rect,points1 ,points2 ,alpha ):
    points = []
    for i in range(0, len(points1)):
        x = ( 1 - alpha ) * points1[i][0] + alpha * points2[i][0]
        y = ( 1 - alpha ) * points1[i][1] + alpha * points2[i][1]
        points.append((x,y))

    if len(points) < 3:# デバグ用に追加
        print("FAIL: coordinates should be atleast 3.",len(points))
        sys.exit()

    triangles, delaunay = calculateDelaunayTriangles(rect, points)
    # cv2.destroyAllWindows() # <--削除
    return triangles, delaunay

def calculateDelaunayTriangles(rect, points):
    subdiv = cv2.Subdiv2D(rect)


    for p in points:
        p = (int(p[0]), int(p[1])) # <--追加
        subdiv.insert(p)
    triangleList = subdiv.getTriangleList()


    delaunayTri = []

    pt = []
    # print("triangleList",triangleList)
    for t in triangleList:
        pt.append((t[0], t[1]))
        pt.append((t[2], t[3]))
        pt.append((t[4], t[5]))

        pt1 = (t[0], t[1])
        pt2 = (t[2], t[3])
        pt3 = (t[4], t[5])


        if rectContains(rect, pt1) and rectContains(rect, pt2) and rectContains(rect, pt3):
            ind = []
            for j in range(0, 3):
                for k in range(0, len(points)):
                    if(abs(pt[j][0] - points[k][0]) < 1.0 and abs(pt[j][1] - points[k][1]) < 1.0):
                        ind.append(k)
                        if len(ind) == 3:
                            delaunayTri.append((ind[0], ind[1], ind[2]))
        # print(delaunayTri)
        pt = []

    return triangleList,delaunayTri

def rectContains(rect, point) :
    if point[0] < rect[0] :
        return False
    elif point[1] < rect[1] :
        return False
    elif point[0] > rect[0] + rect[2] :
        return False
    elif point[1] > rect[1] + rect[3] :
        return False
    return True

def Face_morph(img1, img2, img, tri1, tri2, tri, alpha) :
    """モーフィング画像作成
    Args:
    img1 : 画像1
    img2 : 画像2
    img  : 画像1,2のモーフィング画像(Output用画像)
    tri1 : 画像1の三角形
    tri2 : 画像2の三角形
    tri  : 画像1,2の間の三角形
    alpha: 重み
    """

    # 各三角形の座標を含む最小の矩形領域 (バウンディングボックス)を取得
    # (左上のx座標, 左上のy座標, 幅, 高さ)
    r1 = cv2.boundingRect(np.float32([tri1]))
    r2 = cv2.boundingRect(np.float32([tri2]))
    r = cv2.boundingRect(np.float32([tri]))

    # バウンディングボックスを左上を原点(0, 0)とした座標に変換
    t1Rect = []
    t2Rect = []
    tRect = []
    for i in range(0, 3):
        tRect.append(((tri[i][0] - r[0]),(tri[i][1] - r[1])))
        t1Rect.append(((tri1[i][0] - r1[0]),(tri1[i][1] - r1[1])))
        t2Rect.append(((tri2[i][0] - r2[0]),(tri2[i][1] - r2[1])))

        # 三角形のマスクを生成
        # 三角形の領域のピクセル値は1で、残りの領域のピクセル値は0になる
        mask = np.zeros((r[3], r[2], 3), dtype = np.float32)
        cv2.fillConvexPoly(mask, np.int32(tRect), (1.0, 1.0, 1.0), 16, 0)

        # アフィン変換の入力画像を用意
        img1Rect = img1[r1[1]:r1[1] + r1[3], r1[0]:r1[0] + r1[2]]
        img2Rect = img2[r2[1]:r2[1] + r2[3], r2[0]:r2[0] + r2[2]]

    # アフィン変換の変換行列を生成
    warpMat1 = cv2.getAffineTransform( np.float32(t1Rect), np.float32(tRect) )
    warpMat2 = cv2.getAffineTransform( np.float32(t2Rect), np.float32(tRect) )
    size = (r[2], r[3])

    # アフィン変換の実行
    # 1.src:入力画像、2.M:変換行列、3.dsize:出力画像のサイズ、4.flags:変換方法、5.borderMode:境界の対処方法
    warpImage1 = cv2.warpAffine( img1Rect, warpMat1, (size[0], size[1]), flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101 )
    warpImage2 = cv2.warpAffine( img2Rect, warpMat2, (size[0], size[1]), flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101 )

    # 2つの画像に重みを付けて、三角形の最終的なピクセル値を見つける
    imgRect = (1.0 - alpha) * warpImage1 + alpha * warpImage2


    # マスクと投影結果を使用して論理AND演算を実行し、
    # 三角形領域の投影されたピクセル値を取得しOutput用画像にコピー
    img[r[1]:r[1]+r[3], r[0]:r[0]+r[2]] = img[r[1]:r[1]+r[3], r[0]:r[0]+r[2]] * ( 1 - mask ) + imgRect * mask
    return img#  <-- 追加

def betweenPoints(point1, point2, alpha) :
    points = []
    for i in range(0, len(points1)):
        x = ( 1 - alpha ) * points1[i][0] + alpha * points2[i][0]
        y = ( 1 - alpha ) * points1[i][1] + alpha * points2[i][1]
        points.append((x,y))
    return points

if __name__ == '__main__': #  <-- 修正
    os.makedirs("./output",exist_ok=True) # <-- 追加

    path = os.path.split(__file__)[0]+"\\" # <--デバグ用に追加

    # モーフィングする画像取得
    filename1 = 'image/Grace_Murray_Hopper.jpg'
    filename2 = 'image/Frances_Elizabeth_Allen.jpg'

    img1 = cv2.imread(path + filename1)
    img2 = cv2.imread(path + filename2)

    if img1 is None:# <--追加
        # 画像が読めないとNoneになるため
        print("File loading error @img1")
        sys.exit()


    if img2 is None:# <--追加
        # 画像が読めないとNoneになるため
        print("File loading error @img2")
        sys.exit()


    # 画像をfloat型に変換
    img1 = np.float32(img1)
    img2 = np.float32(img2)

    # 長方形を取得
    size = img1.shape
    rect = (0, 0, size[1], size[0])

    # 顔の特徴点を取得
    points1 = Face_landmarks(filename1) # <--修正
    points2 = Face_landmarks(filename2) # <--修正

    # 1~99%割合を変えてモーフィング
    for cnt in range(1, 100):
        alpha = cnt * 0.01

        # 画像1,2の特徴点の間を取得
        points = betweenPoints(points1,points2,alpha)

        # ドロネーの三角形(座標配列とpoints要素番号)を取得
        triangles, delaunay = Face_delaunay(rect,points1,points2,alpha)# <--修正

        # モーフィング画像初期化
        imgMorph = np.zeros(img1.shape, dtype = img1.dtype)

        # ドロネー三角形の配列要素番号を読込
        for (i, (x, y, z)) in enumerate(delaunay):
            # ドロネー三角形のピクセル位置を取得
            tri1 = [points1[x], points1[y], points1[z]]
            tri2 = [points2[x], points2[y], points2[z]]
            tri = [points[x], points[y], points[z]]

            # モーフィング画像を作成
            imgMorph= Face_morph(img1, img2, imgMorph, tri1, tri2, tri, alpha) #<--修正

        # モーフィング画像をint型に変換し出力
        imgMorph = np.uint8(imgMorph)
        cv2.imwrite(path +'output/alpha_[%s]_picture-%s.jpg' % (str(cnt).zfill(2),str(cnt).zfill(3)),imgMorph) #<--修正
        cv2.imshow("imgMorph",imgMorph) # <-- 追加
        cv2.waitKey(10) # <-- 追加

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.50%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る