Reapply "优化照片涂抹功能架构"

This reverts commit 13575fb2aa.
This commit is contained in:
2024-07-15 15:21:10 +08:00
parent f4a2a38df0
commit 7cbf0c0756
6 changed files with 535 additions and 438 deletions

202
util/image_util.py Normal file
View File

@@ -0,0 +1,202 @@
import logging
import math
import urllib.request
import cv2
import numpy
from paddleclas import PaddleClas
def read(image_path):
"""
从网络或本地读取图片
:param image_path: 网络或本地路径
:return: NumPy数组形式的图片
"""
if image_path.startswith("http"):
# 发送HTTP请求并获取图像数据
resp = urllib.request.urlopen(image_path)
# 将数据读取为字节流
image_data = resp.read()
# 将字节流转换为NumPy数组
image_np = numpy.frombuffer(image_data, numpy.uint8)
# 解码NumPy数组为OpenCV图像格式
image = cv2.imdecode(image_np, cv2.IMREAD_COLOR)
else:
image = cv2.imread(image_path)
return image
def capture(image, rectangle):
"""
截取图片
:param image: 图片NumPy数组
:param rectangle: 要截取的矩形
:return: 截取之后的图片NumPy
"""
x1, y1, x2, y2 = rectangle
return image[int(y1):int(y2), int(x1):int(x2)]
def split(image, ratio=1.414, overlap=0.05):
"""
分割图片,只分割过长的图片,暂不处理过宽的图片
:param image:图片可以是NumPy数组或文件路径
:param ratio: 分割后的比例
:param overlap: 图片之间的覆盖比例
:return: 分割后的图片组(NumPy数组形式)
"""
split_result = []
if isinstance(image, str):
image = read(image)
# 获取图片的宽度和高度
height, width = image.shape[:2]
# 计算宽高比
img_ratio = height / width
# 检查是否需要裁剪
if img_ratio > ratio:
split_ratio = ratio - overlap
# 分割后的高度
new_img_height = width * ratio
for i in range(math.ceil(height / (width * split_ratio))):
offset = round(width * split_ratio * i)
# 参数形式为[y1:y2, x1:x2]
cropped_img = capture(image, [0, offset, width, offset + new_img_height])
split_result.append({"img": cropped_img, "x_offset": 0, "y_offset": offset})
else:
split_result.append({"img": image, "x_offset": 0, "y_offset": 0})
return split_result
def parse_rotation_angles(image):
"""
判断图片旋转角度,逆时针旋转该角度后为正。可能值["0", "90", "180", "270"]
:param image: 图片NumPy数组或文件路径
:return: 最有可能的两个角度
"""
angles = ['0', '90']
model = PaddleClas(model_name="text_image_orientation")
clas_result = model.predict(input_data=image)
try:
clas_result = next(clas_result)[0]
if clas_result["scores"][0] < 0.5:
return angles
angles = clas_result["label_names"]
except Exception as e:
logging.error("获取图片旋转角度失败", exc_info=e)
return angles
def rotate(image, angle):
"""
旋转图片
:param image: 图片NumPy数组
:param angle: 逆时针旋转角度
:return: 旋转后的图片NumPy数组
"""
if angle == 0:
return image
height, width = image.shape[:2]
if angle == 180:
new_width = width
new_height = height
else:
new_width = height
new_height = width
# 绕图像的中心旋转
# 参数:旋转中心 旋转度数 scale
matrix = cv2.getRotationMatrix2D((width / 2, height / 2), angle, 1)
# 旋转后平移
matrix[0, 2] += (new_width - width) / 2
matrix[1, 2] += (new_height - height) / 2
# 参数:原始图像 旋转参数 元素图像宽高
rotated = cv2.warpAffine(image, matrix, (new_width, new_height))
return rotated
def invert_rotate_point(point, center, angle):
"""
反向旋转图片上的点
:param point: 点
:param center: 旋转中心
:param angle: 旋转角度
:return: 旋转后的点坐标
"""
matrix = cv2.getRotationMatrix2D(center, angle, 1)
if angle != 180:
# 旋转后平移
matrix[0, 2] += center[1] - center[0]
matrix[1, 2] += center[0] - center[1]
reverse_matrix = cv2.invertAffineTransform(matrix)
point = numpy.array([[point[0]], [point[1]], [1]])
return numpy.dot(reverse_matrix, point)
def invert_rotate_rectangle(rectangle, center, angle):
"""
反向旋转图片上的矩形
:param rectangle: 矩形
:param center: 旋转中心
:param angle: 旋转角度
:return: 旋转后的矩形坐标
"""
if angle == 0:
return list(rectangle)
x1, y1, x2, y2 = rectangle
# 计算矩形的四个顶点
top_left = (x1, y1)
bot_left = (x1, y2)
top_right = (x2, y1)
bot_right = (x2, y2)
# 旋转矩形的四个顶点
rot_top_left = invert_rotate_point(top_left, center, angle).astype(int)
rot_bot_left = invert_rotate_point(bot_left, center, angle).astype(int)
rot_bot_right = invert_rotate_point(bot_right, center, angle).astype(int)
rot_top_right = invert_rotate_point(top_right, center, angle).astype(int)
# 找出旋转后矩形的新左上角和右下角坐标
new_top_left = (min(rot_top_left[0], rot_bot_left[0], rot_bot_right[0], rot_top_right[0]),
min(rot_top_left[1], rot_bot_left[1], rot_bot_right[1], rot_top_right[1]))
new_bot_right = (max(rot_top_left[0], rot_bot_left[0], rot_bot_right[0], rot_top_right[0]),
max(rot_top_left[1], rot_bot_left[1], rot_bot_right[1], rot_top_right[1]))
return [new_top_left[0], new_top_left[1], new_bot_right[0], new_bot_right[1]]
def expand_to_a4_size(image, center=False):
"""
将图片扩充到a4大小
:param image: 图片NumPy数组
:param center: 是否将原图置于中间
:return: 扩充后的图片NumPy数组和偏移量
"""
h, w = image.shape[:2]
offset_x, offset_y = 0, 0
if h * 1.0 / w >= 1.42:
exp_w = int(h / 1.414 - w)
if center:
offset_x = int(exp_w / 2)
exp_img = numpy.zeros((h, offset_x, 3), dtype="uint8")
exp_img.fill(255)
image = numpy.hstack([exp_img, image, exp_img])
else:
exp_img = numpy.zeros((h, exp_w, 3), dtype="uint8")
exp_img.fill(255)
image = numpy.hstack([image, exp_img])
elif h * 1.0 / w <= 1.40:
exp_h = int(w * 1.414 - h)
if center:
offset_y = int(exp_h / 2)
exp_img = numpy.zeros((offset_y, w, 3), dtype="uint8")
exp_img.fill(255)
image = numpy.vstack([exp_img, image, exp_img])
else:
exp_img = numpy.zeros((exp_h, w, 3), dtype="uint8")
exp_img.fill(255)
image = numpy.vstack([image, exp_img])
return image, offset_x, offset_y