【Emgu CV教程】2.11、基本方法之ROI的高级应用

上一篇文章,讲的是如何在图像中获取到一个矩形的ROI,并赋值给一个单独的Mat变量进行后续处理。除了这种最基础的用法以外,还会出现更高级一些的应用,比如把ROI拷贝到另外一张图片、特殊形状的ROI怎么提取出来等等,别着急,咱们慢慢讲明白。

也是干货满满的一篇文章。

1、小图复制到大图上,第一种方法。

目标:将 可乐.jpg,大小为250*250,复制到 珠穆朗玛峰.jpg,大小为1440*900 上。

我们定义

  • smallMat =  可乐.jpg
  • bigMat = 珠穆朗玛峰.jpg

执行如下代码:

Mat dstMat = bigMat.Clone();
Mat roi = new Mat(dstMat, new Rectangle(0, 0, smallMat.Cols, smallMat.Rows)); // 浅拷贝,当roi发生变化,dstMat也会变化
smallMat.CopyTo(roi); // 将smallMat的数据区拷贝到roi,而且dstMat也会变化
CvInvoke.PutText(roi, "ROI", new System.Drawing.Point(10, 30), FontFace.HersheyComplex, 0.75, new Bgr(0, 0, 0).MCvScalar, 2, LineType.EightConnected, false);
CvInvoke.Imshow("Final Mat, " + dstMat.Size.ToString(), dstMat);

效果如下:

其实我们是借助了public Mat(Mat mat, Rectangle roi) 这个函数,定义了一个Mat变量,叫roi,注意了,这种方式是浅拷贝,有什么特性,想一想????然后把smallMat全部通过CopyTo()函数拷贝到roi上,这样dstMat就会在左上角P(0,0)的位置开始,显示实际的smallMat。这是一种浅拷贝的方式,使用时要特别注意。

2、小图复制到大图上,第二种方法。

还可以利用Image元素内置的ROI方法,官方对其描述如下:

可以看出,ROI方法就是一个矩形。还是以  可乐.jpg  和  珠穆朗玛峰.jpg 为例,执行如下代码就可以得到和第一种方法相同的结果:

Mat dstMat = new Mat();
Image<Bgr, int> smallImage = smallMat.ToImage<Bgr, int>();
Image<Bgr, int> bigImage = bigMat.ToImage<Bgr, int>();
bigImage.ROI = new Rectangle(0, 0, smallMat.Width, smallMat.Height);
smallImage.CopyTo(bigImage);
bigImage.ROI = System.Drawing.Rectangle.Empty;
dstMat = bigImage.Mat;
dstMat.ConvertTo(dstMat, DepthType.Cv8U);
CvInvoke.Imshow("Final Mat, " + dstMat.Size.ToString(), dstMat);

步骤说明:

  • 定义两个Image变量,代替两个Mat变量进行下面的计算。 
  • 代表珠穆朗玛峰的Image变量,也就是bigImage,设定ROI区域,就是左上角起始位置P(0,0),并且是 可乐.jpg 的宽度和高度。
  • 可乐.jpg利用 CopyTo()函数 复制到 珠穆朗玛峰.jpg。注意了,因为珠穆朗玛峰.jpg设定了ROI,所以可以这样执行。否则两个尺寸不通的图片 ,不能直接CopyTo。
  • 清空bigImage的ROI,必须要有这一步,否则的结果是什么样,读者可以试试。
  • Image元素转Mat元素并显示。

3、小图复制到大图的指定位置。 

上面的两种方法,都是把小图放在了大图的 P(0,0)位置,假如我想把小图放在P(200,50)的位置上,该怎么办呢?其实很简单,第一种方法的代码:

Mat roi = new Mat(dstMat, new Rectangle(0, 0, smallMat.Cols, smallMat.Rows));

改成:

Mat roi = new Mat(dstMat, new Rectangle(200, 50, smallMat.Cols, smallMat.Rows));

或者第二种方法的代码:

bigImage.ROI = new Rectangle(0, 0, smallMat.Width, smallMat.Height);

改成:

bigImage.ROI = new Rectangle(200, 50, smallMat.Width, smallMat.Height);

就是在定义ROI时,改变位置,效果如下: 

 4、通过mask掩码融合图像

上面的几种方法,都像是把小图硬生生的贴在大图上。注意一下,小图 可乐.jpg,背景颜色是白色的,如果我只想把可乐瓶子复制到大图,而白色背景不复制,那怎么办呢???

这种就不是简单的拷贝粘贴了,好像有个更专业的叫法:融合。注意了,下面介绍的只适合白色背景图片融入到大图。这回以 黑猪.png ,大小为216*143,和 珠穆朗玛峰.jpg 举例。

先看代码:

Mat dstMat = bigMat.Clone();
Mat roi = new Mat(dstMat, new Rectangle(400, 100, smallMat.Cols, smallMat.Rows)); // 浅拷贝,当roi发生变化,dstMat也会变化

// 定义掩码
Mat maskMat = new Mat();
CvInvoke.CvtColor(smallMat, maskMat, ColorConversion.Bgr2Gray); // 彩色转灰度图
CvInvoke.Threshold(maskMat, maskMat, 200, 255, ThresholdType.BinaryInv); // 二值法处理
CvInvoke.Imshow("Threshold small image", maskMat);

smallMat.CopyTo(roi, maskMat); // 将smallMat的数据区拷贝到roi,而且dstMat也会变化
CvInvoke.PutText(roi, "ROI", new System.Drawing.Point(10, 30), FontFace.HersheyComplex, 0.75, new Bgr(0, 0, 0).MCvScalar, 2, LineType.EightConnected, false);
CvInvoke.Imshow("Final Mat, " + dstMat.Size.ToString(), dstMat);

就是在使用CopyTo()函数的时候,增加了一个掩码图。这个掩码图是这样的:

这个掩码图是什么,其实就是一个灰度图,更准确的说是一个二值化图像。白色背景变成了黑色,而小猪部分作为前景,是白色的。掩码图必须是和 smallMat大小一致,而且是单通道、Cv8U深度的图像。这时候再执行  smallMat.CopyTo(roi, maskMat); 就会把smallMat中白色去掉,只留下前景的小猪。代码执行效果如下:

这才是真正的母猪飞上天的效果。那如果用可乐.jpg 和 珠穆朗玛峰.jpg,效果是这样的:

掩码的作用就是:如果掩码图像的值是0,则CopyTo不起作用。如果掩码图像的值大于0,才将输入图像CopyTo到输出的图像中去。进一步思考,掩码图其实就是将不规则的ROI复制到大图上。

 5、特殊形状的ROI复制

利用掩码的特征,我们可以将特殊形状的ROI复制到大图上,这回以lena.jpg为例。我们建立一个lenaMask.jpg,尺寸大小和lena.jpg一样,背景为黑色,看上去是这样的:

其实就是有五个ROI,三角形,左向箭头、五角星、右向箭头、心形。利用这个掩码图进行复制,效果是这样的:

看到了吧,只有掩码图中非0的部分,才会复制到  珠穆朗玛峰.jpg 上,神奇不???

这一部分内容有些难以理解,需要读者多写代码进行练习。前期先用我提供的代码,然后多更改参数,多试验几张不同的照片,就能很快理解了。

原创不易,请勿抄袭。共同进步,相互学习。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值