File size: 16,473 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
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
import cv2
from PIL import Image
import numpy as np
import functools
import time

def calTime(mark):
    """
    一个输出函数时间的装饰器.
    :param mark: str, 可选填, 如果填了就会在print开头加上mark标签。
    """
    if isinstance(mark, str):
        def decorater(func):
            @functools.wraps(func)
            def wrapper(*args, **kw):
                start_time = time.time()
                return_param = func(*args, **kw)
                print("[Mark-{}] {} 函数花费的时间为 {:.2f}.".format(mark, func.__name__, time.time() - start_time))
                return return_param

            return wrapper

        return decorater
    else:
        func = mark

        @functools.wraps(func)
        def wrapper(*args, **kw):
            start_time = time.time()
            return_param = func(*args, **kw)
            print("{} 函数花费的时间为 {:.2f}.".format(func.__name__, time.time() - start_time))
            return return_param

        return wrapper


def ChangeImageDPI(input_path, output_path, dpi=300):
    """
    改变输入图像的dpi.
    input_path: 输入图像路径
    output_path: 输出图像路径
    dpi:打印分辨率
    """
    image = Image.open(input_path)
    image.save(output_path, dpi=(dpi, dpi))
    # print(1)
    print("Your Image's DPI have been changed. The last DPI = ({},{}) ".format(dpi,dpi))


def IDphotos_cut(x1, y1, x2, y2, img):
    """
    在图片上进行滑动裁剪,输入输出为
    输入:一张图片img,和裁剪框信息(x1,x2,y1,y2)
    输出: 裁剪好的图片,然后裁剪框超出了图像范围,那么将用0矩阵补位
    ------------------------------------
    x:裁剪框左上的横坐标
    y:裁剪框左上的纵坐标
    x2:裁剪框右下的横坐标
    y2:裁剪框右下的纵坐标
    crop_size:裁剪框大小
    img:裁剪图像(numpy.array)
    output_path:裁剪图片的输出路径
    ------------------------------------
    """

    crop_size = (y2-y1, x2-x1)
    """
    ------------------------------------
    temp_x_1:裁剪框左边超出图像部分
    temp_y_1:裁剪框上边超出图像部分
    temp_x_2:裁剪框右边超出图像部分
    temp_y_2:裁剪框下边超出图像部分
    ------------------------------------
    """
    temp_x_1 = 0
    temp_y_1 = 0
    temp_x_2 = 0
    temp_y_2 = 0

    if y1 < 0:
        temp_y_1 = abs(y1)
        y1 = 0
    if y2 > img.shape[0]:
        temp_y_2 = y2
        y2 = img.shape[0]
        temp_y_2 = temp_y_2 - y2

    if x1 < 0:
        temp_x_1 = abs(x1)
        x1 = 0
    if x2 > img.shape[1]:
        temp_x_2 = x2
        x2 = img.shape[1]
        temp_x_2 = temp_x_2 - x2

    # 生成一张全透明背景
    print("crop_size:", crop_size)
    background_bgr = np.full((crop_size[0], crop_size[1]), 255, dtype=np.uint8)
    background_a = np.full((crop_size[0], crop_size[1]), 0, dtype=np.uint8)
    background = cv2.merge((background_bgr, background_bgr, background_bgr, background_a))

    background[temp_y_1: crop_size[0] - temp_y_2, temp_x_1: crop_size[1] - temp_x_2] = img[y1:y2, x1:x2]

    return background


def resize_image_esp(input_image, esp=2000):
    """
    输入:
    input_path:numpy图片
    esp:限制的最大边长
    """
    # resize函数=>可以让原图压缩到最大边为esp的尺寸(不改变比例)
    width = input_image.shape[0]

    length = input_image.shape[1]
    max_num = max(width, length)

    if max_num > esp:
        print("Image resizing...")
        if width == max_num:
            length = int((esp / width) * length)
            width = esp

        else:
            width = int((esp / length) * width)
            length = esp
        print(length, width)
        im_resize = cv2.resize(input_image, (length, width), interpolation=cv2.INTER_AREA)
        return im_resize
    else:
        return input_image


def resize_image_by_min(input_image, esp=600):
    """
    将图像缩放为最短边至少为esp的图像。
    :param input_image: 输入图像(OpenCV矩阵)
    :param esp: 缩放后的最短边长
    :return: 缩放后的图像,缩放倍率
    """
    height, width = input_image.shape[0], input_image.shape[1]
    min_border = min(height, width)
    if min_border < esp:
        if height >= width:
            new_width = esp
            new_height = height * esp // width
        else:
            new_height = esp
            new_width = width * esp // height

        return cv2.resize(input_image, (new_width, new_height), interpolation=cv2.INTER_AREA), new_height / height

    else:
        return input_image, 1


