OpenCV 数字识别程序解析

///////////////////////////////////////////////////////////////////////////////
//轮廓提取主函数
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/types_c.h>
#include <iostream>
using namespace std;
using namespace cv;
int getPixelSum(Mat& image){
	int a = 0;
	for (int row = 0; row < image.rows; row++) {
		uchar* current_pixel = image.ptr<uchar>(row);
		for (int col = 0; col < image.cols; col++) {
			a += *current_pixel++;	//指针遍历像素点反转颜色
		}
	}
	return a;
}

//模板匹配函数,两图像做差
int imgMatch(Mat& image, int& rate, int& num) {
	Mat imgSub;
	double min = 10e6;
	num = 0;
	rate = 0;

	for (int i = 1; i < 6; i++) {
		Mat templatimg = imread("/home/lxy/图片/摄像头/" + std::to_string(i) + ".png", IMREAD_GRAYSCALE);
		resize(image, image, Size(32, 48), 0, 0, cv::INTER_LINEAR);	//将两图像大小调至相同
		//cout << "nihao" <<endl;
		resize(templatimg, templatimg, Size(32,48), 0, 0, cv::INTER_LINEAR);
		
		absdiff(templatimg, image, imgSub);
		rate = getPixelSum(imgSub);
		if (rate < min) {
			min = rate;
			num = i;
		}
		rate = min;
	}
	return num;
}

int main()
{
	//读取一张图像,转换为灰度图并进行二值化处理
    Mat srcImage ;  //读取图片
	VideoCapture capture(2);
	while (capture.isOpened()) {
		capture.read(srcImage);	//逐帧读取视频
		if (srcImage.empty()) {	//如果视频结束或未检测到摄像头则跳出循环
			break;
		}
    //Mat srcImage ;  //读取图片
	Mat dstImage, grayImage, binImage;
    srcImage.copyTo(dstImage);  //将读取到的图片,深拷贝为dstImage
    cvtColor(srcImage, grayImage, COLOR_BGR2GRAY);                  //转换灰度图
    threshold(grayImage, binImage, 100, 255, cv::THRESH_BINARY_INV);     //转换二值图,设置阈值,高于100认为255

	//寻找轮廓
    vector<vector<Point>> contours;  //定义轮廓和层次结构
    vector<Vec4i> hierarchy;
    findContours(binImage, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); //寻找轮廓
    int i = 0;
    vector<vector<Point>>::iterator It;
    Rect a4rect[15];	//假设最多不会超过15个轮廓
	for (It = contours.begin(); It < contours.end(); It++) {                        //画出包围数字的最小矩形
		a4rect[i].x = (float)boundingRect(*It).tl().x;
		a4rect[i].y = (float)boundingRect(*It).tl().y;
		a4rect[i].width = (float)boundingRect(*It).br().x - (float)boundingRect(*It).tl().x;
		a4rect[i].height = (float)boundingRect(*It).br().y - (float)boundingRect(*It).tl().y;
		if ((a4rect[i].height > 80) && (a4rect[i].width > 50) && (a4rect[i].height < 640) && (a4rect[i].width < 480)) {
			rectangle(dstImage, a4rect[i], Scalar(0, 0, 255), 2, 8, 0);	//在原图像中用红框画出识别到的各轮廓
			rectangle(binImage, a4rect[i], Scalar(0, 0, 0), 0, 8, 0);
			i++;
		}
	}
	
	imshow("dstImage", dstImage);
	waitKey(1);
    
	//将图像轮廓逐一与模板匹配
	Mat num[15];
	int matchingNum = 0;	//匹配到的数字
	int matchingRate = 0;	//相似率
	Mat a4Img = dstImage;				//提取A4纸区域
	Mat a4binImg;
	cvtColor(a4Img, a4binImg, CV_BGR2GRAY);
    for (int j = 0; j < i; j++) {
		
    	a4binImg(a4rect[j]).copyTo(num[j]);		//提取包围数字的矩形区域至num[j]
        imgMatch(num[j], matchingRate, matchingNum);	//数字匹配
        if (matchingRate < 400000) {
        	cout << "识别数字:" << matchingNum << "\t匹配率:" <<  matchingRate << endl;
			//imwrite(to_string(matchingNum) + ".jpg", num[j]);
		}
	}
	}
    system("pause");
    return 0;
}

核心流程

​摄像头捕获 → 2. ​灰度化+二值化 → 3. ​轮廓检测 → 4. ​区域筛选 → 5. ​模板匹配 → 6. ​输出结果

示例输出

