直接用指针比cv::Mat::at快,因其省去边界检查、类型校验和重复地址计算;在3×3滤波中性能高2–5倍,但需确保mat.isContinuous()为true,并用mat.step而非cols×elemSize()计算行偏移。

为什么直接用指针比 cv::Mat::at 快?
因为 cv::Mat::at 每次调用都要做边界检查、类型校验和地址计算,而原始指针跳过所有运行时开销。在 3×3 均值滤波这类需密集访问邻域的场景中,性能差距可达 2–5 倍(实测 OpenCV 4.8 + Release 模式)。
但要注意:cv::Mat 数据必须是连续的(mat.isContinuous() 返回 true),否则指针跨行访问会越界。多数 cv::imread 或 cv::Mat::create 创建的单通道/三通道图默认连续,但经过 ROI、转置或某些变换后可能不连续,务必先检查。
如何安全获取并遍历图像指针?
核心是用 mat.data 获取首字节地址,再按 mat.step 和 mat.cols 手动算行首偏移。不要硬写 mat.data + i * mat.cols * step——step 已含对齐字节数(常为 4 的倍数),直接用它更可靠。
- 单通道
CV_8UC1:用uchar*指针,ptr = mat.data,第i行第j列地址为ptr + i * mat.step + j - 三通道
CV_8UC3:同样用uchar*,列索引乘 3,即ptr + i * mat.step + j * 3 - 避免
mat.ptr<uchar>(i)在循环内反复调用——它每次仍做一次行首计算,不如在外层缓存uchar* row_ptr
3×3 卷积滤波的指针写法要点
手动展开邻域访问比用循环更稳,也利于编译器向量化(尤其开启 -O3 -march=native 后)。但必须处理好图像边缘——指针不自动保护边界,越界读写会导致崩溃或脏数据。
立即学习“C++免费学习笔记(深入)”;
- 推荐做法:先用
cv::copyMakeBorder补一圈(如BORDER_REFLECT),再对「有效区域」用指针遍历,避免运行时条件分支拖慢主循环 - 权重矩阵提前存为局部数组,比如
const float k[9] = {1.f/9, ...},别在循环里反复除法 - 输出结果写入新
cv::Mat(CV_32FC1或CV_8UC1),注意目标 Mat 需预分配且与输入同尺寸;若原地更新,要确保输入未被覆盖(建议用双缓冲)
容易被忽略的内存对齐与类型陷阱
cv::Mat::step 不等于 cols * elemSize()——OpenCV 为 SIMD 对齐会在每行末尾补零,所以必须用 step 计算下一行起始,不能只靠列宽推导。
另一个坑:uchar* 读取像素后若要做浮点运算(如高斯滤波),直接转 float 再归一化,别用 static_cast<float>(*ptr) / 255.f 这种写法——整数提升顺序可能导致截断,应先转 int 再转 float,或直接用 cv::saturate_cast<uchar> 写回。
最后提醒:Release 模式下指针优势才明显,Debug 下编译器优化关闭,且断言开销大,测试性能务必切到 Release。


