def detect_distance(value, crop_heigh, max=0.06, min=0.04):
    """
    检测人头顶与照片顶部的距离是否在适当范围内。
    输入:与顶部的差值
    输出:(status, move_value)
    status=0 不动
    status=1 人脸应向上移动(裁剪框向下移动)
    status-2 人脸应向下移动(裁剪框向上移动)
    ---------------------------------------
    value:头顶与照片顶部的距离·
    crop_heigh: 裁剪框的高度
    max: 距离的最大值
    min: 距离的最小值
    ---------------------------------------
    """
    value = value / crop_heigh  # 头顶往上的像素占图像的比例
    if min <= value <= max:
        return 0, 0
    elif value > max:
        # 头顶往上的像素比例高于max
        move_value = value - max
        move_value = int(move_value * crop_heigh)
        # print("上移{}".format(move_value))
        return 1, move_value
    else:
        # 头顶往上的像素比例低于min
        move_value = min - value
        move_value = int(move_value * crop_heigh)
        # print("下移{}".format(move_value))
        return -1, move_value


def draw_picture_dots(image, dots, pen_size=10, pen_color=(0, 0, 255)):
    """
    给一张照片上绘制点。
    image: Opencv图像矩阵
    dots: 一堆点,形如[(100,100),(150,100)]
    pen_size: 画笔的大小
    pen_color: 画笔的颜色
    """
    if isinstance(dots, dict):
        dots = [v for u, v in dots.items()]
    image = image.copy()
    for x, y in dots:
        cv2.circle(image, (int(x), int(y)), pen_size, pen_color, -1)
    return image


def draw_picture_rectangle(image, bbox, pen_size=2, pen_color=(0, 0, 255)):
    image = image.copy()
    x1 = int(bbox[0])
    y1 = int(bbox[1])
    x2 = int(bbox[2])
    y2 = int(bbox[3])
    cv2.rectangle(image, (x1,y1), (x2, y2), pen_color, pen_size)
    return image


