c#中WinForm用OpencvSharp实现ROI区域提取的示例
作者:Iawfy_ 发布时间:2023-03-22 13:54:10
已经自学OpencvSharp一段时间了(目前工作用的是C#,就学了Opencvsharp了,vs2015,opencvsharp3),收获也有一些,现在就将我在学习过程中的收获分享出来吧。
图像处理,很常见的问题,但对于大多数时候而言,我们往往不需要去处理整张图片,而是只需要处理一部分,这就涉及到了ROI(Region of interest)的提取了。我目前提取ROI的方法是采用掩膜Mask的方法。具体的思路就是:在图像操作的时候,定义一张同等大小的空的Mask,也就是全部是0,然后将我们想要的ROI轮廓画在Mask上,并填充内部,就会得到新的Mask,这个新的Mask就只有在ROI区域非0,其余地方元素都是0,再把用Cv2.BoundingRect()将包含ROI区域的轮廓的最小矩形找出来,分别将原图与Mask这两幅图像的这个最小矩形部分提出来 ,最后再调用Cv2.BitwiseAnd()这个方法,通常情况而言,一副图像与自己本身进行与运算,输出的还是本身图像,带上掩膜Mask后,就只会输出图像在Mask非0区域部分(也就是我们所需要的ROI)的图像了,这样就实现了我们的ROI提取了。
接下来,就分享几种常见ROI区域提取吧。
部分代码如下:
主要使用的变量
/// 放在yVars.ImgOptions中
public struct ROIMatt
{
public static string Image;//原图
public static bool IsSelectRegion = false;
public static int step; //ROI区域移动步长
public static int angel; // 旋转一次 angel±=step; 旋转角度
public static yDirections direct = yDirections.NULL;
public static yROIRegionType ROIType = yROIRegionType.Rectangle;
// 矩形ROI
// 矩形四个点坐标 都是相对于图像的坐标 而不是相对于picturebox的坐标
// 矩形四个点相对位置 刚开始确定矩形时就这样 经过旋转后位置变 但相对位置还是这样
// 1 2 // 按顺时针数的点 1-->2-->4-->3-->1-->2-->4-->3-->1
// 3 4
public static OpenCvSharp.Point rectFirstPoint = new OpenCvSharp.Point();
public static OpenCvSharp.Point rectSecondPoint = new OpenCvSharp.Point();
public static OpenCvSharp.Point rectThirdPoint = new OpenCvSharp.Point();
public static OpenCvSharp.Point rectFourthPoint = new OpenCvSharp.Point();
public static double rectWidth = 0;
public static double rectHeight = 0;
// 圆形ROI
public static OpenCvSharp.Point cirCenter = new OpenCvSharp.Point(0.0,0.0); // 圆心
public static int cirRadious = 0; // 半径
// 椭圆ROI
public static OpenCvSharp.Point elpCenter = new OpenCvSharp.Point(0.0,0.0); // 椭圆中心点
public static double elpAngel = 0.0;//椭圆倾斜角度
public static double elpLongAxis = 0.0; // 长轴
public static double elpShortAxis = 0.0; // 短轴
}
我的picturebox的SizeMode是StretchImage的,可能看起来跟想要的结果有点差异,但是实际上是一样的。
首先是最常见的矩形。
对于正矩形而言,我们可以直接定义出图像的ROI区域
public Mat(Mat m, Rect roi);
这样定义的图像就是原图m的指定区域了。但对于倾斜的矩阵,RotatedRect,而言,就得需要使用掩膜了,提取ROI的方法以及结果如下:
public static void ImgMattingRect()
{
Mat pic = new Mat(yVars.ImgOptions.ROIMatt.Image);
Mat mask = Mat.Zeros(pic.Size(), MatType.CV_8UC1);
OpenCvSharp.Point2f[] coutours = new OpenCvSharp.Point2f[4];
coutours[0] = yVars.ImgOptions.ROIMatt.rectFirstPoint;
coutours[1] = yVars.ImgOptions.ROIMatt.rectSecondPoint;
coutours[2] = yVars.ImgOptions.ROIMatt.rectFourthPoint;
coutours[3] = yVars.ImgOptions.ROIMatt.rectThirdPoint;
List<OpenCvSharp.Point> listt = new List<OpenCvSharp.Point>();
for (int i = 0; i < coutours.Count(); i++)
{
listt.Add(new OpenCvSharp.Point(coutours[i].X, coutours[i].Y));
}
List<List<OpenCvSharp.Point>> pp = new List<List<OpenCvSharp.Point>>() { listt };
Cv2.FillPoly(mask, pp, new Scalar(255, 255, 255));
OpenCvSharp.Rect rect = Cv2.BoundingRect(coutours);
Mat src = new Mat(pic, rect);
Mat maskROI = new Mat(mask, rect);
Mat picOut = new Mat();
Cv2.BitwiseAnd(src, src, picOut, maskROI);
Form1.Instance.pbxMattImage.Image = yImgConvert.MatToBitmap(picOut);
yVars.ImgOptions.ROIMatt.rectFirstPoint = new OpenCvSharp.Point(0, 0);
yVars.ImgOptions.ROIMatt.rectSecondPoint = new OpenCvSharp.Point(0, 0);
yVars.ImgOptions.ROIMatt.rectThirdPoint = new OpenCvSharp.Point(0, 0);
yVars.ImgOptions.ROIMatt.rectFourthPoint = new OpenCvSharp.Point(0, 0);
}
缩放平移和旋转就只要改变矩形的四个顶点坐标就行了。方法都一样就不赘述了。
圆形ROI区域,
方法如下:
public static void ImgMattingCircle()
{
Mat mm = new Mat(yVars.ImgOptions.ROIMatt.Image);
Mat mask = Mat.Zeros(mm.Size(), MatType.CV_8UC3);
Cv2.Circle(mask, yVars.ImgOptions.ROIMatt.cirCenter, yVars.ImgOptions.ROIMatt.cirRadious, Scalar.Red, 1, LineTypes.AntiAlias);
Cv2.FloodFill(mask, yVars.ImgOptions.ROIMatt.cirCenter, Scalar.Red);
mask.ConvertTo(mask, MatType.CV_8UC1);
int xx = yVars.ImgOptions.ROIMatt.cirCenter.X - yVars.ImgOptions.ROIMatt.cirRadious;
int yy = yVars.ImgOptions.ROIMatt.cirCenter.Y - yVars.ImgOptions.ROIMatt.cirRadious;
int rr = 2 * yVars.ImgOptions.ROIMatt.cirRadious;
// 圆的外接正方形
Rect rect = new Rect(new OpenCvSharp.Point(xx, yy), new OpenCvSharp.Size(rr, rr));
Mat src = new Mat(mm, rect);
Mat maskRoI = new Mat(mask, rect);
Cv2.CvtColor(maskRoI, maskRoI, ColorConversionCodes.BGR2GRAY);
Mat picOut = new Mat();
Cv2.BitwiseAnd(src, src, picOut, maskRoI);
Form1.Instance.pbxMattImage.Image = yImgConvert.MatToBitmap(picOut);
yVars.ImgOptions.ROIMatt.cirCenter = new OpenCvSharp.Point(0, 0);
yVars.ImgOptions.ROIMatt.cirRadious = 0;
}
效果展示:
圆形ROI的移动时就只有圆心坐标变 半径不变,而缩放时只改变半径,圆心不变,注意移动时别超出图像界限就行。
椭圆ROI
在Opencvsharp中绘制椭圆有两种方式
//
// 摘要:
// Draws simple or thick elliptic arc or fills ellipse sector
//
// 参数:
// img:
// Image.
//
// box:
// The enclosing box of the ellipse drawn
//
// color:
// Ellipse color.
//
// thickness:
// Thickness of the ellipse boundary. [By default this is 1]
//
// lineType:
// Type of the ellipse boundary. [By default this is LineType.Link8]
public static void Ellipse(InputOutputArray img, RotatedRect box, Scalar color, int thickness = 1, LineTypes lineType = LineTypes.Link8);
//
// 摘要:
// Draws simple or thick elliptic arc or fills ellipse sector
//
// 参数:
// img:
// Image.
//
// center:
// Center of the ellipse.
//
// axes:
// Length of the ellipse axes.
//
// angle:
// Rotation angle.
//
// startAngle:
// Starting angle of the elliptic arc.
//
// endAngle:
// Ending angle of the elliptic arc.
//
// color:
// Ellipse color.
//
// thickness:
// Thickness of the ellipse arc. [By default this is 1]
//
// lineType:
// Type of the ellipse boundary. [By default this is LineType.Link8]
//
// shift:
// Number of fractional bits in the center coordinates and axes' values. [By default
// this is 0]
public static void Ellipse(InputOutputArray img, Point center, Size axes, double angle, double startAngle, double endAngle, Scalar color, int thickness = 1, LineTypes lineType = LineTypes.Link8, int shift = 0);
我们采用第一种方式,即可以将椭圆转化成一个RotatedRect,只要在画RotatedRect的时候改成画椭圆即可,就可以回到第一种矩形的ROI提取上面了, 代码如下:
public static void ImgMattingEllipse()
{
Mat mm = new Mat(yVars.ImgOptions.ROIMatt.Image);
Mat mask = Mat.Zeros(mm.Size(), MatType.CV_8UC3);
RotatedRect rorect = new RotatedRect(yVars.ImgOptions.ROIMatt.elpCenter, new Size2f(yVars.ImgOptions.ROIMatt.elpLongAxis, yVars.ImgOptions.ROIMatt.elpShortAxis), (float)yVars.ImgOptions.ROIMatt.elpAngel);
Cv2.Ellipse(mask, rorect, Scalar.Red);
Mat gray = new Mat();
Cv2.CvtColor(mask, gray, ColorConversionCodes.BGR2GRAY);
Cv2.Threshold(gray, gray, 100, 255, ThresholdTypes.Otsu);
OpenCvSharp.Point[][] contours;
HierarchyIndex[] hierarchly;
Cv2.FindContours(gray, out contours, out hierarchly, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple, new OpenCvSharp.Point(0, 0));
Rect rect = Cv2.BoundingRect(contours[0]);
Cv2.FloodFill(mask, yVars.ImgOptions.ROIMatt.elpCenter, Scalar.Red);
mask.ConvertTo(mask, MatType.CV_8UC1);
Mat src = new Mat(mm, rect);
Mat maskRoI = new Mat(mask, rect);
Cv2.CvtColor(maskRoI, maskRoI, ColorConversionCodes.BGR2GRAY);
Mat picOut = new Mat();
Cv2.BitwiseAnd(src, src, picOut, maskRoI);
Form1.Instance.pbxMattImage.Image = yImgConvert.MatToBitmap(picOut);
}
实验结果如下:
平移缩放旋转等操作就可以看成对RotatedRect的操作即可。
来源:https://blog.csdn.net/Iawfy_/article/details/123275118
猜你喜欢
- 1. 什么是AOPAOP (Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期 * 实现在
- 写在前面:接下来很长一段时间的文章主要会记录一些项目中实际遇到的问题及对应的解决方案,在相应代码分析时会直指问题所在,不会将无关的流程代码贴
- Java选择的泛型类型叫做类型擦除式泛型。什么是类型擦除式泛型呢?就是Java语言中的泛型只存在于程序源码之中,在编译后的字节码文件里,则全
- 比如有海量的文本文件,如订单,页面点击事件的记录,量特别大,很难搞定。那么我们该怎样解决海量数据的计算?1、获取总行数2、计算每个文件中存多
- 一、实现原理使用MockMvc发起请求,然后执行API中相应的代码,在执行的过程中使mock模拟底层数据的返回,最后结果验证。二、常用注解介
- 就网络和应用程序而言,键盘快捷键很重要,今天我们要谈的便是让这类快捷键得以在Flutter运作的小部件:Focus、Shortcuts和Ac
- spring.activemq.pool.enabled=false时,每发送一条数据都需要创建一个连接,这样会出现频繁创建和销毁连接的场景
- 目前很多业务使用微服务架构,服务模块划分有这2种方式:服务功能划分业务划分不管哪种方式,一次接口调用都需要多个服务协同完成,其中一个服务出现
- 先看看代码再说:package com.b510.note; import java.math.BigInteger;
- 基本概念Semaphore也是一个线程同步的辅助类,可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制并发访问
- struts2可以非常简单地使用FreeMarker模板作为视图技术,对于传统的jsp页面而言,FreeMarker是一个绝佳的替代方案。除
- 一、首先我们先创建一个Maven项目把我们需要的包先准备好 1.打开pom.xml文件引入依赖,以下是整个pom.xml文件<
- SpringBoot 整合 Redis 数据库实现数据缓存的本质是整合 Redis 数据库,通过对需要“缓存&r
- 微服务开发中经常有这样的需求,公司自定义了通用的请求头,需要在微服务的调用链中转发,比如在请求头中加入了token,或者某个自定义的信息un
- 重新认识 Java 的 System.in以前也写过不少命令行的程序,处理文件时总需要通过参数指定路径,直到今天看资料时发现了一种我自己从来
- 我们深知在操作Java流对象后要将流关闭,但往往事情不尽人意,大致有以下几种不能一定将流关闭的写法:1.在try中关流,而没在finally
- 在jdk1.4中提出的技术,非阻塞IO,采用的是基于事件处理方式。传统的io技术为阻塞的,比如读一个文件,惹read方法是阻塞的,直到有数据
- jdk中自带了很多工具可以用于性能分析,位于jdk的bin目录下,jvisualvm工具可以以图形化的方式更加直观的监控本地以及远程的jav
- 前言在这一期的文章中,我将继续介绍 Either,使用它构建树形结构,该结构允许我模拟 Scala 的模式匹配来构建遍历方法。在 Java
- 问题springboot 集成springcloud时常常由于版本问题而报错,如下:com.sun.jersey.api.client.Cl