摄像头手写数字识别(基于KNN算法)

文章主要总结一下调试某位大神的程序过程和遇到的问题,代码文章参考来源: 摄像头识别手写数字

1、工作环境

Ubuntu16.04,opencv2, IDE : Qt5.9.1Creator, 编译器:cmake

下载了大神的文章程序后发现没有main文件,就是需要打开摄像头的程序,由于对opencv 也是刚入门,走了不少弯路,针对原来的程序主要做了一下更改

2.工作历程

(1)写了主程序main.cpp将打开摄像头并处理训练数据放在主函数里了,读取数据,提取特征还是调用的原程序

原程序为windows下的,读取文件的路径需要更改,原dealData.h中:

//样本数据的保存路径
string dir_path = "E:\\data1\\sample\\";
// 更改为
string dir_path = "/home/sun/catkin_nr/src/nub_r/data1/sample/";
//其中双斜杠都要改为单斜杠
sprintf(path, "%s%d/.", dir_path.c_str(), i);
sprintf(tmp, "%s%d/%s", dir_path.c_str(), i, ptr->d_name);


(2)findContours输入为二值数据图,在阈值二值化之前必须先将rgb图灰度化,开始不了解,浪费了好长时间找问题,唉。。。

  cvtColor(dstImage, grayImage, COLOR_BGR2GRAY);//extremely important 
  threshold(grayImage, Image, 120, 255, CV_THRESH_BINARY_INV);
  findContours(Image,contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);

(3)关于训练图片的尺寸,可以修改,原来博客中为128×128,后来我考虑到数字宽高不是1:1,所以将宽高改为64:96(此处取值也需注意,详见下面的博客),主要是作特征提取用。opencv命令为 HOGDescriptor(),关于此命令相关介绍参考:OpenCV HOGDescriptor 参数图解   讲的很详细。

(4)识别效果图:

门牌识别:

摄像头识别:


3.工作总结

该程序仅能进行 简单的数字识别,且识别距离有限,对图像光照要求比较高,改进空间很大,对于门牌识别还需要对门牌区域进行识别,切割,图像增强处理,畸变处理,路还很长。。。。。。识别算法比KNN好的还有很多,就当是一个开始吧!

4. main.cpp完整代码

(dealdata.h和hogmat.h没多大变化,就改个路径/,参见参考的博客附件)

//created 2018.4.17
#include <ros/ros.h>//用ROS_INFO来输出信息,没有ros环境可以改掉
#include <stdio.h>
#include <iostream>
#include <sstream> // for converting the command line parameter to integer
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/ml/ml.hpp>
#include "dealdata.h"
#include "hogmat.h"
using namespace cv;
using namespace std;
using namespace ml;

Mat trainImage;//用于存放训练图
Mat labelImage;//用于存放标签
int predict(Mat inputImage);
Mat deal_camera(Mat srcImage);
Ptr<KNearest> knn;
int result;
//load picture part added
vector<Mat> ROI;//用于存放图中抠出的数字区域
vector<Rect> ROIposition;//ROI在图像中的位置
vector<vector<Point> > contours;//点容器的容器,用于存放轮廓的点的容器的容器
vector<Vec4i> hierarchy;//点的指针容器
int result_2;//预测的结果
int weigth;//宽度
int height;//高度
Mat _roi;
Rect rect;
Point positiosn;
int brx; //右下角的横坐标
int bry; //右下角的竖坐标
int tlx; //左上角的横坐标
int tly; //左上角的竖坐标
int main(int argc, char** argv)
{   //open the camera
    if(argv[1] == NULL)
      {
          ROS_INFO("argv[1]=NULL\n");
          return 1;
      }
    istringstream video_sourceCmd(argv[1]);//local computer is 0,usb camera is 1!
    int video_source;
    if(!(video_sourceCmd >> video_source))
     {   ROS_INFO("video_sourceCmd is %d\n",video_source);
         return 1;
     }
     VideoCapture cap(video_source);
    if (!cap.isOpened())
    {   cout << "Failed to open camera." << endl;
        return -1;
    }
    //Train data!
    vector<string> samplePath;
    vector<int> labels;
    dealData::samplePath(samplePath, labels);
    //导入样本,并做好标签图
    for (int i = 0, _size = (int)samplePath.size(); i < _size; ++i)
    {
      Mat tmp = hogMat::getHogMat(samplePath[i]);
      trainImage.push_back(tmp);
      labelImage.push_back(labels[i]);
    }
    //创建KNN,并且设置N值为5
    knn = KNearest::create();
    knn->setDefaultK(5);
    knn->setIsClassifier(true);
    //生成训练数据
    Ptr<TrainData> tData = TrainData::create(trainImage, ROW_SAMPLE, labelImage);
    cout << "It's training!" << endl;
    knn->train(tData);
    //send picture
    for(;;)
    {   Mat frame;
        cap >> frame;
        frame = deal_camera(frame);
        imshow("original", frame);
        if(waitKey(30) >= 0)
        break;
    }
    return 0;
}
int predict(Mat inputImage)
{
  //预测函数
  if (trainImage.size == 0)
  {
    cout << "请先初始化" << endl;
    return -1;
  }

  Mat input = hogMat::gotHogMat(inputImage);
  return (int)knn->predict(input); //返回预测结果
}
Mat deal_camera(Mat srcImage)
{
  Mat dstImage, grayImage, Image;
  srcImage.copyTo(dstImage);
  cvtColor(dstImage, grayImage, COLOR_BGR2GRAY);
  threshold(grayImage, Image, 120, 255, CV_THRESH_BINARY_INV);
  findContours(Image,contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
  //added for picture
  vector< vector<Point> >::iterator It;
  for (It = contours.begin(); It < contours.end(); It++)
  {   //画出可包围数字的最小矩
    rect = boundingRect(*It);
    weigth = rect.br().x - rect.tl().x;//宽
    height = rect.br().y - rect.tl().y;//高
    if ((weigth < height && height< 4*weigth)  && ((weigth > 10) || (height > 10)))//稍作修改
    {  //根据数字的特征排除掉一些可能不是数字的图形,然后进行一下处理
      Mat roi = Image(rect);
      roi.copyTo(_roi);//深拷贝出来
      ROI.push_back(_roi);//保存,方便操作
      ROIposition.push_back(rect);
      rectangle(srcImage, rect, Scalar(255, 255, 255), 1);
      if ((height * 2) < weigth)
        result = 1;
      else{
        Mat pre;
        resize(_roi, pre, Size(64,96));
        threshold(pre, pre, 120, 255, CV_THRESH_BINARY);
        result = predict(pre);
      }
      char output[10] = { 0 };
      sprintf(output, "%d", result);
      positiosn = Point(rect.br().x - 7, rect.br().y + 25);
      putText(srcImage,output,positiosn,1, 1.0,Scalar(0, 0, 0),1);//在屏幕上打印字
      cout<<result<<endl;
    }
  }
  return srcImage ;
}

猜你喜欢

转载自blog.csdn.net/qq_30460905/article/details/80027882
今日推荐