根据前两篇博客roi截图和掩码操作进行总结优化,涉及到的知识点讲解都在两篇文章中,这里不多加赘述,该文章仅作为总结。
OpenCvSharp-鼠标框选截取感兴趣区域(ROI)-附源代码
c#-OpenCvSharp-掩码操作(附源码)
仅在鼠标移动事件中加入掩码操作,感觉代码还有优化的地方,后面会回头继续优化。各位读者有对代码进行优化的话可以联系我,一起交流学习
以下是代码
using OpenCvSharp;
using OpenCvSharp.Extensions;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Point = OpenCvSharp.Point;
namespace 绘制ROI_掩码_
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Mat src;// 存储图像的Mat对象
//选择图像文件
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "Image Files(*.jpg;*.png*;*.bmp*)|*.jpg;*.png*;*.bmp";
if (ofd.ShowDialog() != DialogResult.OK)
return;
string imagePath = ofd.FileName;
src = Cv2.ImRead(imagePath, ImreadModes.AnyColor);
Cv2.ImShow("src image", src);
}
static Mat tempMat; // 用于临时存储原始图像
static Point sp = new Point(-1, -1); // 起始点坐标
static Point ep = new Point(-1, -1); // 终点坐标
private void button2_Click(object sender, EventArgs e)
{
if (src == null)
{
MessageBox.Show("请先选择图像文件!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
// 创建鼠标回调函数
MouseCallback draw = new MouseCallback(DrawRectangle);
// 复制图像
tempMat = new Mat(src.Size(), src.Type());
Cv2.CopyTo(src, tempMat);
// 分两步将Mat对象转换为IntPtr,用于传递给回调函数。
// 将Mat对象src作为参数传递给Alloc方法,以创建一个GCHandle对象,并将其与srcImage关联起来
System.Runtime.InteropServices.GCHandle handle = System.Runtime.InteropServices.GCHandle.Alloc(src);
// 再将GCHandle对象转换为IntPtr,即从GCHandle对象中获取对象所在的内存地址。
IntPtr ptr = System.Runtime.InteropServices.GCHandle.ToIntPtr(handle);
// 设置鼠标回调函数,将ptr作为额外的用户数据,通过Cv2.SetMouseCallback方法传递给鼠标回调函数DrawRectangle
Cv2.SetMouseCallback("src image", draw, ptr);
Cv2.WaitKey();
}
// 鼠标回调函数DrawRectangle,它是一个委托类型MouseCallback的实现
public static void DrawRectangle(MouseEventTypes @event, int x, int y, MouseEventFlags flags, IntPtr userData)
{
// 获取图像数据 从userData指针中获取用户数据,并将其转换为GCHandle对象
System.Runtime.InteropServices.GCHandle handle = System.Runtime.InteropServices.GCHandle.FromIntPtr(userData);
Mat src = (Mat)handle.Target;
// 鼠标左键按下事件
if (@event == MouseEventTypes.LButtonDown)
{
sp.X = x;
sp.Y = y;
Console.WriteLine("起点坐标 ({0},{1})", sp.X, sp.Y);
}
// 鼠标左键抬起事件
else if (@event == MouseEventTypes.LButtonUp)
{
ep.X = x;
ep.Y = y;
Console.WriteLine("终点坐标 ({0},{1})", ep.X, ep.Y);
// 绘制矩形
if (ep.X > sp.X && ep.Y > sp.Y)
{
// Cv2.Rectangle方法用于在图像(Mat对象)上绘制矩形
// Cv2.Rectangle(Mat img, Point pt1, Point pt2, Scalar color, int thickness = 1, LineTypes lineType = LineTypes.Link8, int shift = 0);
Cv2.Rectangle(src, sp, ep, new Scalar(0, 0, 255), 2, LineTypes.AntiAlias, 0);
// 用SubMat()提取子区域,该代码用来显示截取出来的图片
Cv2.ImShow("ROI区域", src.SubMat(sp.Y, ep.Y, sp.X, ep.X));
// 显示绘制矩形后的图像
Cv2.ImShow("src image", src);
// 完成绘制矩形后,sp.X和sp.Y重置为-1。确保下一次绘制矩形时能够正确记录起点坐标
sp.X = -1;
sp.Y = -1;
}
}
// 鼠标移动事件 (加入掩码操作部分)
else if (@event == MouseEventTypes.MouseMove && sp.X > 0 && sp.Y > 0)
{
ep.X = x;
ep.Y = y;
if (ep.X > sp.X && ep.Y > sp.Y)
{
// 创建临时图像并将原始图像复制到临时图像中
Mat tempMat = new Mat();
src.CopyTo(tempMat);
// 计算矩形区域
Rect roi = new Rect(sp.X, sp.Y, ep.X - sp.X, ep.Y - sp.Y);
// 创建掩码并将矩形区域设为非零值
Mat mask = new Mat(src.Size(), MatType.CV_8UC1, Scalar.All(0));
// 将矩形区域在掩码中设为非零值(255),这个矩形区域将被用于后续的掩码操作
mask[roi].SetTo(new Scalar(255));
// 应用掩码并显示结果
Mat result = new Mat(); // 创建一个空的Mat对象,用于存储掩码操作后的结果
tempMat.CopyTo(result, mask);
// 显示经过掩码操作后的图像 result,即只显示矩形区域内的内容,其他区域被掩盖
Cv2.ImShow("Masked Image", result);
}
}
}
}
}
效果图: