C# OpenCvSharp 图像校正

news2024/11/19 2:22:07

效果

Demo下载 

 代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using OpenCvSharp;
using OpenCvSharp.Extensions;

namespace OpenCvSharp_图像校正
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        string img = "test.png";

        private void Form1_Load(object sender, EventArgs e)
        {
            pictureBox1.Image = new Bitmap(img);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Mat src = new Mat(img);

            //转化为灰度图
            //Cv2.CvtColor(src, src, ColorConversionCodes.RGB2GRAY);

            InputArray kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(3, 3));
            Cv2.MorphologyEx(src, src, MorphTypes.Close, kernel, new OpenCvSharp.Point(-1, -1), 3);
            //Cv2.ImShow("MorphologyEx", src);

            /*
                ksize,高斯内核大小,ksize.width和ksize.height必须是正奇数,两者可以不相同,值越大越模糊
                sigmaX,Y轴方向的标准差,值越大越模糊
                sigmaY,X轴方向的标准差,值越大越模糊
             */
            Cv2.GaussianBlur(src, src, new OpenCvSharp.Size(11, 11), 2, 2);
            //Cv2.ImShow("GaussianBlur", src);

            //Canny边缘检测
            Mat canny_Image = new Mat();
            Cv2.Canny(src, canny_Image, 10, 30, 3, false);

            OpenCvSharp.Point[][] contours;
            HierarchyIndex[] hierarchly;
            /*
	          findContours找到轮廓
	          第一个参数:单通道图像矩阵,可以是灰度图,但更常用的是二值图像,一般是经过Canny、拉普拉斯等边缘检测算子处理过的二值图像;
	          第二个参数:contours 
	          第三个参数:hierarchy
	          第四个参数:轮廓的检索模式
	  		        取值一:CV_RETR_EXTERNAL 只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略
	  		        取值二:CV_RETR_LIST     检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立,没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓,所以hierarchy向量内所有元素的第3、第4个分量都会被置为-1,具体下文会讲到
	  		        取值三:CV_RETR_CCOMP    检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层
	  		        取值四:CV_RETR_TREE     检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。
	          第五个参数:轮廓的近似方法
	  		        取值一:CV_CHAIN_APPROX_NONE   保存物体边界上所有连续的轮廓点到contours向量内
	  		        取值二:CV_CHAIN_APPROX_SIMPLE 仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量内,拐点与拐点之间直线段上的信息点不予保留
	  		        取值三和四:CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法
	          第六个参数:Point偏移量,所有的轮廓信息相对于原始图像对应点的偏移量,相当于在每一个检测出的轮廓点上加上该偏移量,且Point可以是负值。不填为默认不偏移Point()
	         */
            Cv2.FindContours(canny_Image, out contours, out hierarchly,
                RetrievalModes.External,
                ContourApproximationModes.ApproxSimple,
                new OpenCvSharp.Point(0, 0));

            if (contours.Length == 0)
            {
                MessageBox.Show("边缘检测失败");
                return;
            }

            Random rnd = new Random();
            Scalar color;
            color = new Scalar(0, 255, 0);
            for (int i = 0; i < contours.Length; i++)
            {
                color = new Scalar(rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255));
                Cv2.DrawContours(src, contours, i, color, 2, LineTypes.Link4);
            }
            //Cv2.ImShow("contours", src);

            //求出面积最大的轮廓
            double max_area = 0.0;
            double currentArea = 0.0;
            OpenCvSharp.Point[] max_contour = null;
            for (int i = 0; i < contours.Length; i++)
            {
                currentArea = Cv2.ContourArea(contours[i]);
                if (currentArea > max_area)
                {
                    max_area = currentArea;
                    max_contour = contours[i];
                }
            }

            //多边形拟合凸包的四个顶点
            OpenCvSharp.Point[] hull = Cv2.ConvexHull(max_contour);
            double epsilon = 0.02 * Cv2.ArcLength(max_contour, true);
            OpenCvSharp.Point[] approx = Cv2.ApproxPolyDP(hull, epsilon, true);

            if (approx.Length != 4)
            {
                MessageBox.Show("拟合凸包的四个顶点失败");
                return;
            }

            Scalar scalar2 = new Scalar(0, 255, 255);

            Cv2.Line(src, approx[0], approx[1], scalar2, 1, LineTypes.Link4);
            Cv2.Line(src, approx[1], approx[2], scalar2, 1, LineTypes.Link4);
            Cv2.Line(src, approx[2], approx[3], scalar2, 1, LineTypes.Link4);
            Cv2.Line(src, approx[3], approx[0], scalar2, 1, LineTypes.Link4);

            //排序
            Array.Sort(approx, (cs1, cs2) =>
            {
                if (cs1 != null && cs1 != null)
                {
                    if (cs1.Y > cs2.Y)
                        return 1;
                    else if (cs1.Y == cs2.Y)
                    {
                        if (cs1.X < cs2.X)
                            return 1;
                        else return -1;
                    }
                    else
                        return -1;
                }
                return 0;

            });

            //算法找出的角点
            OpenCvSharp.Point2f[] srcPt = new OpenCvSharp.Point2f[4];
            srcPt[0] = approx[0];
            srcPt[1] = approx[1];
            srcPt[2] = approx[3];
            srcPt[3] = approx[2];

            //最小外接矩形
            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 src2 = new Mat(img);
            Mat final = new Mat();
            Mat warpmatrix = Cv2.GetPerspectiveTransform(srcPt, dstPt);//获得变换矩阵
            Cv2.WarpPerspective(src2, final, warpmatrix, src.Size());//投射变换,将结果赋给final

            Bitmap temp = BitmapConverter.ToBitmap(final);

            pictureBox2.Image = temp;

            DrawLine(srcPt, dstPt);

            //Application.DoEvents();
            //System.Threading.Thread.Sleep(1000);
            //pictureBox2.Image = CutImage(temp, (int)p2f[0].X, (int)p2f[0].Y, (int)p2f[2].X, (int)p2f[2].Y);

        }

        void DrawLine(OpenCvSharp.Point2f[] srcPt, OpenCvSharp.Point2f[] dstPt)
        {
            Bitmap bmp = new Bitmap(img);
            Graphics g = Graphics.FromImage(bmp);

            Pen pen = new Pen(Color.Red, 3);
            Pen pen2 = new Pen(Color.Blue, 3);

            g.DrawLine(pen, srcPt[0].X, srcPt[0].Y, srcPt[1].X, srcPt[1].Y);
            g.DrawLine(pen, srcPt[1].X, srcPt[1].Y, srcPt[2].X, srcPt[2].Y);
            g.DrawLine(pen, srcPt[2].X, srcPt[2].Y, srcPt[3].X, srcPt[3].Y);
            g.DrawLine(pen, srcPt[3].X, srcPt[3].Y, srcPt[0].X, srcPt[0].Y);

            g.DrawLine(pen2, dstPt[0].X, dstPt[0].Y, dstPt[1].X, dstPt[1].Y);
            g.DrawLine(pen2, dstPt[1].X, dstPt[1].Y, dstPt[2].X, dstPt[2].Y);
            g.DrawLine(pen2, dstPt[2].X, dstPt[2].Y, dstPt[3].X, dstPt[3].Y);
            g.DrawLine(pen2, dstPt[3].X, dstPt[3].Y, dstPt[0].X, dstPt[0].Y);

            pictureBox1.Image = bmp;

        }

        /// <summary>
        /// 剪裁图片
        /// </summary>
        /// <param name="src">原图片</param>
        /// <param name="left">左坐标</param>
        /// <param name="top">顶部坐标</param>
        /// <param name="right">右坐标</param>
        /// <param name="bottom">底部坐标</param>
        /// <returns>剪裁后的图片</returns>
        public Image CutImage(Image src, int left, int top, int right, int bottom)
        {
            Bitmap srcBitmap = new Bitmap(src);
            int width = right - left;
            int height = bottom - top;
            Bitmap destBitmap = new Bitmap(width, height);
            using (Graphics g = Graphics.FromImage(destBitmap))
            {
                g.Clear(Color.Transparent);
                //设置画布的描绘质量         
                g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                g.DrawImage(srcBitmap, new Rectangle(0, 0, width, height), left, top, width, height, GraphicsUnit.Pixel);
            }
            return destBitmap;
        }
    }
}

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

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

