仿射变换和透视变换

本文介绍了仿射变换和透视变换在图像处理中的应用,特别是针对直线检测的场景。通过调整变换矩阵,实现了对图像的矫正,以消除误检。文中详细解释了cv2.warpAffine函数的参数,并提供了两种不同的仿射变换方法,分别基于角度计算和三点对应坐标。此外,还展示了如何进行透视变换,将梯形图像拉展成矩形。通过示例代码,比较了不同变换方法的效果,强调了边界填充模式的选择对结果的影响。
摘要由CSDN通过智能技术生成

最近对场景图片做一些研究和矫正,所以对仿射变换和透视变换有些了解,然后把自己的使用过程分享如下

仿射变换函数:

dst=cv2.warpAffine (src,M,dsize[,flags[,borderMode[,borderValue]

1)src:**代表要仿射的原始图像。
2)M:**代表一个2×3的变换矩阵。使用不同的变换矩阵,就可以实现不同的仿射变换。
3)dsize:**代表输出图像的尺寸大小。
4)flags:**代表插值方法,默认为INTER_LINEAR。当该值为WARP_INVERSE_MAP时,意味着M是逆变换类型,实现从目标图像dst到原始图像src的逆变换。
5)borderMode: 代表边类型,默认为BORDER_CONSTANT。当该值为BORDER_TRANSPARENT时,意味着目标图像内的值不做改变,这些值对应原始图像内的异常值。
6)borderValue:**代表边界值,默认是0。
重点介绍borderMode,最近在做直线检测,然后发现仿射变换后指定像素填充后做的直线检测会产生误检,所以想着使用图像边界元素自填充;然后就查找了备选的几种模式,目前包括一下几种填充模式:

目前常用的cv2.BORDER_CONSTANT:常值填充;然后使用边界元素的像素值填充,使用cv2.BORDER_REPLICATE ;这样在做直线检测时,就不会出现仿射变换引起的误检了

1.仿射变换

作用:提高识别的效果;如:在ocr字符识别的第一步会将文字区域进行矫正;

实施该方法的前提:有外接四边形的四个坐标点,如果标签是矩形,那么就没有办法进行矫正了;

样例图片:目标是将车厢上边缘处理为水平

原图,知道车厢的四个坐标点:

实施代码:

方法一:

找到合适的角度\Theta将车厢上边缘放平:

