'Delaunay face swap on webcam, OpenCV gives !_src.empty() error
I have two webcam feeds and I want to swap faces between them using OpenCV and triangulation. So far I'm able to move sourceframe face to replace destinationframe face. However now I try to do it other way around. With current code, I get the following error:
new_source_face_canvas_area_gray = cv2.cvtColor(new_source_face_canvas_area, cv2.COLOR_BGR2GRAY)
cv2.error: OpenCV(4.5.3) /tmp/pip-install-7krwkm_h/opencv-contrib-python_1e8bcbc3da2744c690bb65b52d8197bb/opencv/modules/imgproc/src/color.cpp:182:
error: (-215:Assertion failed) !_src.empty() in function 'cvtColor'
Is anyone able to point out what's happening there? I've tried to be logical when reversing the face swap from destionationframe to sourceframe, but I'm sure I'm doing silly mistake when trying to make everything "the opposite". Many thanks for your help!
sourceframe = video_process.frame
destinationframe = video_process.frame2
height, width, channels = destinationframe.shape
height2, width2, channels2 = sourceframe.shape
sourcegray = cv2.cvtColor(sourceframe, cv2.COLOR_BGR2GRAY)
destinationgray = cv2.cvtColor(destinationframe, cv2.COLOR_BGR2GRAY)
sourcemask = np.zeros_like(sourcegray)
destinationmask = np.zeros_like(destinationgray)
source_image_canvas = np.zeros((height2, width2, channels2), np.uint8)
destination_image_canvas = np.zeros((height, width, channels), np.uint8)
indexes_triangles = []
#landmarks of first face
sourcefaces = video_process.faces
destinationfaces = video_process.faces2
for sourceface in sourcefaces:
sourcelandmarks = video_process.landmarks
sourcelandmarks_points = []
for n in range(0, 68):
x = sourcelandmarks.part(n).x
y = sourcelandmarks.part(n).y
sourcelandmarks_points.append((x, y))
source_triangle_points = np.array(sourcelandmarks_points, np.int32)
sourceconvexhull = cv2.convexHull(source_triangle_points)
cv2.fillConvexPoly(sourcemask, sourceconvexhull, 255)
# Delaunay triangulation
sourcerect = cv2.boundingRect(sourceconvexhull)
sourcesubdiv = cv2.Subdiv2D(sourcerect)
sourcesubdiv.insert(sourcelandmarks_points)
sourcetriangles = sourcesubdiv.getTriangleList()
sourcetriangles = np.array(sourcetriangles, dtype=np.int32)
for t in sourcetriangles:
pt1 = (t[0], t[1])
pt2 = (t[2], t[3])
pt3 = (t[4], t[5])
index_pt1 = np.where((source_triangle_points == pt1).all(axis=1))
index_pt1 = extract_index_nparray(index_pt1)
index_pt2 = np.where((source_triangle_points == pt2).all(axis=1))
index_pt2 = extract_index_nparray(index_pt2)
index_pt3 = np.where((source_triangle_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:
source_triangle = [index_pt1, index_pt2, index_pt3]
indexes_triangles.append(source_triangle)
# Face 2
for destinationface in destinationfaces:
destinationlandmarks = video_process.landmarks2
destinationlandmarks_points = []
for n in range(0, 68):
x = destinationlandmarks.part(n).x
y = destinationlandmarks.part(n).y
destinationlandmarks_points.append((x, y))
destination_triangle_points = np.array(destinationlandmarks_points, np.int32)
destinationconvexhull = cv2.convexHull(destination_triangle_points)
cv2.fillConvexPoly(destinationmask, destinationconvexhull, 255)
# Iterating through all source delaunay triangle and superimposing source triangles in empty destination canvas after warping to same size as destination triangles' shape
for triangle_index in indexes_triangles:
# Triangulation of the first face
tr1_pt1 = sourcelandmarks_points[triangle_index[0]]
tr1_pt2 = sourcelandmarks_points[triangle_index[1]]
tr1_pt3 = sourcelandmarks_points[triangle_index[2]]
source_triangle = np.array([tr1_pt1, tr1_pt2, tr1_pt3], np.int32)
# Source rectangle
source_rectangle = cv2.boundingRect(source_triangle)
(x, y, w, h) = source_rectangle
(xu, yu, wu, hu) = source_rectangle
cropped_source_rectangle = sourceframe[y: yu + hu, x: xu + wu]
cropped_source_rectangle_mask = np.zeros((hu, wu), np.uint8)
source_triangle_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_source_rectangle_mask, source_triangle_points, 255)
# Triangulation of second face
tr2_pt1 = destinationlandmarks_points[triangle_index[0]]
tr2_pt2 = destinationlandmarks_points[triangle_index[1]]
tr2_pt3 = destinationlandmarks_points[triangle_index[2]]
destination_triangle = np.array([tr2_pt1, tr2_pt2, tr2_pt3], np.int32)
# Dest rectangle
destination_rectangle = cv2.boundingRect(destination_triangle)
(x, y, w, h) = destination_rectangle
cropped_destination_rectangle = sourceframe[y: y + h, x: x + w]
cropped_destination_rectangle_mask = np.zeros((h, w), np.uint8)
destination_triangle_points = 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_destination_rectangle_mask, destination_triangle_points, 255)
# Warp source triangle to match shape of destination triangle and put it over destination triangle mask
source_triangle_points = np.float32(source_triangle_points)
destination_triangle_points = np.float32(destination_triangle_points)
matrix = cv2.getAffineTransform(source_triangle_points, destination_triangle_points)
matrix2 = cv2.getAffineTransform(destination_triangle_points, source_triangle_points)
warped_rectangle = cv2.warpAffine(cropped_source_rectangle, matrix, (w, h))
warped_triangle = cv2.bitwise_and(warped_rectangle, warped_rectangle, mask=cropped_destination_rectangle_mask)
warped_rectangle_2 = cv2.warpAffine(cropped_destination_rectangle, matrix2, (wu, hu))
warped_triangle_2 = cv2.bitwise_and(warped_rectangle_2, warped_rectangle_2, mask=cropped_source_rectangle_mask)
# Reconstructing destination face in empty canvas of destination image
new_dest_face_canvas_area = destination_image_canvas[y: y + h, x: x + w] # h y etc. are from dest rect and it works
new_dest_face_canvas_area_gray = cv2.cvtColor(new_dest_face_canvas_area, cv2.COLOR_BGR2GRAY)
_, mask_created_triangle = cv2.threshold(new_dest_face_canvas_area_gray, 1, 255, cv2.THRESH_BINARY_INV)
warped_triangle = cv2.bitwise_and(warped_triangle, warped_triangle, mask=mask_created_triangle)
new_dest_face_canvas_area = cv2.add(new_dest_face_canvas_area, warped_triangle)
destination_image_canvas[y: y + h, x: x + w] = new_dest_face_canvas_area
# Reconstructing source face in empty canvas of source image
new_source_face_canvas_area = source_image_canvas[y: yu + hu, x: xu + wu] # hu et are from source rect now
new_source_face_canvas_area_gray = cv2.cvtColor(new_source_face_canvas_area, cv2.COLOR_BGR2GRAY)
_, mask_created_triangle_2 = cv2.threshold(new_source_face_canvas_area_gray, 1, 255, cv2.THRESH_BINARY_INV)
warped_triangle_2 = cv2.bitwise_and(warped_triangle_2, warped_triangle_2, mask=mask_created_triangle_2)
new_source_face_canvas_area = cv2.add(new_source_face_canvas_area, warped_triangle_2)
source_image_canvas[y: yu + hu, x: xu + wu] = new_source_face_canvas_area
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
