使用opencv库实现对数码管的识别(含代码)

1 篇文章 0 订阅
1 篇文章 0 订阅

引言

本文是笔者在参加夏令营时对负责项目的一个总结,这个项目的主要内容就是使用摄像头对着一块装有几段数码管的板子,然后识别出数码管所呈现的数字。其实这个项目整体并不难,opencv库 在这个项目中主要是对图片进行处理,获取紧贴数码管数字的一个矩形区域。之后就需要其他算法来进行处理,本文我使用了比较简单但十分有效的穿线法,也是网上很多类似问题的解决方法。后续我可能会使用其他方法来解决这个问题.

导入库并定义命名空间

#include <opencv2/opencv.hpp>
#include  <iostream>

using namespace cv;
using namespace std;

导入图片

Mat g_srcImage;
g_srcImage = imread( "p1.png", 1 );
imshow("原始图",g_srcImage);                       //显示原始图

对图片进行处理

  • opencv库 中有许多简单但十分实用的工具来处理图片,像霍夫变换,角点提取,我在这里主要使用hsv通道处理,canny边缘检测,轮廓获取等几种工具.

1.原始图有可能尺寸大小不合适,可以使用resize函数进行调整

//获取图片的高和宽,方便按比例进行尺寸调整
int w = g_srcImage.cols;
int h = g_srcImage.rows;

resize(g_srcImage,g_srcImage,Size(500,500*h/w));

2.将图片原通道转换为hsv通道,如果原图不是rgb通道,可以修改cvtColor函数中最后一个参数.

Mat hsv;
cvtColor(g_srcImage,hsv,COLOR_BGR2HSV);
//转换为hsv空间

3.最关键的一步!!

  • 下列代码可以通过筛选图片中hsv三个通道的值来将数码管处像素值变为最大,其余地方变为最小,从而将图片中数码管与其他部分区分开.
  • 为什么要使用hsv通道来筛选呢?因为hsv分别指色调,饱和度,亮度,而数码管是发光的,用饱和度和亮度来筛选数码管处的像素点效果比较好.
  • 筛选的原理是分别为hsv三个通道的值设置上下限,三个通道的值都在上限与下限值之间的变为最大,否则变为最小.
  • 上下限的选择需要自己实地进行测量.
//设置hsv的上下限  
	int iLowH = 59;    
    int iHighH =255;    
    int iLowS = 159;     
    int iHighS = 255;    
    int iLowV = 190;    
    int iHighV = 255;

//分离通道
vector <Mat> hsvsplit;
split(inputt,hsvsplit);

//筛选
inRange(inputt,Scalar(iLowH,iLowS,iLowV),Scalar(iHighH,iHighS,iHighV),hsv);
  • 上述代码可以实现的效果如下:

原始图 效果图

