File size: 13,284 Bytes
d5d20be
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
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