import cv2
import numpy as np
import dlib
import time

def extract_index_nparray(nparray):
    index = None
    for num in nparray[0]:
        index = num
        break
    return index


img = cv2.imread("modifiche\\bradley_cooper.jpg")
img2 = cv2.imread("modifiche\\volto.jpg")
face_cascade = cv2.CascadeClassifier('modifiche\\haarcascade_frontalface_alt.xml')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img2_gray = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
mask = np.zeros_like(img_gray)

img2_new_face = np.zeros_like(img2)

detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("modifiche\\shape_predictor_68_face_landmarks.dat")
# Face 1
cap = cv2.VideoCapture(0)
while(True):
    ret, frame = cap.read()
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    faces = face_cascade.detectMultiScale(gray, scaleFactor=1.05, minNeighbors=5, minSize=(50, 50),
                                          flags=cv2.CASCADE_SCALE_IMAGE)
    for(x,y,w,h) in faces:
        #print(x,y,w,h)
        padding_top = int(h * 0.50)
        padding_bottom = int(h * 0.35)
        start_cord_x = x
        start_cord_y = y - padding_top
        end_cord_x = x + w
        new_h = h + padding_top + padding_bottom
        end_cord_y = start_cord_y + new_h
        faccia = gray[start_cord_y:end_cord_y, start_cord_x:end_cord_x]
        roi_gray = gray[y:y+h, x:x+w] #(y_cordi_start, y_cordi_emd)

        roi_color = frame[y:y+h, x:x+w]
        #volto_sconosciuto_features = face_recognition.face_encodings(frame, roi_color)
        # create a binary thresholded image

        roi_color = frame[y:y+h, x:x+w]
        gr = cv2.cvtColor(frame[y:y+h, x:x+w], cv2.COLOR_BGR2BGRA)
        mask = np.zeros_like(frame)
        img = frame
        c = 1

    if c == 1:
        break

cv2.imshow("s", frame)

cv2.waitKey(0)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
mask = np.zeros_like(img_gray)
faces = detector(img,0)

print("C")
for face in faces:
    print("C")
    landmarks = predictor(img_gray, face)
    landmarks_points = []
    for n in range(0, 68):
        x = landmarks.part(n).x
        y = landmarks.part(n).y
        landmarks_points.append((x, y))

        # cv2.circle(img, (x, y), 3, (0, 0, 255), -1)

    points = np.array(landmarks_points, np.int32)
    convexhull = cv2.convexHull(points)
    # cv2.polylines(img, [convexhull], True, (255, 0, 0), 3)
    cv2.fillConvexPoly(mask, convexhull, 255)

    face_image_1 = cv2.bitwise_and(img, img, mask=mask)

    # Delaunay triangulation
    rect = cv2.boundingRect(convexhull)
    subdiv = cv2.Subdiv2D(rect)
    subdiv.insert(landmarks_points)
    triangles = subdiv.getTriangleList()
    triangles = np.array(triangles, dtype=np.int32)

    indexes_triangles = []
    for t in triangles:
        pt1 = (t[0], t[1])
        pt2 = (t[2], t[3])
        pt3 = (t[4], t[5])

        index_pt1 = np.where((points == pt1).all(axis=1))
        index_pt1 = extract_index_nparray(index_pt1)

        index_pt2 = np.where((points == pt2).all(axis=1))
        index_pt2 = extract_index_nparray(index_pt2)

        index_pt3 = np.where((points == pt3).all(axis=1))
        index_pt3 = extract_index_nparray(index_pt3)

        if index_pt1 is not None and index_pt2 is not None and index_pt3 is not None:
            triangle = [index_pt1, index_pt2, index_pt3]
            indexes_triangles.append(triangle)




# Face 2
faces2 = detector(img2_gray)
for face in faces2:
	landmarks = predictor(img2_gray, face)
	landmarks_points2 = []
	for n in range(0, 68):
		x = landmarks.part(n).x
		y = landmarks.part(n).y
		landmarks_points2.append((x, y))

		#cv2.circle(img2, (x, y), 3, (0, 255, 0), -1)
	points2 = np.array(landmarks_points2, np.int32)
	convexhull2 = cv2.convexHull(points2)

