OpenCV技术实现:如何从掩码中提取数字到std::vector<int>数组
从掩码中提取数字并转为std::vector
基于你已有的掩码处理代码,接下来可以通过轮廓检测、轮廓筛选排序、数字识别这几个步骤,把图像中的数字提取为std::vector<int>,具体实现如下:
步骤1:提取并筛选数字轮廓
在现有掩码处理代码之后,添加轮廓检测逻辑,过滤掉噪声小轮廓,只保留符合数字形状特征的轮廓:
void find_numbers(const cv::Mat& image, std::vector<int>& out_numbers){ // 你的原有代码... cv::bitwise_and(dlt,dlt, dst, msk); // 1. 提取最外层轮廓(忽略数字内部孔洞,比如0、8的空白区域) std::vector<std::vector<cv::Point>> contours; std::vector<cv::Vec4i> hierarchy; cv::findContours(dst, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); // 2. 筛选有效轮廓:根据面积、宽高比过滤噪声 std::vector<std::vector<cv::Point>> valid_contours; for (auto& cnt : contours) { double area = cv::contourArea(cnt); cv::Rect rect = cv::boundingRect(cnt); // 阈值根据你的数字实际尺寸调整,示例值适配常规数字 if (area > 100 && (double)rect.width/rect.height > 0.3 && (double)rect.width/rect.height < 1.2) { valid_contours.push_back(cnt); } }
步骤2:按从左到右顺序排序轮廓
数字是横向排列的,需按轮廓的x坐标排序,保证识别顺序与视觉一致:
// 按轮廓左上角x坐标从小到大排序 std::sort(valid_contours.begin(), valid_contours.end(), [](const std::vector<cv::Point>& a, const std::vector<cv::Point>& b) { return cv::boundingRect(a).x < cv::boundingRect(b).x; });
步骤3:数字识别并填充vector
针对固定字体的数字,模板匹配是最简便的方案。先准备0-9的数字模板(与目标数字尺寸、风格一致的单数字灰度图),再对每个数字区域做匹配:
模板加载逻辑(可提前初始化)
// 存储0-9的数字模板 std::vector<cv::Mat> num_templates; void load_num_templates() { for (int i = 0; i < 10; ++i) { std::string path = "templates/" + std::to_string(i) + ".png"; cv::Mat temp = cv::imread(path, cv::IMREAD_GRAYSCALE); if (!temp.empty()) { num_templates.push_back(temp); } } }
在find_numbers中完成匹配
// 3. 对每个有效轮廓做模板匹配,识别数字 for (auto& cnt : valid_contours) { cv::Rect rect = cv::boundingRect(cnt); cv::Mat num_roi = dst(rect); // 将ROI调整为与模板一致的尺寸(示例为20x30,根据你的模板修改) cv::resize(num_roi, num_roi, cv::Size(20, 30)); double max_match_val = 0; int matched_num = -1; for (int i = 0; i < num_templates.size(); ++i) { cv::Mat result; cv::matchTemplate(num_roi, num_templates[i], result, cv::TM_CCOEFF_NORMED); double val; cv::minMaxLoc(result, nullptr, &val); // 匹配阈值,大于0.8认为匹配成功 if (val > max_match_val && val > 0.8) { max_match_val = val; matched_num = i; } } if (matched_num != -1) { out_numbers.push_back(matched_num); } } }
替代方案:无模板形状识别
如果数字风格极度规整,也可以通过轮廓特征识别:
- 统计孔洞数量:0、8有2个孔洞,6、9有1个,其余数字无孔洞
- 轮廓近似顶点数:1的顶点数最少,7有独特折线特征
这种方法无需模板,但仅适配固定的简单字体。
内容的提问来源于stack exchange,提问作者Aleksandr
相关产品推荐
相关产品推荐

