攝像頭手寫數字識別(基於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 ;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章