前言
当前有一个需求,在之前进行对进行发票设别标注的时候遇到的痛点,那就是需要用户下载指定环境,而且有很多小毛病,无疑是增加了工作量。在这个基础上,就决定自己在web进行图像标注,由web端进行画框进行标图进行保存四个坐标,根据之后再根据四个坐标进行图像切片。基于此就有了这个文章,本文将介绍如何使用高斯面积公式(也称为Shoelace公式)计算多边形的有向面积,并结合图像处理技术对图像进行旋转裁剪。
裁剪的图片
实现的效果
实现步骤
- 计算多边形的有向面积:利用高斯面积公式计算多边形的有向面积。如果面积为负,说明顶点是按逆时针顺序排列的,需要进行调整以确保顺时针顺序。
- 计算裁剪图像的宽度和高度:根据调整后的顶点坐标,计算旋转裁剪后图像的宽度和高度。
- 执行旋转裁剪:使用计算得到的宽度和高度,对图像进行旋转裁剪。
计算多边形的有向面积
d = 0.0
# 使用高斯面积公式计算多边形的有向面积
for index in range(-1, 3):
d += -0.5 * (points[index + 1][1] + points[index][1]) * (
points[index + 1][0] - points[index][0])
# 如果面积为负,交换点的位置以确保顺时针顺序
if d < 0:
tmp = np.array(points)
points[1], points[3] = tmp[3], tmp[1]
为什么要确保为顺时针,这是因为我们需要计算固定长和宽
顺时针的情况
逆时针的情况
高斯面积公式
- Shoelace公式(也称为高斯面积公式或鞋带公式)是一个用于计算二维平面上简单多边形面积的算法。这个公式的基本思想是将多边形的顶点视为平面上的点,并通过这些点的坐标来计算多边形的面积。
高斯面积公式
鞋带公式
长的很像鞋带
为什么叫做鞋带公式,这是因为在计算的过程很像鞋带一样缠绕着,
鞋带公式是这样子算的:
鞋带公式多边形面积计算
比如一个多边形(四角形),
P1(x1,y2)=(2,1),P2(x2,y2)=(3, 3),P3(x3,y3)=(5, 4),P4(x4,y4)=(4, 2)
可得面积:
为什么要加绝对值,这就是涉及到高斯面积公式的推理过程,结合这个多边形案例
梯形公式
计算P1到P2的面积
(x1, y1) = (2, 1) (x2, y2) = (3, 3)
2/(x2y1 - x1y2) + 2/(x2y2-x1y1)
area = 2
计算P2 到 P3梯形面积
(x2, y2) = (3, 3) (x3, y3) = (5, 4)
2/(x3y2 - x2y3) + 2/(x3y3-x2y2)
area = 7
计算P3 到 P4 梯形面积
这里发现当前的x4-x3 当x3大于x4时,计算的梯形面积为负数
(x3, y3) = (5, 4) (x4, y4) = (4, 2)
2/(x4y3 - x3y4) + 2/(x4y4-x3y3)
are = -3
计算x4y4 x1y1 梯形面积
这里发现当前的x1-x4 当x4大于x1时,计算的梯形面积为负数
(x1, y1) = (2, 1) (x4, y4) = (4, 2)
2/(x1y4 - x4y1) + 2/(x1y1-x4y4)
area = -3
最后相加可得高斯面积公式
根据面积相加可得
area1 + area2 + area3 + area4 = area
2 + 7 - 3 -3 = 3
使用勾股定理计算图像长和宽
[0, 0],[4, 0],[4, 3], [0, 3]
# 计算裁剪图像的宽度
img_crop_width = int(
max(
# 勾股定理
np.linalg.norm(points[0] - points[1]), # 距离 (0,0) 到 (4,0)
np.linalg.norm(points[2] - points[3]))) # 距离 (4,3) 到 (0,3)
# 计算裁剪图像的高度
img_crop_height = int(
max(
np.linalg.norm(points[0] - points[3]), # 距离 (0,0) 到 (0,3)
np.linalg.norm(points[1] - points[2]))) # 距离 (4,0) 到 (4,3)
pts_std = np.float32([[0, 0], [img_crop_width, 0],
[img_crop_width, img_crop_height],
[0, img_crop_height]])
手动计算
得到长和框、裁剪四个坐标
img_crop_width: 4, img_crop_height: 3
pts_std = [[0. 0.] [4. 0.][4. 3.][0. 3.]]
执行旋转裁剪
# 获取透视变换矩阵
M = cv2.getPerspectiveTransform(points, pts_std)
# 进行透视变换
dst_img = cv2.warpPerspective(
img,
M, (img_crop_width, img_crop_height),
borderMode=cv2.BORDER_REPLICATE,
flags=cv2.INTER_CUBIC)
# 获取变换后图像的高度和宽度
dst_img_height, dst_img_width = dst_img.shape[0:2]
# 如果高度和宽度的比例大于等于1.5,则旋转图像
if dst_img_height * 1.0 / dst_img_width >= 1.5:
dst_img = np.rot90(dst_img)
获取透视变换矩阵
M = cv2.getPerspectiveTransform(points, pts_std)
- cv2.getPerspectiveTransform(points, pts_std) 计算并返回一个 3x3 的透视变换矩阵 M。
- points 是原始图像中四个点的坐标。
- pts_std 是目标图像中对应四个点的坐标。
透视变换矩阵 M 用于将原始图像中的四个点映射到目标图像中的四个点。
poinst = np.float32([[260, 100], [600, 100], [260, 400], [600, 400]]) pts_std = np.float32([[0, 0], [600, 0], [0, 600], [600, 600]])
进行透视变换
dst_img = cv2.warpPerspective(
img,
M, (img_crop_width, img_crop_height),
borderMode=cv2.BORDER_REPLICATE,
flags=cv2.INTER_CUBIC)
- cv2.warpPerspective ,它会根据你提供的变换矩阵,把图片中的某些部分拉伸、压缩或扭曲到你希望的位置。
- img 是输入的原始图像。
- M 是透视变换矩阵。
- (img_crop_width, img_crop_height) 指定输出图像的大小。
- borderMode=cv2.BORDER_REPLICATE 当变换过程中有些像素点移到图像边界外时,使用边缘的像素颜色填充这些位置。
示例完整代码
# 进行切片和旋转
def get_rotate_crop(img, points):
d = 0.0
# 使用高斯面积公式计算多边形的有向面积
for index in range(-1, 3):
d += -0.5 * (points[index + 1][1] + points[index][1]) * (
points[index + 1][0] - points[index][0])
# 如果面积为负,交换点的位置以确保顺时针顺序
if d < 0:
tmp = np.array(points)
points[1], points[3] = tmp[3], tmp[1]
try:
# 计算裁剪图像的宽度
img_crop_width = int(
max(
np.linalg.norm(points[0] - points[1]),
np.linalg.norm(points[2] - points[3])))
# 计算裁剪图像的高度
img_crop_height = int(
max(
np.linalg.norm(points[0] - points[3]),
np.linalg.norm(points[1] - points[2])))
# 标注的目标坐标
pts_std = np.float32([[0, 0], [img_crop_width, 0],
[img_crop_width, img_crop_height],
[0, img_crop_height]])
# 获取透视变换矩阵
M = cv2.getPerspectiveTransform(points, pts_std)
# 进行透视变换
dst_img = cv2.warpPerspective(
img,
M, (img_crop_width, img_crop_height),
borderMode=cv2.BORDER_REPLICATE,
flags=cv2.INTER_CUBIC)
# 获取变换后图像的高度和宽度
dst_img_height, dst_img_width = dst_img.shape[0:2]
# 如果高度和宽度的比例大于等于1.5,则旋转图像
if dst_img_height * 1.0 / dst_img_width >= 1.5:
dst_img = np.rot90(dst_img)
return dst_img
except Exception as e:
print(e)
参考文章
https://en.wikipedia.org/wiki/Shoelace_formula
https://waiterxiaoyy.github.io/2020/03/20/%E9%9E%8B%E5%B8%A6%...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。