# Triangulation of both faces
for triangle_index in indexes_triangles:
	# Triangulation of the first face
	tr1_pt1 = landmarks_points[triangle_index[0]]
	tr1_pt2 = landmarks_points[triangle_index[1]]
	tr1_pt3 = landmarks_points[triangle_index[2]]
	triangle1 = np.array([tr1_pt1, tr1_pt2, tr1_pt3], np.int32)

	rect1 = cv2.boundingRect(triangle1)
	(x, y, w, h) = rect1
	cropped_triangle = img[y: y + h, x: x + w]
	cropped_tr1_mask = np.zeros((h, w), np.uint8)

	points = np.array([[tr1_pt1[0] - x, tr1_pt1[1] - y],
					  [tr1_pt2[0] - x, tr1_pt2[1] - y],
					  [tr1_pt3[0] - x, tr1_pt3[1] - y]], np.int32)

	cv2.fillConvexPoly(cropped_tr1_mask, points, 255)
	cropped_triangle = cv2.bitwise_and(cropped_triangle, cropped_triangle,
									   mask=cropped_tr1_mask)


	# cv2.line(img, tr1_pt1, tr1_pt2, (0, 0, 255), 2)
	# cv2.line(img, tr1_pt3, tr1_pt2, (0, 0, 255), 2)
	# cv2.line(img, tr1_pt1, tr1_pt3, (0, 0, 255), 2)

	# Triangulation of second face
	tr2_pt1 = landmarks_points2[triangle_index[0]]
	tr2_pt2 = landmarks_points2[triangle_index[1]]
	tr2_pt3 = landmarks_points2[triangle_index[2]]
	triangle2 = np.array([tr2_pt1, tr2_pt2, tr2_pt3], np.int32)

	rect2 = cv2.boundingRect(triangle2)
	(x, y, w, h) = rect2
	cropped_triangle2 = img2[y: y + h, x: x + w]
	cropped_tr2_mask = np.zeros((h, w), np.uint8)

	points2 = np.array([[tr2_pt1[0] - x, tr2_pt1[1] - y],
					   [tr2_pt2[0] - x, tr2_pt2[1] - y],
					   [tr2_pt3[0] - x, tr2_pt3[1] - y]], np.int32)

	cv2.fillConvexPoly(cropped_tr2_mask, points2, 255)
	cropped_triangle2 = cv2.bitwise_and(cropped_triangle2, cropped_triangle2,
									   mask=cropped_tr2_mask)

	# cv2.line(img2, tr2_pt1, tr2_pt2, (0, 0, 255), 2)
	# cv2.line(img2, tr2_pt3, tr2_pt2, (0, 0, 255), 2)
	# cv2.line(img2, tr2_pt1, tr2_pt3, (0, 0, 255), 2)

	# Warp triangles
	points = np.float32(points)
	points2 = np.float32(points2)
	M = cv2.getAffineTransform(points, points2)
	warped_triangle = cv2.warpAffine(cropped_triangle, M, (w, h))

	# Reconstructing destination face
	img2_new_face_rect_area = img2_new_face[y: y + h, x: x + w]
	img2_new_face_rect_area = cv2.add(img2_new_face_rect_area, warped_triangle)
	img2_new_face[y: y + h, x: x + w] = img2_new_face_rect_area

# Face swapped (putting 1st face into 2nd face)
img2_face_mask = np.zeros_like(img2_gray)
img2_head_mask = cv2.fillConvexPoly(img2_face_mask, convexhull2, 255)
img2_face_mask = cv2.bitwise_not(img2_head_mask)

img2_head_noface = np.zeros_like(img)

img2_head_noface = cv2.bitwise_and(img2, img2, mask=img2_face_mask)
result = cv2.add(img2_head_noface, img2_new_face)


cv2.imshow("Img", img)
cv2.imshow("img2", img2)
cv2.imshow("result", result)

cv2.waitKey(0)
cv2.destroyAllWindows()
