import dlib
import cv2

"""
Explanation: Pythonを使って顔ランドマークで遊んでみよう
今回はPythonを使ったプログラミングをやってみます。ただの数値計算では面白くないので

WebCAMを使って自分の顔をキャプチャ
顔検出
顔ランドマーク検出
ランドマークを使って何かやる

という流れです。
使うパッケージ
この例では

OpenCV: 画像処理ライブラリ(cv2)
dlib: 機械学習ライブラリ

を使います。
1. WebCAMを使って自分の顔をキャプチャ
まず，OpenCV(cv2)とdlibを使う宣言をします。C言語の#includeみたいなもんです。
セルが緑色の状態(青だったらEnterを押す)でSHIFT+Enterを押して下さい。そうするとIn[?]となっているセル内のPython文が実行されます。
その際， Errorのようなメッセージが出なければ成功です。メッセージは英語ですが少し気合を入れれば読めます。
End of explanation
"""


cap = cv2.VideoCapture(0)

"""
Explanation: これでdlibとcv2が使えるようになりました。dlib.あるいはcv2.の後に関数名を付けることでそれぞれの機能を呼び出せます。早速WebCAMを使えるようにしましょう。
End of explanation
"""


ret, img = cap.read()

"""
Explanation: カメラのタリーが光りましたか? 光らない場合は括弧の中の数字を1や2に変えてみて下さい。
次に画像をキャプチャします。カメラに目線を送りながら次のセルを実行しましょう。
End of explanation
"""


print(ret)

"""
Explanation: capはWebCAMを使うための操縦桿(ハンドル)と思って下さい。それにread(読め)と命令した訳です。では，成功したか確認しましょう。readという関数(機能)は成功したか否かの結果と，画像を返してくれます。
End of explanation
"""


cv2.imshow('image', img)
cv2.waitKey(2000)

"""
Explanation: Trueと出ましたか? 出ていれば成功です。画像を見てみましょう。
End of explanation
"""


detector = dlib.get_frontal_face_detector()

"""
Explanation: 自分の顔が出てきましたか? waitKey(2000)は2000ms待って終了する意味です。この2000を0にすると特別な意味になり，入力待ちになります。(ウィンドウを選択してアクティブな状態にしてから何かキーを押して下さい。Outに何か数字が出るでしょう。この数字はキーの認識番号とでも思って下さい。)
2. 顔検出
さて，顔検出をやってみます。OpenCVにも機能がありますがdlibの機能を使います。
End of explanation
"""


dets = detector(img, 1)
len(dets)

"""
Explanation: detectorはdlibのget(よこせ) frontal(正面の) face(顔) detector(検出器)の結果。という意味です。要するに今度は顔検出の操縦桿がdetectorということです。では早速使ってみましょう。
End of explanation
"""


dets[0]

"""
Explanation: "1"以上の数字が出てきたら成功です。これは検出した顔の数です。1行目で画像imgから， upsamplingを1回だけして(色々な大きさの顔に対応する処理)，その結果をdetsに入れてます。
ではdetsの中身を見てみましょう。
End of explanation
"""


dlib.rectangle?

"""
Explanation: rectangle(xxx, xxx, xxx, xxx)と出てきましたね。これはdlibのrectangleというモノです。訳がわからないのでdlib.rectangle?と実行してみましょう。
End of explanation
"""


print(dets[0].left())
print(dets[0].top())
print(dets[0].right())
print(dets[0].bottom())

"""
Explanation: 恐らく
```Python
Docstring:      This object represents a rectangular area of an image.
Init docstring:
init( (object)arg1) -> None
init( (object)arg1, (int)left, (int)top, (int)right, (int)bottom) -> None
File:         
Type:           class
```
のような表示が出てきたと思います。詳しく説明しませんが，rectangle(四角形)にleft, top, right, bottomとくれば何となく想像できるでしょう。
End of explanation
"""


cv2.rectangle?

img = cv2.rectangle(img, (dets[0].left(), dets[0].top()), (dets[0].right(), dets[0].bottom()), (255, 0, 0))
cv2.imshow('image', img)
cv2.waitKey(2000)

