【Emgu.CV教程】第13篇 、基本方法之ROI的高级应用

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

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

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

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

【Emgu.CV教程】第13篇 、基本方法之ROI的高级应用_第1张图片

        我们定义

  • 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 smallImage = smallMat.ToImage();
Image bigImage = bigMat.ToImage();
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 举例。

【Emgu.CV教程】第13篇 、基本方法之ROI的高级应用_第2张图片

       先看代码:

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()函数的时候,增加了一个掩码图。这个掩码图是这样的:

【Emgu.CV教程】第13篇 、基本方法之ROI的高级应用_第3张图片

 

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

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

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

 5、特殊形状的ROI复制

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

【Emgu.CV教程】第13篇 、基本方法之ROI的高级应用_第4张图片

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

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

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

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

你可能感兴趣的:(Emgu.CV使用教程,人工智能,计算机视觉,c#,图像处理)