c# OpenCvSharp透视矫正六步实现透视矫正(八)

透视矫正,引用文档拍照扫描,相片矫正这块。

  1. 读取图像Cv2.ImRead();
  2. 预处理(灰度化,高斯滤波、边缘检测)
  3. 轮廓检测(获取到最大轮廓)
  4. 获取最大面积轮廓的四个顶点
  5. 标识最小矩形坐标
  6. 透视矫正显示

完整代码

 // 1、读取图像Mat image = Cv2.ImRead("2.jpg", ImreadModes.Color);//2、预处理(灰度化,高斯滤波、边缘检测)Mat src_gray = new Mat();Cv2.CvtColor(image, src_gray, ColorConversionCodes.BGR2GRAY); // 转换为灰度图像Cv2.GaussianBlur(src_gray, src_gray, new Size(5, 5), 0, 0); // 进行高斯模糊Mat canny_Image = new Mat();Cv2.Canny(src_gray, canny_Image, 75, 200);//3、轮廓检测Point[][] contours;HierarchyIndex[] hierarchy;Cv2.FindContours(canny_Image, out contours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple);// 计算轮廓的面积double maxArea = 0;int maxAreaIndex = -1;for (int i = 0; i < contours.Length; i++){double area = Cv2.ContourArea(contours[i]);if (area > maxArea){maxArea = area;maxAreaIndex = i;}}// 获取最大面积的轮廓Point[] largestContour = contours[maxAreaIndex];//4、获取最大面积轮廓的四个顶点。Point[] approx = Cv2.ApproxPolyDP(contours[maxAreaIndex], 0.02 * Cv2.ArcLength(contours[maxAreaIndex], true), true);Cv2.DrawContours(image, new Point[][] { approx }, -1, Scalar.Blue, 2);//可以注释掉for (int i = 0; i < 4; i++){// 设置目标图像的四个顶点坐标//Cv2.PutText(image, "H"+i, new Point(approx[i].X, approx[i].Y), HersheyFonts.HersheySimplex, 1, new Scalar(0, 0, 255), 2, LineTypes.Link4);}//5、透视转换OpenCvSharp.Point2f[] srcPt = new OpenCvSharp.Point2f[4];srcPt[0] = approx[0];srcPt[1] = approx[3];srcPt[2] = approx[2];srcPt[3] = approx[1];RotatedRect rect = Cv2.MinAreaRect(srcPt);Rect box = rect.BoundingRect();OpenCvSharp.Point2f[] dstPt = new OpenCvSharp.Point2f[4];//可以注释掉用于观察坐标点是否对齐dstPt[0].X = 0;dstPt[0].Y = 0;dstPt[1].X = 0 + box.Width;dstPt[1].Y = 0;dstPt[2].X = 0 + box.Width;dstPt[2].Y = 0 + box.Height;dstPt[3].X = 0;dstPt[3].Y = 0 + box.Height;Mat final = new Mat(box.Height, box.Width, MatType.CV_8UC3);Mat warpmatrix = Cv2.GetPerspectiveTransform(srcPt, dstPt);//获得变换矩阵Cv2.WarpPerspective(image, final, warpmatrix, final.Size());//投射变换,将结果赋给finalCv2.ImShow("获取新正四边形", final);Cv2.WaitKey(0);Rect roi = new Rect(box.X, box.Y, box.Width, box.Height);//坐标 x,y 尺寸 长宽Mat croppedImage = new Mat(final, roi);for (int i = 0; i < 4; i++){// 设置目标图像的四个顶点坐标Cv2.PutText(image, "A" + i, new Point(dstPt[i].X, dstPt[i].Y), HersheyFonts.HersheySimplex, 1, new Scalar(0, 0, 255), 2, LineTypes.Link4);}// 显示结果Cv2.ImShow("透视矫正图像", image);Cv2.WaitKey(0);

一 、读取图像Cv2.ImRead()

// 1、读取图像
Mat image = Cv2.ImRead("2.jpg", ImreadModes.Color);

二、预处理(灰度化,高斯滤波、边缘检测) 

灰度化:Cv2.CvtColor();

高斯滤波:Cv2.GaussianBlur();

边缘检测:Cv2.Canny();

