Opencv中文网

复制图像之clone、copyTo、convertTo函数

在正式讲解这3个函数之前,让我们来看一看下面的例子。

#include<opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;

int main()
{    
    Mat redImage(Size(500, 300), CV_8UC3, Scalar(0, 0, 255)); // 全红色图像
    imshow("redImage1", redImage);
    Mat greenImage = redImage;
    greenImage.setTo(Scalar(0, 255, 0));//修改为绿色
    imshow("redImage2", redImage);
    waitKey(0);//等待按键  
    return 0;
}
输出结果

在本例中,首先创建了一个宽500像素、高300像素、背景为红色的3通道位图redImage,然后将其显示到redImage1窗体中,接着声明了另一个Mat对象greenImage,并将redImage赋值给greenImage,然后利用setTo()函数将greenImage图像的所有像素修改为绿色,最后再次显示redImage图像,我们会发现它的每个像素值也变成了绿色。

通过这个示例,说明简单的赋值并没有发生深拷贝,redImage和greenImage共享了同一段图像数据内存,它们仅仅只是拥有各自不同的数据头。

如果我们希望根据redImage对象来创建一个完全独立的新对象,对新对象的修改不会影响到原图像矩阵的数据,这时就需要使用深拷贝技术了。

在OpenCV中,与深拷贝图像有关的函数分别是clone()、copyTo()和convertTo(),本节将介绍它们之间的使用区别。

1.clone()函数

表示创建某个Mat对象的完全独立的数据克隆,该函数会直接返回一个新的Mat对象,使用简单,无需提前分配目标矩阵内存。

Mat img1 = imread("image.jpg");
Mat img2 = img1.clone(); // img2 是 img1 的深拷贝

如上述代码所示,您可以大胆地对img2对象进行图像处理,不会影响到img1对象中的图像数据。

2.copyTo()函数

表示将图像数据复制到目标矩阵中,如果目标矩阵未分配内存或尺寸/类型不匹配,会先分配内存,或先释放目标内存后再重新分配内存。还支持掩码(mask)使用,只复制掩码非零的部分。下面是它的语法:

oldMat.copyTo(newMat);
oldMat.copyTo(newMat, mask); // 仅复制 mask 非零区域

如果不使用掩码,下面这两种用法是相等的。newImage1和newImage2都是对redImage的深拷贝

Mat redImage(Size(500, 300), CV_8UC3, Scalar(0, 0, 255)); // 全红色图像
Mat newImage1 = redImage.clone();//将redImage数据深拷贝到newImage1
Mat newImage2;
redImage.copyTo(newImage2);//将redImage数据深拷贝到newImage2

3.convertTo()函数

在OpenCV中,convertTo函数表示对Mat图像的转换操作,并支持缩放(scale)和偏移(shift)。它的功能远超clone和copyTo。事实上,这个函数的功能是将Mat的像素值从一种数据类型(如uchar)转换为另一种(如float),在转换时可对像素值进行线性变换,若目标类型范围不足(比如floatl转uchar),还会自动截断到有效范围(如0-255)。

函数原型如下

void cv::Mat::convertTo(
    OutputArray dst,    // 输出矩阵
    int rtype,          // 目标数据类型(如 CV_32F)
    double alpha = 1,   // 缩放因子(默认1)
    double beta = 0     // 偏移量(默认0)
) const;

参数说明

  • dst:输出矩阵,尺寸和通道数与输入相同
  • rtype:目标数据类型(如 CV_8U, CV_32F)
  • alpha :(scale) 缩放系数(乘法因子)
  • beta :shift) 偏移量(加法项)

那么,在使用convertTo函数时会发生深拷贝吗?答案是不一定。

当dst未初始化或尺寸/类型不匹配时会发生深拷贝,若匹配,就复用dst的内存。但是,dst和原Mat对象也是完全独立的。

Mat src = Mat::ones(3, 3, CV_8UC1);
Mat dst(3, 3, CV_32FC1); // 已初始化且类型匹配

src.convertTo(dst, CV_32F); // 复用 dst 内存,无深拷贝

在 OpenCV 中,Mat::ones() 是一个静态成员函数,用于创建一个所有元素初始化为 1 的矩阵。它的原型如下:cv::Mat cv::Mat::ones(int rows, int cols, int type);,其中,rows:矩阵的行数。cols:矩阵的列数。type:矩阵的数据类型(如 CV_8UC1CV_32FC3 等)。

如果验证是否发生深拷贝?

不管是clone、copyTo还是convertTo,都可以通过对比原对象和目标对象的数据指针来确认是否发生了深拷贝,如果数据指针相同,说明共享了数据内存空间。例如下面这段代码

Mat redImage(Size(500, 300), CV_8UC3, Scalar(0, 0, 255)); // 全红色图像
Mat newImage1 = redImage.clone();//将redImage数据深拷贝到newImage1
Mat newImage2;
redImage.copyTo(newImage2);//将redImage数据深拷贝到newImage2

// 检查数据指针是否相同
cout << "redImage.data = " << (void*)redImage.data << endl;
cout << "newImage1.data = " << (void*)newImage1.data << endl;
cout << "newImage2.data = " << (void*)newImage2.data << endl;

输出结果

redImage.data = 000002A150EE00C0
newImage1.data = 000002A150F4DF40
newImage2.data = 000002A150FE00C0

有关convertTo函数的转换、缩放和偏移等知识点,我们还会在后面详细讲解。

——重庆教主 2025年5月8日

copyright @重庆教主 WPF中文网 联系站长:(QQ)23611316 (微信)movieclip (QQ群).NET小白课堂:864486030 | 本文由WPF中文网原创发布,谢绝转载 渝ICP备2023009518号-1