OpenCV进阶操纵:图像的透视变更

打印 上一主题 下一主题

主题 1416|帖子 1416|积分 4248



  

前言

在图像处理中,透视变更(Perspective Transformation) 是一种强大的技能,能够校正因视角倾斜导致的图像变形。无论是扫描文档的主动矫正、车牌识别,照旧增强现实(AR)中的假造物体叠加,透视变更都饰演着紧张角色。本文将通过OpenCV库,手把手教你把握透视变更的核心原理与代码实现。

一、什么是透视变更?

透视变更是一种将图像从任意视角投影到新视角的几何变更。与仅能处理平移、旋转和缩放的仿射变更差别,透视变更可以处理三维视角变化,彻底改变图像的投影关系,实现“视角拉正”的效果。

核心特点


  • 可将倾斜拍摄的图像转换为正视图
  • 需要提供4组对应点(原图坐标 + 目标坐标)
  • 通过变更矩阵实现非线性映射
二、透视变更的过程

对一张我们即将做透视变更图像,起首要获取到图像中的4个坐标点,用于与目标图像中的坐标对应,这四个点照旧有次序的以坐标轴原点为参照点,距离原点近来的点为0号坐标,最远的为2号坐标,这两个点是最容易区分出来的;1号和3号位置可以通过坐标相减作为区分,距离X轴近的坐标的y值小于x值,以是按照x坐标减去y坐标得到的值1号坐标的值大于3号坐标的值。
区分0和2号坐标点:对四个点每个点坐标的x和y的值相加求和,我们发现,针对任意图片表面,如果被四个点描绘,距离原点近来的点求和的值最小,在右下点的值求和的数值最大,可以区分出左上和右下两个点
区分1和3号坐标点:对四个点每个点坐标的x和y的值相减(x-y),针对任意图片表面,如果被四个点描绘,位于右上角做差的值为一个很大的正数,在左下点的值做差的数值为负数,可以区分出左下和右上两个点。

水平为x轴
垂直为y轴
三、OpenCV透视变更核心函数

cv2.getPerspectiveTransform()


  • 作用:根据4组对应点计算3x3透视变更矩阵。
  • 输入参数:
  • src: 原图4个点的坐标(格式:np.float32([[x1,y1], [x2,y2], …]))
  • dst: 目标图像对应4个点的坐标
cv2.warpPerspective()


  • 作用:应用变更矩阵执行透视变更。
  • 参数:
  • src: 输入图像
  • M: 变更矩阵
  • dsize: 输出图像尺寸
四、文档扫描校正(代码)

目标:将倾斜文档转为正视图
1、预处理

  1. import numpy as np
  2. import cv2
  3. def cv_show(name,img):
  4.     cv2.imshow(name,img)
  5.     cv2.waitKey(0)
  6. # 调整图像高宽,保持图像宽高比不变
  7. def resize(image,width=None,height=None ,inter=cv2.INTER_AREA):  # 输入参数为图像、可选宽度、可选高度、插值方式默认为cv2.INTER_AREA,即面积插值
  8.     dim = None   # 存储计算后的目标尺寸w、h
  9.     (h,w) = image.shape[:2]  # 返回输入图像高宽
  10.     if width is None and height is None:   # 判断是否指定了宽和高大小,如果没有指定则返回原图
  11.         return image
  12.     if width is None:   # 判断如果没有指定宽度大小,则表示指定了高度大小,那么运行内部代码
  13.         r = height/float(h)   # 指定高度与原图高度的比值
  14.         dim = (int(w*r),height)   # 宽度乘以比值得到新的宽度,此处得到新的宽高
  15.     else:  # 此处表示为width不是None,即指定了宽度,与上述方法一致,计算比值
  16.         r = width/float(w)
  17.         dim = (width,int(h*r))
  18.     resized = cv2.resize(image,dim,interpolation=inter)     # 指定图像大小为上述的dim,inter默认为cV2.INTER_AREA,即面积插值,适用于缩放图像。
  19.     return resized
复制代码
2、定义表面点的排序函数

  1. def order_points(pts):   # 对输入的四个点按照左上、右上、右下、左下进行排序
  2.     rect = np.zeros((4,2),dtype='float32')   # 创建一个4*2的数组,用来存储排序之后的坐标位置
  3.     # 按顺序找到对应坐标0123分别是左上、右上、右下、左下
  4.     s = pts.sum(axis=1)   # 对pts矩阵的每个点的x y相加
  5.     rect[0] = pts[np.argmin(s)]    # np.argmin(s)表示数组s中最小值的索引,表示左上的点的坐标
  6.     rect[2] = pts[np.argmax(s)]    # 返回最大值索引,即右下角的点坐标
  7.     diff = np.diff(pts,axis=1)   # 对pts矩阵的每一行的点求差值
  8.     rect[1] = pts[np.argmin(diff)]   # 差值最小的点为右上角点
  9.     rect[3] = pts[np.argmax(diff)]   # 差值最大表示左下角点
  10.     return rect   # 返回排序好的四个点的坐标
