当前位置: 首页 > news >正文

计算机视觉----图像投影(透视)变换(小案例)

一、数据准备

xiaopiao.png

二、案例详解

现在需要对这张小票进行识别达到下图的效果

设计思路:

1、整体处理框架
  1. 图像读取与缩放:先读取原始图像,为提高处理效率,将图像按比例缩小
  2. 轮廓检测:通过边缘检测找到图像中所有物体的轮廓
  3. 目标定位:筛选出面积最大的轮廓(票据主体),并提取其四个顶点
  4. 透视矫正:利用透视变换将倾斜的票据转换为正视角的矩形
  5. 图像优化:通过二值化和形态学操作,增强票据文字与背景的对比度
2、分步处理思路详解

1. 图像准备阶段

  • 读取原始图像:使用cv2.imread加载票据图片

  • 缩放处理:将图像按比例缩小到高度 500 像素(通过resize函数)

    • 目的:减少计算量,加快后续处理速度

    • 关键:记录缩放比例(原始高度 / 500),用于后续坐标还原

2. 轮廓检测阶段

  • 灰度化:将彩色图像转为灰度图(cv2.cvtColor),简化处理通道

  • 二值化:用 OTSU 算法自动阈值分割(cv2.threshold),将图像转为黑白对比明显的二值图

  • 轮廓提取:使用cv2.findContours找出图像中所有轮廓,这些轮廓代表了不同物体的边缘

3. 目标轮廓筛选

  • 按面积排序:票据通常是图像中面积最大的物体,因此取面积最大的轮廓

  • 轮廓近似:通过cv2.approxPolyDP将复杂轮廓简化为多边形

    • 目标:得到 4 个顶点(票据的四个角)

    • 原理:用 Douglas-Peucker 算法,通过设定精度(周长的 5%)来简化轮廓

4. 透视变换矫正

  • 坐标排序:通过order_points函数将 4 个顶点按 "左上→右上→右下→左下" 排序

  • 计算目标尺寸:根据原始顶点坐标计算票据的实际宽高(用勾股定理算边长)

  • 透视变换

    • cv2.getPerspectiveTransform计算变换矩阵

    • cv2.warpPerspective将倾斜票据转换为正矩形

    • 关键:使用之前记录的缩放比例,将缩小图像的坐标还原为原始图像坐标

5. 图像优化处理

  • 再次二值化:将矫正后的图像转为黑白二值图,突出文字信息

  • 腐蚀操作:用cv2.erode去除细小噪点,使文字边缘更清晰

  • 旋转调整:根据需要旋转图像(cv2.rotate),使票据方向符合阅读习惯

3、核心技术点
  1. 透视变换:解决票据因拍摄角度导致的倾斜和透视变形问题
  2. 轮廓检测与筛选:准确识别票据主体,排除背景干扰
  3. 坐标计算:通过几何方法确定票据的顶点位置和实际尺寸
  4. 图像预处理:通过二值化和形态学操作优化图像质量

三、完整代码

