Akjava's picture
improve copy image code
ec53eb5
import os
import json
#from glibvision.cv2_utils import get_numpy_text
from glibvision.numpy_utils import bulge_polygon
import numpy as np
import math
USE_CACHE = True
# face structures are same
# MIT LICENSED
# https://github.com/ageitgey/face_recognition
TOP_LIP = "top_lip"
BOTTOM_LIP = "bottom_lip"
PARTS_CHIN ="chin"
PARTS_LEFT_EYEBROW ="left_eyebrow"
PARTS_RIGHT_EYEBROW ="right_eyebrow"
PARTS_LEFT_EYE ="left_eye"
PARTS_RIGHT_EYE ="right_eye"
POINTS_TOP_LIP = "top_lip"
POINTS_BOTTOM_LIP = "bottom_lip"
POINTS_CHIN = "chin"
COLOR_WHITE=(255,255,255)
COLOR_BLACK=(0,0,0)
COLOR_ALPHA=(0,0,0,0)
DEBUG = False
DEBUG_CHIN = False
face_recognition = None
def load_image_file(path):
image = face_recognition.load_image_file(path)
data_path=path+".json"
if USE_CACHE and os.path.exists(data_path):
with open(data_path, "r") as f:
face_landmarks_list = json.loads(f.read())
else:
face_landmarks_list = image_to_landmarks_list(image)
if USE_CACHE:
json_data = json.dumps(face_landmarks_list)
with open(data_path, "w") as f:
f.write(json_data)
return image,face_landmarks_list
def save_landmarks(face_landmarks,out_path):
json_data = json.dumps(face_landmarks)
with open(out_path, "w") as f:
f.write(json_data)
def load_landmarks(input_path):
with open(input_path, "r") as f:
face_landmarks_list = json.loads(f.read())
return face_landmarks_list
def image_to_landmarks_list(image):
face_landmarks_list = face_recognition.face_landmarks(image)
return face_landmarks_list
def fill_polygon(image,face_landmarks_list,key,thickness=1,line_color=(255,255,255),fill_color = (255,255,255)):
points=get_landmark_points(face_landmarks_list,key)
np_points = np.array(points,dtype=np.int32)
cv2.fillPoly(image, [np_points], fill_color)
cv2.polylines(image, [np_points], isClosed=True, color=line_color, thickness=thickness)
def fill_lip(image,face_landmarks_list,thickness=1,line_color=(255,255,255),fill_color = (255,255,255)):
points1=get_landmark_points(face_landmarks_list,TOP_LIP)[0:7]
points2=get_landmark_points(face_landmarks_list,BOTTOM_LIP)[0:7]
np_points = np.array(points1+points2[::-1],dtype=np.int32)
cv2.fillPoly(image, [np_points], fill_color)
if thickness > 0:
cv2.polylines(image, [np_points], isClosed=False, color=line_color, thickness=thickness)
def fill_top(image,face_landmarks_list,thickness=1,line_color=(255,255,255),fill_color = (255,255,255)):
points1=get_landmark_points(face_landmarks_list,TOP_LIP)[0:7]
np_points = np.array(points1,dtype=np.int32)
cv2.fillPoly(image, [np_points], fill_color)
if thickness > 0:
cv2.polylines(image, [np_points], isClosed=False, color=line_color, thickness=thickness)
def fill_top_lower(image,face_landmarks_list,thickness=1,line_color=(255,255,255),fill_color = (255,255,255)):
top_lip_points=get_landmark_points(face_landmarks_list,TOP_LIP) # 5 to 7 ,1 t- 11
points1 = [lerp_points(top_lip_points[5],top_lip_points[7],0.7)]+ \
[mid_points(top_lip_points[7],top_lip_points[8])]+ \
list(top_lip_points[8:11]) +\
[mid_points(top_lip_points[10],top_lip_points[11])]+ \
[lerp_points(top_lip_points[1],top_lip_points[11],0.7)]+\
[mid_points(top_lip_points[2],top_lip_points[10])]+\
[mid_points(top_lip_points[3],top_lip_points[9])]+\
[mid_points(top_lip_points[4],top_lip_points[8])]
np_points = np.array(points1,dtype=np.int32)
cv2.fillPoly(image, [np_points], fill_color)
if thickness > 0:
cv2.polylines(image, [np_points], isClosed=False, color=line_color, thickness=thickness)
def get_lip_mask_points(face_landmarks_list):
points1=get_landmark_points(face_landmarks_list,TOP_LIP)[0:7]
points2=get_landmark_points(face_landmarks_list,BOTTOM_LIP)[0:7]
return points1+points2
from scipy.special import comb
def bernstein_poly(i, n, t):
"""
n 次ベジェ曲線の i 番目の Bernstein 基底関数を計算する
"""
return comb(n, i) * (t**(n-i)) * (1 - t)**i
def bezier_curve(points, num_points=100):
"""
与えられた点からベジェ曲線を計算する
"""
nPoints = len(points)
xPoints = np.array([p[0] for p in points])
yPoints = np.array([p[1] for p in points])
t = np.linspace(0.0, 1.0, num_points)
polynomial_array = np.array([bernstein_poly(i, nPoints-1, t) for i in range(0, nPoints)])
xvals = np.dot(xPoints, polynomial_array)
yvals = np.dot(yPoints, polynomial_array)
return np.array(list(zip(xvals, yvals)))
import cv2
import numpy as np
def fill_eyes(image,face_landmarks_list,thickness=1,line_color=(255,255,255),fill_color = (255,255,255)):
points1=get_landmark_points(face_landmarks_list,PARTS_LEFT_EYE)
points2=get_landmark_points(face_landmarks_list,PARTS_RIGHT_EYE)
for points in [points1,points2]:
#points = bezier_curve(points, num_points=10)
#print(points)
points = bulge_polygon(points, bulge_factor=0.2)
#print(points)
np_points = np.array(points,dtype=np.int32)
cv2.fillPoly(image, [np_points], fill_color)
if thickness > 0:
cv2.polylines(image, [np_points], isClosed=False, color=line_color, thickness=thickness)
def fill_face(image,face_landmarks_list,thickness=1,line_color=(255,255,255),fill_color = (255,255,255)):
points1=get_landmark_points(face_landmarks_list,PARTS_LEFT_EYEBROW)
points2=get_landmark_points(face_landmarks_list,PARTS_RIGHT_EYEBROW)
points3=get_landmark_points(face_landmarks_list,PARTS_CHIN)
np_points = np.array(points1+points2+points3[::-1],dtype=np.int32)
cv2.fillPoly(image, [np_points], fill_color)
cv2.polylines(image, [np_points], isClosed=False, color=line_color, thickness=thickness)
def fill_face_inside(image,face_landmarks_list,thickness=1,line_color=(255,255,255),fill_color = (255,255,255)):
print("not support yet")
return None
points1=get_landmark_points(face_landmarks_list,PARTS_LEFT_EYEBROW)
points2=get_landmark_points(face_landmarks_list,PARTS_RIGHT_EYEBROW)
points3=get_landmark_points(face_landmarks_list,PARTS_CHIN)
points3=get_landmark_points(face_landmarks_list,PARTS_CHIN)
points3=get_landmark_points(face_landmarks_list,PARTS_CHIN)
np_points = np.array(points1+points2+points3[::-1],dtype=np.int32)
cv2.fillPoly(image, [np_points], fill_color)
cv2.polylines(image, [np_points], isClosed=False, color=line_color, thickness=thickness)
def half_pt(point1,point2):
return [sum(x) / 2 for x in zip(point1, point2)]
def line_lip(image,face_landmarks_list,key,thickness=1,line_color=(255,255,255)):
points=get_landmark_points(face_landmarks_list,key)
print(len(points))
#st=[(points[0]+points[11])/2]
st = [sum(x) / 2 for x in zip(points[0], points[11])]
#et=[(points[6]+points[7])/2]
et = [sum(x) / 2 for x in zip(points[6], points[7])]
print(et)
print(points)
np_points = np.array([st]+points[1:6]+[et],dtype=np.int32)
#if key == TOP_LIP:
cv2.polylines(image, [np_points], isClosed=False, color=line_color, thickness=thickness)
def get_lip_hole_points(face_landmarks_list):
top_points=get_landmark_points(face_landmarks_list,TOP_LIP)
bottom_points=get_landmark_points(face_landmarks_list,BOTTOM_LIP)
return top_points[7:]+bottom_points[7:]#[::-1]
#np_points = np.array(top_points[7:]+bottom_points[7:][::-1],dtype=np.int32)
def get_lip_hole_top_points(face_landmarks_list):
top_points=get_landmark_points(face_landmarks_list,TOP_LIP)
#bottom_points=get_landmark_points(face_landmarks_list,BOTTOM_LIP)
return top_points[7:]
def get_lip_hole_bottom_points(face_landmarks_list):
#top_points=get_landmark_points(face_landmarks_list,TOP_LIP)
bottom_points=get_landmark_points(face_landmarks_list,BOTTOM_LIP)
#inverted for connect top
return bottom_points[7:][::-1]
#for hide too long tooth
def get_lip_hole_bottom_half_points(face_landmarks_list):
#top_points=get_landmark_points(face_landmarks_list,TOP_LIP)
bottom_points=get_landmark_points(face_landmarks_list,BOTTOM_LIP)
#inverted for connect top
st = [sum(x) / 2 for x in zip(bottom_points[7], bottom_points[8])]
et = [sum(x) / 2 for x in zip(bottom_points[10], bottom_points[11])]
points = [st]+bottom_points[8:11]+[et]
#print(points)
return points[::-1]
def fill_points(points,image,thickness=1,line_color=(255,255,255),fill_color = (255,255,255)):
np_points = np.array(points,dtype=np.int32)
cv2.fillPoly(image, [np_points], fill_color)
if thickness>0:
cv2.polylines(image, [np_points], isClosed=False, color=line_color, thickness=thickness)
def fill_lip_hole_top(image,face_landmarks_list,thickness=1,line_color=(255,255,255),fill_color = (255,255,255)):
np_points = np.array(get_lip_hole_top_points(face_landmarks_list),dtype=np.int32)
cv2.fillPoly(image, [np_points], fill_color)
cv2.polylines(image, [np_points], isClosed=False, color=line_color, thickness=thickness)
def fill_lip_hole(image,face_landmarks_list,thickness=1,line_color=(255,255,255),fill_color = (255,255,255)):
np_points = np.array(get_lip_hole_points(face_landmarks_list),dtype=np.int32)
#print(np_points)
cv2.fillPoly(image, [np_points], fill_color)
cv2.polylines(image, [np_points], isClosed=False, color=line_color, thickness=thickness)
def get_landmark_points(face_landmarks_list,key):
matching_landmark_points = []
for face_landmarks in face_landmarks_list:
for landmark_name, landmark_points in face_landmarks.items():
#matching_landmark_points = landmark_points.copy()
if landmark_name ==key:
for value in landmark_points:
matching_landmark_points.append([value[0],value[1]])
return tuple(matching_landmark_points)
def get_image_size(cv2_image):
return cv2_image.shape[:2]
def get_top_lip_box(face_landmarks_list,margin = 0):
print(f"get_top_lip_box margin = {margin}")
points = get_landmark_points(face_landmarks_list,TOP_LIP)
box= points_to_box(points)
if margin>0:
return ((box[0][0] - margin,box[0][1] - margin),(box[1][0] + margin, box[1][1] + margin))
else:
return box
def get_points_box(face_landmarks_list,key,margin = 0):
print(f"margin = {margin}")
points = get_landmark_points(face_landmarks_list,key)
box= points_to_box(points)
if margin>0:
return ((box[0][0] - margin,box[0][1] - margin),(box[1][0] + margin, box[1][1] + margin))
else:
return box
#for size up
def create_moved_image(image,src_points,dst_points,force_size=None):
# keep top of lip stable but affing must be 4 point
#print(f"src = {src_points}")
#print(f"dst = {dst_points}")
src_pts=np.array([src_points],dtype=np.float32)
dst_pts=np.array([dst_points],dtype=np.float32)
#BORDER_REPLICATE
return warp_with_auto_resize(image, src_pts, dst_pts,cv2.BORDER_REPLICATE,force_size)
# lip-index
"""
1 2 3 4 5
0 6
11 10 9 8 7
"""
def get_top_lip_align_points(face_landmarks_list):
landmark=get_landmark_points(face_landmarks_list,TOP_LIP)
index_center = 3
index_right= 0 #mirror
#index_ritht_top= 2 #mirror
#index_left_top= 4 #mirror
index_left = 6
#if landmark_name ==key:
# 0 is right edge
x1 = landmark[index_right][0]
y1 = landmark[index_right][1]
# 6 is left edge
x2 = landmark[index_left][0]
y2 = landmark[index_left][1]
#left_top = landmark[index_left_top][1]
#right_top = landmark[index_ritht_top][1]
#top = left_top if left_top<right_top else right_top
# bottom center position
cx = (x1+x2)/2
cy = (y1+y2)/2
diffx=(landmark[index_center][0]-cx)
diffy=(landmark[index_center][1]-cy)
#print(f"x1={x1} y1={y1} x2={x2} y2={y2} cx={cx} cy={cy} diffx={diffx} diffy={diffy}")
#plt.scatter(cx,cy, c='r', s=10)
return ((int(x1+diffx),int(y1+diffy)),(int(x2+diffx),int(y2+diffy)),(x1,y1),(x2,y2))
def calculate_new_point(start_point, distance, angle):
x1, y1 = start_point
angle_rad = math.radians(angle)
# 新しい点の座標を計算
new_x = x1 + distance * math.cos(angle_rad)
new_y = y1 + distance * math.sin(angle_rad)
return (new_x, new_y)
def calculate_clockwise_angle(point1, point2):
x1, y1 = point1
x2, y2 = point2
# atan2を使用して角度を計算
angle_rad = math.atan2(y2 - y1, x2 - x1)
# 反時計回りから時計回りに変換
if angle_rad < 0:
angle_rad += 2 * math.pi
# ラジアンから度に変換
angle_deg = math.degrees(angle_rad)
return angle_deg
def get_bottom_lip_align_points(landmarks_list):
points = get_landmark_points(landmarks_list,POINTS_BOTTOM_LIP)
return (points[0],points[3],points[6],points[9])
def get_bottom_lip_width_height(landmarks_list):
points = get_landmark_points(landmarks_list,POINTS_BOTTOM_LIP)
return (points[0][0] -points[6][0],points[3][1] -points[9][1])
def crop_image(image,x1,y1,x2,y2):
return image[y1:y2, x1:x2]
def crop_cv2_image_by_box(image,box):
return crop_image_by_box(image,box)
def crop_image_by_box(image,box):
print(f"crop_cv2_image_by_box yy2 xx2 {box[0][1]}:{box[1][1]},{box[0][0]}:{box[1][0]}")
return image[box[0][1]:box[1][1], box[0][0]:box[1][0]]
def get_top_lip_datas(img,margin=4):
landmarks_list=image_to_landmarks_list(img)
box = get_top_lip_box(landmarks_list,margin)
cropped_img = crop_cv2_image_by_box(img,box)
points = get_top_lip_points(landmarks_list) #its rectangle but not square
return landmarks_list,cropped_img,points,box
def get_bottom_lip_datas(img,margin=4):
landmarks_list=image_to_landmarks_list(img)
box = get_points_box(landmarks_list,POINTS_BOTTOM_LIP,margin)
cropped_img = crop_cv2_image_by_box(img,box)
points = get_bottom_lip_align_points(landmarks_list) #its rectangle but not square
return landmarks_list,cropped_img,points,box
def offset_points(points,offset):
new_points = []
for point in points:
new_points.append((point[0]-offset[0],point[1]-offset[1]))
return new_points
def points_to_box(points):
min_x = 0
min_y = 0
min_x = float('inf')
min_y = float('inf')
max_x= 0
max_y= 0
for point in points:
if point[0]>max_x:
max_x=int(point[0])
if point[1]>max_y:
max_y=int(point[1])
if point[0]<min_x:
min_x=int(point[0])
if point[1]<min_y:
min_y=int(point[1])
return ((min_x,min_y),(max_x,max_y))
import cv2
import numpy as np
def warp_with_auto_resize(img, src_pts, dst_pts, borderMode=cv2.BORDER_TRANSPARENT, force_size=None):
"""
画像を WRAP 変換し、はみ出した場合は自動的にサイズを調整します。
Args:
img: 変換対象の画像 (numpy array)
src_pts: 変換元の四角形の頂点 (numpy array)
dst_pts: 変換先の四角形の頂点 (numpy array)
Returns:
変換後の画像 (numpy array)
"""
# 変換行列を計算
mat = cv2.getPerspectiveTransform(src_pts, dst_pts)
# 変換後の画像サイズを計算
h, w = img.shape[:2]
corners = np.float32([[0, 0], [w, 0], [w, h], [0, h]])
warped_corners = cv2.perspectiveTransform(corners.reshape(-1, 1, 2), mat).reshape(-1, 2)
# 変換後の画像の最小矩形を計算 (元の画像の四隅も含めて計算)
min_x = np.min(warped_corners[:, 0])
min_y = np.min(warped_corners[:, 1])
max_x = np.max(warped_corners[:, 0])
max_y = np.max(warped_corners[:, 1])
new_w, new_h = int(max_x - min_x), int(max_y - min_y)
# 変換行列を更新 (平行移動成分を追加)
mat[0, 2] += -min_x
mat[1, 2] += -min_y
if force_size:
new_w = force_size[0]
new_h = force_size[1]
warped_img = cv2.warpPerspective(img, mat, (new_w, new_h), flags=cv2.INTER_LANCZOS4, borderMode=borderMode)
return warped_img
def warp_with_auto_resize1(img, src_pts, dst_pts,borderMode= cv2.BORDER_TRANSPARENT,force_size=None):
"""
画像を WRAP 変換し、はみ出した場合は自動的にサイズを調整します。
Args:
img: 変換対象の画像 (numpy array)
src_pts: 変換元の四角形の頂点 (numpy array)
dst_pts: 変換先の四角形の頂点 (numpy array)
Returns:
変換後の画像 (numpy array)
"""
# 変換行列を計算
mat = cv2.getPerspectiveTransform(src_pts, dst_pts)
# 変換後の画像サイズを計算
h, w = img.shape[:2]
#print(f"img w{w} h{h}")
corners = np.float32([[0, 0], [w, 0], [w, h], [0, h]])
warped_corners = cv2.perspectiveTransform(corners.reshape(-1, 1, 2), mat).reshape(-1, 2)
# 変換後の画像の最小矩形を計算
min_x, min_y = np.min(warped_corners, axis=0)
max_x, max_y = np.max(warped_corners, axis=0)
new_w, new_h = int(max_x - min_x), int(max_y - min_y)
#print(f"min x {min_x} min y {min_y}")
#print(f"max x {max_x} max y {max_y}")
# 変換行列を更新 (平行移動成分を追加)
mat[0, 2] += -min_x
mat[1, 2] += -min_y
#print(f"audo w{new_w} h{new_h}")
if force_size:
new_w = force_size[0]
new_h = force_size[1]
warped_img = cv2.warpPerspective(img, mat, (new_w, new_h),flags=cv2.INTER_LANCZOS4, borderMode=borderMode)
return warped_img
def get_channel(np_array):
return np_array.shape[2] if np_array.ndim == 3 else 1
def print_numpy(np_array,key=""):
channel = get_channel(np_array)
print(f"{key} shape = {np_array.shape} channel = {channel} ndim = {np_array.ndim} size = {np_array.size}")
def create_color_image(img,color=(255,255,255)):
mask = np.zeros_like(img)
h, w = img.shape[:2]
cv2.rectangle(mask, (0, 0), (w, h), color, -1)
return mask
def create_mask(img,color=(255,255,255)):
mask = np.zeros_like(img)
h, w = img.shape[:2]
cv2.rectangle(mask, (0, 0), (w, h), color, -1)
return mask
def create_rgba(width,height):
return np.zeros((height, width, 4), dtype=np.uint8)
def create_rgb(width,height):
return np.zeros((height, width, 3), dtype=np.uint8)
def create_gray(width,height):
return np.zeros((height, width), dtype=np.uint8)
def copy_image(img1, img2, x, y):
"""
Paste img2 onto img1 at position (x, y).
If img2 extends beyond the bounds of img1, only the overlapping part is pasted.
Parameters:
img1 (numpy.ndarray): The base image to modify (H, W, C).
img2 (numpy.ndarray): The image to paste onto img1 (h, w, C).
x (int): The x-coordinate where img2 will be placed.
y (int): The y-coordinate where img2 will be placed.
Raises:
TypeError: If img1 or img2 are not NumPy arrays.
ValueError: If x or y are out of bounds of img1.
ValueError: If img1 and img2 do not have the same number of channels or are not 3-dimensional arrays.
"""
# Type check
if not isinstance(img1, np.ndarray) or not isinstance(img2, np.ndarray):
raise TypeError("img1 and img2 must be NumPy arrays.")
# Channel count check
if img1.ndim != 3 or img2.ndim != 3 or img1.shape[2] != img2.shape[2]:
raise ValueError("img1 and img2 must have the same number of channels and be 3-dimensional arrays.")
# Bounds check
max_y, max_x, _ = img1.shape
if not (0 <= y < max_y and 0 <= x < max_x):
raise ValueError(f"x ({x}) and y ({y}) must be within the bounds of img1 ({max_x}, {max_y}).")
# Calculate the height and width of the overlapping part
h = min(img2.shape[0], max_y - y)
w = min(img2.shape[1], max_x - x)
# Paste the overlapping part
img1[y:y+h, x:x+w] = img2[:h, :w]
def copy_color(img1,x,y,x2,y2,color):
color_img = np.full((y2-y, x2-x, 4), color, dtype=np.uint8)
img1[y:y2, x:x2] = color_img
def multiply_point(point,multiply):
return int(point[0]*multiply),int(point[1]*multiply)
def get_resized_top_pos(points,multiply=0.5):
diff_left = multiply_point((points[0][0]-points[2][0],points[0][1]-points[2][1]),multiply)
diff_right = multiply_point((points[1][0]-points[3][0],points[1][1]-points[3][1]),multiply)
return (diff_right,diff_left)
def get_alpha_image(base_image,landmarks_list,key,margin = 0,dilation_size = 2,gaussian_size = 2):
box = get_points_box(landmarks_list,key,margin)
# box expand margin
cropped_img = crop_cv2_image_by_box(base_image,box)
# convert RGBA
if cropped_img.shape[2] == 3: # Check if the image has 3 channels (RGB)
image_rgba = cv2.cvtColor(cropped_img, cv2.COLOR_BGR2BGRA)
mask = np.zeros(cropped_img.shape[:2], dtype="uint8")
else:
print("already alpha skipped")
image_rgba = cropped_img
mask = np.zeros(cropped_img.shape[:2], dtype="uint8")
#mask = cropped_img[:, :, 3].copy() # if you use this .some how block
global_points = get_landmark_points(landmarks_list,key)
local_points = offset_points(global_points,box[0])
print(local_points)
# create Lip Mask
np_points = np.array(local_points,dtype=np.int32)
cv2.fillPoly(mask, [np_points], 255)
kernel = np.ones((dilation_size, dilation_size), np.uint8)
dilated_mask = cv2.dilate(mask, kernel, iterations=1)
#dilated_mask = cv2.erode(mask, kernel, iterations=1) # TODO support dilation_size
# Gaussian Blur
if gaussian_size > 0:
smooth_mask = cv2.GaussianBlur(dilated_mask, (0,0 ), sigmaX=gaussian_size, sigmaY=gaussian_size)
expanded_mask = np.expand_dims(smooth_mask, axis=-1)
else:
expanded_mask = np.expand_dims(dilated_mask, axis=-1)
#lip_utils.print_numpy(image_rgba,"rgba")
#lip_utils.print_numpy(smooth_mask,"smooth")
#lip_utils.print_numpy(expanded_mask,"expanded_mask")
image_rgba[..., 3] = expanded_mask[..., 0]
return image_rgba,box
def apply_mask(image,mask):
if len(mask.shape) == 3:
expanded_mask = mask
else:
expanded_mask = np.expand_dims(mask, axis=-1)
if len(mask.shape)!=3:
error = f"image must be shape 3 {image.shape}"
raise ValueError(error)
if get_channel(image)!=4:
image_rgba = cv2.cvtColor(image, cv2.COLOR_BGR2BGRA) #why rgb to gray?
else:
image_rgba = image
image_rgba[..., 3] = expanded_mask[..., 0]
return image_rgba
def apply_mask_alpha(image,mask,invert=False):
if len(mask.shape) == 3:
expanded_mask = mask
else:
expanded_mask = np.expand_dims(mask, axis=-1)
image_rgba = cv2.cvtColor(image, cv2.COLOR_BGR2BGRA)
if invert:
image_rgba[..., 3] = expanded_mask[..., 0]
else:
image_rgba[..., 3] = 255 - expanded_mask[..., 0]
return image_rgba
def print_width_height(image,label):
new_h,new_w = get_image_size(image)
print(f"{label}:width = {new_w} height = {new_h}")
def create_mask_from_points(img,points,dilation_size=4,gaussian_size=4):
np_points = np.array(points,dtype=np.int32)
mask = np.zeros(img.shape[:2], dtype="uint8")
cv2.fillPoly(mask, [np_points], 255)
kernel = np.ones((abs(dilation_size),abs(dilation_size) ), np.uint8)
if dilation_size > 0:
dilated_mask = cv2.dilate(mask, kernel, iterations=1)
else:
dilated_mask = cv2.erode(mask, kernel, iterations=1) # TODO support dilation_size
# Gaussian Blur
if gaussian_size > 0:
smooth_mask = cv2.GaussianBlur(dilated_mask, (0,0 ), sigmaX=gaussian_size, sigmaY=gaussian_size)
expanded_mask = np.expand_dims(smooth_mask, axis=-1)
else:
expanded_mask = np.expand_dims(dilated_mask, axis=-1)
return expanded_mask
#lip_utils.print_numpy(image_rgba,"rgba")
#lip_utils.print_numpy(smooth_mask,"smooth")
#lip_utils.print_numpy(expanded_mask,"expanded_mask")
def mid_points(point1,point2):
return [sum(x) / 2 for x in zip(point1,point2)]
def lerp_points(point1, point2, lerp):
return [(1.0 - lerp) * p1 + lerp * p2 for p1, p2 in zip(point1, point2)]
def get_jaw_points(face_landmarks_list):
chin_points = get_landmark_points(face_landmarks_list,POINTS_CHIN)
bottom_lip_points = get_landmark_points(face_landmarks_list,POINTS_BOTTOM_LIP)
points =[]
points.extend(chin_points[4:13])
points.append(mid_points(chin_points[12],bottom_lip_points[0]))
points.append(mid_points(chin_points[8],bottom_lip_points[3]))
points.append(mid_points(chin_points[4],bottom_lip_points[6]))
return points
def get_bottom_mid_drop_size(open_size_y,lip_height):
# when full open case open_size_y 40 lip become half
mid_lip_move_ratio = open_size_y/80.0 if open_size_y>0 else 0
return mid_lip_move_ratio*lip_height
def fade_in_x(img,size):
if size==0:
return
per_pixel = 1.0/size
for y in range(img.shape[0]):
for x in range(img.shape[1]):
if x <size:
alpha_base = per_pixel * x
# アルファ値を変更し、ピクセルに設定
#print(f"before x ={x} = {img[y,x,3]} after = {img[y,x,3] * alpha_base}")
img[y, x, 3] = img[y,x,3] * alpha_base
def fade_out_x(img,size):
if size==0:
return
per_pixel = 1.0/size
w = img.shape[1]
for y in range(img.shape[0]):
for x in range(img.shape[1]):
if x >w:
diff = x - w
alpha_base = 1.0 - (per_pixel * x)
# アルファ値を変更し、ピクセルに設定
#print(f"before x ={x} = {img[y,x,3]} after = {img[y,x,3] * alpha_base}")
img[y, x, 3] = img[y,x,3] * alpha_base
def alpha_blend_with_image2_alpha(image1, image2):
return cv2.addWeighted(image1, 1, image2, 1, 0)
def numpy_alpha_blend_with_image2_alpha(image1, image2,invert=False):
"""
image1をimage2のアルファチャンネルを使用してアルファブレンディングします。
"""
# 画像のサイズを確認し、必要に応じてリサイズします。
if image1.shape[:2] != image2.shape[:2]:
image1 = cv2.resize(image1, (image2.shape[1], image2.shape[0]))
src1 = np.array(image1)
src2 = np.array(image2)
mask1 = np.array(image2[:, :, 3])
mask1 = mask1 / 255
mask1 = np.expand_dims(mask1, axis=-1)
if invert:
dst = src1 * (1-mask1) + src2 * mask1
else:
dst = src1 * mask1 + src2 * (1 - mask1)
# アルファブレンディングを行います。
#blended = cv2.cvtColor(dst, cv2.COLOR_BGRA2BGRA)
dst = dst.astype(np.uint8)
return dst
def distance_2d(point1, point2):
return math.sqrt((point2[0] - point1[0])**2 + (point2[1] - point1[1])**2)
# points[index][x=0 y=1] index is see landmark image by plot2.py
def get_top_lip_thicks(landmarks_list,is_distance_base=False):
points = get_landmark_points(landmarks_list,POINTS_TOP_LIP)
if is_distance_base:
return (distance_2d(points[10],points[2]),distance_2d(points[9],points[3]),distance_2d(points[8],points[4]))
return (points[10][1] -points[2][1],points[9][1] -points[3][1],points[8][1] -points[4][1])
def scale_down_values(data, scale_factor=0.25):
"""
Scales down the values in a list of dictionaries by a given scale factor.
Parameters:
- data: A list of dictionaries where each dictionary represents facial landmarks.
- scale_factor: The factor by which to scale down the values. Default is 0.25 (1/4).
Returns:
- A new list of dictionaries with scaled down values.
"""
scaled_data = []
for item in data:
scaled_item = {}
for key, values in item.items():
scaled_values = [(int(x * scale_factor), int(y * scale_factor)) for x, y in values]
scaled_item[key] = scaled_values
scaled_data.append(scaled_item)
return scaled_data
def save_landmarks(face_landmarks,out_path):
json_data = json.dumps(face_landmarks)
with open(out_path, "w") as f:
f.write(json_data)
def load_landmarks(input_path):
with open(input_path, "r") as f:
face_landmarks_list = json.loads(f.read())
return face_landmarks_list