import cv2 import numpy as np from ..utils import get_box_pro, cut_BiggestAreas, locate_neck, get_cutbox_image from .move_image import move from ..vision import add_background, cover_image from ..matting_tools import get_modnet_matting from .neck_processing import transformationNeck from .cuny_tools import checkSharpCorner, checkJaw, checkHairLOrR,\ checkLongHair, checkLongHair2, convert_black_array, find_black test_image_path = "./supple_image/" def change_cloth(input_image:np.array, cloth_model, CLOTH_WIDTH, CLOTH_X, CLOTH_WIDTH_CHANGE, CLOTH_X_CHANGE, CLOTH_Y, neck_ratio=0.2, space_adjust=None, hair_front=True ): # ============= 1. 得到头脖子图、纯头图、纯脖子图的相关信息 =============== # # 1.1 获取原图input_image属性 input_height, input_width = input_image.shape[0], input_image.shape[1] # print("input_height:", input_height) # print("input_width", input_width) b, g, r, input_a = cv2.split(input_image) # 1.2 得到头脖子图headneck_image、纯头图head_image input_image = add_background(input_image, bgr=(255, 255, 255)) headneck_image = get_modnet_matting(input_image, checkpoint_path="./checkpoint/huanying_headneck3.onnx") head_image = get_modnet_matting(input_image, checkpoint_path="./checkpoint/huanying_head3.onnx") # 1.3 得到优化后的脖子图neck_threshold_image _, _, _, headneck_a = cv2.split(headneck_image) _, _, _, head_a = cv2.split(head_image) neck_a = cv2.subtract(headneck_a, head_a) _, neck_threshold_a = cv2.threshold(neck_a, 180, 255, cv2.THRESH_BINARY) neck_threshold_image = cut_BiggestAreas(cv2.merge( (np.uint8(b), np.uint8(g), np.uint8(r), np.uint8(neck_threshold_a)))) # 1.4 得到优化后的头脖子图headneck_threshold_image _, headneck_threshold_a = cv2.threshold(headneck_a, 180, 255, cv2.THRESH_BINARY) headneck_threshold_image = cut_BiggestAreas( cv2.merge((np.uint8(b), np.uint8(g), np.uint8(r), np.uint8(headneck_threshold_a)))) # 1.5 获取脖子图、头脖子图的A矩阵 _, _, _, neck_threshold_a2 = cv2.split(neck_threshold_image) _, _, _, headneck_threshold_a2 = cv2.split(headneck_threshold_image) # 1.6 获取头发的底部坐标信息,以及头的左右坐标信息 _, headneck_y_bottom, headneck_x_left, headneck_x_right = get_box_pro(headneck_threshold_image, model=2, correction_factor=0) headneck_y_bottom = input_height-headneck_y_bottom headneck_x_right = input_width-headneck_x_right # ============= 2. 得到原来的衣服的相关信息 =============== # # 2.1 抠出原来衣服cloth_image_input cloth_origin_image_a = cv2.subtract(np.uint8(input_a), np.uint8(headneck_a)) _, cloth_origin_image_a = cv2.threshold(cloth_origin_image_a, 180, 255, cv2.THRESH_BINARY) cloth_image_input = cut_BiggestAreas(cv2.merge((np.uint8(b), np.uint8(g), np.uint8(r), np.uint8(cloth_origin_image_a)))) # 2.2 对cloth_image_input做裁剪(减去上面的大片透明区域) cloth_image_input_top_y, _, _, _ = get_box_pro(cloth_image_input, model=2) cloth_image_input_cut = cloth_image_input[cloth_image_input_top_y:, :] # ============= 3.计算脖子的衔接点信息,为新服装拼接作准备 ===============# # 3.1 得到裁剪透明区域后的脖子图neck_cut_image,以及它的坐标信息 neck_y_top, neck_y_bottom, neck_x_left, neck_x_right = get_box_pro(neck_threshold_image, model=2) neck_cut_image = get_cutbox_image(neck_threshold_image) neck_height = input_height - (neck_y_top + neck_y_bottom) neck_width = input_width - (neck_x_right + neck_x_left) # 3.2 对neck_cut_image做“尖尖”检测,得到较低的“尖尖”对于脖子高度的比率y_neck_corner_ratio y_neck_corner = checkSharpCorner(neck_cut_image) y_neck_corner_ratio = y_neck_corner / neck_height # 3.3 取y_neck_corner_ratio与新衣服预先设定好的neck_ratio的最大值,作为最终的neck_ratio neck_ratio = max(neck_ratio, y_neck_corner_ratio) # 3.4 计算在neck_ratio下的脖子左衔接点坐标neck_left_x_byRatio,neck_left_y_byRatio、宽度neck_width_byRatio neck_coordinate1, neck_coordinate2, neck_width_byRatio = locate_neck(neck_cut_image, float(neck_ratio)) neck_width_byRatio = neck_width_byRatio + CLOTH_WIDTH_CHANGE neck_left_x_byRatio = neck_x_left + neck_coordinate1[1] + CLOTH_X_CHANGE neck_left_y_byRatio = neck_y_top + neck_coordinate1[0] # ============= 4.读取新衣服图,调整大小 =============== # # 4.1 得到新衣服图片的拼贴坐标x, y以及脖子最底部的坐标y_neckline CLOTH_HEIGHT = CLOTH_Y RESIZE_RATIO = neck_width_byRatio / CLOTH_WIDTH x, y = int(neck_left_x_byRatio - CLOTH_X * RESIZE_RATIO), neck_left_y_byRatio y_neckline = y + int(CLOTH_HEIGHT * RESIZE_RATIO) # 4.2 读取新衣服,并进行缩放 cloth = cv2.imread(cloth_model, -1) cloth_height, cloth_width = cloth.shape[0], cloth.shape[1] cloth = cv2.resize(cloth, (int(cloth_width * RESIZE_RATIO), int(cloth_height * RESIZE_RATIO)), interpolation=cv2.INTER_AREA) # 4.3 获得新衣服的A矩阵 _, _, _, cloth_a = cv2.split(cloth) # ============= 5. 判断头发的前后关系,以及对于长发的背景填充、判定是否为长发等 =============== # # 5.1 根据hair_number, 判断是0:头发披在后面、1:左前右后、2:左后右前还是3:都在前面 hair_number = checkHairLOrR(cloth_image_input_cut, input_a, neck_a, cloth_image_input_top_y) # 5.2 对于长发的背景填充-将原衣服区域的部分变成黑色,并填充到最终图片作为背景 cloth_image_input_save = cloth_origin_image_a[:int(y+cloth_height*RESIZE_RATIO), max(0, headneck_x_left-1):min(headneck_x_right+1, input_width)] headneck_threshold_a_save = headneck_a[:int(y+cloth_height*RESIZE_RATIO), max(0, headneck_x_left-1):min(headneck_x_right+1, input_width)] headneck_mask = convert_black_array(headneck_threshold_a_save) kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15, 15)) cloth_image_input_save = cv2.dilate(cloth_image_input_save, kernel) cloth_image_input_save = np.uint8(cloth_image_input_save*headneck_mask) # 5.3 检测是否为长发 head_bottom_y = input_height - get_box_pro(head_image, model=2, correction_factor=0)[1] isLongHair01 = checkLongHair(neck_cut_image, head_bottom_y, neck_top_y=neck_y_top) isLongHair02 = checkLongHair2(head_bottom_y, cloth_image_input_top_y) isLongHair = isLongHair01 and isLongHair02 # ============= 6.第一轮服装拼贴 =============== # # 6.1 创建一个空白背景background background = np.uint8((np.zeros([input_height, input_width, 4]))) # 6.2 盖上headneck_image result_headneck_image = cover_image(headneck_image, background, 0, 0, mode=3) # 6.3 如果space_adjust开启的话,background的底部将增加一些行数 if space_adjust is not None: insert_array = np.uint8(np.zeros((space_adjust, input_width, 4))) result_headneck_image = np.r_[result_headneck_image, insert_array] # 6.4 盖上新衣服 result_cloth_image = cover_image(cloth, result_headneck_image, x, y, mode=3) # 6.5 截出脖子与衣服交接的区域neck_cloth_image,以及它的A矩阵neck_cloth_a neck_cloth_image = result_cloth_image[y:y_neckline, neck_left_x_byRatio:neck_left_x_byRatio+neck_width_byRatio] _, _, _, neck_cloth_a = cv2.split(neck_cloth_image) _, neck_cloth_a = cv2.threshold(neck_cloth_a, 127, 255, cv2.THRESH_BINARY) # ============= 7.第二轮服装拼贴 =============== # # 7.1 检测neck_cloth_a中是否有黑点(即镂空区域) # 如果black_dots_y不为None,说明存在镂空区域——需要进行脖子拉伸;反而则不存在,不需要 black_dots_y = find_black(neck_cloth_a) # cv2.imwrite(test_image_path+"neck_cloth_a.jpg", neck_cloth_a) # flag: 用于指示是否进行拉伸 flag = 0 # 7.2 如果存在镂空区域,则进行拉伸 if black_dots_y != None: flag = 1 # cutNeckHeight:要拉伸的区域的顶部y值 # neckBelow:脖子底部的y值 # toHeight:拉伸区域的高度 cutNeckHeight = black_dots_y + y - 6 # if cutNeckHeight < neck_y_top+checkJaw(neck_cut_image, y_start=checkSharpCorner(neck_cut_image))[1]: # print("拒绝!!!!!!") # return 0, 0, 0, 0, 0 neckBelow = input_height-neck_y_bottom toHeight = y_neckline-cutNeckHeight print("cutNeckHeight:", cutNeckHeight) print("toHeight:", toHeight) print("neckBelow:", neckBelow) # cv2.imwrite(test_image_path+"neck_image.png", neck_threshold_image) # 对原有的脖子做拉伸,得到new_neck_image new_neck_image = transformationNeck(neck_threshold_image, cutNeckHeight=cutNeckHeight, neckBelow=neckBelow, toHeight=toHeight) # cv2.imwrite(test_image_path+"new_neck_image.png", new_neck_image) # 重新进行拼贴 result_headneck_image = cover_image(new_neck_image, result_headneck_image, 0, 0, mode=3) result_headneck_image = cover_image(head_image, result_headneck_image, 0, 0, mode=3) result_cloth_image = cover_image(cloth, result_headneck_image, x, y, mode=3) _, _, _, neck_a = cv2.split(new_neck_image) # 7.3 下面是对最终图的A矩阵进行处理 # 首先将neck_a与新衣服衔接点的左边两边区域删去,得到neck_a_leftright neck_a_copy = neck_a.copy() neck_a_copy[neck_left_y_byRatio:, :max(0, neck_left_x_byRatio-4)] = 0 neck_a_copy[neck_left_y_byRatio:, min(input_width, neck_left_x_byRatio + neck_width_byRatio - CLOTH_X_CHANGE+4):] = 0 n_a_leftright = cv2.subtract(neck_a, neck_a_copy) # 7.4 如果存在镂空区域,则对headneck_a做进一步处理 if black_dots_y != None: neck_a = cv2.subtract(neck_a, n_a_leftright) # 得到去掉脖子两翼的新的headneck_a headneck_a = cv2.subtract(headneck_a, n_a_leftright) # 将headneck_a覆盖上拉伸后的脖子A矩阵 headneck_a = np.uint8(cover_image(neck_a, headneck_a, 0, 0, mode=2)) else: headneck_a = cv2.subtract(headneck_a, n_a_leftright) # 7.5 如果是长发 if isLongHair: # 在背景加入黑色矩形,填充抠头模型可能会出现的,部分长发没有抠全的部分 black_background_x1 = int(neck_left_x_byRatio - neck_width_byRatio * 0.1) black_background_x2 = int(neck_left_x_byRatio + neck_width_byRatio * 1.1) black_background_y1 = int(neck_y_top - neck_height * 0.1) black_background_y2 = min(input_height - neck_y_bottom - 3, head_bottom_y) headneck_a[black_background_y1:black_background_y2, black_background_x1:black_background_x2] = 255 # 在背景中,将原本衣服区域置为黑色 headneck_a = cover_image(cloth_image_input_save, headneck_a, max(0, headneck_x_left-1), 0, mode=2) # 7.6 如果space_adjust开启的话,headneck_a的底部将增加一些行数 if space_adjust is not None: insert_array = np.uint8(np.zeros((space_adjust, input_width))) headneck_a = np.r_[headneck_a, insert_array] # 7.7 盖上新衣服 new_a = np.uint8(cover_image(cloth_a, headneck_a, x, y, mode=2)) # neck_cloth_a = new_a[y:y_neckline, neck_left_x_byRatio:neck_left_x_byRatio + neck_width_byRatio] # _, neck_cloth_a = cv2.threshold(neck_cloth_a, 127, 255, cv2.THRESH_BINARY) # cv2.imwrite(test_image_path + "neck_cloth_a2.jpg", neck_cloth_a) # # if find_black(neck_cloth_a) != None: # print("拒绝!!!!") # return "拒绝" # 7.8 如果有头发披在前面 if hair_front: # 如果头发披在左边 if hair_number == 1: result_cloth_image = cover_image(head_image[:, :head_image.shape[1] // 2], result_cloth_image, 0, 0, mode=3) # 如果头发披在右边 elif hair_number == 2: result_cloth_image = cover_image(head_image[:, head_image.shape[1] // 2:], result_cloth_image, head_image.shape[1] // 2, 0, mode=3) # 如果头发披在两边 elif hair_number == 3: result_cloth_image = cover_image(head_image, result_cloth_image, 0, 0, mode=3) # 7.9 合成最终图片,并做底部空隙的移动 r, g, b, _ = cv2.split(result_cloth_image) result_image = move(cv2.merge((r, g, b, new_a))) # 7.10 返回:结果图、是否拉伸、头发前披状态、是否长发 return 1, result_image, flag, hair_number, isLongHair if __name__ == "__main__": pass