opencv将图像数据存在一个二维矩阵里。
下面代码复制了一个图像矩阵。
Mat srcPic = new Mat("test1.png", ImreadModes.Color);
Mat aPic = new Mat(srcPic,new OpenCvSharp.Rect(100,120, srcPic.Width-100, srcPic.Height-120));
Mat bPic = aPic;
using (new OpenCvSharp.Window("src image", srcPic))
using (new OpenCvSharp.Window("copy image", aPic))
{
Cv2.WaitKey();
}
上述所有对象都指向同一个数据矩阵,使用其中任何一个对象进行修改都会影响所有其他对象。实际上,不同的对象只是为相同的底层数据提供不同的访问方法。然而,它们的头部部分是不同的。
上面代码使用区域new OpenCvSharp.Rect(100,120, srcPic.Width-100, srcPic.Height-120)来创建一个不同的矩阵,注意复制操作符只复制头文件和指向大矩阵的指针,而不复制数据本身。
真正有趣的部分是,您可以创建只引用完整数据的一部分的标题。例如,要在图像中创建一个感兴趣的区域(ROI),只需创建一个具有新边界的新标题。
关于Mat您需要知道的第一件事是,您不再需要手动分配它的内存并在不需要它的时候释放它。虽然这样做仍然是有可能的,但大多数OpenCV函数将自动分配其输出数据。如果传递一个已经存在的Mat对象,它已经为矩阵分配了所需的空间,那么这个对象将被重用。换句话说,我们在任何时候都只使用执行任务所需的内存。
Mat基本上是一个包含两个数据部分的类:矩阵头(包含诸如矩阵的大小、用于存储的方法、存储矩阵的地址等信息)和一个指向包含像素值的矩阵的指针(根据所选择的存储方法获取任何维度)。矩阵标题的大小是恒定的,但是矩阵本身的大小可能因图像而异,通常会大几个数量级。
我们不应该忘记我们正在讨论的是图像处理算法,这往往是计算量很大的。
我们最不想做的就是通过对可能较大的图像进行不必要的复制来进一步降低程序的速度。为了解决这个问题,OpenCV使用了一个引用计数系统。
其思想是,每个Mat对象都有自己的头,但是,通过使两个Mat对象的矩阵指针指向相同的地址,可以在两个Mat对象之间共享一个矩阵。
此外,复制操作符只复制头文件和指向大矩阵的指针,而不复制数据本身。
现在你可能会问——如果矩阵本身可能属于多个Mat对象,当它不再需要时,谁负责清理它呢?
简短的回答是:最后一个使用它的对象。这是通过引用计数机制来处理的。每当有人复制Mat对象的头文件时,矩阵的计数器就会增加。每当清理标头时,此计数器将减少。当计数器达到零时,矩阵被释放。
如果要复制数据本本身,需要使用cv::Mat::clone() and cv::Mat::copyTo()
copyTo
下面是第一种方式,好理解,直接复制。
void cv::Mat::copyTo ( OutputArray m ) const
下面是copyTo的第二种重载方式
void cv::Mat::copyTo ( OutputArray m,
InputArray mask
) const
mask mask与源Mat相同大小,是操作掩码,其非零元素指示需要复制哪些矩阵元素。掩码必须是CV_8U类型,可以有一个或多个通道。
clone和copyTo差不多,都是复制数据。
Mat srcPic = new Mat("test1.png", ImreadModes.Color);
Mat aPic=new Mat();
srcPic.CopyTo(aPic);
Mat bPic = srcPic.Clone();
bPic[110, 180, 10, 100] = srcPic[30, 100, 10, 100];
using (new OpenCvSharp.Window("a image", aPic))
using (new OpenCvSharp.Window("b image", bPic))
using (new OpenCvSharp.Window("src image", srcPic))
{
Cv2.WaitKey();
}
下面这一行修改了bPic图像数据。但并没有影响srcPic。
bPic[110, 180, 10, 100] = srcPic[30, 100, 10, 100];
Mat srcPic = new Mat("test1.png", ImreadModes.Color);
Mat aPic=new Mat();
srcPic.CopyTo(aPic);
Mat bPic = new Mat(srcPic, new OpenCvSharp.Rect(0, 0, srcPic.Width, srcPic.Height));
bPic[110, 180, 10, 100] = srcPic[30, 100, 10, 100];
using (new OpenCvSharp.Window("a image", aPic))
using (new OpenCvSharp.Window("b image", bPic))
using (new OpenCvSharp.Window("src image", srcPic))
{
Cv2.WaitKey();
}