识别数字:3 匹配率:120000
识别数字:5 匹配率:95000

  1. 程序结构概述
    cpp
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/types_c.h>
#include <iostream>
using namespace std;
using namespace cv;
​头文件:引入 OpenCV 核心库和图像处理模块。
​命名空间:使用 std 和 cv 简化代码。
​2. 核心函数解析
​**(1) getPixelSum(Mat& image)**
cpp
int getPixelSum(Mat& image) {
    int a = 0;
    for (int row = 0; row < image.rows; row++) {
        uchar* current_pixel = image.ptr<uchar>(row);
        for (int col = 0; col < image.cols; col++) {
            a += *current_pixel++;  // 累加像素值
        }
    }
    return a;
}

​功能:计算图像所有像素值的总和。
​用途:在模板匹配中衡量两图像的差异(差异越小,匹配度越高)。
​**(2) imgMatch(Mat& image, int& rate, int& num)**
cpp

int imgMatch(Mat& image, int& rate, int& num) {
    Mat imgSub;
    double min = 10e6;  // 初始化最小差异为一个极大值
    num = 0;
    rate = 0;

    for (int i = 1; i < 6; i++) {
        // 读取模板图像(1.png ~ 5.png)
        Mat templatimg = imread("/home/lxy/图片/摄像头/" + std::to_string(i) + ".png", IMREAD_GRAYSCALE);
        
        // 统一图像尺寸为 32x48
        resize(image, image, Size(32, 48), 0, 0, cv::INTER_LINEAR);
        resize(templatimg, templatimg, Size(32, 48), 0, 0, cv::INTER_LINEAR);
        
        // 计算两图像的绝对差异
        absdiff(templatimg, image, imgSub);
        rate = getPixelSum(imgSub);  // 差异总和

        // 更新最小差异和对应的模板编号
        if (rate < min) {
            min = rate;
            num = i;
        }
        rate = min;
    }
    ret

urn num;
}
​功能:将输入图像与 5 个模板图像(1.png ~ 5.png)逐一比较,返回最匹配的数字(1~5)。
​关键步骤:
​读取模板:从指定路径加载模板图像。
​尺寸归一化:统一调整为 32x48 像素。
​差异计算:通过 absdiff 计算像素差异,累加差异值。
​最佳匹配:选择差异最小的模板编号作为识别结果。
​3. 主函数 main() 解析
​**(1) 初始化摄像头**
cpp

VideoCapture capture(2);
while (capture.isOpened()) {
    capture.read(srcImage);  // 逐帧读取视频
    if (srcImage.empty()) break;  // 检查是否为空帧
}

​功能:打开摄像头(设备号 2),实时捕获视频帧。
​**(2) 图像预处理**
cpp

Mat dstImage, grayImage, binImage;
srcImage.copyTo(dstImage);  // 深拷贝原始图像
cvtColor(srcImage, grayImage, COLOR_BGR2GRAY);  // 转为灰度图
threshold(grayImage, binImage, 100, 255, cv::THRESH_BINARY_INV);  // 二值化(反色)

​步骤:
​灰度化:减少计算量。
​二值化:通过阈值 100 将图像转为黑白(反色处理,便于轮廓检测)。
​**(3) 轮廓检测与筛选**
cpp

vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(binImage, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);

Rect a4rect[15];  // 存储轮廓的矩形区域
for (It = contours.begin(); It < contours.end(); It++) {
    a4rect[i] = boundingRect(*It);  // 计算轮廓的外接矩形
    // 筛选符合条件的矩形(高度>80,宽度>50,且不超过图像尺寸)
    if ((a4rect[i].height > 80) && (a4rect[i].width > 50) && 
        (a4rect[i].height < 640) && (a4rect[i].width < 480)) {
        rectangle(dstImage, a4rect[i], Scalar(0, 0, 255), 2, 8, 0);  // 在原图画红框
        i++;
    }
}

​功能:检测图像中的轮廓,筛选出可能是数字的区域。
​关键参数:
RETR_EXTERNAL:只检测最外层轮廓。
CHAIN_APPROX_NONE:存储轮廓所有点。
​筛选逻辑:排除过小或过大的矩形(避免噪声干扰)。
​**(4) 数字识别与输出**
cpp

Mat num[15];
for (int j = 0; j < i; j++) {
    a4binImg(a4rect[j]).copyTo(num[j]);  // 提取数字区域
    imgMatch(num[j], matchingRate, matchingNum);  // 模板匹配
    if (matchingRate < 400000) {  // 差异阈值
        cout << "识别数字:" << matchingNum << "\t匹配率:" << matchingRate << endl;
    }
}

​功能:对每个候选区域调用 imgMatch 进行模板匹配。
​输出结果:打印识别到的数字和匹配差异值(差异越小越匹配)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

博导ai君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值