4.对图片进行膨胀操作,边缘检测,轮廓获取,再用矩形将轮廓框起来.

	Mat threshold_output;
	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	
	threshold( hsv, threshold_output, 180, 255, THRESH_BINARY );
	
	Mat Imageg = threshold_output.clone();
	
	//imshow( "边缘图", threshold_output );
	
	findContours( threshold_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0) );
	
	
	vector<vector<Point> > contours_poly( contours.size() );
	vector<Rect> boundRect( contours.size() );
	
	for( unsigned int i = 0; i < contours.size(); i++ )
	{ 
		approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true );
		boundRect[i] = boundingRect( Mat(contours_poly[i]) );
	}
	Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );
	for( int unsigned i = 0; i<contours.size( ); i++ )
	{
		Scalar color = Scalar( 125,125,90 );
		drawContours( drawing, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
		rectangle( threshold_output, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0 );
	}
  • 前四步都是对图片的处理,整体代码如下:
#include <opencv2/opencv.hpp>
#include  <iostream>

using namespace cv;
using namespace std;

int main()
{
    Mat g_srcImage;
    g_srcImage = imread( "num.jpeg", 1 );

    //获取图片的高和宽,方便按比例进行尺寸调整
    int w = g_srcImage.cols;
    int h = g_srcImage.rows;
    resize(g_srcImage,g_srcImage,Size(250,250*h/w));

    imshow("原始图",g_srcImage);                       //显示原始图
    
    Mat hsv;
    cvtColor(g_srcImage,hsv,COLOR_BGR2HSV);
    //转换为hsv空间

    //设置hsv的上下限  
    int iLowH = 59;    
    int iHighH =255;    
    int iLowS = 159;     
    int iHighS = 255;    
    int iLowV = 190;    
    int iHighV = 255;

    //分离通道
    vector <Mat> hsvsplit;
    split(g_srcImage,hsvsplit);

    //筛选
    inRange(g_srcImage,Scalar(iLowH,iLowS,iLowV),Scalar(iHighH,iHighS,iHighV),hsv);
    //imshow("hsv效果图",hsv);

    //形态学变换
    Mat element=getStructuringElement(MORPH_RECT,Size(5,5));//获取核
    dilate(hsv,hsv,element); //膨胀
    //imshow("膨胀效果图",hsv);

    Mat threshold_output;
	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	
	threshold( hsv, threshold_output, 180, 255, THRESH_BINARY );
	
	Mat Imageg = threshold_output.clone();
	
	imshow( "边缘图", threshold_output );
	
	
	
	findContours( threshold_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0) );
	
	
	vector<vector<Point> > contours_poly( contours.size() );
	vector<Rect> boundRect( contours.size() );
	
	for( unsigned int i = 0; i < contours.size(); i++ )
	{ 
		approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true );
		boundRect[i] = boundingRect( Mat(contours_poly[i]) );
	}
	Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );
	for( int unsigned i = 0; i<contours.size( ); i++ )
	{
		Scalar color = Scalar( 125,125,90 );
		drawContours( drawing, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
		rectangle( threshold_output, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0 );
	}

    imshow("效果图",threshold_output);
    waitKey(0);

}
  • 效果如下:
    原始图 在这里插入图片描述
    5.接下来就是要对矩形进行遍历,用穿线法判断是否为数字,是哪个数字.(穿线法的原理这里先不讲,以后补充)

最终代码:

#include <opencv2/opencv.hpp>
#include  <iostream>

using namespace cv;
using namespace std;

bool cross(Mat image,int x1,int y1,int x2,int y2);
int getnum(Mat image);
int getgrey(Mat image,int x,int y);

int main()
{
    Mat g_srcImage;
    g_srcImage = imread( "num.jpeg", 1 );

    //获取图片的高和宽,方便按比例进行尺寸调整
    int w = g_srcImage.cols;
    int h = g_srcImage.rows;
    resize(g_srcImage,g_srcImage,Size(250,250*h/w));

    imshow("原始图",g_srcImage);                       //显示原始图
    
    Mat hsv;
    cvtColor(g_srcImage,hsv,COLOR_BGR2HSV);
    //转换为hsv空间

    //设置hsv的上下限  
    int iLowH = 59;    
    int iHighH =255;    
    int iLowS = 159;     
    int iHighS = 255;    
    int iLowV = 190;    
    int iHighV = 255;

    //分离通道
    vector <Mat> hsvsplit;
    split(g_srcImage,hsvsplit);

    //筛选
    inRange(g_srcImage,Scalar(iLowH,iLowS,iLowV),Scalar(iHighH,iHighS,iHighV),hsv);
    //imshow("hsv效果图",hsv);

    //形态学变换
    Mat element=getStructuringElement(MORPH_RECT,Size(5,5));//获取核
    dilate(hsv,hsv,element); //膨胀
    //imshow("膨胀效果图",hsv);

    Mat threshold_output;
	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	
	threshold( hsv, threshold_output, 180, 255, THRESH_BINARY );
	
	Mat Imageg = threshold_output.clone();
	
	imshow( "边缘图", threshold_output );
	
	
	
	findContours( threshold_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0) );
	
	
	vector<vector<Point> > contours_poly( contours.size() );
	vector<Rect> boundRect( contours.size() );
	
	for( unsigned int i = 0; i < contours.size(); i++ )
	{ 
		approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true );
		boundRect[i] = boundingRect( Mat(contours_poly[i]) );
	}
	Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );
	for( int unsigned i = 0; i<contours.size( ); i++ )
	{
		Scalar color = Scalar( 125,125,90 );
		drawContours( drawing, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
		rectangle( threshold_output, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0 );
	}

    imshow("效果图",threshold_output);


Rect roirect;
for (int unsigned i = 0;i<boundRect.size();i++)
{
	roirect = boundRect[i];
	Mat final;
    final = Imageg(boundRect[i]);
		if ((double(boundRect[i].height)/double(boundRect[i].width))>3.5&& (double(boundRect[i].height)/double(boundRect[i].width))<5)
		{
			roirect = boundRect[i];
			Mat final;
			final = Imageg(roirect);
			int num;
			num = 1;

		}
		else if ((double(boundRect[i].height)/double(boundRect[i].width))>1.35&& (double(boundRect[i].height)/double(boundRect[i].width))<2.4)
		{
			roirect = boundRect[i];
			Mat final;
			final = Imageg(roirect);
			int num;
            num = getnum(final);
            
            if (num != -1)
            {
                cout <<"get the num: "<<num<<endl;
            }

			
		}
	
	
}

    waitKey(0);

}
bool cross(Mat image,int x1,int y1,int x2,int y2)