//2、预处理(灰度化,高斯滤波、边缘检测)Mat src_gray = new Mat();
Cv2.CvtColor(image, src_gray, ColorConversionCodes.BGR2GRAY); // 转换为灰度图像
Cv2.GaussianBlur(src_gray, src_gray, new Size(5, 5), 0, 0); // 进行高斯模糊
Mat canny_Image = new Mat();
Cv2.Canny(src_gray, canny_Image, 75, 200);

 

三、轮廓检测(获取到最大轮廓) 

通过Cv2.ContourArea()计算轮廓的面积,选出最大轮廓

//3、轮廓检测
Point[][] contours;
HierarchyIndex[] hierarchy;
Cv2.FindContours(canny_Image, out contours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple);// 计算轮廓的面积
double maxArea = 0;
int maxAreaIndex = -1;
for (int i = 0; i < contours.Length; i++)
{double area = Cv2.ContourArea(contours[i]);if (area > maxArea){maxArea = area;maxAreaIndex = i;}
}// 获取最大面积的轮廓
Point[] largestContour = contours[maxAreaIndex];

 

四、 获取最大面积轮廓的四个顶点。

Cv2.ApproxPolyDP() 获取4个顶点坐标

//4、获取最大面积轮廓的四个顶点。
Point[] approx = Cv2.ApproxPolyDP(contours[maxAreaIndex], 0.02 * Cv2.ArcLength(contours[maxAreaIndex], true), true);

 标识四个顶点

 //可以注释掉for (int i = 0; i < 4; i++){// 设置目标图像的四个顶点坐标Cv2.PutText(image, "H"+i, new Point(approx[i].X, approx[i].Y), HersheyFonts.HersheySimplex, 1, new Scalar(0, 0, 255), 2, LineTypes.Link4);}

五、标识最小矩形坐标

获取顶点内最小矩形Cv2.MinAreaRect(srcPt);

//获取四个顶点坐标最小矩形顶点
RotatedRect rect = Cv2.MinAreaRect(srcPt);
Rect box = rect.BoundingRect();
OpenCvSharp.Point2f[] dstPt = new OpenCvSharp.Point2f[4];
stPt[0].X = box.X;dstPt[0].Y = box.Y;dstPt[1].X = box.X + box.Width;dstPt[1].Y = box.Y;dstPt[2].X = box.X + box.Width;dstPt[2].Y = box.Y + box.Height;dstPt[3].X = box.X;dstPt[3].Y = box.Y + box.Height;Mat final = new Mat();Mat warpmatrix = Cv2.GetPerspectiveTransform(srcPt, dstPt);//获得变换矩阵Cv2.WarpPerspective(image, final, warpmatrix, image.Size());//投射变换,将结果赋给finalRect roi = new Rect(box.X, box.Y, box.Width, box.Height);//坐标 x,y 尺寸 长宽Mat croppedImage = new Mat(final, roi);for (int i = 0; i < 4; i++){// 设置目标图像的四个顶点坐标Cv2.PutText(image, "A" + i, new Point(dstPt[i].X, dstPt[i].Y), HersheyFonts.HersheySimplex, 1, new Scalar(0, 0, 255), 2, LineTypes.Link4);}

 

两个坐标点顺序不一样,对齐坐标顺序,进行透视坐标转换

 

 //5、透视转换OpenCvSharp.Point2f[] srcPt = new OpenCvSharp.Point2f[4];srcPt[0] = approx[0];srcPt[1] = approx[3];srcPt[2] = approx[2];srcPt[3] = approx[1];RotatedRect rect = Cv2.MinAreaRect(srcPt);Rect box = rect.BoundingRect();OpenCvSharp.Point2f[] dstPt = new OpenCvSharp.Point2f[4];dstPt[0].X = box.X;dstPt[0].Y = box.Y;dstPt[1].X = box.X + box.Width;dstPt[1].Y = box.Y;dstPt[2].X = box.X + box.Width;dstPt[2].Y = box.Y + box.Height;dstPt[3].X = box.X;dstPt[3].Y = box.Y + box.Height;Mat final = new Mat();

六、透视变换显示

