OpenCV识别数码管穿线法(基础版)


一、感想

1.图像预处理迭代

版本1:考虑获得给数字轮廓识别的图像时,是随便挑选阈值的,所以图像就很糟糕。
版本2:通过直接放大图像观察具体的RGB数值分布,就能得到一个特定的颜色通道和阈值。

2.图像形态学运算迭代

版本1:随便选择一种形态学运算,得到的效果并不是很好,要么有干扰白点,要么就是数字之间粘连,要么数码管之间的缝隙还存在
版本2:先开运算去除白点,再闭运算进行消除数码管之间的缝隙(为了整体轮廓识别)

3.图像轮廓识别迭代

版本1:

利用九个数字轮廓识别是从下层一层层往上的特性,识别中间三个

vector<vector<Point> >::iterator lt;
for(lt=g_contours.begin()+3;lt<g_contours.end()-3;lt++)

版本2:

发现如果a、f、g亮的这样的数码管之间没有连接的干扰数字会造成一个数字被认为是不同的轮廓。
从而九个数字有超过九个轮廓,g_rect就会多出3个。
同时我们的数码管板的特性是上层会出现错误的数字(即有断层的数字),中间是好的0-9(即没有断层),下层是好的数字的镜像(即没有断层)
所以轮廓的多出只会出现在上层
因而将轮廓for循环的终止条件改变一下,保证只循环3次。

int s=0;
vector<vector<Point> >::iterator lt;
for(lt=g_contours.begin()+3;s<3;lt++,s++)

版本3:

我们发现有时候识别的结果不是很好,所以就无法满足触发条件。
但我们注意到我们的小车识别时大致静止的,所以可以采用上次识别的三个数字的位置。

4.图像数字排序迭代

版本1

因为轮廓识别时,虽然是从下层往上层,每层都识别完后才移动,但同层之间的识别没有顺序。
所以我们需要对提取的三个轮廓进行排序,排序根据左上角的坐标进行排序。

版本2

容器自定义排序:前期自己的排序有时会出现指针偏移现象而失败,后期采用sort()函数,自定义排序方式,效果好还简单。

5.处理视频迭代

版本1

只是修改了g_srcImage的读取方式,从图像转变为视频。

版本2

出现了如果一开始没有对准图像就报错的现象。原因是因为没对准,所以轮廓识别的结果为0个,所以g_rect为空,所以需要设置触发条件if(contours.size()>=9)
同理,以防万一,给排序也设置一下触发条件if(g_rect.size()==3)

版本3

数字不要太过频繁地输出,同样结果的话,那输出的数字就不变

二、最终版本

在这里插入图片描述

1.滤波

(1)测试代码

https://blog.csdn.net/sandalphon4869/article/details/94725601
只要换一下图片就ok

(2)结论

选择高斯滤波更好,Size值为5.
随便过滤一下,值不用太高,不要也行。
GaussianBlur(g_srcImage,g_srcImage,Size(5,5),0.0);

2.颜色分离和二值化

(1)观察颜色

一直鼠标滑轮拉的话可以看到颜色RGB的分布,可以观察要关注的物体的颜色和其余区域的颜色。这样就能通过观察得到选择特定的颜色通道和阈值。
比如:这里数码管的绿色通道就很好,阈值200就很好区分。
在这里插入图片描述

(2)结论

我们的摄像头结果对绿色和蓝色通道的分离很好,我们选择绿色的,阈值为200.

#include<opencv2/opencv.hpp>
#include<algorithm>
#include<iostream>
using namespace std;
using namespace cv;

#define W_BLUR "Blur"
#define W_GREEN "Green"
#define W_GREENTHRESHOLD "GreenThreshold"


Mat g_srcImage;
Mat g_srcImageBlur;
Mat g_dstImageGreen;
Mat g_dstImageGreenThreshold;


int g_Blur=11;
int g_dstImageGreenThresholdValue=200;

void mySplit()
{
    vector<Mat> channels;
    split(g_srcImageBlur,channels);
    //绿色通道
    g_dstImageGreen=channels.at(1);

    namedWindow(W_GREEN,WINDOW_NORMAL);
    imshow(W_GREEN,g_dstImageGreen);
}

