最近对场景图片做一些研究和矫正,所以对仿射变换和透视变换有些了解,然后把自己的使用过程分享如下
仿射变换函数:
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字符识别的第一步会将文字区域进行矫正;
实施该方法的前提:有外接四边形的四个坐标点,如果标签是矩形,那么就没有办法进行矫正了;
样例图片:目标是将车厢上边缘处理为水平
原图,知道车厢的四个坐标点:
实施代码:
方法一:
找到合适的角度将车厢上边缘放平:
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)
出来的结果: