opencv/c++ 简单数字/浮点数识别

2 篇文章 0 订阅
2 篇文章 0 订阅

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:项目需要识别实时采集图片,识别图片中浮点数
想法如下,采取CV来处理图片,处理的图片进行轮廓分割(这里采用了两种方式)一种为mat数据主动切割识别,一种为CV自带轮廓分割。


一、两种分割思想

  1. 首先图片进行灰度处理
  2. 然后图片进行二值化
  3. 开始图片分割
  4. 采集出图片的Mat数据,按行列来读取,读取mat中最左边row数据都是0的最上行序号,读取mat中row数据为空的最下行数据,然后读取column不为0的咧数,直到column为0 这样就采集到了一个长方形,里面就是要识别的数字。
    !!!!!但是这种方式有问题,比如图片二值化后,部分数据断开, 或者两个数字存在连通,比如77二值化后的数据最顶行可能中间没有空隙,就会出问题
  5. 第二种方式,直接采用cv自带的函数来分割(后来才发现)
    !!!!! 存在相同问题。
    有解决方式,就是对比的图片,将特殊的数全部穷举处理,比如0-9和.的图片为left0 -left10.png
    对于77你可以创建一个11.png去比,如果为11就是77, 过于麻烦最后采用了OCR的处理方式(可看我另外一篇文章Tesseract C++使用)

二、使用步骤

源码

注释掉的就是原来采用的第一种分割方式。还有对比图片生成,需要你自己先用最后一个函数的注释代码生成0-9和.的图片。有时间再做详细修改最近有些忙嘻嘻。

代码如下(示例):

/********************************************************
	* @file    : cvstract.h
	* @brief   : 图像分割识别文件
	* @details :
	* @author  : liuyapeng5
	* @date    : 2021-8-11
*********************************************************/
#pragma once
#include "include/opencv2/highgui.hpp"
#include "include/opencv2/core.hpp"
#include "include/opencv2/opencv.hpp"
#include "include/opencv2/imgproc.hpp"
#include "include/opencv2/imgcodecs.hpp"
#include "include/opencv2/opencv_modules.hpp"
#include <QMessageBox>
#include <QTextCodec>
#include <QDir>
#include <QDebug>
#include <stack>

using namespace cv;
static bool isDian = false;

int getColSum(Mat src, int col)
{
	int sum = 0;
	int height = src.rows;
	int width = src.cols;
	for (int i = 0; i < height; i++)
	{
		sum = sum + src.at <uchar>(i, col);
	}
	return sum;
}

int getRowSum(Mat src, int row)
{
	int sum = 0;
	int height = src.rows;
	int width = src.cols;
	for (int i = 0; i < width; i++)
	{
		sum += src.at <uchar>(row, i);
	}
	return sum;
}


void cutTop(Mat& src, Mat& dstImg)//上下切割
{
	int top, bottom;
	top = 0;
	bottom = src.rows;

	int i;
	for (i = 0; i < src.rows; i++)
	{
		int colValue = getRowSum(src, i);
		//cout <<i<<" th "<< colValue << endl;
		if (colValue > 0)
		{
			top = i;
			break;
		}
	}
	for (int j = src.rows-1; j > i; j--)
	{
		int colValue = getRowSum(src, j);
		//cout << i << " th " << colValue << endl;
		if (colValue != 0)
		{
			bottom = j;
			break;
		}
	}

	int height = bottom - top+3;
	Rect rect(0, top-1, src.cols, height);
	dstImg = src(rect).clone();
}