def generate_gradient(start_color, width, height, mode="updown"):
    # 定义背景颜色
    end_color = (255, 255, 255) # 白色

    # 创建一个空白图像
    r_out = np.zeros((height, width), dtype=int)
    g_out = np.zeros((height, width), dtype=int)
    b_out = np.zeros((height, width), dtype=int)

    if mode == "updown":
        # 生成上下渐变色
        for y in range(height):
            r = int((y / height) * end_color[0] + ((height - y) / height) * start_color[0])
            g = int((y / height) * end_color[1] + ((height - y) / height) * start_color[1])
            b = int((y / height) * end_color[2] + ((height - y) / height) * start_color[2])
            r_out[y, :] = r
            g_out[y, :] = g
            b_out[y, :] = b

    else:
        # 生成中心渐变色
        img = np.zeros((height, width, 3))
        # 定义椭圆中心和半径
        center = (width//2, height//2)
        end_axies = max(height, width)
        # 定义渐变色
        end_color = (255, 255, 255)
        # 绘制椭圆
        for y in range(end_axies):
            axes = (end_axies - y, end_axies - y)
            r = int((y / end_axies) * end_color[0] + ((end_axies - y) / end_axies) * start_color[0])
            g = int((y / end_axies) * end_color[1] + ((end_axies - y) / end_axies) * start_color[1])
            b = int((y / end_axies) * end_color[2] + ((end_axies - y) / end_axies) * start_color[2])

            cv2.ellipse(img, center, axes, 0, 0, 360, (b, g, r), -1)
        b_out, g_out, r_out = cv2.split(np.uint64(img))

    return r_out, g_out, b_out


def add_background(input_image, bgr=(0, 0, 0), mode="pure_color"):
    """
    本函数的功能为为透明图像加上背景。
    :param input_image: numpy.array(4 channels), 透明图像
    :param bgr: tuple, 合成纯色底时的BGR值
    :param new_background: numpy.array(3 channels),合成自定义图像底时的背景图
    :return: output: 合成好的输出图像
    """
    height, width = input_image.shape[0], input_image.shape[1]
    b, g, r, a = cv2.split(input_image)
    a_cal = a / 255
    if mode == "pure_color":
        # 纯色填充
        b2 = np.full([height, width], bgr[0], dtype=int)
        g2 = np.full([height, width], bgr[1], dtype=int)
        r2 = np.full([height, width], bgr[2], dtype=int)
    elif mode == "updown_gradient":
        b2, g2, r2 = generate_gradient(bgr, width, height, mode="updown")
    else:
        b2, g2, r2 = generate_gradient(bgr, width, height, mode="center")

    output = cv2.merge(((b - b2) * a_cal + b2, (g - g2) * a_cal + g2, (r - r2) * a_cal + r2))

    return output


def rotate_bound(image, angle):
    """
    一个旋转函数,输入一张图片和一个旋转角,可以实现不损失图像信息的旋转。
    - image: numpy.array(3 channels)
    - angle: 旋转角(度)
    """
    (h, w) = image.shape[:2]
    (cX, cY) = (w / 2, h / 2)

    M = cv2.getRotationMatrix2D((cX, cY), -angle, 1.0)
    cos = np.abs(M[0, 0])
    sin = np.abs(M[0, 1])

    nW = int((h * sin) + (w * cos))
    nH = int((h * cos) + (w * sin))

    M[0, 2] += (nW / 2) - cX
    M[1, 2] += (nH / 2) - cY

    return cv2.warpAffine(image, M, (nW, nH)), cos, sin


def rotate_bound_4channels(image, a, angle):
    """
    【rotate_bound_4channels的4通道版本】
    一个旋转函数,输入一张图片和一个旋转角,可以实现不损失图像信息的旋转。
    Inputs:
        - image: numpy.array(3 channels), 输入图像
        - a: numpy.array(1 channels), 输入图像的A矩阵
        - angle: 旋转角(度)
    Returns:
        - input_image: numpy.array(3 channels), 对image进行旋转后的图像
        - result_image: numpy.array(4 channels), 旋转且透明的图像
        - cos: float, 旋转角的余弦值
        - sin: float, 旋转角的正弦值
    """
    input_image, cos, sin = rotate_bound(image, angle)
    new_a, _, _ = rotate_bound(a, angle)  # 对做matte旋转,以便之后merge
    b, g, r = cv2.split(input_image)
    result_image = cv2.merge((b, g, r, new_a))  # 得到抠图结果图的无损旋转结果

    return input_image, result_image, cos, sin


def cover_image(image, background, x, y, mode=1):
    """
    mode = 1: directly cover
    mode = 2: cv2.add
    mode = 3: bgra cover
    """
    image = image.copy()
    background = background.copy()
    height1, width1 = background.shape[0], background.shape[1]
    height2, width2 = image.shape[0], image.shape[1]
    wuqiong_bg_y = height1 + 1
    wuqiong_bg_x = width1 + 1
    wuqiong_img_y = height2 + 1
    wuqiong_img_x = width2 + 1

    def cover_mode(image, background, imgy1=0, imgy2=-1, imgx1=0, imgx2=-1, bgy1=0, bgy2=-1, bgx1=0, bgx2=-1, mode=1):
        if mode == 1:
            background[bgy1:bgy2, bgx1:bgx2] = image[imgy1:imgy2, imgx1:imgx2]
        elif mode == 2:
            background[bgy1:bgy2, bgx1:bgx2] = cv2.add(background[bgy1:bgy2, bgx1:bgx2], image[imgy1:imgy2, imgx1:imgx2])
        elif mode == 3:
            b, g, r, a = cv2.split(image[imgy1:imgy2, imgx1:imgx2])
            b2, g2, r2, a2 = cv2.split(background[bgy1:bgy2, bgx1:bgx2])
            background[bgy1:bgy2, bgx1:bgx2, 0] = b * (a / 255) + b2 * (1 - a / 255)
            background[bgy1:bgy2, bgx1:bgx2, 1] = g * (a / 255) + g2 * (1 - a / 255)
            background[bgy1:bgy2, bgx1:bgx2, 2] = r * (a / 255) + r2 * (1 - a / 255)
            background[bgy1:bgy2, bgx1:bgx2, 3] = cv2.add(a, a2)

        return background

    if x >= 0 and y >= 0:
        x2 = x + width2
        y2 = y + height2

        if x2 <= width1 and y2 <= height1:
            background = cover_mode(image, background,0,wuqiong_img_y,0,wuqiong_img_x,y,y2,x,x2,mode)

        elif x2 > width1 and y2 <= height1:
            # background[y:y2, x:] = image[:, :width1 - x]
            background = cover_mode(image, background, 0, wuqiong_img_y, 0, width1-x, y, y2, x, wuqiong_bg_x,mode)

        elif x2 <= width1 and y2 > height1:
            # background[y:, x:x2] = image[:height1 - y, :]
            background = cover_mode(image, background, 0, height1-y, 0, wuqiong_img_x, y, wuqiong_bg_y, x, x2,mode)
        else:
            # background[y:, x:] = image[:height1 - y, :width1 - x]
            background = cover_mode(image, background, 0, height1-y, 0, width1-x, y, wuqiong_bg_y, x, wuqiong_bg_x,mode)

    elif x < 0 and y >= 0:
        x2 = x + width2
        y2 = y + height2

        if x2 <= width1 and y2 <= height1:
            # background[y:y2, :x + width2] = image[:, abs(x):]
            background = cover_mode(image, background, 0, wuqiong_img_y, abs(x), wuqiong_img_x, y, y2, 0, x+width2,mode)
        elif x2 > width1 and y2 <= height1:
            background = cover_mode(image, background, 0, wuqiong_img_y, abs(x), width1+abs(x), y, y2, 0, wuqiong_bg_x,mode)
        elif x2 <= 0:
            pass
        elif x2 <= width1 and y2 > height1:
            background = cover_mode(image, background, 0, height1-y, abs(x), wuqiong_img_x, y, wuqiong_bg_y, 0, x2, mode)
        else:
            # background[y:, :] = image[:height1 - y, abs(x):width1 + abs(x)]
            background = cover_mode(image, background, 0, height1-y, abs(x), width1+abs(x), y, wuqiong_bg_y, 0, wuqiong_bg_x,mode)

    elif x >= 0 and y < 0:
        x2 = x + width2
        y2 = y + height2
        if y2 <= 0:
            pass
        if x2 <= width1 and y2 <= height1:
            # background[:y2, x:x2] = image[abs(y):, :]
            background = cover_mode(image, background, abs(y), wuqiong_img_y, 0, wuqiong_img_x, 0, y2, x, x2,mode)
        elif x2 > width1 and y2 <= height1:
            # background[:y2, x:] = image[abs(y):, :width1 - x]
            background = cover_mode(image, background, abs(y), wuqiong_img_y, 0, width1-x, 0, y2, x, wuqiong_bg_x,mode)
        elif x2 <= width1 and y2 > height1:
            # background[:, x:x2] = image[abs(y):height1 + abs(y), :]
            background = cover_mode(image, background, abs(y), height1+abs(y), 0, wuqiong_img_x, 0, wuqiong_bg_y, x, x2,mode)
        else:
            # background[:, x:] = image[abs(y):height1 + abs(y), :width1 - abs(x)]
            background = cover_mode(image, background, abs(y), height1+abs(y), 0, width1-abs(x), 0, wuqiong_bg_x, x, wuqiong_bg_x,mode)

    else:
        x2 = x + width2
        y2 = y + height2
        if y2 <= 0 or x2 <= 0:
            pass
        if x2 <= width1 and y2 <= height1:
            # background[:y2, :x2] = image[abs(y):, abs(x):]
            background = cover_mode(image, background, abs(y), wuqiong_img_y, abs(x), wuqiong_img_x, 0, y2, 0, x2,mode)
        elif x2 > width1 and y2 <= height1:
            # background[:y2, :] = image[abs(y):, abs(x):width1 + abs(x)]
            background = cover_mode(image, background, abs(y), wuqiong_img_y, abs(x), width1+abs(x), 0, y2, 0, wuqiong_bg_x,mode)
        elif x2 <= width1 and y2 > height1:
            # background[:, :x2] = image[abs(y):height1 + abs(y), abs(x):]
            background = cover_mode(image, background, abs(y), height1+abs(y), abs(x), wuqiong_img_x, 0, wuqiong_bg_y, 0, x2,mode)
        else:
            # background[:, :] = image[abs(y):height1 - abs(y), abs(x):width1 + abs(x)]
            background = cover_mode(image, background, abs(y), height1-abs(y), abs(x), width1+abs(x), 0, wuqiong_bg_y, 0, wuqiong_bg_x,mode)

    return background


def image2bgr(input_image):
    if len(input_image.shape) == 2:
        input_image = input_image[:, :, None]
    if input_image.shape[2] == 1:
        result_image = np.repeat(input_image, 3, axis=2)
    elif input_image.shape[2] == 4:
        result_image = input_image[:, :, 0:3]
    else:
        result_image = input_image

    return result_image


if __name__ == "__main__":
    image = cv2.imread("./03.png", -1)
    result_image = add_background(image, bgr=(255, 255, 255))
    cv2.imwrite("test.jpg", result_image)