"""
Explanation: 答えは四角形の左上，右下の座標です。では画像に四角形を重ねてみましょう。ここではcv2の機能を使います。使い方を見て実行してみましょう。
End of explanation
"""


import cv2
import dlib
cap = cv2.VideoCapture(0)
detector = dlib.get_frontal_face_detector()
key = 0
while key != 27:
    ret, img = cap.read()
    dets = detector(img, 1)
    if len(dets) > 0:
        img = cv2.rectangle(img, (dets[0].left(), dets[0].top()), (dets[0].right(), dets[0].bottom()), (255, 0, 0))
        cv2.imshow('image', img)
    else:
        cv2.imshow('image', img)
    key = cv2.waitKey(10)


"""
Explanation: 顔に四角形が重なりましたか?失敗した場合には顔が正面を向いていないか，rectangleに渡す座標が間違えています。ちなみにこれを連続的に実行すると以下のようになります。(ウィンドウをアクティブにしてESCキーを押すと止まります)
End of explanation
"""


ret, img = cap.read()

"""
Explanation: 3. 顔ランドマーク検出
いよいよ顔ランドマークです。顔ランドマークは学習済みのデータ，shape_predictor_68_face_landmarks.datを使います。これは顔ランドマーク68点を検出できます。その前に仕切り直しです。また顔をカメラに向けて以下を実行して下さい。
End of explanation
"""


predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')

"""
Explanation: では顔ランドマークの検出器の操縦桿を作りましょう。
End of explanation
"""


import urllib.request
urllib.request.urlretrieve("http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2", "shape_predictor_68_face_landmarks.dat.bz2")
import bz2
f = bz2.open("shape_predictor_68_face_landmarks.dat.bz2", "rb")
d = f.read()
f.close()
f = open("shape_predictor_68_face_landmarks.dat","wb")
f.write(d)
f.close()
predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')

"""
Explanation: もし，エラーが出てしまったらshape_predictor_68_face_landmarks.datファイルがこのノートブックファイルと同じ場所にないせいです。ネットからダウンロードしましょう。下のセルがdlib.netからbz2圧縮されたファイルを展開して保存する処理なので，一度実行していれば大丈夫です)
End of explanation
"""


dets = detector(img, 1)
shape = predictor(img, dets[0])

shape

"""
Explanation: 手順としてはdetectorで顔検出し，predictorで検出した顔領域内の顔ランドマークを検出，という流れです。
End of explanation
"""


dlib.full_object_detection?

"""
Explanation: 結果を入れたshapeを見てみようと思ったらdlib.full_object_detection at ....と出てきました。?を使って調べてみましょう。
End of explanation
"""


shape.parts?

"""
Explanation: ```Python
Docstring:      This object represents the location of an object in an image along with the     positions of each of its constituent parts.
Init docstring:
init( (object)arg1) -> None
init( (object)arg1, (object)arg2, (object)arg3) -> object :
    requires 
        - rect: dlib rectangle 
        - parts: list of dlib points
File:        
Type:           class
```
どうもrectとpartsがあるようです。rectは恐らくdetsと同じものでしょう。ではpartsはどうでしょう。
End of explanation
"""


shape.parts()[0]

"""
Explanation: Python
Docstring:
parts( (full_object_detection)arg1) -&gt; points :
    A vector of dlib points representing all of the parts.
Type:      method
と出てきました。実行すると場所が詰まったベクトルが出てくると言っています。ベクトルの何番目は[]で指定できます。
End of explanation
"""


dlib.point?

"""
Explanation: 出ました。0番です。さて，どこでしょう。これはググってみましょう。ついでにdlib.pointも調べてみましょう。
End of explanation
"""


print(shape.parts()[0].x)
print(shape.parts()[0].y)

"""
Explanation: ```Python
Docstring:      This object represents a single point of integer coordinates that maps directly to a dlib::point.
Init docstring:
init( (object)arg1) -> None
init( (object)arg1, (int)x, (int)y) -> None
File:         
Type:           class
```
とあるので，x, yで座標を指定できそうです。
End of explanation
"""