Mat warpmatrix = Cv2.GetPerspectiveTransform(srcPt, dstPt);//获得变换矩阵
Cv2.WarpPerspective(image, final, warpmatrix, final.Size());//投射变换,将结果赋给final
Cv2.ImShow("透视矫正图像", final);

 通过掌握这六个步骤,你可以在C#中使用OpenCvSharp实现透视矫正。祝你成功!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/578157.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux系统安装及管理

目录 一、Linux应用程序基础 1.1应用程序与系统命令的关系 1.2典型应用程序的目录结构 1.3常见的软件包装类型 二、RPM软件包管理 1.RPM是什么&#xff1f; 2.RPM命令的格式 2,1查看已安装的软件包格式 2.2查看未安装的软件包 3.RPM安装包从哪里来&#xff1f; 4.挂…

web前端项目-七彩夜空烟花【附源码】

web前端项目-七彩动态夜空烟花【附源码】 本项目仅使用了HTML&#xff0c;代码简单&#xff0c;实现效果绚丽&#xff0c;且本项目代码直接运行即可实现&#xff0c;无需图片素材&#xff0c;接下来让我们一起实现一场美丽的烟花秀叭 运行效果&#xff1a;鼠标点击和移动可控制…

理解SpringMVC的工作流程

组件 前置控制器 DispatcherServlet。 映射控制器 HandlerMapping。 处理器 Controller。 模型和视图 ModelAndView。 视图解析器 ViewResolver。 工作流程 spring mvc 先将请求发送给 DispatcherServlet。 DispatcherServlet 查询一个或多个 HandlerMapping&#xff0c;找到…

vc 用MySQL Connector/C++

1 下载 MySQL :: Download Connector/C 2 vc配置 添加路径 2.1 右击项目 -> 属性 2.2 配置属性-> vc目录 -> 包含目录 -> 添加 D:\mysql-connector-c-8.2.0-winx64\include\jdbc 具体目录在mysql-connector-c的文件夹中搜索 mysql_driver.h文件 然后把这个文件…

jmeter-set up先登录获取token,再测试

一、何为setup 一种特殊类型的线程组&#xff0c;可用于执行预测试操作&#xff1b;简单来讲就是执行测试线程组前&#xff0c;先执行setup 作用 例如前面&#xff0c;我们说到的&#xff0c;压测之前只用JMeter调用业务接口造数或者通过JDBC操作数据库造数&#xff0c;可以放…

Spring中BeanFactoryPostProcessors的使用和原理

Spring中的BeanFactoryPostProcessor是在Spring容器实例化Bean之后&#xff0c;初始化之前执行的一个扩展机制。它允许开发者在Bean的实例化和初始化之前对BeanDefinition进行修改和处理&#xff0c;从而对Bean的创建过程进行干预和定制化。 BeanFactoryPostProcessor接口定义…

信息网络协议基础-接入网技术

文章目录 概述***基于ATM架构虚电路PVC和SVC信元格式为什么信元格式由AAL决定?网络架构传统电信网络:点对点链路PPP协议协议内容消息过程多协议封装功能电话网接入Internet(DSL 数字用户线路)主要接入技术ADSL关键技术DMTDSLAM体系结构PPPOE帧格式过程特点局域网定义参考模型L…

网络安全法规和模型

基础 ISO信息安全&#xff1a;为数据处理系统建立和采取技术、管理的安全保护&#xff0c;保护计算机硬件、软件、数据不因偶然的或恶意的原因而受到破坏、更改、泄露 信息安全属性&#xff1a; CIA三元组&#xff1a;保密性、完整性、可用性 其他属性&#xff1a;真实性、不…

sql查询分数排名

编写一个 sql 查询来实现分数排名。 如果两个分数相同&#xff0c;则两个分数排名&#xff08;rank&#xff09;相同。请注意&#xff0c;平分后的下一个名次应该是下一个连续的整数值。换句话说&#xff0c;名次之间不应该有“间隔”。 ----------- | id | score | ---------…

前端---表格标签

1. 表格的结构 表格是由行和列组成&#xff0c;好比一个excel文件 2. 表格标签 <table>标签&#xff1a;表示一个表格 <tr>标签&#xff1a;表示表格中的一行 <td>标签&#xff1a;表示表格中的列<th>标签&#xff1a;表示表格中的表头 示例代码: &l…