int cutLeft(Mat& src, Mat& leftImg, Mat& rightImg)//左右切割
{
	int left, right;
	left = 0;
	right = src.cols;

	int i;
	for (i = 0; i < src.cols; i++)
	{
		int colValue = getColSum(src, i);

		if (colValue > 0)
		{
			left = i;
			//qDebug() <<i<<" th1 "<< colValue << endl;
			if (colValue == 510)
			{
				int  Value = getColSum(src, i + 1);
				if (Value == 510)
				{
					Rect rect(left, 0, 2, src.rows);
					leftImg = src(rect).clone();
					cutTop(leftImg, leftImg);
					if (leftImg.rows == 2 && leftImg.cols == 2)
					{
						right = left + 2;
						Rect rectRight(right, 0, src.cols - right, src.rows);
						rightImg = src(rectRight).clone();
						isDian = true;
						return 0;
					}
				}
			}
			break;
		}
	}
	if (left == 0 && !isDian)
	{
		return 1;
	}
	isDian = false;

	for (; i < src.cols; i++)
	{
		int colValue = getColSum(src, i);

		if (colValue == 0)
		{
			right = i;
			//qDebug() << i << " th2 " << colValue << endl;
			break;
		}
	}
	int width = right - left;
	Rect rect(left, 0, width, src.rows);
	leftImg = src(rect).clone();
	Rect rectRight(right, 0, src.cols - right, src.rows);
	rightImg = src(rectRight).clone();
	cutTop(leftImg, leftImg);
	return 0;
}


void getPXSum(Mat& src, int& a)//获取所有像素点和
{
	threshold(src, src, 100, 255, THRESH_BINARY);
	a = 0;
	for (int i = 0; i < src.rows; i++)
	{
		for (int j = 0; j < src.cols; j++)
		{
			a += src.at <uchar>(i, j);
		}
	}
}

int  getSubtract(Mat src, int TemplateNum) //两张图片相减
{
	Mat img_result;
	int min = 1000000;
	int serieNum = 0;
	for (int i = 0; i < TemplateNum; i++) {

		QString str = QDir::currentPath() + QDir::separator() + "src" + QDir::separator() + QString("%1Left.png").arg(i);
		QTextCodec* code = QTextCodec::codecForName("gb18030");
		std::string name = code->fromUnicode(str).data(); // 解决中文乱码以及imread输入为string

		Mat Template = imread(name, COLOR_BGR2GRAY);
		
		Mat dst(src);
		threshold(Template, Template, 100, 255, THRESH_BINARY);
		/*threshold(src, dst, 100, 255, THRESH_BINARY);*/
		resize(dst, dst, Size(16, 16), 0, 0, INTER_CUBIC);
		resize(Template, Template, Size(16, 16), 0, 0, INTER_CUBIC);
		//imshow(name, Template);
		//std::cout << "\n" << dst;
		//std::cout << "\n" << Template;
		absdiff(Template, dst, img_result);
		int diff = 0;
		getPXSum(img_result, diff);
		if (diff < min)
		{
			min = diff;
			serieNum = i;
		}
	}
	//std::cout << "i =========" << min << "\n";

	//std::cout << "i--------------" << serieNum << "  " << serieNum << "\n";
	return serieNum;
}

//对每条连通域上的各点根据y从小到大进行排序
void SortContourPoint(std::vector<std::vector<Point> > &inputContours)
{
	std::vector<Point> tempContoursPoint;
	for (int i = 0; i < inputContours.size(); i++)
	{
		tempContoursPoint.clear();    //每次循环注意清空
		for (int j = i+1; j < inputContours.size(); j++)
		{

				if (inputContours[i][0].x > inputContours[j][0].x)
				{
					swap(inputContours[i], inputContours[j]);
				}
			
		}
	}
}
//src:待分割的二值图,最大值为255
//segMat:分割好的每一个图片
//算法:判断连通域,有几个连通域就会分割成几个子图片
//用途:手写数字识别中进行无黏连数字的分割
void getConnectedDomain(cv::Mat& src, std::vector<cv::Mat>& segMat)//segMat为最终结果,存放分割好的每一个数字  
{
	int img_row = src.rows;
	int img_col = src.cols;
	cv::Mat flag = cv::Mat::zeros(cv::Size(img_col, img_row), CV_8UC1);//标志矩阵,为0则当前像素点未访问过

	for (int i = 0; i < img_row; i++)
	{
		for (int j = 0; j < img_col; j++)
		{
			if (src.ptr<uchar>(i)[j] == 255 && flag.ptr<uchar>(i)[j] == 0)
			{
				cv::Mat subMat = cv::Mat::zeros(cv::Size(img_col, img_row), CV_8UC1);//代表子图
				std::stack<cv::Point2f> cd;
				cd.push(cv::Point2f(j, i));
				flag.ptr<uchar>(i)[j] = 1;
				subMat.ptr<uchar>(i)[j] = 255;

				while (!cd.empty())
				{
					cv::Point2f tmp = cd.top();

					cd.pop();
					cv::Point2f p[4];//邻域像素点,这里用的四邻域  
					p[0] = cv::Point2f(tmp.x - 1 > 0 ? tmp.x - 1 : 0, tmp.y);
					p[1] = cv::Point2f(tmp.x + 1 < img_col - 1 ? tmp.x + 1 : img_row - 1, tmp.y);
					p[2] = cv::Point2f(tmp.x, tmp.y - 1 > 0 ? tmp.y - 1 : 0);
					p[3] = cv::Point2f(tmp.x, tmp.y + 1 < img_row - 1 ? tmp.y + 1 : img_row - 1);
					for (int m = 0; m < 4; m++)
					{
						int x = p[m].y;
						int y = p[m].x;
						if (src.ptr<uchar>(x)[y] == 255 && flag.ptr<uchar>(x)[y] == 0)//若是未访问,则入栈,并标记访问过该点  
						{
							cd.push(p[m]);
							flag.ptr<uchar>(x)[y] = 1;
							subMat.ptr<uchar>(x)[y] = 255;
						}
					}
				}
				segMat.push_back(subMat);
			}
		}
	}
}