{

    if (x1 == x2)
    {
        for (int y = y1;y<y2;y++)
        {
            if(getgrey(image,x1,y)>200)return true;
        }
        return false;
    }
    else
    {
        for (int x = x1;x<x2;x++)
        {
            if(getgrey(image,x,y1)>200)return true;
        }
        return false;
        
    }

}




int getnum(Mat image)
{


 int h = image.rows;
int w = image.cols;


int result;

bool a1,a2,a3,a4,a5,a6,a7;



if(cross(image,int(0.5*w),0,int(0.5*w),int(0.25*h)))a1 = true;
else a1=false;
if(cross(image,0,int(0.25*h),int(0.5*w),int(0.25*h)))a2 = true;
else a2=false;
if(cross(image,int(0.5*w),int(0.25*h),int(w),int(0.25*h)))a3 = true;
else a3=false;

if(cross(image,int(0.5*w),int(0.25*h),int(0.5*w),int(0.75*h)))a4 = true;
else a4=false;
if(cross(image,0,int(0.75*h),int(0.5*w),int(0.75*h)))a5 = true;
else a5=false;
if(cross(image,int(0.5*w),int(0.75*h),int(w),int(0.75*h)))a6 = true;
else a6=false;
if(cross(image,int(0.5*w),int(0.75*h),int(0.5*w),int(h)))a7 = true;
else a7=false;

if(a1 !=0&&a2 ==0&&a3 !=0&&a4 !=0&&a5 !=0&&a6 ==0&&a7 !=0&&(getgrey(image,w*0.5,h*0.25) <= 150)&&(getgrey(image,w*0.5,h*0.75) <= 150)) result = 2;
else if(a1 !=0&&a2 ==0&&a3 !=0&&a4 !=0&&a5 ==0&&a6 !=0&&a7 !=0&&(getgrey(image,w*0.5,h*0.25) <= 150)&&(getgrey(image,w*0.5,h*0.75) <= 150)) result = 3;
else if(a1 ==0&&a2 !=0&&a3 !=0&&a4 !=0&&a5 ==0&&a6 !=0&&a7 ==0&&(getgrey(image,w*0.5,h*0.25) <= 150)&&(getgrey(image,w*0.5,h*0.75) <= 150)) result = 4;
else if(a1 !=0&&a2 !=0&&a3 ==0&&a4 !=0&&a5 ==0&&a6 !=0&&a7 !=0&&(getgrey(image,w*0.5,h*0.25) <= 150)&&(getgrey(image,w*0.5,h*0.75) <= 150)) result = 5;
else if(a1 !=0&&a2 !=0&&a3 ==0&&a4 !=0&&a5 !=0&&a6 !=0&&a7 !=0&&(getgrey(image,w*0.5,h*0.25) <= 150)&&(getgrey(image,w*0.5,h*0.75) <= 150)) result = 6;
else if(a1 !=0&&a2 ==0&&a3 !=0&&a4 ==0&&a5 ==0&&a6 !=0&&a7 ==0&&(getgrey(image,w*0.5,h*0.25) <= 150)&&(getgrey(image,w*0.5,h*0.75) <= 150)) result = 7;
else if(a1 !=0&&a2 ==0&&a3 !=0&&a4 ==0&&a5 ==0&&a6 !=0&&a7 !=0&&(getgrey(image,w*0.5,h*0.25) <= 150)&&(getgrey(image,w*0.5,h*0.75) <= 150)) result = 7;
else if(a1 !=0&&a2 !=0&&a3 !=0&&a4 !=0&&a5 !=0&&a6 !=0&&a7 !=0&&(getgrey(image,w*0.5,h*0.25) <= 150)&&(getgrey(image,w*0.5,h*0.75) <= 150)) result = 8;
else if(a1 !=0&&a2 !=0&&a3 !=0&&a4 !=0&&a5 ==0&&a6 !=0&&a7 !=0&&(getgrey(image,w*0.5,h*0.25) <= 150)&&(getgrey(image,w*0.5,h*0.75) <= 150)) result = 9;
else if(a1 !=0&&a2 !=0&&a3 !=0&&a4 ==0&&a5 !=0&&a6 !=0&&a7 !=0&&(getgrey(image,w*0.5,h*0.25) <= 150)&&(getgrey(image,w*0.5,h*0.75) <= 150)) result = 0;
else 
{
	result = -1;
}

//cout <<"result"<<result<<endl;


//cout<<"result  "<<result<<endl;
return result;
 

//int channels = image.channels();

//waitKey(0);

}