鸿蒙开发中的坑(持续更新……)

最近在使用鸿蒙开发时&#xff0c;碰到了一些坑&#xff0c;特做记录&#xff0c;如&#xff1a;鸿蒙的preview不能预览&#xff0c;轮播图组件Swiper使用时的问题&#xff0c;console.log() 打印的内容 一、鸿蒙的preview不能预览 首先&#xff0c;只有 ets文件才能预览。 其…

Spring系列学习二、Spring框架的环境配置

Spring框架的环境配置 一、Java环境配置二、 Spring框架的安装与配置三、Maven与Gradle环境的配置四、IDE环境配置&#xff08;Eclipse与IntelliJ IDEA&#xff09;五、结语 一、Java环境配置 所有编程旅程总是得从基础开始&#xff0c;如同乐高积木大作的基座&#xff0c;首先…

嵌入式——RTC闹钟Alarm

开发流程 配置RTC时钟设置RTC闹钟配置RTC闹钟中断实现中断函数RTC闹钟初始化 // 闹钟外部中断 exti_flag_clear(EXTI_17); exti_init(EXTI_17,EXTI_INTERRUPT,EXTI_TRIG_RISING);// 重置闹钟 rtc_alarm_disable(RTC_ALARM0);rtc_alarm_struct ras; ras.alarm_mask = RTC_ALARM…

408数据结构错题知识点拾遗

408相关&#xff1a; 408数据结构错题知识点拾遗 408计算机网络错题知识点拾遗 对于数据结构的学习&#xff0c;个人认为要对概念性的东西进行理解&#xff0c;特别是树的性质、图的相关性质和考察的相应算法。应用题强化的话&#xff0c;对于每一章节尾的应用小节&#xff0c…

使用C++ 标准库map关联式容器根绝键值查找文件是否存在

下面这段代码创建了一个简单的示例&#xff0c;演示了如何使用 std::map 存储和检索 std::shared_ptr 类型的对象。代码中的注释已经对每一步进行了说明 #include <iostream> #include <map> #include <string> #include <memory>class ObjFile{ publ…

PHP函数学习总结

version_compare&#xff08;比较php版本&#xff09; 用法&#xff1a; version_compare(string $version1, string $version2, ?string $operator null): int|bool//示例 $result version_compare(PHP_VERSION, 8.0.0) > 0 ? ok : fail;echo $result;// 输出ok证明当…

【Log4j2】Log4j2最佳实践:Log4j2配置超过7天压缩,超过3个月删除文件的滚动日志,分别定义info文件和error文件,按照每小时存储

目录 Log4j2配置 springboot多环境日志配置 参考资料 Log4j2配置 如果你想要在控制台输出美化的日志信息&#xff0c;你可以使用Log4j2的ConsoleAppender和AnsiColorConverter来实现。下面是相应的配置示例&#xff1a; <Configuration status"WARN"><…

蓝桥杯c/c++程序设计——冶炼金属

冶炼金属 问题描述 小蓝有一个神奇的炉子用于将普通金属 O 冶炼成为一种特殊金属 X。这个炉子有一个称作转换率的属性 V&#xff0c;V 是一个正整数&#xff0c;这意味着消耗 V 个普通金属 O 恰好可以冶炼出一个特殊金属 X&#xff0c;当普通金属 O 的数目不足 V 时&#xff0…

Python - 深夜数据结构与算法之 Divide Conquer Backtrack

目录 一.引言 二.分治与回溯简介 1.Divide & Conquer 分治 2.BackTrack 回溯 三.经典算法实战 1.Combination-Of-Phone [17] 2.Permutations [46] 3.Permutations-2 [47] 4.Pow-X [50] 5.N-Queen [51] 6.Combinations [78] 7.Sub-Sets [78] 8.Majority-Elemen…

数组基础及相关例题

目录 1.一维数组的初始化 2.二维数组的初始化 3.字符数组 1.puts 2.gets 3.strcat 4.strcpy 5.strcmp 6.strlen ​编辑 7. strlwr与strupr 易错习题 1 2 3 4 5 6 1.一维数组的初始化 2.二维数组的初始化 注意 第一维的长度不用指定&#xff0c;第二维的…