x1 = shape.parts()[36].x
y1 = shape.parts()[38].y
x2 = shape.parts()[39].x
y2 = shape.parts()[41].y

"""
Explanation: では取り敢えず右目を囲ってみましょう。左端は36番のx，上端は38番のy，右端は39番のx，下端は41番のyを使ってみます。長くなるのでそれぞれx1, y1, x2, y2に代入してしまいましょう。
End of explanation
"""


img = cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255))
cv2.imshow('image', img)
cv2.waitKey(2000)

"""
Explanation: そしてimgに四角形を書き込んでみましょう。
End of explanation
"""


import cv2
import dlib
cap = cv2.VideoCapture(0)
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
key = 0
while key != 27:
    ret, img = cap.read()
    dets = detector(img, 1)
    if len(dets) > 0:
        shape = predictor(img, dets[0])
        x1 = shape.parts()[36].x
        y1 = shape.parts()[38].y
        x2 = shape.parts()[39].x
        y2 = shape.parts()[41].y
        img = cv2.rectangle(img, (dets[0].left(), dets[0].top()), (dets[0].right(), dets[0].bottom()), (255, 0, 0))
        img = cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255))
    cv2.imshow('image', img)
    key = cv2.waitKey(10)

"""
Explanation: 先程の連続処理に手を加えてみましょう。
End of explanation
"""


ret, img = cap.read()
dets = detector(img, 1)
shape = predictor(img, dets[0])

"""
Explanation: 4. 顔ランドマークを使って何かやる
さて，最後です。ランドマークを使って雑コラをしてみます。とりあえず改変OKなものを探してここから拾ってきました。
また仕切り直しですのでカメラを見て下のセルを実行しましょう。
End of explanation
"""


x1 = shape.parts()[17].x
y1 = shape.parts()[19].y
x2 = shape.parts()[26].x
y2 = shape.parts()[29].y

"""
Explanation: 今度は両目を覆いたいので(x1, y1) = (17のx, 19のy), (x2, y2) = (26のx, 29のy)としました。
End of explanation
"""


img = cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255))
cv2.imshow('image', img)
cv2.waitKey(2000)

"""
Explanation: では囲えてるか確認しましょう。
End of explanation
"""


img2 = cv2.imread('cartoon-718659_640.png', cv2.IMREAD_ANYCOLOR)
newSize = (x2 - x1, y2 - y1)
img3 = cv2.resize(img2, newSize)
img[y1:y2, x1:x2] = img3

"""
Explanation: では画像の一部置き換えです。Pythonを使うと簡単ですが注意が必要です。
Python
置き換える画像の読み込み(cv2.imread)
置き換える画像をリサイズ(cv2.resize)，サイズは(x2 - x1, y2 - y1)
元画像[yの範囲, xの範囲] = リサイズした置き換える画像
となります。Pythonは通常「行，列」で扱っているので3行目はxとyが逆になっています。
End of explanation
"""


cv2.imshow('image', img)
cv2.waitKey(2000)

"""
Explanation: さて，確認してみましょう。
End of explanation
"""


import cv2
import dlib
cap = cv2.VideoCapture(0)
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
img2 = cv2.imread('cartoon-718659_640.png', cv2.IMREAD_ANYCOLOR)
key = 0
while key != 27:
    ret, img = cap.read()
    dets = detector(img, 1)
    if len(dets) > 0:
        shape = predictor(img, dets[0])
        x1 = shape.parts()[17].x
        y1 = shape.parts()[19].y
        x2 = shape.parts()[26].x
        y2 = shape.parts()[29].y
        newSize = (x2 - x1, y2 - y1)
        img3 = cv2.resize(img2, newSize)
        img[y1:y2, x1:x2] = img3
    cv2.imshow('image', img)
    key = cv2.waitKey(10)

"""
Explanation: では連続処理にしてみましょう。
End of explanation
"""