void myThreshold()
{
    g_dstImageGreen.copyTo(g_dstImageGreenThreshold);
    
    //阈值
    g_dstImageGreenThreshold=g_dstImageGreenThreshold>g_dstImageGreenThresholdValue;

    namedWindow(W_GREENTHRESHOLD,WINDOW_NORMAL);
    imshow(W_GREENTHRESHOLD,g_dstImageGreenThreshold);
}
int main()
{
	
	g_srcImage=imread("N.jpg");
	namedWindow("[src]",WINDOW_NORMAL);
	imshow("[src]",g_srcImage);
	
	
	g_srcImageBlur=g_srcImage.clone();
	GaussianBlur(g_srcImage,g_srcImageBlur,Size(g_Blur,g_Blur),0.0);
	namedWindow(W_BLUR,WINDOW_NORMAL);
	imshow(W_BLUR,g_srcImageBlur);
	
	//split channels
	mySplit();
	
	//threshold=200
	myThreshold();

	waitKey();

	return 0;
}

在这里插入图片描述

3.形态学运算

开运算、闭运算的核的值Size()根据相机的分辨率来调,分辨率高的图片就大些,低的图片就小些。

void myMorphology()
{
    Mat midImage;
    //开运算:处理白点
	Mat kernelOpen=getStructuringElement(MORPH_RECT,Size(g_dstImageOpenValue,g_dstImageOpenValue));
	morphologyEx(g_dstImageGreenThreshold,midImage,MORPH_OPEN,kernelOpen);

	//处理数码管之间的缝隙
	Mat kernelClose=getStructuringElement(MORPH_RECT,Size(g_dstImageCloseValue,g_dstImageCloseValue));
	morphologyEx(midImage,g_dstImageMorphology,MORPH_CLOSE,kernelClose);

    namedWindow(W_MORPHOLOGY,WINDOW_NORMAL);
	imshow(W_MORPHOLOGY,g_dstImageMorphology);   
    
}

在这里插入图片描述

4.数字轮廓识别

(1)初代代码

void myContours()
{
    vector<vector<Point> > contours;
    vector<Rect> t_rect;

    findContours(g_dstImageMorphology,contours,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);
    
    Mat dstImageContours=g_srcImage.clone();

	vector<vector<Point> >::iterator It;

    namedWindow(W_CONTOURS,WINDOW_NORMAL);
    
    for(It = contours.begin();It<contours.end();It++)
    {
        //画出可包围数字的最小矩形
        Rect rect = boundingRect(*It);
        rectangle(dstImageContours,rect,Scalar(193,0,0),10);
        t_rect.push_back(rect);
        imshow(W_CONTOURS,dstImageContours);
        if(waitKey()=='g');
    }
    

    if(t_rect.size()==3)
    {
        g_rect.assign(t_rect.begin(),t_rect.end());
    }
}

在这里插入图片描述

(2)结论

最终迭代版本

void myContours()
{
    vector<vector<Point> > contours;
    vector<Rect> t_rect;

    findContours(g_dstImageMorphology,contours,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);
    
    Mat dstImageContours=g_srcImage.clone();

	vector<vector<Point> >::iterator It;

    namedWindow(W_CONTOURS,WINDOW_NORMAL);

    if(contours.size()>=9)
    {
        int s=0;
        //只能预防最上层的数字出现断层,不能预防最下层的数字出现断层
        for(It = contours.begin()+3;s<3;It++,s++)
	    {
            //画出可包围数字的最小矩形
            Rect rect = boundingRect(*It);
            rectangle(dstImageContours,rect,Scalar::all(255),20);
            t_rect.push_back(rect);
	    }
	    imshow(W_CONTOURS,dstImageContours);
    }
	//如果不是三个轮廓,那么就不会清除,容器内还是上次的数据
    if(t_rect.size()==3)
    {
        g_rect.assign(t_rect.begin(),t_rect.end());
    }
}

在这里插入图片描述

4.数字识别和截取

因为3中的轮廓识别是随机顺序,所以我们如果想过989的确定顺序,那么就得排序。
排序根据矩形的左上角横坐标来判断先后

