OpenCV::normalize速度过慢,无GPU场景下如何进一步优化?
优化OpenCV归一化大尺寸图像的执行速度
初始场景
使用环境:C++、MSVC2017、OpenCV4.1.3
处理对象:3000×3000尺寸的cv::Mat(浮点型)
初始代码及耗时:
cv::Mat fixFreImg = averMat.clone(); // fixFreImg.rows=3000, fixFreImg.cols=3000 cv::normalize(fixFreImg, fixFreImg, 0, 255, cv::NORM_MINMAX, CV_8U);
耗时:283ms
限制:无GPU,无法使用CV_CUDA模块
已实现的优化(OpenMP并行)
通过手动计算归一化参数+OpenMP并行遍历,将耗时降至156ms,核心代码如下:
// 创建输出图像 cv::Mat normalizedImg(fixFreImg.size(), CV_8U); // 计算归一化参数 double minVal, maxVal; cv::minMaxLoc(fixFreImg, &minVal, &maxVal); double alpha = 255.0 / (maxVal - minVal); double beta = -minVal * alpha; // 使用OpenMP并行化归一化操作 #pragma omp parallel for num_threads(threadNum) for (int i = 0; i < fixFreImg.rows; i++) { uchar* ptr = normalizedImg.ptr<uchar>(i); float* fptr = fixFreImg.ptr<float>(i); for (int j = 0; j < fixFreImg.cols; j++) { ptr[j] = cv::saturate_cast<uchar>(alpha * fptr[j] + beta); } }
进一步优化方案(分块计算+缓存友好)
分块处理的核心是利用CPU缓存的局部性原理,将大图像拆分为小的块(比如64×64,匹配CPU缓存行大小),让每个块的数据尽可能被缓存命中,减少内存访问开销。以下是分块优化的代码实现:
cv::Mat normalizedImg(fixFreImg.size(), CV_8U); double minVal, maxVal; cv::minMaxLoc(fixFreImg, &minVal, &maxVal); double alpha = 255.0 / (maxVal - minVal); double beta = -minVal * alpha; // 定义分块大小,建议取2的幂次,比如64×64(适配CPU缓存) const int blockSize = 64; int rows = fixFreImg.rows; int cols = fixFreImg.cols; #pragma omp parallel for collapse(2) num_threads(threadNum) for (int i = 0; i < rows; i += blockSize) { for (int j = 0; j < cols; j += blockSize) { // 计算当前块的实际边界,避免越界 int blockRowEnd = std::min(i + blockSize, rows); int blockColEnd = std::min(j + blockSize, cols); for (int y = i; y < blockRowEnd; y++) { uchar* dstPtr = normalizedImg.ptr<uchar>(y); float* srcPtr = fixFreImg.ptr<float>(y); for (int x = j; x < blockColEnd; x++) { dstPtr[x] = cv::saturate_cast<uchar>(alpha * srcPtr[x] + beta); } } } }
额外优化点
- 预计算alpha和beta为float类型:将
double类型的alpha、beta转为float,减少浮点运算的类型转换开销,匹配原图像的float类型,更适配CPU浮点单元:float alpha_f = static_cast<float>(alpha); float beta_f = static_cast<float>(beta); // 后续计算用alpha_f和beta_f dstPtr[x] = cv::saturate_cast<uchar>(alpha_f * srcPtr[x] + beta_f); - 并行化minMaxLoc计算:OpenCV默认
cv::minMaxLoc为单线程,可手动并行计算全局最小最大值,压缩预处理时间:double localMin = INFINITY, localMax = -INFINITY; #pragma omp parallel for reduction(min:localMin) reduction(max:localMax) num_threads(threadNum) for (int i = 0; i < rows; i++) { float* ptr = fixFreImg.ptr<float>(i); for (int j = 0; j < cols; j++) { if (ptr[j] < localMin) localMin = ptr[j]; if (ptr[j] > localMax) localMax = ptr[j]; } } minVal = localMin; maxVal = localMax; - 启用MSVC高级优化:在项目属性中开启
/O2或/O3优化级别,让编译器自动进行循环展开、指令级并行等底层优化。
内容的提问来源于stack exchange,提问作者s yl
相关产品推荐
相关产品推荐