QString  cvrun(cv::Mat src, int model)
{

	//QString str = QDir::currentPath() + QDir::separator() + "cc.png";
	//QTextCodec* code = QTextCodec::codecForName("gb18030");
	//std::string name = code->fromUnicode(str).data(); // 解决中文乱码以及imread输入为string

	//src = imread(name );
	//数据判空
	if (!src.data)
	{
		
		qDebug() << "error";
		return 0;
	}
	cvtColor(src, src, COLOR_BGR2GRAY);   //灰度显示
	threshold(src, src, 160, 255, THRESH_BINARY);  //二值化

	//imshow("原始", src);
	腐蚀
	//erode(src, src, NULL );
	//
	//imshow("腐蚀_cv", src);
	膨胀
	//Mat element = getStructuringElement(MORPH_RECT, Size(3, 3));
	//dilate(src, src, element, Point(-1, -1),
	//	 2,
	//	 BORDER_CONSTANT); //iterations=6,6是表示膨胀的次数,表示操作进行了6次膨胀

	//imshow("膨胀_my", src);
	//waitKey(0);
	if (model==0)
	{
		bitwise_not(src, src);//颜色反转
	}

	//std::vector<cv::Mat> segMat;
	//	getConnectedDomain(src, segMat);

	std::vector<std::vector<Point>>  contours;
	Mat hierarchy;

	//切割原始图像
	findContours(src, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE);
	//drawContours(src, contours, 2, Scalar(255, 0, 255), 1);
	//imshow("  g",src);
	waitKey(0);
	//std::cout << src <<"\n";
	//对切出来的数据排序
	SortContourPoint(contours);
	std::vector<std::vector<Point>>::const_iterator iter = contours.begin();
	std::vector<Mat> vecMat;
	int i = 0;
	while (iter != contours.end())
	{
		//获取rect值
		Rect rc = boundingRect(*iter);
		//获取原始图像该位置的数据
		Mat leftImg = src(rc).clone();
		//std::cout << leftImg<<"\n";
		vecMat.push_back(leftImg);
		//char nameLeft[10];
		//sprintf(nameLeft, "%dLeft", i);
		//char nameRight[10];
		//sprintf(nameRight, "%dRight", i);
		//i++;
		imshow(nameLeft, leftImg);
		std::cout << leftImg;
		//std::stringstream ss;
		//ss << nameLeft;
		//imwrite("D:\\" + ss.str() + ".png", leftImg);
		iter++;
	}
	QString strnum = "";
	for (auto it: vecMat )
	{
		//对比数字数据
		int number = getSubtract(it, 11);
		if (number == 10)
		{
			strnum += ".";
		}
		else
		{
			strnum += QString::number(number);

		}

		
	}

	return strnum;
   // imshow("origin", src);
	//std::cout<<src;


	//Mat leftImg, rightImg;
	//int res = cutLeft(src, leftImg, rightImg);
	int i = 0;

	//QString strnum = "";
	//while (res == 0)
	//{
	//	char nameLeft[10];
	//	sprintf(nameLeft, "%dLeft", i);
	//	char nameRight[10];
	//	sprintf(nameRight, "%dRight", i);
	//	i++;
	//	//imshow(nameLeft, leftImg);
	//	std::cout << leftImg;
	//	std::stringstream ss;
	//	ss << nameLeft;
	//	imwrite("D:\\" + ss.str() + ".png", leftImg);
	//	Mat srcTmp = rightImg;

	//	//        QString str = "D:\\1Left.png";
	//	//        QTextCodec *code = QTextCodec::codecForName("gb18030");
	//	//        std::string name = code->fromUnicode(str).data(); // 解决中文乱码以及imread输入为string
	//	//        Mat Template = imread(name, COLOR_BGR2GRAY);
	//	//       // resize(Template, Template, Size(Template.cols, Template.rows), 0, 0, THRESH_BINARY);
	//	//        std::cout<<"\n"<<Template.cols<<  "     " <<Template.rows;
	//	//        std::cout<<"\n"<<Template;
	//	//         std::cout<<"\n"<<leftImg;
	//	int number =  getSubtract(leftImg, 11);
	//	if (number == 10)
	//	{
	//		strnum += ".";
	//	}
	//	else
	//	{
	//		strnum += QString::number(number);

	//	}
	//	
	//	res = cutLeft(srcTmp, leftImg, rightImg);

	//}

	return strnum;
}

