前言
OpenCV基础类型Mat以及GPU上的GpuMat是整个OpenCV工程体系中两种非常基础常用的类型。通常情况下Mat拥有的方法,GpuMat都会有对应的方法。对于内存拷贝,有两个常用的方法copyTo()和clone(),在使用的时候需要特别注意。
Mat
clone
Mat Mat::clone() const
{
Mat m;
copyTo(m);
return m;
}
copyTo
void Mat::copyTo( OutputArray _dst ) const
{
CV_INSTRUMENT_REGION();
#ifdef HAVE_CUDA
if (_dst.isGpuMat())
{
_dst.getGpuMat().upload(*this);
return;
}
#endif
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;
}
if( _dst.isUMat() )
{
_dst.create( dims, size.p, type() );
UMat dst = _dst.getUMat();
CV_Assert(dst.u != NULL);
size_t i, sz[CV_MAX_DIM] = {0}, dstofs[CV_MAX_DIM], esz = elemSize();
CV_Assert(dims > 0 && dims < CV_MAX_DIM);
for( i = 0; i < (size_t)dims; i++ )
sz[i] = size.p[i];
sz[dims-1] *= esz;
dst.ndoffset(dstofs);
dstofs[dims-1] *= esz;
dst.u->currAllocator->upload(dst.u, data, dims, sz, dstofs, dst.step.p, step.p);
return;
}
if( dims <= 2 )
{
_dst.create( rows, cols, type() );
Mat dst = _dst.getMat();
if( data == dst.data )
return;
if( rows > 0 && cols > 0 )
{
Mat src = *this;
Size sz = getContinuousSize2D(src, dst, (int)elemSize());
CV_CheckGE(sz.width, 0, "");
const uchar* sptr = src.data;
uchar* dptr = dst.data;
#if IPP_VERSION_X100 >= 201700
CV_IPP_RUN_FAST(CV_INSTRUMENT_FUN_IPP(ippiCopy_8u_C1R_L, sptr, (int)src.step, dptr, (int)dst.step, ippiSizeL(sz.width, sz.height)) >= 0)
#endif
for (; sz.height--; sptr += src.step, dptr += dst.step)
memcpy(dptr, sptr, sz.width);
}
return;
}
_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);
}
}
clone vs. copyTo
可以看到,使用clone方法会新创建一个Mat,然后调用copyTo方法将原Mat中的内容拷贝到新创建的那个Mat中。
GpuMat
前面说到,GpuMat 一般都有Mat对应的方法,对于内存拷贝,GpuMat也有clone和copyTo两个方法:
clone
inline GpuMat GpuMat::clone() const
{
GpuMat m;
copyTo(m);
return m;
}
copyTo
...
class CudaFuncTable : public GpuFuncTable
{
public:
void copy(const Mat& src, GpuMat& dst) const
{
cudaSafeCall( cudaMemcpy2D(dst.data, dst.step, src.data, src.step, src.cols * src.elemSize(), src.rows, cudaMemcpyHostToDevice) );
}
...
}
...
void cv::gpu::GpuMat::copyTo(GpuMat& m) const
{
CV_DbgAssert(!empty());
m.create(size(), type());
gpuFuncTable()->copy(*this, m);
}
clone vs. copyTo
可以看到,GpuMat使用clone方法同样会新创建一个GpuMat,然后调用copyTo方法将原GpuMat中的内容拷贝到新创建的那个GpuMat中。最后返回新的GpuMat的指针。
这里需要特别注意的是,如果利用OpenCV+CUDA编程,需要特别注意GpuMat的生成是比较费时的,不像Mat那样容易。在需要高性能的时候需要谨慎使用clone方法,而是提前创建好满足大小需求的GpuMat,然后使用copyTo方法拷贝。
参考资料
[1] cv::Mat Class Reference
[2] cv::cuda::GpuMat Class Reference
[3] void Mat::copyTo( OutputArray _dst ) const
[4] Mat Mat::clone() const
[5] void cv::gpu::GpuMat::copyTo(GpuMat& m) const
[6] inline GpuMat GpuMat::clone() const
[7] stackoverflow - What’s the difference between Mat::clone and Mat::copyTo?
[8] cnblogs - opencv之Mat数据类型
[9] OpenCV: cv::cuda::HostMem Class Reference
[10] opencv/cuda_host_mem.cpp · opencv/opencv
[11] opencv/cuda_gpu_mat.cpp · opencv/opencv
[12] opencv/cuda.inl.hpp · opencv/opencv