使用成员变量 作为CopyTo 的目标Mat
通过其他char* data 构造的Mat,需要考虑使用copyTo 或者clone。因为Mat只是浅拷贝,指向的数据还是原来的数据,如果原来的数据被释放了,那么Mat就会指向一个无效的数据,导致内存错误。这里主要对比OpenCV中不同深拷贝方式的效率。
clone 和 copyTo 的区别。
clone
会重新分配内存,然后拷贝数据copyTo
是否申请新的内存空间,取决于dst矩阵头中的大小信息是否与src一至,若一致则只深拷贝并不申请新的空间,否则先申请空间后再进行拷贝。
在视频帧处理中,大部分情况下,视频帧的大小都是不会变化的,所以可以考虑使用成员变量作为copyTo的目标Mat,这样可以减少内存的分配和释放,提高效率。
例如:
// 此处的 ptr指向了真正的数据,这里必须拷贝一份数据,否则ptr指向的数据会被释放
cv::Mat frame;
frame = cv::Mat(m_format.height * 3/2, m_format.width, CV_8UC1, ptr, pitch);
cv::Mat newFrame = frame.clone();
可以改为:
// 使用成员变量作为copyTo的目标Mat
cv::Mat(m_format.height * 3/2, m_format.width, CV_8UC1, ptr, pitch).copyTo(m_newFrame);
耗时对比:
frame2 = frame.clone() : 516us
frameCopyToGlobal = frame.clone() : 706us
frame.copyTo(frameCopyToGlobal) : 155us
frame.copyTo(frame3) : 566us
frameCopyToGlobal = frame.clone() 这种写法耗时最长,是因为除了clone 之外,还需要把上一次frameCopyToGlobal中的数据先释放掉,然后再重新分配内存,再拷贝数据。
frame.copyTo(frameCopyToGlobal) 这种写法耗时最短,因为frameCopyToGlobal已经分配了内存,只需要拷贝数据即可。
使用成员变量作为resize, cvtColor的目标Mat
这一类的函数,都会调用_dst.create(dsize, src.type());
生成一个_dst
,当_dst
是成员变量并且大小和类型和 src
相同时,可以减少内存的分配和释放,提高效率。同时也避免了临时变量的生成,拷贝,释放的过程。
void cv::resize( InputArray _src, OutputArray _dst, Size dsize,
double inv_scale_x, double inv_scale_y, int interpolation )
{
CV_INSTRUMENT_REGION();
Size ssize = _src.size();
CV_Assert( !ssize.empty() );
if( dsize.empty() )
{
CV_Assert(inv_scale_x > 0); CV_Assert(inv_scale_y > 0);
dsize = Size(saturate_cast<int>(ssize.width*inv_scale_x),
saturate_cast<int>(ssize.height*inv_scale_y));
CV_Assert( !dsize.empty() );
}
else
{
inv_scale_x = (double)dsize.width/ssize.width;
inv_scale_y = (double)dsize.height/ssize.height;
CV_Assert(inv_scale_x > 0); CV_Assert(inv_scale_y > 0);
}
if (interpolation == INTER_LINEAR_EXACT && (_src.depth() == CV_32F || _src.depth() == CV_64F))
interpolation = INTER_LINEAR; // If depth isn't supported fallback to generic resize
CV_OCL_RUN(_src.dims() <= 2 && _dst.isUMat() && _src.cols() > 10 && _src.rows() > 10,
ocl_resize(_src, _dst, dsize, inv_scale_x, inv_scale_y, interpolation))
// Fake reference to source. Resolves issue 13577 in case of src == dst.
UMat srcUMat;
if (_src.isUMat())
srcUMat = _src.getUMat();
Mat src = _src.getMat();
_dst.create(dsize, src.type());
Mat dst = _dst.getMat();
if (dsize == ssize)
{
// Source and destination are of same size. Use simple copy.
src.copyTo(dst);
return;
}
hal::resize(src.type(), src.data, src.step, src.cols, src.rows, dst.data, dst.step, dst.cols, dst.rows, inv_scale_x, inv_scale_y, interpolation);
}
操作720P resieze到1080P 在release 重复100次的测试结果:
resizeToLocalFrame : 940us resizeToToGlobal: 521us
resizeToLocalFrame : 972us resizeToToGlobal: 541us
resizeToLocalFrame : 977us resizeToToGlobal: 537us
resizeToLocalFrame : 995us resizeToToGlobal: 560us
resizeToLocalFrame : 993us resizeToToGlobal: 552us
resizeToLocalFrame : 977us resizeToToGlobal: 540us
resizeToLocalFrame : 963us resizeToToGlobal: 551us
cvtColor的测试结果
cvtColorframe2 : 446us cvtColorToGlobal: 119us
cvtColorframe2 : 436us cvtColorToGlobal: 115us
cvtColorframe2 : 464us cvtColorToGlobal: 117us
cvtColorframe2 : 461us cvtColorToGlobal: 122us
cvtColorframe2 : 440us cvtColorToGlobal: 122us
在取子图时,使用copyTo 构造新的Mat
这种情况一般出现在跨语言调用,比如C#的Mat 传入c++的算法SDK中,或者需要网络传输帧的时候。如果直接不拷贝可能造成读取的图片花屏。
cv::Mat frame = src(cv::Rect(0,,0,100,100));
cv::Mat newFrame(100,100, frame.type(), frame.data); // x
newFrame(100,100, frame.type(), frame.data).copyTo(newFrame) // √