复制代码
3、定义透视变更函数

  1. # 将透视扭曲的矩形变换成一个规则的矩阵
  2. def four_point_transform(image,pts):
  3.     # 获取输入坐标点
  4.     rect = order_points(pts)  # 为上述排序的四个点
  5.     (tl,tr,br,bl) = rect   # 分别返回给四个值,分别表示为左上、右上、右下、左下
  6.     # 计算输入的w和h值
  7.     widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1]-bl[1]) ** 2))   # 计算四边形底边的宽度
  8.     widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1]-tl[1]) ** 2))   # 计算顶边的宽度
  9.     maxWidth = max(int(widthA), int(widthB))   # 返回最大宽度
  10.     heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))   # 计算左上角到右下角的对角线长度
  11.     heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))   # 计算右上角到左下角的高的长度
  12.     maxHeight = max(int(heightA),int(heightB))   # 返回最长的高度
  13.     # 变换后对应坐标位置
  14.     dst = np.array([[0,0],   # 定义四个点,表示变换后的矩阵的角点
  15.                     [maxWidth-1,0],
  16.                     [maxWidth-1,maxHeight-1],
  17.                     [0,maxHeight-1]],dtype='float32')
  18.     M = cv2.getPerspectiveTransform(rect,dst)  # 根据原始点和变换后的点计算透视变换矩阵M
  19.     warped = cv2.warpPerspective(image,M,(maxWidth,maxHeight))  # 对原始图像,针推变换矩阵和输出图像大小进行透视变换,返回变换后的图片
  20.     # 返回变换后的结果
  21.     return warped
复制代码
4、读取原图并缩放

  1. # # 读取输入
  2. image = cv2.imread('fapiao.jpg')   # 读取原图
  3. cv_show('image',image)   # 展示原图
  4. # 图片过大,进行缩小处理
  5. ratio = image.shape[0] / 500.0  # 计算缩小比率,[0]表示图像的高
  6. orig = image.copy()   # 对原图复制生成副本
  7. image = resize(orig, height=500)   # 更改图像尺寸,输入高度自动生成宽度
  8. cv_show('1',image)   # 展示缩放后的图片
复制代码

5、表面检测

  1. gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)  # 灰度图
  2. edged = cv2.threshold(gray,0,255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]  # 进行二值化,cv2.THRESH_OTSU自动寻找最优全局阈值,255表示高于最优阈值时将其更改为255
  3. cnts = cv2.findContours(edged.copy(),cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)[1]  # 轮廓检测
  4. # cv2.RETR_LIST表示检索所有轮廓,但是不建立层次关系
  5. # cv2.CHAIN_APPROX_SIMPLE 表示只保存轮廓拐点的信息
  6. # 总体返回处理的图像、轮廓列表、层次结构,这里返回索引为1,表示返回轮廓列表
  7. image_contours = cv2.drawContours(image.copy(),cnts,-1,(0,0,255),1)  # 绘制所有轮廓
  8. # 在原始图像的副本上绘制了轮廓
  9. # 绘制轮廓的位置为上述获取的拐点信息,绘制线条颜色为红色BRG(0,0,255),线条粗细为1个像素
  10. cv_show('image_contours',image_contours)  # 展示绘制好的图片
复制代码

6、绘制最大表面

  1. screenCnt = sorted(cnts,key = cv2.contourArea,reverse=True)[0]   # 对上述获取的轮廓列表,排序依据是轮廓面积,reverse=True表示降序,[0]表示获取面积最大的轮廓
  2. peri = cv2.arcLength(screenCnt,True)   # 计算最大轮廓的周长
  3. screenCnt = cv2.approxPolyDP(screenCnt,0.02*peri,True)  # 轮廓近似,近似为一个多边形,表示新的轮廓与原来的轮廓最大距离不超过原始轮廓宽度的0.02倍,True表示轮廓为闭合的
  4. image_contour = cv2.drawContours(image.copy(),[screenCnt],-1,(0,255,0),2)  # 绘制轮廓,将上述找到的轮廓绘制到原图的副本上
  5. cv2.imshow('image_contour',image_contour)
  6. cv2.waitKey(0)
复制代码

7、对最大表面进行透视变更

  1. warped = four_point_transform(orig,screenCnt.reshape(4,2)*ratio)  # 输入参数原图,将最大轮廓图形状改变为4*2的格式,即四个点,然后乘以上述定义的比率来缩放轮廓
  2. cv2.imwrite('invoice_new.jpg',warped)   # 将经过透视变换处理的图片存入本地
  3. cv2.namedWindow('xx',cv2.WINDOW_NORMAL)  # 设置一个窗口,名称为xx,这个窗口大小用户可通过拖动随意调节大小
  4. cv2.imshow('xx',warped)  # 展示经过透视变换处理的图片
  5. cv2.waitKey(0)
复制代码

8、旋转、二值化处理

  1. # 二值处理
  2. warped = cv2.cvtColor(warped,cv2.COLOR_BGR2GRAY)   # 导入新的图片的灰度图
  3. ref = cv2.threshold(warped,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1]  # 对灰度图进行二值化处理
  4. kernel = np.ones((2,2),np.uint8)   # 设置一个单位矩阵,大小为2*2,表示设置核kernel的大小
  5. ref_new = cv2.morphologyEx(ref,cv2.MORPH_CLOSE,kernel)   # 闭运算,先膨胀再腐蚀
  6. ref_new = resize(ref_new.copy(),width=500)   # 对闭运算处理完的图像重置大小
  7. cv_show('yy',ref_new)
  8. rotated_image = cv2.rotate(ref_new,cv2.ROTATE_90_COUNTERCLOCKWISE)  # 对图像逆时针旋转90度
  9. cv2.imshow('result',rotated_image)
  10. cv2.waitKey(0)
复制代码


总结

通过OpenCV的透视变更,我们能够轻松办理因拍摄角度导致的图像形变题目。无论是手动标定点照旧结合主动检测算法,这一技能都为复杂场景下的图像处理提供了基础支持。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

星球的眼睛

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表