结论:
clone代码: mat.inl.hpp
直接调用了copyTo函数,新建了一个临时变量m,返回赋值(地址的头+尺寸)给开发者定义的变量
inline
Mat Mat::clone() const
{
Mat m;
copyTo(m);
return m;
}
copyTo代码:
/** @brief Copies the matrix to another one.
The method copies the matrix data to another matrix. Before copying the data, the method invokes :
@code
m.create(this->size(), this->type());
@endcode
so that the destination matrix is reallocated if needed. While m.copyTo(m); works flawlessly, the
function does not handle the case of a partial overlap between the source and the destination
matrices.
When the operation mask is specified, if the Mat::create call shown above reallocates the matrix,
the newly allocated matrix is initialized with all zeros before copying the data.
@param m Destination matrix. If it does not have a proper size or type before the operation, it is
reallocated.
*/
void copyTo( OutputArray m ) const;
copyTo详细实现: copy.cpp
void Mat::copyTo( OutputArray _dst ) const
{
CV_INSTRUMENT_REGION()
int dtype = _dst.type();
if( _dst.fixedType() && dtype != type() )
{
CV_Assert( channels() == CV_MAT_CN(dtype) );
convertTo( _dst, dtype );
return;
}
if( empty() )
{
_dst.release();
return;
}
//GPU版本的Mat转化
// ................
//维度小于等于2的copyTo
// ..............
//正常的copyTo
_dst.create( dims, size, type() );
Mat dst = _dst.getMat();
if( data == dst.data )
return;
if( total() != 0 )
{
const Mat* arrays[] = { this, &dst };
uchar* ptrs[2];
NAryMatIterator it(arrays, ptrs, 2);
size_t sz = it.size*elemSize();
for( size_t i = 0; i < it.nplanes; i++, ++it )
memcpy(ptrs[1], ptrs[0], sz);
}
}
可以看到,首先先create了一个同样大小的区块,再利用内存拷贝赋值,关键看看create函数对于一个已经开辟空间Mat的处理
create函数详细实现:
void Mat::create(int d, const int* _sizes, int _type)
{
int i;
CV_Assert(0 <= d && d <= CV_MAX_DIM && _sizes);
_type = CV_MAT_TYPE(_type);
if( data && (d == dims || (d == 1 && dims <= 2)) && _type == type() )
{
if( d == 2 && rows == _sizes[0] && cols == _sizes[1] )
return;
for( i = 0; i < d; i++ )
if( size[i] != _sizes[i] )
break;
if( i == d && (d > 1 || size[1] == 1))
return;
}
int _sizes_backup[CV_MAX_DIM]; // #5991
if (_sizes == (this->size.p))
{
for(i = 0; i < d; i++ )
_sizes_backup[i] = _sizes[i];
_sizes = _sizes_backup;
}
release();
if( d == 0 )
return;
flags = (_type & CV_MAT_TYPE_MASK) | MAGIC_VAL;
setSize(*this, d, _sizes, 0, true);
if( total() > 0 )
{
MatAllocator *a = allocator, *a0 = getDefaultAllocator();
#ifdef HAVE_TGPU
if( !a || a == tegra::getAllocator() )
a = tegra::getAllocator(d, _sizes, _type);
#endif
if(!a)
a = a0;
CV_TRY
{
u = a->allocate(dims, size, _type, 0, step.p, 0, USAGE_DEFAULT);
CV_Assert(u != 0);
}
CV_CATCH_ALL
{
if(a != a0)
u = a0->allocate(dims, size, _type, 0, step.p, 0, USAGE_DEFAULT);
CV_Assert(u != 0);
}
CV_Assert( step[dims-1] == (size_t)CV_ELEM_SIZE(flags) );
}
addref();
finalizeHdr(*this);
}
可以看到关键的代码:如果尺寸一致,直接return了
if( dims <= 2 && rows == _rows && cols == _cols && type() == _type && data )
return;
结论:
1.clone直接调用了copyTo函数,会新建一个临时变量来存储Mat继而再赋值给目标值
2.copyTo如果目标Mat和被复制的Mat的尺寸和类型一致,不会重新开辟空间,直接内存拷贝
所以自然是copyTo更快了……
测试代码:
#include<opencv2/opencv.hpp>
#include<string>
#include<iostream>
#include<chrono>
using namespace cv;
using namespace std;
using namespace std::chrono;
int main() {
cv::Mat frame = imread("XXX.png", -1);
cv::Mat cloneMat = frame.clone();
cv::Mat copyToMat;
frame.copyTo(copyToMat);
auto start = steady_clock::now();
for (int i = 0; i<10000; ++i)
frame.copyTo(copyToMat);
auto end = steady_clock::now();
auto tt = duration_cast<microseconds>(end - start);
cout << "copyTo 10000次用时:" << tt.count() / 1000 << " 毫秒" << std::endl;;
start = steady_clock::now();
for (int i = 0; i<10000; ++i)
cloneMat = frame.clone();
end = steady_clock::now();
tt = duration_cast<microseconds>(end - start);
cout << "clone 10000次用时:" << tt.count() / 1000 << " 毫秒" << std::endl;
system("pause");
}