欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

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

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)识别效果图:

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

摄像头识别:

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

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