import numpy as np
import cv2
def cv_show(name, img):"""显示图像"""cv2.imshow(name, img)cv2.waitKey(0)def order_points(pts):# 一共4个坐标点rect = np.zeros((4, 2), dtype="float32")  # 用来存储排序之后的坐标# 按顺序找到对应坐标0123分别是 左上,右上,右下,左下s = pts.sum(axis=1)  # 对pts矩阵的每一行进行求和操作。(x+y)rect[0] = pts[np.argmin(s)]rect[2] = pts[np.argmax(s)]diff = np.diff(pts, axis=1)  # 对pts矩阵的每一行进行求差操作。(y - x)rect[1] = pts[np.argmin(diff)]rect[3] = pts[np.argmax(diff)]return rectdef four_point_transform(image,pts):rect = order_points(pts)(tl,tr,br,bl) = rectwidthA = np.sqrt(((br[0]-bl[0])**2)+((br[1]-bl[1])**2))widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))maxWidth = max(int(widthA),int(widthB))heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))maxHeight = max(int(heightA), int(heightB))dst = np.array([[0,0],[maxWidth+1,0],[maxWidth+1,maxHeight+1],[0,maxHeight+1]],dtype="float32")M= cv2.getPerspectiveTransform(rect,dst)warped = cv2.warpPerspective(image,M,(maxWidth,maxHeight))return warpeddef resize(image, width=None, height=None, inter=cv2.INTER_AREA):dim = None(h, w) = image.shape[:2]if width is None and height is None:return imageif width is None:r = height / float(h)dim = (int(w * r), height)else:r = width / float(w)dim = (width, int(h * r))resized = cv2.resize(image, dim, interpolation=inter)return resized#数据读取
image = cv2.imread('xiaopiao.jpg')
cv_show('image',image)#图片缩小处理
ratio = image.shape[0]/500
orig = image.copy()
image = resize(orig,height=500)
cv_show('1',image)#轮毂检测
print('STEP 1: 轮毂检测')
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)edged = cv2.threshold(gray,0,255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cnts = cv2.findContours(edged.copy(),cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)[-2]
image_contours = cv2.drawContours(image.copy(),cnts,-1,(0,0,255),1)
cv_show('image_contours',image_contours)print("STEP 2:获取最大轮毂")
screenCnt = sorted(cnts,key=cv2.contourArea,reverse=True)[0]
print(screenCnt.shape)
peri = cv2.arcLength(screenCnt,True)
screenCnt = cv2.approxPolyDP(screenCnt,0.05*peri,True)
print(screenCnt.shape)
image_contour = cv2.drawContours(image.copy(),[screenCnt],-1,(0,0,255),2)cv_show('image_contour',image_contour)warped = four_point_transform(orig,screenCnt.reshape(4,2)*ratio)
image = np.rot90(warped, k=1)
cv2.imwrite('invoice_new.jpg',image)
cv2.namedWindow('xx',cv2.WINDOW_NORMAL)
cv_show('xx',image)# 三值处理
warped =cv2.cvtColor(warped,cv2.COLOR_BGR2GRAY)
ref = cv2.threshold(warped, 0, 255,cv2.THRESH_BINARY |cv2.THRESH_OTSU)[1]
cv_show( 'ref',ref)
ref=resize(ref,width=900)
kernel=np.ones((2,2),np.uint8)# 设置kenenel大小
ref_new =cv2.erode(ref,kernel,iterations=1)#腐蚀# cv_show('ref new',ref new)
rotated_image = cv2.rotate(ref_new, cv2.ROTATE_90_COUNTERCLOCKWISE)
cv2.imshow("result",rotated_image)
cv2.waitKey(0)

四、完整效果

小票的轮毂定位

定位后将其完整切下来并调整比例

突出文字效果


文章转载自:

http://UStQRVqO.rttkL.cn
http://8toKmwbk.rttkL.cn
http://fFnYDsAC.rttkL.cn
http://0BFYruQi.rttkL.cn
http://3TdeS7lQ.rttkL.cn
http://uYMxa65u.rttkL.cn
http://LU3VEzL2.rttkL.cn
http://MOcEcma7.rttkL.cn
http://BJ0PvkK5.rttkL.cn
http://IVrAUfNi.rttkL.cn
http://xtDvXram.rttkL.cn
http://3FEE9Bmg.rttkL.cn
http://1nxgvEMb.rttkL.cn
http://OXbE4Xtw.rttkL.cn
http://voQlBKxz.rttkL.cn
http://8PlQXrTe.rttkL.cn
http://GrB8yrED.rttkL.cn
http://n9GGAr1D.rttkL.cn
http://i0xYx5CR.rttkL.cn
http://UGlPA8TY.rttkL.cn
http://AasWBSfs.rttkL.cn
http://enop6Wvk.rttkL.cn
http://PouEU5DB.rttkL.cn
http://ZUgWamFo.rttkL.cn
http://hzNXVd8j.rttkL.cn
http://C0Z7e5pn.rttkL.cn
http://cXnMm9W6.rttkL.cn
http://jGj3Qcbl.rttkL.cn
http://UJkp7VFC.rttkL.cn
http://eVmA0YoA.rttkL.cn
http://www.dtcms.com/a/378140.html

相关文章:

  • Docker 学习笔记(七):Docker Swarm 服务管理与 Containerd 实践
  • 3-10〔OSCP ◈ 研记〕❘ WEB应用攻击▸XSS攻击理论基础
  • 微信小程序开发笔记(01_小程序基础与配置文件)
  • ArcGIS JSAPI 高级教程 - ArcGIS Maps SDK for JavaScript - 自定义(GLSL)修改高亮图层样式
  • idea npm install 很慢(nodejs)
  • Elasticsearch 创建索引别名的正确姿势
  • Kite Compositor for Mac v2.1.2 安装教程|DMG文件安装步骤(Mac用户必看)
  • 深入探索 Unity 错误排查过程:从“滚动条问题”到“鼠标悬浮异常”
  • 【设计模式】从游戏角度开始了解设计模式 --- 抽象工厂模式
  • 南京大学实现非线性光学新范式丨《Light》报道光电可调谐液晶二次谐波衍射研究
  • 【Java】Hibernate-5:Hibernate的一级缓存
  • 【C#】理解.NET内存机制:堆、栈与装箱拆箱的底层逻辑及优化技巧
  • 【软件设计师】信息安全
  • Mac本地docker安装Kibana+ElasticSearch
  • 使用VMware Workstation Pro搭建Ubuntu服务器虚拟机
  • 对于统计类sql是否应该在原业务表进行操作的分析
  • vite+vue+js项目使用ts报错
  • SGLang简介
  • 信息检索技术综述:从传统稀疏检索到现代深度学习方法
  • 我对rabbitmq的理解(第一次)
  • 算法练习——跳跃游戏II
  • binlog 的事件类型
  • CKA01--HPA自动扩缩容
  • Vue加载资源‘如图片’的“直接引入“方式和“request()“的区别
  • 豆瓣网影视数据分析与应用
  • 多技术融合提升环境生态水文、土地土壤、农业大气等领域的数据分析与项目科研水平
  • 基于 Apache Doris 的用户画像数据模型设计方案
  • Windows Node.js 安装及环境配置详细教程
  • Pandas与SQL融合:用pandasql高效分析Uber数据
  • spark sql之from_json函数