int getgrey(Mat image,int x,int y)
{

int w = image.cols; 
     
Mat_<uchar>::iterator it = image.begin<uchar>();
    
Mat_<uchar>::iterator itend = image.end<uchar>();    
int pixel = *(it + y * w + x);
return pixel; 
}

结果:

get the num: 8
get the num: 8
get the num: 5
get the num: 8
get the num: 6
get the num: 8
get the num: 9
get the num: 0
实现单个数码管数字识别,可以采用以下步骤: 1. 读取图像并进行预处理:使用OpenCV读取数码管图像,并进行预处理操作,如灰度化、二值化等操作。 2. 分割数字区域:通过数码管的形状和布局,可以将数字区域分割出来,得到每个数字的图像。 3. 特征提取:对于每个数字图像,提取特征,如轮廓、边缘等。 4. 训练模型:使用机器学习算法或深度学习算法,对提取的数字特征进行训练,得到一个数字识别模型。 5. 预测数字:将每个数字图像输入到训练好的模型中,预测出数字的值。 以下是一个基于OpenCV和机器学习的单个数码管数字识别的示例代码: ```python import cv2 import numpy as np from sklearn.externals import joblib # 读取图像并进行预处理 img = cv2.imread('digit.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV) # 分割数字区域 contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) digit_rects = [] for contour in contours: x, y, w, h = cv2.boundingRect(contour) if w > 10 and h > 10: digit_rects.append((x, y, w, h)) # 特征提取 svm = joblib.load('svm.pkl') for digit_rect in digit_rects: x, y, w, h = digit_rect digit_roi = gray[y:y+h, x:x+w] digit_roi = cv2.resize(digit_roi, (20, 20)) _, digit_roi = cv2.threshold(digit_roi, 127, 255, cv2.THRESH_BINARY) digit_roi = digit_roi.astype(np.float32) / 255.0 digit_features = digit_roi.reshape((1, -1)) # 预测数字 digit = svm.predict(digit_features)[0] cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2) cv2.putText(img, str(digit), (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) cv2.imshow('image', img) cv2.waitKey(0) cv2.destroyAllWindows() ``` 其中,使用SVM作为机器学习算法进行数字识别,需要训练好的模型文件`sam.pkl`。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值