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

文章目录

  • 一、感想
    • 1.图像预处理迭代
    • 2.图像形态学运算迭代
    • 3.图像轮廓识别迭代
    • 4.图像数字排序迭代
    • 5.处理视频迭代
  • 二、最终版本
    • 1.滤波
      • (1)测试代码
      • (2)结论
    • 2.颜色分离和二值化
    • (1)观察颜色
    • (2)结论
    • 3.形态学运算
    • 4.数字轮廓识别
      • (1)初代代码
      • (2)结论
    • 4.数字识别和截取
    • 5.数字识别(穿线法)
    • 6.图片形式识别release
    • 7.在线视频识别release
  • 三、实战-进阶的版本


一、感想

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

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

二、最终版本

OpenCV识别数码管穿线法(基础版)_第1张图片

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
#include
#include
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;
}

OpenCV识别数码管穿线法(基础版)_第2张图片

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);   
    
}

OpenCV识别数码管穿线法(基础版)_第3张图片

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());
    }
}

OpenCV识别数码管穿线法(基础版)_第4张图片

(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());
    }
}

OpenCV识别数码管穿线法(基础版)_第5张图片

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]);
  
}

OpenCV识别数码管穿线法(基础版)_第6张图片

5.数字识别(穿线法)

OpenCV识别数码管穿线法(基础版)_第7张图片

//识别中间行的单个数字,识别中间行只需要调用三次,传入不同的参数
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
OpenCV识别数码管穿线法(基础版)_第8张图片

#include
#include
#include
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 g_rect;需要清零,保证每次读帧都是新的中间的三个数字。
同样的话,那输出的数字就不变

代码下载:https://download.csdn.net/download/sandalphon4869/11310232
OpenCV识别数码管穿线法(基础版)_第9张图片

#include
#include
#include
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

你可能感兴趣的:(opencv(C++))