File size: 7,846 Bytes
ca46a75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9b2289b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
r"""
@DATE: 2024/9/5 19:25
@File: utils.py
@IDE: pycharm
@Description:
    通用图像处理工具
"""
import cv2
import numpy as np


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 get_box(
    image: np.ndarray,
    model: int = 1,
    correction_factor=None,
    thresh: int = 127,
):
    """
    本函数能够实现输入一张四通道图像,返回图像中最大连续非透明面积的区域的矩形坐标
    本函数将采用 opencv 内置函数来解析整个图像的 mask,并提供一些参数,用于读取图像的位置信息
    Args:
        image: 四通道矩阵图像
        model: 返回值模式
        correction_factor: 提供一些边缘扩张接口,输入格式为 list 或者 int:[up, down, left, right]。
                    举个例子,假设我们希望剪切出的矩形框左边能够偏左 1 个像素,则输入 [0, 0, 1, 0];
                        如果希望右边偏右 1 个像素,则输入 [0, 0, 0, 1]
                    如果输入为 int,则默认只会对左右两边做拓展,比如输入 2,则和 [0, 0, 2, 2] 是等效的
        thresh: 二值化阈值,为了保持一些羽化效果,thresh 必须要小
    Returns:
        model 为 1 时,将会返回切割出的矩形框的四个坐标点信息
        model 为 2 时,将会返回矩形框四边相距于原图四边的距离
    """
    # ------------ 数据格式规范部分 -------------- #
    # 输入必须为四通道
    if correction_factor is None:
        correction_factor = [0, 0, 0, 0]
    if not isinstance(image, np.ndarray) or len(cv2.split(image)) != 4:
        raise TypeError("输入的图像必须为四通道 np.ndarray 类型矩阵!")
    # correction_factor 规范化
    if isinstance(correction_factor, int):
        correction_factor = [0, 0, correction_factor, correction_factor]
    elif not isinstance(correction_factor, list):
        raise TypeError("correction_factor 必须为 int 或者 list 类型!")
    # ------------ 数据格式规范完毕 -------------- #
    # 分离 mask
    _, _, _, mask = cv2.split(image)
    # mask 二值化处理
    _, mask = cv2.threshold(mask, thresh=thresh, maxval=255, type=0)
    contours, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    temp = np.ones(image.shape, np.uint8) * 255
    cv2.drawContours(temp, contours, -1, (0, 0, 255), -1)
    contours_area = []
    for cnt in contours:
        contours_area.append(cv2.contourArea(cnt))
    idx = contours_area.index(max(contours_area))
    x, y, w, h = cv2.boundingRect(contours[idx])  # 框出图像
    # ------------ 开始输出数据 -------------- #
    height, width, _ = image.shape
    y_up = y - correction_factor[0] if y - correction_factor[0] >= 0 else 0
    y_down = (
        y + h + correction_factor[1]
        if y + h + correction_factor[1] < height
        else height - 1
    )
    x_left = x - correction_factor[2] if x - correction_factor[2] >= 0 else 0
    x_right = (
        x + w + correction_factor[3]
        if x + w + correction_factor[3] < width
        else width - 1
    )
    if model == 1:
        # model=1,将会返回切割出的矩形框的四个坐标点信息
        return [y_up, y_down, x_left, x_right]
    elif model == 2:
        # model=2, 将会返回矩形框四边相距于原图四边的距离
        return [y_up, height - y_down, x_left, width - x_right]
    else:
        raise EOFError("请选择正确的模式!")


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


def cutting_rect_pan(
    x1, y1, x2, y2, width, height, L1, L2, L3, clockwise, standard_size
):
    """
    本函数的功能是对旋转矫正结果图的裁剪框进行修正 ———— 解决"旋转三角形"现象。
    Args:
        - x1: int, 裁剪框左上角的横坐标
        - y1: int, 裁剪框左上角的纵坐标
        - x2: int, 裁剪框右下角的横坐标
        - y2: int, 裁剪框右下角的纵坐标
        - width: int, 待裁剪图的宽度
        - height:int, 待裁剪图的高度
        - L1: CLassObject, 根据旋转点连线所构造函数
        - L2: CLassObject, 根据旋转点连线所构造函数
        - L3: ClassObject, 一个特殊裁切点的坐标
        - clockwise: int, 旋转时针状态
        - standard_size: tuple, 标准照的尺寸

    Returns:
        - x1: int, 新的裁剪框左上角的横坐标
        - y1: int, 新的裁剪框左上角的纵坐标
        - x2: int, 新的裁剪框右下角的横坐标
        - y2: int, 新的裁剪框右下角的纵坐标
        - x_bias: int, 裁剪框横坐标方向上的计算偏置量
        - y_bias: int, 裁剪框纵坐标方向上的计算偏置量
    """
    # 用于计算的裁剪框坐标x1_cal,x2_cal,y1_cal,y2_cal(如果裁剪框超出了图像范围,则缩小直至在范围内)
    x1_std = x1 if x1 > 0 else 0
    x2_std = x2 if x2 < width else width
    # y1_std = y1 if y1 > 0 else 0
    y2_std = y2 if y2 < height else height

    # 初始化x和y的计算偏置项x_bias和y_bias
    x_bias = 0
    y_bias = 0

    # 如果顺时针偏转
    if clockwise == 1:
        if y2 > L1.forward_x(x1_std):
            y_bias = int(-(y2_std - L1.forward_x(x1_std)))
        if y2 > L2.forward_x(x2_std):
            x_bias = int(-(x2_std - L2.forward_y(y2_std)))
        x2 = x2_std + x_bias
        if x1 < L3.x:
            x1 = L3.x
    # 如果逆时针偏转
    else:
        if y2 > L1.forward_x(x1_std):
            x_bias = int(L1.forward_y(y2_std) - x1_std)
        if y2 > L2.forward_x(x2_std):
            y_bias = int(-(y2_std - L2.forward_x(x2_std)))
        x1 = x1_std + x_bias
        if x2 > L3.x:
            x2 = L3.x

    # 计算裁剪框的y的变化
    y2 = int(y2_std + y_bias)
    new_cut_width = x2 - x1
    new_cut_height = int(new_cut_width / standard_size[1] * standard_size[0])
    y1 = y2 - new_cut_height

    return x1, y1, x2, y2, x_bias, y_bias