如何基于图像宽度分析颜色分布及定位目标像素区域(OpenCV 4.0.0 C++)
嘿,这个需求我刚好有过类似的实现经验,用OpenCV 4.0.0 + C++完全能搞定,我给你拆解成一步步的实操方案,跟着来肯定没问题~
整体思路
核心就是先锁定红色边框限定的分析区域,再统计该区域内每一列的白色像素占比,最后通过占比阈值找到右侧的密集区域,完全契合你说的“提取水平切片分析”的思路,只不过我们不用真的切图,直接按列统计更高效。
1. 定位红色边框的ROI区域
首先得把红色框出来的范围提取出来,不然整个图像分析太宽泛。红色在HSV空间里容易区分,我们可以通过颜色阈值+轮廓检测来获取ROI:
// 读取目标图像 cv::Mat img = cv::imread("your_image_path.jpg"); if (img.empty()) { std::cerr << "Failed to load image!" << std::endl; return -1; } // 转HSV色彩空间,方便检测红色 cv::Mat hsv_img; cv::cvtColor(img, hsv_img, cv::COLOR_BGR2HSV); // 红色的HSV阈值(分两段,因为HSV里红色是首尾衔接的) cv::Mat red_mask1, red_mask2; cv::inRange(hsv_img, cv::Scalar(0, 120, 70), cv::Scalar(10, 255, 255), red_mask1); cv::inRange(hsv_img, cv::Scalar(170, 120, 70), cv::Scalar(180, 255, 255), red_mask2); cv::Mat red_mask = red_mask1 | red_mask2; // 找红色边框的轮廓,取面积最大的那个(就是我们要的限定框) std::vector<std::vector<cv::Point>> contours; cv::findContours(red_mask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); if (contours.empty()) { std::cerr << "No red border detected!" << std::endl; return -1; } int max_contour_idx = 0; double max_area = cv::contourArea(contours[0]); for (int i = 1; i < contours.size(); ++i) { double area = cv::contourArea(contours[i]); if (area > max_area) { max_area = area; max_contour_idx = i; } } // 获取ROI的矩形坐标,提取ROI区域 cv::Rect roi_rect = cv::boundingRect(contours[max_contour_idx]); cv::Mat roi = img(roi_rect);
2. 统计每一列的白色像素占比
接下来我们对ROI的每一列统计白色像素数量,模拟你说的“720x1切片”的分析逻辑:
// ROI转灰度图,方便二值化提取白色 cv::Mat gray_roi; cv::cvtColor(roi, gray_roi, cv::COLOR_BGR2GRAY); // 二值化提取白色区域(阈值可根据实际图像调整,黑白图的白色一般接近255) cv::Mat white_mask; cv::threshold(gray_roi, white_mask, 240, 255, cv::THRESH_BINARY); // 统计每一列的白色像素数 int roi_width = roi.cols; int roi_height = roi.rows; std::vector<int> white_count_per_col(roi_width, 0); for (int col = 0; col < roi_width; ++col) { // 提取当前列,统计非零像素(白色)的数量 white_count_per_col[col] = cv::countNonZero(white_mask.col(col)); } // 计算每一列的白色像素占比(占该列总像素的比例) std::vector<float> white_ratio_per_col(roi_width, 0.0f); for (int col = 0; col < roi_width; ++col) { white_ratio_per_col[col] = static_cast<float>(white_count_per_col[col]) / roi_height; }
3. 定位右侧的白色密集区域
现在有了每一列的占比数据,我们可以设定一个阈值(比如占比超过80%就算密集),从右往左找连续符合条件的区域,最后转换回原图坐标:
// 自定义参数,可根据需求调整 const float DENSITY_THRESHOLD = 0.8f; // 白色占比超过这个值就算密集 const int MIN_CONTINUOUS_COLS = 15; // 至少连续N列符合条件才算有效区域 int start_col = -1; int end_col = roi_width - 1; int continuous_match = 0; // 从右往左遍历,找连续的密集列 for (int col = roi_width - 1; col >= 0; --col) { if (white_ratio_per_col[col] >= DENSITY_THRESHOLD) { continuous_match++; // 找到足够多的连续密集列,记录起始位置 if (continuous_match == MIN_CONTINUOUS_COLS) { start_col = col; break; } } else { continuous_match = 0; end_col = col; } } // 如果找到有效区域,转换为原图坐标并可视化 if (start_col != -1) { int orig_start_x = roi_rect.x + start_col; int orig_end_x = roi_rect.x + end_col; int orig_y = roi_rect.y; int orig_h = roi_rect.height; std::cout << "白色密集区域(原图坐标):\n" << "X范围:" << orig_start_x << " ~ " << orig_end_x << "\n" << "Y范围:" << orig_y << " ~ " << orig_y + orig_h << std::endl; // 用浅粉色标注目标区域,和你说的需求对应 cv::rectangle(img, cv::Rect(orig_start_x, orig_y, orig_end_x - orig_start_x, orig_h), cv::Scalar(255, 105, 180), 2); cv::imwrite("result_with_mark.jpg", img); } else { std::cerr << "未找到符合条件的白色密集区域!" << std::endl; }
一些实用优化建议
- 如果图像有噪声,可以在二值化前加高斯模糊:
cv::GaussianBlur(gray_roi, gray_roi, cv::Size(3,3), 0);,减少误判。 - 红色的HSV阈值可以根据实际图像微调,比如用OpenCV的滑动条工具实时调试,能更精准地定位红色边框。
- 要是不需要严格的连续列,也可以用滑动窗口计算平均占比(比如每20列算一个窗口的平均占比),这样结果会更平滑。
内容的提问来源于stack exchange,提问作者Erdem Tuna
相关产品推荐
相关产品推荐