相关文章

解决在IDEA中Ctrl+Y快捷键失效问题

之前我们一般使用CtrlY的方式删除当前行&#xff0c;但是有的时候这个快捷键就会被其他软件占用&#xff0c;就需要我们重新配置一下&#xff1a; 1、点击IDEA中的设置 2、点击Keymap—>Editor Actions 3、往下翻&#xff0c;双击Delete Line&#xff0c;再点击Add Keyboar…

【Opencv】PIL Opencv 向图片写入文字并旋转文字,Opencv图片旋转不截断,Opencv图片旋转不裁剪

文章目录 失真Pillow的实现Opencv的实现不裁剪的旋转图像旋转文字并贴图 失真 刚性变换&#xff1a; 只有物体的位置(平移变换)和朝向(旋转变换)发生改变&#xff0c;而形状不变&#xff0c;得到的变换称为刚性变换。刚性变换是最一般的变换。 使用透视变换&#xff0c;文字会…

Multi-Query Attention 阅读笔记

《Fast Transformer Decoding: One Write-Head is All You Need》 核心贡献&#xff1a;优化 multi-head attention 为文中命名的 multi-query attention&#xff0c;减少多head相关运算&#xff0c;不降低精度 且 大幅提升解码速度。 具体对比如下&#xff1a; multi-head a…

