STM32F103C8T6单片机通过OV7670摄像头和1.8寸TFT屏幕实现简单的数字识别
创始人
2024-11-09 20:43:12
0

一、整体思路

        首先单片机获取摄像头的图片并存储,对图片中的像素做二值化处理,然后和数字的点阵数组作比较,找到偏差最小的一个数字作为识别结果。这个想法是我自然而然想出来的,并没有深刻思考效率和正确率,所以它的误差比较大,主要和图片中数字的位置和字形相关。

二、识别过程

1.获取图片并存储

        STM32F103C8T6单片机运行时的数据内存是20KB,而一帧分辨率为128*160的图片大小为40KB,所以不能直接完整地存储下来,我的解决办法是只存储32*64大小的部分图片,也可以认为这一小部分是识别区域。准备白纸写下待识别的数字

        差不多这样就可以,不过那个数字1还是写小了,就是需要描粗一点,不然二值化处理的时候就成全白了,然后拍摄图片,将纸上的数字放到识别区域内(红线框起来的,也就是数字7)

        关于单片机连接摄像头和显示屏的方法我已经发过了,这个识别区域也只是在正常显示的基础上加了几条线,所以下面写一下图片显示函数的代码

void ov7670() { 	    while(OV7670_VS()==0){}; 		OV7670_W_WRST(0);         OV7670_W_WRST(1);         OV7670_W_WEN(1); 		Delay_us(40000); 		while(OV7670_VS()==0); 		OV7670_W_WRST(0);         OV7670_W_WRST(1);         OV7670_W_WEN(0); 				 		Delay_us(50000);	 				 		OV7670_W_OE(0);		 		OV7670_W_RRST(0); 		 		OV7670_W_RCLK(0); 		Delay_us(20000); 		OV7670_W_RCLK(1); 		Delay_us(20000); 		OV7670_W_RCLK(0); 		Delay_us(20000);          OV7670_W_RRST(1); 		Delay_us(20000); 		OV7670_W_RCLK(1); 		  		set_windows(0,0,lcddev.width-1,lcddev.height-1); 		for(h=0;h<160;h++)//128 		{ 			for(w=0;w<128;w++) 			{ 				OV7670_W_RCLK(0); 				value1 = (uint8_t)GPIOA->IDR & 0xff; 				OV7670_W_RCLK(1); 				OV7670_W_RCLK(0); 				value2 = (uint8_t)GPIOA->IDR & 0xff; 				OV7670_W_RCLK(1); 				color = (value1 << 8) | value2; 				TFT_WRITE_u16_DATA(color); 				if((w

2.处理图片

        对存储的部分图片进行二值化处理,让图片变成黑白两色,便于接下来的识别。二值化的依据是像素点的灰度值,所以先计算出每个像素点的灰度值。对于RGB565格式的像素,采用式子((R>>8)*77+(G>>3)*150+(B<<3)*29+128)/256可以大概算出灰度值,代码如下

uint8_t togrey(uint16_t color)//适用于RGB565格式的像素值 { 	uint8_t grey; 	grey = (uint8_t)((((color&0xf800)>>8)*77+((color&0x07e0)>>3)*150+((color&0x001f)<<3)*29+128)/256); 	return grey; }

        之后根据灰度值进行二值化处理,将灰度值大于100的像素点改成白色,其余改成黑色。在数字的点阵数组中,一个像素点用一个二进制位表示,0代表白色1代表黑色,相邻八个像素点组成一个字节,所以也把存储图片的像素点用这种方式表示出来,即白色的像素点用0表示,黑色的像素点用1表示,并把相邻八个像素点组合,代码如下

void dis()//二值化处理 { 	uint8_t w,h,n=0; 	set_windows(0,0,weight-1,hight-1); 	for(h=0; h100) 			{ 				TFT_WRITE_u16_DATA(0xffff); 				 			} 			else  			{ 				TFT_WRITE_u16_DATA(0x0000); 				n |= 0x01; 			} 			if(w%8==7) 			{ 				number[h*4+w/8] = n;//运行这一句之后得到可以和点阵数组作比较的数组 				n=0; 			}  		} 	} }

        如果把二值化处理之后的数组再次显示出来,就是下图红线框里的样子,中间是识别结果。

3.识别数字

        我认为,识别是显示的逆过程,一个数字显示在屏幕上,是将一些固定的点显示成黑色或白色,反过来,只要找到这些位置相似的点,就可以认为它是这个数字。在TFT屏幕上一个分辨率为32*64大小的数字0,其点阵数组如下

{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xE0,0x00, 0x00,0x1F,0xF8,0x00,0x00,0x3C,0x1E,0x00,0x00,0x70,0x0F,0x00,0x00,0xE0,0x07,0x00, 0x01,0xE0,0x03,0x80,0x03,0xC0,0x03,0xC0,0x03,0xC0,0x01,0xC0,0x07,0x80,0x01,0xE0, 0x07,0x80,0x00,0xE0,0x07,0x00,0x00,0xE0,0x0F,0x00,0x00,0xF0,0x0F,0x00,0x00,0xF0, 0x0F,0x00,0x00,0xF0,0x0F,0x00,0x00,0x70,0x1E,0x00,0x00,0x78,0x1E,0x00,0x00,0x78, 0x1E,0x00,0x00,0x78,0x1E,0x00,0x00,0x78,0x1E,0x00,0x00,0x78,0x1E,0x00,0x00,0x78, 0x1E,0x00,0x00,0x78,0x1E,0x00,0x00,0x78,0x1E,0x00,0x00,0x78,0x1E,0x00,0x00,0x78, 0x1E,0x00,0x00,0x78,0x1E,0x00,0x00,0x78,0x1E,0x00,0x00,0x78,0x1E,0x00,0x00,0x78, 0x0F,0x00,0x00,0x70,0x0F,0x00,0x00,0xF0,0x0F,0x00,0x00,0xF0,0x0F,0x00,0x00,0xF0, 0x07,0x00,0x00,0xE0,0x07,0x80,0x01,0xE0,0x07,0x80,0x01,0xE0,0x03,0xC0,0x01,0xC0, 0x03,0xC0,0x03,0xC0,0x01,0xE0,0x03,0x80,0x00,0xE0,0x07,0x00,0x00,0x70,0x0F,0x00, 0x00,0x3C,0x1E,0x00,0x00,0x1F,0xF8,0x00,0x00,0x07,0xE0,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}//数字0

        这个点阵数组和上面二值化处理后得到的数组大小一样,所以可以使其两两比较,选出其中差别最小的,认为它们是同一个数字。我的比较方法是,将同位置像素点的差值的绝对值加在一起,然后选出十个值当中最小的,代码如下

uint8_t compare() { 	uint8_t RGB=0; 	int rgb[10]={0}; 	uint16_t i=0,j=0; 	for(j=0; j<10; j++) 	{ 		for(i=0; i<256; i++) 		{ 			if(number[i] >= num[j][i])//取绝对值 			{ 				rgb[j] += number[i] - num[j][i]; 			} 			else rgb[j] += num[j][i] - number[i] ; 		} 	} 	for(j=1; j<10; j++)//选出值最小的 	{ 		if(rgb[RGB] > rgb[j]) 			RGB = j; 	} 	return RGB; }

 4.结果

        图片的显示和处理应该明确地区分开,我选用了一个按键,每按一次就在显示和处理当中切换一次,按键在下图红色椭圆圈起来的位置。

        在识别区域内的数字清晰之后,按下按键开始识别,识别结果就会显示在屏幕中间的位置,如下

        可见,识别的结果对于数字的字形和摆放位置关联较大。这个数字识别是关于摄像头和单片机的简单应用,没有用到更深刻更高效的算法,所以它的误差是显而易见的,写这三篇文章是想记录一下学习过程,现在F103C8T6接OV7670的资料在网上是比较少的,我在调试摄像头时花费了很长的时间,经历最多的无非就是失败了,我已经习惯了它的花屏、没反应等情况,虽然它桀骜不驯,但是好在它没有坏,从头至尾调试几百次,只为一句莫问收获,但问耕耘,以此和看到这篇文章的同学们共勉。

相关内容

热门资讯

九分钟推荐!九游辅助辅助在哪里... 九分钟推荐!九游辅助辅助在哪里(辅助挂)果然是真的挂,扑克教程(新版有挂)-哔哩哔哩1、金币登录送、...
第2分钟神器!福州十八扑外卦,... 第2分钟神器!福州十八扑外卦,约战沙城攻略,本来真的有挂(有挂细节)-哔哩哔哩1、福州十八扑外卦透视...
3分钟分享!白金岛十胡卡辅助(... 3分钟分享!白金岛十胡卡辅助(辅助挂)都是真的有挂,新版2025教程(有挂助手)-哔哩哔哩1、完成白...
第三分钟了解!四川家园游戏辅助... 第三分钟了解!四川家园游戏辅助软件(辅助挂)一直有挂,新2025教程(有挂讲解)-哔哩哔哩四川家园游...
2分钟攻略!方片透视辅助,约局... 2分钟攻略!方片透视辅助,约局吧辅助器下载,果然有挂(有人有挂)-哔哩哔哩1、操作简单,无需注册,只...
2分钟曝光!0759湛江吴川三... 2分钟曝光!0759湛江吴川三脚鸡辅助(辅助挂)总是真的有挂,2025新版(有挂方式)-哔哩哔哩1、...
第7分钟软件!中至上饶打炸辅助... 第7分钟软件!中至上饶打炸辅助器开挂,哈灵永久辅助,真是有挂(详细教程)-哔哩哔哩1、超多福利:超高...
第2分钟解谜!河洛杠次高科技(... 第2分钟解谜!河洛杠次高科技(辅助挂)竟然真的是有挂,科技教程(果真有挂)-哔哩哔哩1、玩家可以在河...
4分钟曝光!斗城麻将微信有没有... 4分钟曝光!斗城麻将微信有没有挂(辅助挂)好像有挂,高科技教程(有挂细节)-哔哩哔哩;1、进入游戏-...
9分钟技巧!多乐找刺激窍门,顺... 9分钟技巧!多乐找刺激窍门,顺欣茶楼软件,竟然有挂(果真有挂)-哔哩哔哩1、完成多乐找刺激窍门的残局...