def dumpRotateImage(self, img, degree):
    height, width = img.shape[:2]
    # 旋转后图像的新尺寸
    heightNew = int(
    width * math.fabs(math.sin(math.radians(degree))) + height * math.fabs(math.cos(math.radians(degree))))
    widthNew = int(
    height * math.fabs(math.sin(math.radians(degree))) + width * math.fabs(math.cos(math.radians(degree))))

    matRotation = cv2.getRotationMatrix2D((width // 2, height // 2), degree, 1)  # 旋转矩阵

    # 平移后,部分像素后会漏出原图,所以加入平移操作,使得原图旋转后完全落在图像中:
    # 加入平移操作
    matRotation[0, 2] += (widthNew - width) // 2
    matRotation[1, 2] += (heightNew - height) // 2

    imgRotation = cv2.warpAffine(img, matRotation, (widthNew, heightNew), borderValue=(255, 255, 255))

     return imgRotation, matRotation

if __name__=='__main__':
    image = cv2.imread(img_path)
    points_str = '145.10,263.40,1698.83,140.18,1424.10,909.50,484.30,1015.60'

    # id_num_point = self.validate_label(id_num_point)  # 以左上顶点开始的顺时针box坐标
    id_num_point_list = list(map(float, points_str.split(',')))
    x_ = id_num_point_list[2] - id_num_point_list[0]
    y_ = id_num_point_list[3] - id_num_point_list[1]
    l = abs(math.sqrt(x_ * x_ + y_ * y_))
    angle_1 = math.asin(y_ / l)

    angle_1_ = math.degrees(angle_1)
    imgRotation, matRotation = self.dumpRotateImage(image, angle_1_)
    save_img_path = os.path.join(self.save_dir, file)
    cv2.imwrite(save_img_path, imgRotation)

方法二:

找到三个对应的坐标点,将车厢上边缘放平:

代码

img_path = os.path.join(self.img_dir, file)
image = cv2.imread(img_path)
rows, cols, c = image.shape
points_str = '145.10,263.40,1698.83,140.18,1424.10,909.50,484.30,1015.60'

# id_num_point = self.validate_label(id_num_point)  # 以左上顶点开始的顺时针box坐标
id_num_point_list = list(map(float, points_str.split(',')))
x_ = id_num_point_list[2] - id_num_point_list[0]
 y_ = id_num_point_list[3] - id_num_point_list[1]
l = abs(math.sqrt(x_ * x_ + y_ * y_))

x_1 = id_num_point_list[6] - id_num_point_list[0]
y_1 = id_num_point_list[7] - id_num_point_list[1]
l1 = abs(math.sqrt(x_1 * x_1 + y_1 * y_1))

# new_x1y1 = (id_num_point_list[0]+l, id_num_point_list[1])
# new_x3y3 = (id_num_point_list[0], id_num_point_list[1]+l1)

pts1 = np.float32([[id_num_point_list[0], id_num_point_list[1]],
                   [id_num_point_list[2], id_num_point_list[3]],
                   [id_num_point_list[4], id_num_point_list[5]]])
pts2 = np.float32([[id_num_point_list[0], id_num_point_list[1]],
                   [id_num_point_list[0]+l, id_num_point_list[1]],
                   [id_num_point_list[0], id_num_point_list[1]+l1]])
M = cv2.getAffineTransform(pts1, pts2)
# 第三个参数:变换后的图像大小
res = cv2.warpAffine(image, M, (rows, cols))

# angle_1_ = math.degrees(angle_1)
# imgRotation, matRotation = self.dumpRotateImage(image, angle_1_)

save_img_path = os.path.join(self.save_dir, file)
cv2.imwrite(save_img_path, res)

可以发现 使用方法一图片没有溢出,而使用方法2图片溢出,使得整个结果显示不够完整(不知道自己有没有处理错);

2.透视变换

将梯形拉展成矩形。

实线框是原图,虚线框是最终的结果

原始图片:

 使用代码:

img_path = os.path.join(self.img_dir, file)
image = cv2.imread(img_path)
points_str = '145.10,263.40,1698.83,140.18,1424.10,909.50,484.30,1015.60'

# id_num_point = self.validate_label(id_num_point)  # 以左上顶点开始的顺时针box坐标
id_num_point_list = list(map(float, points_str.split(',')))
x_ = id_num_point_list[6] - id_num_point_list[4]
y_ = id_num_point_list[7] - id_num_point_list[5]
l = abs(math.sqrt(x_ * x_ + y_ * y_))
# angle_1 = math.asin(y_ / l)

rows, cols, channels = image.shape
p1 = np.float32([[id_num_point_list[0], id_num_point_list[1]],\
                 [id_num_point_list[2], id_num_point_list[3]],\
                 [id_num_point_list[4], id_num_point_list[5]],\
                 [id_num_point_list[6], id_num_point_list[7]]])

x_1 = id_num_point_list[4] - id_num_point_list[2]
            y_1 = id_num_point_list[5] - id_num_point_list[3]
            l_1 = abs(math.sqrt(x_1 * x_1 + y_1 * y_1))

p2 = np.float32([[id_num_point_list[4]-l, id_num_point_list[5] - l_1],
                 [id_num_point_list[4], id_num_point_list[5] - l_1],
                 [id_num_point_list[4], id_num_point_list[5]],
                 [id_num_point_list[4]-l, id_num_point_list[5]]])
M = cv2.getPerspectiveTransform(p1, p2)
dst = cv2.warpPerspective(image, M, (cols, rows))
save_img_path = os.path.join(self.save_dir, file)
cv2.imwrite(save_img_path, dst)

出来的结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猫猫与橙子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值