当前位置:   article > 正文

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

emgucv roi 黑

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

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

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

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

我们定义

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

执行如下代码:

  1. Mat dstMat = bigMat.Clone();
  2. Mat roi = new Mat(dstMat, new Rectangle(0, 0, smallMat.Cols, smallMat.Rows)); // 浅拷贝,当roi发生变化,dstMat也会变化
  3. smallMat.CopyTo(roi); // 将smallMat的数据区拷贝到roi,而且dstMat也会变化
  4. CvInvoke.PutText(roi, "ROI", new System.Drawing.Point(10, 30), FontFace.HersheyComplex, 0.75, new Bgr(0, 0, 0).MCvScalar, 2, LineType.EightConnected, false);
  5. 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 为例,执行如下代码就可以得到和第一种方法相同的结果:

  1. Mat dstMat = new Mat();
  2. Image<Bgr, int> smallImage = smallMat.ToImage<Bgr, int>();
  3. Image<Bgr, int> bigImage = bigMat.ToImage<Bgr, int>();
  4. bigImage.ROI = new Rectangle(0, 0, smallMat.Width, smallMat.Height);
  5. smallImage.CopyTo(bigImage);
  6. bigImage.ROI = System.Drawing.Rectangle.Empty;
  7. dstMat = bigImage.Mat;
  8. dstMat.ConvertTo(dstMat, DepthType.Cv8U);
  9. 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 举例。

先看代码:

  1. Mat dstMat = bigMat.Clone();
  2. Mat roi = new Mat(dstMat, new Rectangle(400, 100, smallMat.Cols, smallMat.Rows)); // 浅拷贝,当roi发生变化,dstMat也会变化
  3. // 定义掩码
  4. Mat maskMat = new Mat();
  5. CvInvoke.CvtColor(smallMat, maskMat, ColorConversion.Bgr2Gray); // 彩色转灰度图
  6. CvInvoke.Threshold(maskMat, maskMat, 200, 255, ThresholdType.BinaryInv); // 二值法处理
  7. CvInvoke.Imshow("Threshold small image", maskMat);
  8. smallMat.CopyTo(roi, maskMat); // 将smallMat的数据区拷贝到roi,而且dstMat也会变化
  9. CvInvoke.PutText(roi, "ROI", new System.Drawing.Point(10, 30), FontFace.HersheyComplex, 0.75, new Bgr(0, 0, 0).MCvScalar, 2, LineType.EightConnected, false);
  10. 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 上,神奇不???

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/1001648
推荐阅读
相关标签
  

闽ICP备14008679号