基于Opencv穿针引线识别晶体管数字

该博客介绍了一种数字图像处理方法,通过OpenCV库进行图像预处理,包括灰度化、模糊、阈值分割、边缘检测等步骤,用于识别数字。文章详细展示了代码流程,对图像进行轮廓检测和矩形框选,最终提取并识别分割出的数字图像。识别过程中,根据数字的几何特征判断其形状,实现简单的数字识别算法。
摘要由CSDN通过智能技术生成

接上一篇文章,开头我就不做详细介绍了,直接先贴代码,然后在最后有一段文字解释代码的思想
在这里插入图片描述

#include <opencv2/core/core.hpp>
#include <opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <ctime>
using namespace std ;
using namespace cv;
#include <map>
//定义全局变量
Mat src_threshold;
Mat src_dil;
vector<Mat>ROI_image;//待测图片
//定义全局函数
void myDiscern(Mat image);
int main() 
{
	clock_t start ,finish;
	start=clock();
	Mat src;
	src=imread("D:\\vspic\\picture\\number6.jpg");
	resize(src,src,Size(src.cols/7,src.rows/7));
	imshow("src",src);
	Mat src_gray;
	cvtColor(src,src_gray,COLOR_BGR2GRAY);
	//imshow("gsrc_ray",src_gray);
	Mat src_blur;
	blur(src_gray,src_blur,Size(9,9));
	//GaussianBlur(src_gray,src_blur,Size(11,11),1,1);
	Mat src_threshold;
	threshold(src_blur,src_threshold,150,255,THRESH_OTSU);
	//imshow("src_threshold",src_threshold);
	Mat src_canny;
	Canny(src_threshold,src_canny,125,255,3);
	//imshow("src_canny",src_canny);
	vector<vector<Point>>contours_src;
	vector<Vec4i>hierarchy_src(contours_src.size());
	findContours(src_canny,contours_src,hierarchy_src,RETR_EXTERNAL,CHAIN_APPROX_NONE);
	Rect rect_s;
	Rect choose_rect;
	for (size_t i=0;i<contours_src.size();i++)
	{
		rect_s=boundingRect(contours_src[i]);
		double width=rect_s.width;
		double height= rect_s.height;
		double bizhi=width/height;
		if (bizhi>1.5&&height>50)
		{
			/*rectangle(src,rect_s.tl(),rect_s.br(),Scalar(255,255,255),1,1,0);*/
			choose_rect=Rect(rect_s.x+20,rect_s.y+30,rect_s.x-30,rect_s.y-108);
		}
	}
	Mat roi;
	roi=src(choose_rect);
	//imshow("src_",roi);
	Mat img =roi;
	Mat gray_img;
	// 生成灰度图像
	cvtColor(img, gray_img, CV_BGR2GRAY);
	// 高斯模糊
	Mat img_gau;
	GaussianBlur(gray_img, img_gau, Size(5, 5), 0, 0);
	// 阈值分割
	Mat img_seg;
	threshold(img_gau, img_seg, 0, 255, THRESH_BINARY + THRESH_OTSU);
	Mat element;
	element=getStructuringElement(MORPH_RECT,Size(6,6));
	erode(img_seg,src_dil,element);
	imshow("src_dil",src_dil);
	// 边缘检测,提取轮廓
	Mat img_canny;
	Canny(src_dil, img_canny, 200, 100);
	//imshow("canny",img_canny);
	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy(contours.size());
	findContours(img_canny, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE, Point());//寻找轮廓
	int size = (int)(contours.size());//轮廓的数量
	//cout<<size<<endl;6个
	// 保存符号边框的序号
	vector<int> num_order;//定义一个整型int容器
	map<int, int> num_map;//容器,需要关键字和模板对象两个模板参数,此处定义一个int作为索引,并拥有相关连的指向int的指针
	for (int i = 0; i < size; i++)
   {
		// 获取边框数据
		Rect number_rect = boundingRect(contours[i]);
		int width = number_rect.width;//获取矩形的宽
		int height = number_rect.height;//获取矩形的高
		// 去除较小的干扰边框,筛选出合适的区域
		if (width > img.cols/20 )
		{
			rectangle(img,number_rect.tl(),number_rect.br(),Scalar(255,255,255),1,1,0);//绘制矩形
			imshow("img",img);//显示矩形框
			num_order.push_back(number_rect.x);//把矩形的x坐标放入number_order容器中,将一个新的元素添加到vector的最后面,
			//位置为当前元素的下一个元素
			num_map[number_rect.x] = i;//向map中存入键值对,number_rect.x是关键字,i是值
			/*把矩形框的x坐标与对应的i值一起放入map容器中,形成一一对应的键值对
			*/
		}
	}
	// 按符号顺序提取
	sort(num_order.begin(), num_order.end());/*把number_order容器中的内容按照从小到大的顺序排列,这里面是X的坐标*/
	for (int i = 0; i < num_order.size(); i++) {
		Rect number_rect = boundingRect(contours[num_map.find(num_order[i])->second]);//num_order里面放的是坐标
		//cout<<"num_map的值是:"<<num_map.find(num_order[i])->second<<endl;
		Rect choose_rect(number_rect.x, 0, number_rect.width, img.rows);//矩形左上角x,y的坐标以及矩形的宽和高
		Mat number_img = src_dil(choose_rect);
		cout<<"输出图片的大小"<<number_img.size()<<endl;
		ROI_image.push_back(number_img);//保存为待测图片
		//imshow("number" + to_string(i), number_img);
		char name[50];
		sprintf_s(name,"D:\\vs2012\\model\\%d.jpg",i);//保存分割完毕待识别的图片
		imwrite(name, number_img);	
	}
	cout<<"图片分割完毕"<<endl;
	for (int i=0;i<=4;i++)
	{
		char name[50];
		sprintf_s(name,"D:\\vs2012\\model\\%d.jpg",i);//这里是读取文件夹中的图片
		Mat temp;
		temp=imread(name);//彩色图片
		cvtColor(temp,temp,CV_BGR2GRAY);
		Mat temp_threshold;
		threshold(temp,temp_threshold,150,255,THRESH_BINARY);
		//cout<<temp.channels()<<endl;
		myDiscern(temp);
		//myThreshold(temp);
	}
	finish=clock();
	double all_time=double(finish-start)/CLOCKS_PER_SEC;
	/*cout<<"运行总时间是:"<<all_time<<endl;*/
	waitKey(0);
	return 0;
}
//数字识别
void myDiscern(Mat image)
{
	double weight=image.cols;//cols是宽,cols是列
	double height=image.rows;//rows是高,rows是行
	/*这里为啥要先找1呢,因为1和其他分割好的图片尺寸不一样,
	比较窄,根据这个特征可以直接判断他就是1,如果采用下边代码中的方式
	有可能识别出8*/
	if (height>3*weight)
	{
		cout<<"1";
		return;
	}
	//竖线
	int x_half=image.cols/2;//定义图片的竖直轴线
	//将图片分成三段式上中下
	//上横线
	int y_one_third=image.rows/3;//定义第一段距离
	//下横线
	int y_two_third=image.rows*2/3;//定义第二段距离
	int a=0,b=0,c=0,d=0,e=0,g=0,f=0;
	//竖线识别agd段
	for (int i=0;i<image.rows;i++)//遍历行
	{
		uchar *data=image.ptr<uchar>(i);//获取第i行的首地址(指针)
		if (i<y_one_third)//判断第i行是否在第一段范围内,
		{
			if (data[x_half]==0)//判断轴线上的一点像素值是否是0
			{
				a=1;
			}
		}
		else if (i>y_one_third&&i<y_two_third)
		{
			if (data[x_half]==0)
			{
				g=1;
			}
		}
		else
		{
			if (data[x_half]==255)
			{
				d=1;
			}
		}
	}
	//上横线识别
	for (int j=0;j<image.cols;j++)//遍历列
	{
		uchar *data=image.ptr<uchar>(y_one_third);//获取第y_one_third行的首地址(指针)
		//f识别
		if (j<x_half)//判断第j列是否小于中间轴线,如果小于就是代表在左半部分
		{
			if (data[j]==0)//再次进行判断,判断第j列的像素是否为0
			{
				f=1;
			}
		}
		//b识别
		else
		{
			if (data[j]==0)
			{
				b=1;
			}
		}
	}
	//下横线识别
	for (int z=0;z<image.cols;z++)
	{
		uchar *data=image.ptr<uchar>(y_two_third);
		//e识别
		if (z<x_half)
		{
			if (data[z]==0)
			{
				e=1;
			}
		}
		//c识别
		else
		{
			if (data[z]==0)
			{
				c=1;
			}
		}
	}
	if (a==1&&b==1&&c==1&&d==1&&e==1&&f==1&&g==0)
	{
		cout<<"0";
	}
	else if (a==0&&b==1&&c==1&&d==0&&e==0&&f==0&&g==0)
	{
		cout<<"1";
	}
	else if (a==1&&b==1&&c==0&&d==1&&e==1&&f==0&&g==1)
	{
		cout<<"2";
	}
	else if (a==1&&b==1&&c==1&&d==1&&e==0&&f==0&&g==1)
	{
		cout<<"3";
	}
	else if (a==0&&b==1&&c==1&&d==0&&e==0&&f==1&&g==1)
	{
		cout<<"4";
	}
	else if (a==1&&b==0&&c==1&&d==1&&e==0&&f==1&&g==1)
	{
		cout<<"5";
	}
	else if (a==1&&b==0&&c==1&&d==1&&e==1&&f==1&&g==1)
	{
		cout<<"6";
	}
	else if (a==1&&b==1&&c==1&&d==0&&e==0&&f==0&&g==0)
	{
		cout<<"7";
	}
	else if (a==1&&b==1&&c==1&&d==1&&e==1&&f==1&&g==1)
	{
		cout<<"8";
	}
	else if (a==1&&b==1&&c==1&&d==1&&e==0&&f==1&&g==1)
	{
		cout<<"9";
	}
}

整体思想:根据没个数字特征,来判断哪段晶体管是0或者255,比如:9是e段为0,其他段为255,根据这个特征可以判断出他就是9,
首先把图片先分为上中下三段识别,找出agd,然后再根据中间轴线找出左右,这样一次就可以判断,
这里要提出的一点是我一开始是想用的image.at(x,y)来遍历素有的像素,但是网上有大佬用指针来遍历像素,我也就用了指针,这样的遍历速度是最快的,节约时间。
指针遍历像素

	for (int i=0;i<image.rows;i++)//遍历行
	{
		uchar *data=image.ptr<uchar>(i);//获取第i行的首地址(指针)
		if (i<y_one_third)//判断第i行是否在第一段范围内,
		{
			if (data[x_half]==0)//判断轴线上的一点像素值是否是0
			{
				a=1;
			}
		}
		else if (i>y_one_third&&i<y_two_third)
		{
			if (data[x_half]==0)
			{
				g=1;
			}
		}

由于我也是处于学习过程,有很多欠缺的地方,希望读者多提提建议,我们共同进步,欢迎交流

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值