该处使用的url网络请求的数据。


总结

提示:这里对文章进行总结:
以上就是今天要讲的内容,本文仅仅简单介绍简单数字识别的CV的使用

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: OpenCV是一个开源计算机视觉库,可以用于图像处理和计算机视觉任务。数字识别计算机视觉中的一个重要应用领域,通过使用OpenCV的图像处理和模式识别功能,可以实现数字识别功能。 要实现数字识别,首先需要准备一组带有标签的数字样本图像。可以使用OpenCV提供的图像读取功能,将样本图像加载到内存中。 接下来,可以通过OpenCV提供的图像预处理功能,对图像进行预处理,例如去噪、增强对比度等,以提高数字识别的准确性。 然后,可以采用OpenCV的特征提取和模式识别功能,提取样本图像中的数字特征,并训练一个分类器来识别这些数字特征。 训练分类器可以使用OpenCV提供的机器学习库,例如支持向量机(SVM)或多层感知器(MLP)等。通过使用训练样本,可以训练一个分类器来学习如何区分不同的数字。 一旦分类器训练完成,就可以使用它来识别新的数字图像。通过将新的数字图像传入分类器,可以得到识别结果,即图像中的数字。 总结来说,通过利用OpenCV的图像处理和模式识别功能,结合合适的特征提取和分类器训练方法,就可以实现数字识别。这样就能够实现自动识别数字图像中的数字,对于许多应用场景如自动化识别、字符识别等具有重要意义。 ### 回答2: OpenCV是一个开源的计算机视觉库,提供了各种用于图像处理和计算机视觉算法的函数和工具。通过使用OpenCV,我们可以实现数字识别的功能。 在C语言中,我们可以使用OpenCV库的功能来进行数字识别的处理。首先,我们需要将原始图像加载到内存中。可以使用OpenCV的函数来读取和处理图像文件,包括数字图片。 接下来,我们需要对图像进行预处理,以提取图像中的数字信息。这包括将图像转换为灰度图像,使用阈值化将图像二值化,去除噪声等。通过这些预处理步骤,我们可以将图像中的数字与背景分离开来。 然后,我们可以使用OpenCV提供的形态学操作函数来进一步提取图像中的数字。形态学操作包括膨胀、腐蚀、开运算、闭运算等,可以帮助我们更好地提取图像中的数字形状。 最后,我们可以使用机器学习技术,如神经网络、支持向量机等,在提取的数字上训练一个分类器来实现数字识别。通过将数字与其对应的标签进行训练,我们可以将输入的图像与已知数字进行匹配,并识别出图像中的数字。 使用OpenCV进行数字识别的C语言实现需要具备一定的图像处理和编程知识。通过合理的预处理和特征提取,结合合适的机器学习算法,我们可以实现准确的数字识别功能。OpenCV提供了丰富的函数和工具,使得数字识别的实现变得更加简单和高效。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值