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