void myNumberSort()
{
    sort(g_rect.begin(),g_rect.end(),comp);
    

    for(int i=0;i<3;i++)
    {
        Mat ROI=g_dstImageMorphology(Rect(g_rect.at(i)));
        g_dstImageNumber[i]=ROI.clone();
    }

    namedWindow(W_NUMBER1,WINDOW_NORMAL);
    imshow(W_NUMBER1,g_dstImageNumber[0]);
    namedWindow(W_NUMBER2,WINDOW_NORMAL);
    imshow(W_NUMBER2,g_dstImageNumber[1]);
    namedWindow(W_NUMBER3,WINDOW_NORMAL);
    imshow(W_NUMBER3,g_dstImageNumber[2]);
  
}

在这里插入图片描述

5.数字识别(穿线法)

在这里插入图片描述

//识别中间行的单个数字,识别中间行只需要调用三次,传入不同的参数
void myDiscern(Mat n)
{
	//1的图像,使用穿线会是8。应该从它的尺寸入手,高远大于宽,这里我们选取3倍比.
    if(3*n.cols<n.rows)
    {
        cout<<'1';
        return;
    }
    //竖线
    int x_half=n.cols/2;
    //上横线
    int y_one_third=n.rows/3;
    //下横线
    int y_two_third=n.rows*2/3;
    //每段数码管,0灭,1亮
    int a=0,b=0,c=0,d=0,e=0,f=0,g=0;
	
	//竖线识别a,g,d段
    for(int i=0;i<n.rows;i++)
    {
        uchar *data=n.ptr<uchar>(i);
        if(i<y_one_third)
        {
            if(data[x_half]==255) a=1;
        }
        else if(i>y_one_third&&i<y_two_third)
        {
            if(data[x_half]==255) g=1;
        }
        else
        {
            if(data[x_half]==255) d=1;
        }
    }

	//上横线识别:
    for(int j=0;j<n.cols;j++)
    {
        uchar *data=n.ptr<uchar>(y_one_third);
        //f
        if(j<x_half)
        {
            if(data[j]==255) f=1;
        }
        //b
        else
        {
            if(data[j]==255) b=1;
        }
    }
	
	//下横线识别:
    for(int j=0;j<n.cols;j++)
    {
        uchar *data=n.ptr<uchar>(y_two_third);
		//e
        if(j<x_half)
        {
            if(data[j]==255) e=1;
        }
        //c
        else
        {
            if(data[j]==255) 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";
    }
    else
    {
        printf("[error_%d_%d_%d_%d_%d_%d_%d]",a,b,c,d,e,f,g);
    }

}

6.图片形式识别release

代码下载:https://download.csdn.net/download/sandalphon4869/11310232
在这里插入图片描述

#include<opencv2/opencv.hpp>
#include<algorithm>
#include<iostream>
using namespace std;
using namespace cv;

#define W_BLUR "Blur"
#define W_GREEN "Green"
#define W_GREENTHRESHOLD "GreenThreshold"
#define W_MORPHOLOGY "Morphology"
#define W_CONTOURS "Contours"
#define W_NUMBER1 "Number1"
#define W_NUMBER2 "Number2"
#define W_NUMBER3 "Number3"


Mat g_srcImage;
Mat g_srcImageBlur;
Mat g_dstImageGreen;
Mat g_dstImageGreenThreshold;
Mat g_dstImageMorphology;
Mat g_dstImageNumber[3];


int g_Blur=5;
int g_dstImageGreenThresholdValue=200;
int g_dstImageOpenValue=7;
int g_dstImageCloseValue=8;

vector<Rect> g_rect;

void mySplit()
{

    vector<Mat> channels;
    split(g_srcImageBlur,channels);
    g_dstImageGreen=channels.at(1);

    namedWindow(W_GREEN,WINDOW_NORMAL);
    imshow(W_GREEN,g_dstImageGreen);
}

void myThreshold()
{
    //deep copy
    g_dstImageGreen.copyTo(g_dstImageGreenThreshold);
    
    //Threshold
    g_dstImageGreenThreshold=g_dstImageGreenThreshold>g_dstImageGreenThresholdValue;

    namedWindow(W_GREENTHRESHOLD,WINDOW_NORMAL);
    imshow(W_GREENTHRESHOLD,g_dstImageGreenThreshold);

}

void myMorphology()
{
    Mat midImage;
	Mat kernelOpen=getStructuringElement(MORPH_RECT,Size(g_dstImageOpenValue,g_dstImageOpenValue));
	morphologyEx(g_dstImageGreenThreshold,midImage,MORPH_OPEN,kernelOpen);


	Mat kernelClose=getStructuringElement(MORPH_RECT,Size(g_dstImageCloseValue,g_dstImageCloseValue));
	morphologyEx(midImage,g_dstImageMorphology,MORPH_CLOSE,kernelClose);

    namedWindow(W_MORPHOLOGY,WINDOW_NORMAL);
	imshow(W_MORPHOLOGY,g_dstImageMorphology);   
    
}


void myContours()
{
    vector<vector<Point> > contours;
    vector<Rect> t_rect;

    findContours(g_dstImageMorphology,contours,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);
    
    Mat dstImageContours=g_srcImage.clone();

	vector<vector<Point> >::iterator It;

    namedWindow(W_CONTOURS,WINDOW_NORMAL);

    if(contours.size()>=9)
    {
        int s=0;
        //只能预防最上层的数字出现断层,不能预防最下层的数字出现断层
        for(It = contours.begin()+3;s<3;It++,s++)
	    {
            //画出可包围数字的最小矩形
            Rect rect = boundingRect(*It);
            rectangle(dstImageContours,rect,Scalar(193,0,0),10);
            t_rect.push_back(rect);
	    }
        imshow(W_CONTOURS,dstImageContours);
    }

    if(t_rect.size()==3)
    {
        g_rect.assign(t_rect.begin(),t_rect.end());
    }
}

bool comp(const Rect &a, const Rect &b){
    return a.x< b.x;
}

void myNumberSort()
{
    sort(g_rect.begin(),g_rect.end(),comp);
    

    for(int i=0;i<3;i++)
    {
        Mat ROI=g_dstImageMorphology(Rect(g_rect.at(i)));
        g_dstImageNumber[i]=ROI.clone();
    }

    namedWindow(W_NUMBER1,WINDOW_NORMAL);
    imshow(W_NUMBER1,g_dstImageNumber[0]);
    namedWindow(W_NUMBER2,WINDOW_NORMAL);
    imshow(W_NUMBER2,g_dstImageNumber[1]);
    namedWindow(W_NUMBER3,WINDOW_NORMAL);
    imshow(W_NUMBER3,g_dstImageNumber[2]);
    

}


void myDiscern(Mat n)
{
    if(3*n.cols<n.rows)
    {
        cout<<'1';
        return;
    }
    int x_half=n.cols/2;
    int y_one_third=n.rows/3;
    int y_two_third=n.rows*2/3;
    int a=0,b=0,c=0,d=0,e=0,f=0,g=0;

    for(int i=0;i<n.rows;i++)
    {
        uchar *data=n.ptr<uchar>(i);
        if(i<y_one_third)
        {
            if(data[x_half]==255) a=1;
        }
        else if(i>y_one_third&&i<y_two_third)
        {
            if(data[x_half]==255) g=1;
        }
        else
        {
            if(data[x_half]==255) d=1;
        }
    }
    for(int j=0;j<n.cols;j++)
    {
        uchar *data=n.ptr<uchar>(y_one_third);
        if(j<x_half)
        {
            if(data[j]==255) f=1;
        }
        else
        {
            if(data[j]==255) b=1;
        }
    }
    for(int j=0;j<n.cols;j++)
    {
        uchar *data=n.ptr<uchar>(y_two_third);
        if(j<x_half)
        {
            if(data[j]==255) e=1;
        }
        else
        {
            if(data[j]==255) 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";
    }
    else
    {
        printf("[error_%d_%d_%d_%d_%d_%d_%d]",a,b,c,d,e,f,g);
    }

}


int main()
{
    g_srcImage=imread("N.jpg");
    namedWindow("[src]",WINDOW_NORMAL);
    imshow("[src]",g_srcImage);
    

    g_srcImageBlur=g_srcImage.clone();
    GaussianBlur(g_srcImage,g_srcImageBlur,Size(g_Blur,g_Blur),0.0);
    // namedWindow(W_BLUR,WINDOW_NORMAL);
    // imshow(W_BLUR,g_srcImageBlur);
    
    //split channels
    mySplit();

    //threshold=160
    myThreshold();

    //clear small white and connection breakpoint
    myMorphology();

    //draw contours
    myContours();

    if(g_rect.size()==3)
    {
        //sort numbers
        myNumberSort();

        //discern number
        cout<<"Number:";
        myDiscern(g_dstImageNumber[0]);
        myDiscern(g_dstImageNumber[1]);
        myDiscern(g_dstImageNumber[2]);
        cout<<endl;
    }

    waitKey();
         
    
	return 0;
}

7.在线视频识别release

vector<Rect> g_rect;需要清零,保证每次读帧都是新的中间的三个数字。
同样的话,那输出的数字就不变

代码下载:https://download.csdn.net/download/sandalphon4869/11310232
在这里插入图片描述

#include<opencv2/opencv.hpp>
#include<algorithm>
#include<iostream>
using namespace std;
using namespace cv;

#define W_BLUR "Blur"
#define W_GREEN "Green"
#define W_GREENTHRESHOLD "GreenThreshold"
#define W_MORPHOLOGY "Morphology"
#define W_CONTOURS "Contours"
#define W_NUMBER1 "Number1"
#define W_NUMBER2 "Number2"
#define W_NUMBER3 "Number3"


Mat g_srcImage;
Mat g_srcImageBlur;
Mat g_dstImageGreen;
Mat g_dstImageGreenThreshold;
Mat g_dstImageMorphology;
Mat g_dstImageNumber[3];


int g_Blur=5;
int g_dstImageGreenThresholdValue=200;
int g_dstImageOpenValue=7;
int g_dstImageCloseValue=8;

char g_number[4]={'z','e','r'};

vector<Rect> g_rect;

void mySplit()
{

    vector<Mat> channels;
    split(g_srcImageBlur,channels);
    g_dstImageGreen=channels.at(1);

    // namedWindow(W_GREEN,WINDOW_NORMAL);
    // imshow(W_GREEN,g_dstImageGreen);
}

void myThreshold()
{
    //deep copy
    g_dstImageGreen.copyTo(g_dstImageGreenThreshold);
    
    //Threshold
    g_dstImageGreenThreshold=g_dstImageGreenThreshold>g_dstImageGreenThresholdValue;

    // namedWindow(W_GREENTHRESHOLD,WINDOW_NORMAL);
    // imshow(W_GREENTHRESHOLD,g_dstImageGreenThreshold);

}

void myMorphology()
{
    Mat midImage;
	Mat kernelOpen=getStructuringElement(MORPH_RECT,Size(g_dstImageOpenValue,g_dstImageOpenValue));
	morphologyEx(g_dstImageGreenThreshold,midImage,MORPH_OPEN,kernelOpen);


	Mat kernelClose=getStructuringElement(MORPH_RECT,Size(g_dstImageCloseValue,g_dstImageCloseValue));
	morphologyEx(midImage,g_dstImageMorphology,MORPH_CLOSE,kernelClose);

    namedWindow(W_MORPHOLOGY,WINDOW_NORMAL);
	imshow(W_MORPHOLOGY,g_dstImageMorphology);   
    
}


void myContours()
{
    vector<vector<Point> > contours;
    vector<Rect> t_rect;

    findContours(g_dstImageMorphology,contours,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);
    
    Mat dstImageContours=g_srcImage.clone();

	vector<vector<Point> >::iterator It;

    namedWindow(W_CONTOURS,WINDOW_NORMAL);

    if(contours.size()>=9)
    {
        int s=0;
        //只能预防最上层的数字出现断层,不能预防最下层的数字出现断层
        for(It = contours.begin()+3;s<3;It++,s++)
	    {
            //画出可包围数字的最小矩形
            Rect rect = boundingRect(*It);
            rectangle(dstImageContours,rect,Scalar(193,0,0),10);
            t_rect.push_back(rect);
	    }
	    imshow(W_CONTOURS,dstImageContours);
    }

    if(t_rect.size()==3)
    {
        g_rect.assign(t_rect.begin(),t_rect.end());
    }
}

bool comp(const Rect &a, const Rect &b){
    return a.x< b.x;
}

void myNumberSort()
{
    sort(g_rect.begin(),g_rect.end(),comp);
    

    for(int i=0;i<3;i++)
    {
        Mat ROI=g_dstImageMorphology(Rect(g_rect.at(i)));
        g_dstImageNumber[i]=ROI.clone();
    }

    namedWindow(W_NUMBER1,WINDOW_NORMAL);
    imshow(W_NUMBER1,g_dstImageNumber[0]);
    namedWindow(W_NUMBER2,WINDOW_NORMAL);
    imshow(W_NUMBER2,g_dstImageNumber[1]);
    namedWindow(W_NUMBER3,WINDOW_NORMAL);
    imshow(W_NUMBER3,g_dstImageNumber[2]);
    

}


char myDiscern(Mat n)
{
    if(3*n.cols<n.rows)
    {
        return '1';
    }
    int x_half=n.cols/2;
    int y_one_third=n.rows/3;
    int y_two_third=n.rows*2/3;
    int a=0,b=0,c=0,d=0,e=0,f=0,g=0;

    for(int i=0;i<n.rows;i++)
    {
        uchar *data=n.ptr<uchar>(i);
        if(i<y_one_third)
        {
            if(data[x_half]==255) a=1;
        }
        else if(i>y_one_third&&i<y_two_third)
        {
            if(data[x_half]==255) g=1;
        }
        else
        {
            if(data[x_half]==255) d=1;
        }
    }
    for(int j=0;j<n.cols;j++)
    {
        uchar *data=n.ptr<uchar>(y_one_third);
        if(j<x_half)
        {
            if(data[j]==255) f=1;
        }
        else
        {
            if(data[j]==255) b=1;
        }
    }
    for(int j=0;j<n.cols;j++)
    {
        uchar *data=n.ptr<uchar>(y_two_third);
        if(j<x_half)
        {
            if(data[j]==255) e=1;
        }
        else
        {
            if(data[j]==255) c=1;
        }
    }

    if(a==1 && b==1 && c==1 && d==1 && e==1 && f==1 && g==0)
    {
        return '0';
    }
    else if(a==1 && b==1 && c==0 && d==1 && e==1 && f==0 && g==1)
    {
        return '2';
    }
    else if(a==1 && b==1 && c==1 && d==1 && e==0 && f==0 && g==1)
    {
        return '3';
    }
    else if(a==0 && b==1 && c==1 && d==0 && e==0 && f==1 && g==1)
    {
        return '4';
    }
    else if(a==1 && b==0 && c==1 && d==1 && e==0 && f==1 && g==1)
    {
        return '5';
    }
    else if(a==1 && b==0 && c==1 && d==1 && e==1 && f==1 && g==1)
    {
        return '6';
    }
    else if(a==1 && b==1 && c==1 && d==0 && e==0 && f==0 && g==0)
    {
        return '7';
    }
    else if(a==1 && b==1 && c==1 && d==1 && e==1 && f==1 && g==1)
    {
        return '8';
    }
    else if(a==1 && b==1 && c==1 && d==1 && e==0 && f==1 && g==1)
    {
        return '9';
    }
    else
    {
        //printf("[error_%d_%d_%d_%d_%d_%d_%d]\n",a,b,c,d,e,f,g);
        return 'e';
    }

}


int main()
{
    VideoCapture capture("V1.webm");

    while(1)
    {
        capture>>g_srcImage;
        namedWindow("[Video]",WINDOW_NORMAL);
        imshow("[Video]",g_srcImage);
        

        g_srcImageBlur=g_srcImage.clone();
        GaussianBlur(g_srcImage,g_srcImageBlur,Size(g_Blur,g_Blur),0.0);
        // namedWindow(W_BLUR,WINDOW_NORMAL);
        // imshow(W_BLUR,g_srcImageBlur);
        
        //split channels
        mySplit();

        //threshold=160
        myThreshold();

        //clear small white and connection breakpoint
        myMorphology();

        //draw contours
        myContours();

        if(g_rect.size()==3)
        {
            //sort numbers
            myNumberSort();

            //discern number
            char t_number[3];

            for(int i=0;i<3;i++)
            {
                t_number[i]=myDiscern(g_dstImageNumber[i]);
                if(t_number[i]=='e')
                {
                    t_number[0]='e';
                    break;
                }
            }

            //第一次的时候,将识别出的t_number赋给g_number
            if(g_number[0]=='z' && t_number[0]!='e')
            {
                for(int i=0;i<3;i++)
                {
                    g_number[i]=t_number[i];
                    cout<<g_number[i];
                }
                cout<<endl;
            }
            //g_number存在时,将非错误的且不同于上次的结果赋给并输出
            else if(g_number[0]!='z' &&t_number[0]!='e'&& (t_number[0]!=g_number[0] || t_number[1]!=g_number[1] || t_number[2]!=g_number[2]))
            {
                for(int i=0;i<3;i++)
                {
                    g_number[i]=t_number[i];
                    cout<<g_number[i];
                }
                cout<<endl;
            }
            //其他情况:第一次t_number没识别;g_number存在时,t_number没识别出和t_number和上次一样
            else;
            
        }


        if(waitKey(100)=='q') return 0;

        // if(waitKey()=='g');
        // else if(waitKey()=='q')return 0;
         
    }
	return 0;
}

三、实战-进阶的版本

https://blog.csdn.net/sandalphon4869/article/details/95398033

  • 9
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenCV 穿线(Hough 变换)是一种常用的图像处理方,可以用于检测直线、圆等几何形状。在单个数码管数字识别中,我们可以使用穿线来检测数码管的线段,然后根据线段的位置和长度来判断数字。 下面是实现步骤: 1. 首先读入图像,并进行灰度化和二值化处理。 ```python import cv2 import numpy as np img = cv2.imread('digit.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) _, thresh = cv2.threshold(gray, 50, 255, cv2.THRESH_BINARY_INV) ``` 2. 对二值化后的图像进行边缘检测,得到边缘图像。 ```python edges = cv2.Canny(thresh, 50, 150) ``` 3. 对边缘图像进行穿线变换,得到所有直线的参数。 ```python lines = cv2.HoughLines(edges, 1, np.pi/180, 150) ``` 4. 遍历所有直线,筛选出符合条件的线段,并记录线段的位置和长度。 ```python segments = [] for line in lines: rho, theta = line[0] if theta < np.pi/4 or theta > 3*np.pi/4: x1 = int(rho/np.cos(theta)) x2 = int((rho - thresh.shape[0]*np.sin(theta))/np.cos(theta)) length = abs(x2 - x1) if length > 10: segments.append((x1, x2)) ``` 5. 根据线段的位置和长度,判断数字。 ```python digits = [] for i in range(7): x_min = int(i*img.shape[1]/7) x_max = int((i+1)*img.shape[1]/7) segment = None for s in segments: if s[0] >= x_min and s[1] <= x_max: if segment is None or abs(s[1]-s[0]) > abs(segment[1]-segment[0]): segment = s if segment is None: digits.append(' ') elif abs(segment[1]-segment[0]) < (x_max-x_min)/2: digits.append('.') else: digits.append(str(int(round(1.0*(segment[0]+segment[1])/2/(x_max-x_min)*10)))) ``` 完整代码如下: ```python import cv2 import numpy as np img = cv2.imread('digit.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) _, thresh = cv2.threshold(gray, 50, 255, cv2.THRESH_BINARY_INV) edges = cv2.Canny(thresh, 50, 150) lines = cv2.HoughLines(edges, 1, np.pi/180, 150) segments = [] for line in lines: rho, theta = line[0] if theta < np.pi/4 or theta > 3*np.pi/4: x1 = int(rho/np.cos(theta)) x2 = int((rho - thresh.shape[0]*np.sin(theta))/np.cos(theta)) length = abs(x2 - x1) if length > 10: segments.append((x1, x2)) digits = [] for i in range(7): x_min = int(i*img.shape[1]/7) x_max = int((i+1)*img.shape[1]/7) segment = None for s in segments: if s[0] >= x_min and s[1] <= x_max: if segment is None or abs(s[1]-s[0]) > abs(segment[1]-segment[0]): segment = s if segment is None: digits.append(' ') elif abs(segment[1]-segment[0]) < (x_max-x_min)/2: digits.append('.') else: digits.append(str(int(round(1.0*(segment[0]+segment[1])/2/(x_max-x_min)*10)))) print(digits) ``` 其中,`digit.jpg` 是包含单个数码管的图片,可以根据实际情况进行替换。运行后输出的 `digits` 即为识别出的数字

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值