win下编译ycm报错 “could not find any instance of Visual Studio” 解决办法

ycm-core/YouCompleteMe: A code-completion engine for Vim 是一款vim插件&#xff0c;可为不同的代码类型提供语义级别的补全能力。 问题描述 通过vim-plug插件管理器安装ycm后&#xff0c;还需对进入其插件目录~\vimfiles\plugged\YouCompleteMe 进行编译python .\install…

分类预测 | MATLAB实现GA-LSTM遗传算法优化长短期记忆网络的数据多输入分类预测

分类预测 | MATLAB实现GA-LSTM遗传算法优化长短期记忆网络的数据多输入分类预测 目录 分类预测 | MATLAB实现GA-LSTM遗传算法优化长短期记忆网络的数据多输入分类预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 MATLAB实现GA-LSTM遗传算法优化长短期记忆网络的数据多…

DS18B20-STM32温度读取

command data 高地位组合即为温度数据 1. 初始化时钟 void Init_DS18B20(void) {char CY 1;while (CY){DQ_OUT();DQ_SET_H(); delay_us(20); DQ_SET_L(); //送出低电平复位信号delay_us(360); //延时至少480usdelay_us(240);DQ_SET_H(); …

忆联携手中国移动,企业级NVME SSD评测及生态推进计划发布仪式圆满成功

6月27日&#xff0c;中国信通院、中国移动和中国电信在ODCC 2023夏季全会上发布了“企业级NVME SSD评测及生态推进计划”&#xff08;以下简称“推进计划”&#xff09;&#xff0c;忆联作为中国移动重要的合作伙伴受邀出席该“推进计划”成立仪式环节&#xff0c;与中国信通院…

vue watch监听不生效,解决办法

1、在data里定义监听的属性&#xff0c;解决问题 2、使用深度监听

(0day通用)中庆纳博某系统敏感信息泄露+未授权修改密码

申明&#xff1a;本次测试只作为学习用处&#xff0c;请勿未授权进行渗透测试&#xff0c;切勿用于其它用途&#xff01; 1.漏洞背景 北京中庆纳博信息技术有限公司&#xff0c;简称中庆纳博&#xff0c;是有20年历史的中庆集团旗下核心企业&#xff0c;专注于教育信息化的深度…

UDP SocketAPI

1、TCP与UDP区别 TCP&#xff1a;有连接&#xff0c;可靠传输&#xff0c;面向字节流&#xff0c;全双工 UDP&#xff1a;无连接&#xff0c;不可靠传输&#xff0c;面向数据报&#xff0c;全双工 2、UDP sockeAPI的核心类 DatagramSocket&#xff1a;相当于对socket文件进…

Swift 单元测试入门

含义&#xff1a;编程语言中的单元测试是为了确保编写的代码按预期工作。 给定一个特定的输入&#xff0c;希望代码带有一个特定的输出。通过测试代码&#xff0c;能够给当前的重构和发布建立信心&#xff0c;因为将能够确保代码在成功运行的测试套件后按预期工作。 一、单元测…

macos中回退键是Command+shift+z,我该如何改成Command+y?

macos中回退键是Commandshiftz&#xff0c;我该如何改成Commandy? 操作如下&#xff0c;打开系统设置-键盘 在「菜单标题」中手动输入需要更改快捷键的菜单项名称&#xff0c;注意要完全一致&#xff08;见下图&#xff09;。 最后说一句&#xff0c;撤销与重做是非常基础的系…

VS2017编译64位库出现问题解决方法

1、问题&#xff1a;VS2017编译32位Release库正常通过,但是64位库总是报错 解决方法&#xff1a;添加setupapi.lib到依赖项中

线性代数笔记整理

文章目录 1 行列式2 矩阵&#xff08;本质是数表&#xff09;3 方程组的解4 向量5 矩阵的特征值和特征向量6 相似矩阵和相似对角化7 合同对角化8 二次型及其标准型 1 行列式 2 矩阵&#xff08;本质是数表&#xff09; 3 方程组的解 4 向量 5 矩阵的特征值和特征向量 6 相似矩阵…

【MATLAB第47期】基于MATLAB的多卷积层的卷积神经网络MCNN分类预测模型,含交叉验证,可自定义层数

【MATLAB第47期】基于MATLAB的多卷积层的卷积神经网络MCNN分类预测模型&#xff0c;含交叉验证&#xff0c;可自定义层数 一、展示效果 依次对比卷积层数为1/2/3时的分类预测结果 可得出&#xff0c;随着卷积层数量增加&#xff0c;训练集/测试集正确率基本上得到改进。 1.一…

GPIO模拟时序控制外设4——红外发射管

文章目录 前言红外发射管简介NEC协议HS0038NEC 的逻辑“1”与逻辑“0”NEC的数据帧格式 编程思路1. GPIO管脚2. 模拟同步头3.发送逻辑“0”与逻辑“1”发送一个字节数据发送一帧数据结束码现象 总结 前言 上一篇介绍了使用GPIO模拟时序实现I2C协议的功能&#xff0c;本文继续使…

DELL戴尔笔记本电脑灵越Inspiron 14 54105418原厂Win10系统恢复原装OEM出厂状态系统

Dell戴尔笔记本电脑&#xff0c;灵越Inspiron 14 5410&5418原装出厂OEM系统镜像原厂系统文件 系统自带所有驱动、出厂主题壁纸LOGO、Office办公软件等预装程序 链接&#xff1a;https://pan.baidu.com/s/1Qj_dW5lj71e9d71-je4dXw?pwdz3z1 提取码&#xff1a;z3z1

Oracle Recovery Tools恢复csc higher than block scn---惜分飞

有客户强制关闭数据库,结果有数据块报坏块,dbv检查为:csc higher than block scn问题 该问题主要是由于scn异常导致通过Oracle Recovery工具进行修复 dbv再次验证数据块ok,Oracle Recovery完美代替bbed解决该问题 通过OraRecovery工具快速解决csc higher than block scn故障…

IntelliJ IDEA如何自动生成serialVersionUID

IntelliJ IDEA如何自动生成serialVersionUID&#xff1f; 实体对象在流中传输时&#xff0c;需要将其序列化。 对象的类型实现Serializable接口public class ClassName implements java.io.Serializable { } 生成版本号serialVersionUID单击类名&#xff0c;按Alt Enter,在出…