在前文的模板匹配中,基于边缘的模板匹配的速度并不友好,尝试对算法进行时间的优化。其中,对OpenCV的Mat遍历操作十分普遍,本文结合OpenCV的Mat的存储结构,试图找到一种的高效遍历Mat的方法,为后续进行时间优化作铺垫。
网上有类似文章可供参考:
opencv Mat遍历速度实验 https://blog.csdn.net/fanghtao/article/details/80995997
opencv高效遍历图像 https://blog.csdn.net/jacke121/article/details/94432519
OpenCV的图像类型的格式:CV_[bite](U|S|F)C [channels]
bite : 比特数,位数,对应在Mat中每个像素的所占的空间大小。
U|S|F :
U : unsigned int , 无符号整型
S : signed int , 有符号整型
F : float , 单精度浮点型
channels:图像的通道数
本场景中,使用的图像类型为CV_8UC1,即单通道的uchar格式,与CV_32FC1的单通道float格式在运算速度上差异较大。
cv::Mat resultImage;
resultImage.create(5000, 5000, CV_8UC1);
OpenCV提供了方便访问Mat元素的指针ptr,正常的访问格式如下:
resultImage.create(5000, 5000, CV_8UC1);
int resultImageRow = 0;
int resultImageCol = 0;
//先访问行指针,再访问相应元素
uchar * resultImagePtr = resultImage.ptr<uchar>(resultImageRow);
resultImagePtr[resultImageCol] = 1;
//直接访问相应元素
resultImage.ptr<uchar>(resultImageRow)[resultImageCol]= 1;
通过上述代码,我们注意到:
resultImage.ptr(resultImageRow)返回的是一个行指针头;
resultImage.ptr(resultImageRow)[resultImageCol]返回的是相应的元素;
这点在后续时间的讨论中,两者的差异性会愈发明显。
注意事项:uchar指针对应CV_8UC1,int指针对应CV_32SC1,float指针对应CV_32FC1。
在场景设计中,我们使用VS的release模式,与debug模式时间差异性较大。
(1)直接遍历目标元素:
for (int resultImageRow = 0; resultImageRow < 5000; resultImageRow++)
for (int resultImageCol = 0; resultImageCol < 5000; resultImageCol++)
resultImage.ptr<uchar>(resultImageRow)[resultImageCol]= 1;
代码精简,利用ptr直接遍历目标元素,效率最低。
Run Time:0.035s
(2)行指针逐行遍历目标元素:
uchar* resultImagePtr;
for (int resultImageRow = 0; resultImageRow < 5000; resultImageRow++)
{
resultImagePtr = resultImage.ptr<uchar>(resultImageRow);
for (int resultImageCol = 0; resultImageCol < 5000; resultImageCol++)
resultImagePtr[resultImageCol] = 1;
}
先访问行指针ptr,再依次遍历目标元素,效率中等。
Run Time:0.004s
(3)指针按行列遍历目标元素:
除ptr指针之外,OpenCV还提供Image.data指向数据的首部。在大部分场景中,OpenCV的数据是连续存储的,可以通过isContinuous()判断矩阵元素在每行末尾是否连续存储而没有间隙。经过一些场景测试,目前未发现网上提到的行结尾不满4Byte会自动补零的情况。
uchar* resultImageDataStart = resultImage.data;
for (int resultImageRow = 0; resultImageRow < 5000; resultImageRow++)
for (int resultImageCol = 0; resultImageCol < 5000; resultImageCol++)
*(resultImageDataStart++) = 1;
利用元素存储的连续性,按行列滑动指针位置遍历目标元素,效率较高。
Run Time:0.003s
注意事项:resultImage.data默认uchar格式,倘若图像格式为CV_32FC1,需要强制格式转化。
float* resultImageDataStart = (float*) resultImage.data;
(4)指针单循环遍历目标元素:
uchar* resultImageDataStart = resultImage.data;
for (int resultImageSeq = 0; resultImageSeq < 25000000; resultImageSeq++)
*(resultImageDataStart++) = 1;
指针连续遍历目标元素,代码精简,效率最高。
Run Time:0.003s
综上所述:
遍历速度:ptr(row)[col] < ptr(row) < data