使用OpenCV计算Blob质心结果偏差问题求助
质心计算向左向上偏差的问题排查与修复
核心问题分析
1. 循环内变量重定义导致累加完全失效
在质心计算的双重循环中,你错误地在循环体内重新定义了sum_x和sum_y局部变量,直接覆盖了外部用于累加的变量:
for (e=0; e<thresholded_window.rows; e++) { for (r=0; r<thresholded_window.cols; r++) { double intensity = thresholded_window.at<double>(e, r); double sum_x = sum_x + intensity * r; // 重定义sum_x,外部累加变量始终为0 double sum_y = sum_y + intensity * e; // 同理sum_y也被重定义 } }
这导致sum_x和sum_y始终保持初始值0,最终计算出的窗口内质心为(0,0),加上ROI的左上角坐标x_min/y_min,结果自然偏向图像左上角,与你描述的偏差完全吻合。
2. 像素类型访问错误
thresholded_window由8位灰度图(CV_8U)处理得到,你使用at<double>访问会导致内存越界,读取到错误的像素值,进一步干扰质心计算。必须使用at<uint8_t>获取原始像素值后再转换为double类型。
3. 质心计算公式分母错误
质心的正确计算公式应为:
$$x_c = \frac{\sum (I(x,y) \cdot x)}{\sum I(x,y)}, \quad y_c = \frac{\sum (I(x,y) \cdot y)}{\sum I(x,y)}$$
你错误地使用了降采样后Blob的像素数量max_group_size作为分母,而非ROI内阈值后所有像素的强度总和,这会导致质心计算结果严重偏离真实值。
4. 坐标顺序混淆风险
OpenCV中cv::Mat的坐标顺序是(行, 列)即(y, x),你最终输出的real_centroid[0]对应y坐标,real_centroid[1]对应x坐标,若测试时的实际质心是(x, y)顺序,会造成坐标含义的混淆,建议输出时明确标注。
修复后的关键代码片段
修正质心计算部分
/* Compute the centroid of this window. */ double sum_x = 0; double sum_y = 0; double sum_intensity = 0; // 新增:累加所有有效像素的强度总和 double *windowed_centroid = new double[2]; for (e=0; e<thresholded_window.rows; e++) { for (r=0; r<thresholded_window.cols; r++) { // 正确访问8位灰度图的像素值 uint8_t pixel_val = thresholded_window.at<uint8_t>(e, r); double intensity = static_cast<double>(pixel_val); // 直接累加,不再重定义变量 sum_x += intensity * r; sum_y += intensity * e; sum_intensity += intensity; } } // 使用强度总和作为分母,同时避免除以0的异常情况 if (sum_intensity > 0) { windowed_centroid[1] = sum_x / sum_intensity; // 对应原图像x坐标 windowed_centroid[0] = sum_y / sum_intensity; // 对应原图像y坐标 } else { // 处理ROI内无有效像素的异常情况 windowed_centroid[0] = windowed_centroid[1] = 0; }
其他潜在优化点
- 连通分量分析阶段,加入
extreme_size_thresh和min_size_thresh的判断,剔除不符合尺寸的区域,避免误选背景或噪声:if (members > extreme_size_thresh || members < min_size_thresh) { continue; } - 构建ROI时,确保坐标不超出原图像边界,防止越界访问:
x_min = std::max(0, x_min_decimated * decimation - border_size); y_min = std::max(0, y_min_decimated * decimation - border_size); x_max = std::min(static_cast<int>(SizeX), x_max_decimated * decimation + border_size); y_max = std::min(static_cast<int>(SizeY), y_max_decimated * decimation + border_size);
内容的提问来源于stack exchange,提问作者Brassard1984
相关产品推荐
相关产品推荐

