前言:本来想随便发发代码用来保存的,但是后来想想算了还是整理一下,项目很基础,资料给的很全了复制代码就可以复现出来的,图片放后面了,模型可以自己训练,想要多大要多大,后文有获取数据集的cpp,直接调用figure里面的modeltrain()函数,改改地址就可以训练了,不过我的数字检测在main.cpp里设置了要先检测到颜色再识别,后文有数字加颜色图
颜色检测:hsv,inrange
数字识别:svm
装甲板解算位置角度:pnp解算
装甲板预测位移:卡尔曼滤波(我调得不好)
allprocess.h
#pragma once
#include<iostream>
#include<opencv2/opencv.hpp>
#include <io.h>
#include<string.h>
using namespace std;
using namespace cv;
class Process
{
public:
virtual void ALLProcess() {};
//根据文件地址读取文件名(vector)
void getFiles(string path, vector<string>& files);
//四舍五入函数
double Round_5(double n);
};
allprocess.cpp
#include"allprocess.h"
//根据文件地址读取文件名(vector)
void Process::getFiles(string path, vector<string>& files)
{
long long hFile = 0;
struct _finddata_t fileinfo;
string p;
if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)
//long _findfirst( char *filespec, struct _finddata_t *fileinfo )查找文件名,成功返回句柄,失败返回-1
//append函数是向string的后面追加字符或字符串
//“*”表示查找所有文件
//assign给字符串赋值
//c_str将 const string* 类型 转化为 const char* 类型(临时,必须立即保存)
{
do
{
if ((fileinfo.attrib & _A_SUBDIR)) //代码中使用if ((FileInfo.attrib & _A_SUBDIR))来判断是否为文件夹
{
if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
//strcmp函数用于比较两个字符串的大小关系,相等返回0
getFiles(p.assign(path).append("\\").append(fileinfo.name), files);//回调
}
else//否则是文件
{
files.push_back(p.assign(path).append("\\").append(fileinfo.name));
}
} while (_findnext(hFile, &fileinfo) == 0); //第一个参数为文件句柄,第二个参数同样为_finddata_t结构体指针。若查找成功,返回0,失败返回 - 1。
_findclose(hFile);
}
}
//double四舍五入5位数
double Process::Round_5(double n)
{
n *= 10000.0;
if (n > 0)
{
return (n - int(n) >= 0.5 ? int(n) + 1 : int(n))/ 10000.0;
}
else
{
return (-n - int(-n) >= 0.5 ? -(int(-n) + 1) : -int(-n))/ 10000.0;
}
}
armor.h
#pragma once
#include <iostream>
#include <stdio.h>
#include <fstream>
#include <math.h>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/highgui/highgui.hpp>
#include"allprocess.h"
using namespace std;
using namespace cv;
class Armor :public Process
{
public:
void ALLProcess(Mat& frame, Mat& show);
//输入标定板点数尺寸和点间距离
void GetBoard(Size boardsize, int boardwidth, int boardheight);
//求相机内参和畸变系数
void GetCameraMatrixAndDistCoeffs(string path);
//pnp解算
void PnPsolve(vector<Point2f>ImgPoints);
//求图像中装甲板点ImgPoints
bool GetImgPoints(vector<Point2f>& ImgPoints, Mat& frame);
//卡尔曼滤波初始化
void KalmanCreate();
//预测位移
void KalmanPredict(vector<Point2f>& ImgPoints, Mat& show);
//卡尔曼滤波归零
void KalmanToZero();
public:
Mat CameraMatrix = (Mat_<float>(3, 3) << 2307.842272090333, 0, 1377.049837615474, 0, 2330.05724282553, 1051.045675372421, 0, 0, 1);;
Mat DistCoeffs = (Mat_<float>(1, 5) << -0.008925922674868865, 0.5408053400127586, -0.001359772222680041, 0.00832733114954946, -1.628019929390208);
Size BoardSize = Size(0,0);//世界坐标系下标定板点数
int BoardWidth = 0;
int BoardHeight = 0;
//装甲板点集
int Longwide = 230;
int Lightheight = 40;
vector<Point3f>ArmorPoints = { Point3f(0,0,0),Point3f(Longwide,0,0), Point3f(Longwide,Lightheight,0), Point3f(0,Lightheight,0) }; //中间点
//解算角度
double HornArr[3][2] = {0};
//解算距离
double Distance=0;
//卡尔曼滤波
KalmanFilter KF;
Mat measurement;//测量值
};
armor.cpp
#include"armor.h"
void Armor::ALLProcess(Mat& frame, Mat& show)
{
vector<Point2f>ImgPoints;
bool ifsuccess = GetImgPoints(ImgPoints ,frame);
if (ifsuccess)
{
//画出轮廓
for (int i = 0; i < 4; i++)
{
if (i == 3)
{
line(show, ImgPoints[3], ImgPoints[0], Scalar(0, 255, 255), 2);
break;
}
line(show, ImgPoints[i], ImgPoints[i + 1], Scalar(0, 255, 255), 2);
}
KalmanPredict(ImgPoints, show);
PnPsolve(ImgPoints);
//输出角度
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 2; j++)
{
cout << "horn" << i << "的第" << j + 1 << "个可能解: " << Armor::HornArr[i][j] << endl;
}
}
//输出距离
cout << "distance为:" << Armor::Distance << endl;
cout << endl;
}
else
{
//卡尔曼归零
KalmanToZero();
//输出角度归零
memset(HornArr, 0, 6);
//距离归零
Armor::Distance = 0;
//cout << "无法识别装甲板" << endl;
//cout << endl;
}
}
//输入标定板点数尺寸和点间距离
void Armor::GetBoard(Size boardsize,int boardwidth,int boardheight)
{
Armor::BoardSize = boardsize;
Armor::BoardWidth = boardwidth;
Armor::BoardHeight = boardheight;
}
//求相机内参和畸变系数
void Armor::GetCameraMatrixAndDistCoeffs(string path)
{
vector<string>imgnames;
vector<vector<Point3f>>worldpoints;
vector<vector<Point2f>>imgpoints;
//图像点
getFiles(path, imgnames);
for (int i = 0; i < imgnames.size(); i++)
{
Mat img = imread(imgnames[i].c_str());
Mat gray;
cvtColor(img, gray, COLOR_BGR2GRAY);
vector<Point2f>board_point;
findChessboardCorners(gray, Armor::BoardSize, board_point);
find4QuadCornerSubpix(gray, board_point, Size(3, 3));
imgpoints.push_back(board_point);
}
//世界坐标系点
for (int i = 0; i < imgpoints.size(); i++)
{
vector<Point3f>temppoint;
for (int i = 0; i < Armor::BoardSize.height; i++)
{
for (int j = 0; j < Armor::BoardSize.width; j++)
{
Point3f realpoint;
realpoint.x = j * Armor::BoardWidth;
realpoint.y = i * Armor::BoardHeight;
realpoint.z = 0;
temppoint.push_back(realpoint);
}
}
worldpoints.push_back(temppoint);
}
//相机内参矩阵
Armor::CameraMatrix = Mat (3, 3, CV_32F, Scalar::all(0));
//相机的五个畸变参数
Armor::DistCoeffs = Mat (1, 5, CV_32F, Scalar::all(0));
//每幅图像旋转向量
vector<Mat>rvecs;
//每幅图像平移向量
vector<Mat>tvecs;
//相机标定
calibrateCamera(worldpoints, imgpoints, Armor::BoardSize, Armor::CameraMatrix, Armor::DistCoeffs, rvecs, tvecs);
}
//pnp解算
void Armor::PnPsolve(vector<Point2f>ImgPoints)
{
//pnp解算
Mat TvecVector;//平移向量
Mat RvecVector;//旋转向量
solvePnPRansac(Armor::ArmorPoints, ImgPoints, Armor::CameraMatrix, Armor::DistCoeffs, RvecVector, TvecVector);
//转矩阵
Mat RvecMatrix;//旋转矩阵
Rodrigues(RvecVector, RvecMatrix);
//计算角度(xhorn,yhorn,zhorn)
if ((RvecMatrix.at<double>(2, 0) != 1) && (RvecMatrix.at<double>(2, 0) != -1))
{
HornArr[0][0] = asin(-RvecMatrix.at<double>(2, 0));
HornArr[0][1] = CV_PI - HornArr[0][0];
float base1 = 0;//正负
float base2 = 0;
if (cos(HornArr[0][0]) > 0)
{
base1 = 1;
base2 = -1;
}
else
{
base1 = -1;
base2 = 1;
}
HornArr[1][0] = atan2(RvecMatrix.at<double>(2, 1) * base1, RvecMatrix.at<double>(2, 2) * base1);
HornArr[1][1] = atan2(RvecMatrix.at<double>(2, 1) * base2, RvecMatrix.at<double>(2, 2) * base2);
HornArr[2][0] = atan2(RvecMatrix.at<double>(1, 0) * base1, RvecMatrix.at<double>(0, 0) * base1);
HornArr[2][1] = atan2(RvecMatrix.at<double>(1, 0) * base2, RvecMatrix.at<double>(0, 0) * base2);
}
//实际上等于1或-1就没有意义了
//else if (RvecMatrix.at<double>(2, 0) == -1)
//{
// xhorn1 = -CV_PI / 2;
// //yhorn1 = zhorn1 + atan2(RvecMatrix.at<double>(1, 2), RvecMatrix.at<double>(1, 3));
//}
//else if (RvecMatrix.at<double>(2, 0) == 1)
//{
// xhorn1 = CV_PI / 2;
// //yhorn1 = -zhorn1 + atan2(-RvecMatrix.at<double>(1, 2), -RvecMatrix.at<double>(1, 3));
//}
double x = TvecVector.at<double>(0, 0);
double y = TvecVector.at<double>(1, 0);
double z = TvecVector.at<double>(2, 0);
Armor::Distance = sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2));
}
//求图像中装甲板的点集
bool Armor::GetImgPoints(vector<Point2f>& ImgPoints,Mat &frame)
{
Mat framecopy;
frame.copyTo(framecopy);
GaussianBlur(framecopy, framecopy, Size(3, 3), 40, 40);
vector<Mat>ColorVal;
split(framecopy, ColorVal);
Mat gray = ColorVal[0] - ColorVal[2];
Mat thresh;
threshold(gray, thresh, 200, 255, THRESH_BINARY + THRESH_OTSU);
Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));
morphologyEx(thresh, thresh, MORPH_CLOSE, element, Point(-1, -1), 3);
//二层轮廓检测
vector<vector<Point>>armorcontours;
vector<vector<Point>>targetcontours;
vector<Vec4i>hierarchy;
findContours(thresh, armorcontours,hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
for (int i = 0; i < armorcontours.size(); i++)//一筛:外层轮廓有无二层轮廓;二筛二层轮廓面积比例;三筛:筛选结果为2
{
if (hierarchy[i][3] != -1 || hierarchy[i][2] == -1) //筛不是外层轮廓 或 没有二层轮廓
{
continue;
}
int num = hierarchy[i][2];
if (contourArea(armorcontours[num]) > (contourArea(armorcontours[i]) * 0.1) ) //筛二层轮廓面积小于0.3总面积
{
targetcontours.push_back(armorcontours[num]);
}
}
if (targetcontours.size() != 2)
{
return false;
}
else
{
//targetcontours[0].insert(targetcontours[0].end(), targetcontours[1].begin(), targetcontours[1].end());
//RotatedRect rect = minAreaRect(targetcontours[0]);
//vector<Point2f>tempPoints;
//rect.points(tempPoints);
//
//if (tempPoints[0].y > tempPoints[2].y) //点在下
//{
// for (int i = 1; i < 4; i++)
// {
// ImgPoints.push_back(tempPoints[i]);
// }
// ImgPoints.push_back(tempPoints[0]);
//}
//else //点在上
//{
// for (int i = 0; i < 4; i++)
// {
// ImgPoints.push_back(tempPoints[i]);
// }
//}
vector<Point2f>rectPoints;
for (int i = 0; i < 2; i++)
{
RotatedRect rect;
rect = minAreaRect(targetcontours[i]);
vector<Point2f>tempPoints;
rect.points(tempPoints);
if (tempPoints[0].y > tempPoints[2].y)//点在下
{
rectPoints.push_back(Point((tempPoints[1].x + tempPoints[2].x) / 2, (tempPoints[1].y + tempPoints[2].y) / 2));
rectPoints.push_back(Point((tempPoints[0].x + tempPoints[3].x) / 2, (tempPoints[0].y + tempPoints[3].y) / 2));
}
else if (tempPoints[0].y < tempPoints[2].y)//点在上
{
rectPoints.push_back(Point((tempPoints[0].x + tempPoints[1].x) / 2, (tempPoints[0].y + tempPoints[1].y) / 2));
rectPoints.push_back(Point((tempPoints[2].x + tempPoints[3].x) / 2, (tempPoints[2].y + tempPoints[3].y) / 2));
}
}
RotatedRect thirdRect = minAreaRect(rectPoints);
vector<Point2f>third_Points;
thirdRect.points(third_Points);
if (third_Points[0].y > third_Points[2].y)//点在下
{
for (int i = 1; i < 4; i++)
{
ImgPoints.push_back(third_Points[i]);
}
ImgPoints.push_back(third_Points[0]);
}
else
{
for (int i = 0; i < 4; i++)
{
ImgPoints.push_back(third_Points[i]);
}
}
return true;
}
}
//卡尔曼滤波初始化
void Armor::KalmanCreate()
{
KF = KalmanFilter(4, 2, 0); //状态值4×1向量(x,y,△x,△y) //测量值2×1向量(x,y)
KF.transitionMatrix = (Mat_<float>(4, 4) << 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1);
setIdentity(KF.measurementMatrix); //测量矩阵H
setIdentity(KF.processNoiseCov, Scalar::all(1e-5)); //系统噪声方差矩阵Q
setIdentity(KF.measurementNoiseCov, Scalar::all(1e-1)); //测量噪声方差矩阵R
setIdentity(KF.errorCovPost, Scalar::all(1)); //后验错误估计协方差矩阵P
randn(KF.statePost, Scalar::all(0), Scalar::all(0.1));
Armor::measurement = Mat::zeros(2, 1, CV_32F);
}
//卡尔曼滤波预测位移
void Armor::KalmanPredict(vector<Point2f>& ImgPoints,Mat &show)
{
Point2f Mcenter = Point2f(0, 0);
for (int i = 0; i < 4; i++)
{
Mcenter.x += ImgPoints[i].x;
Mcenter.y += ImgPoints[i].y;
}
Mcenter.x /= 4;
Mcenter.y /= 4;
Mat prediction = KF.predict();
measurement.at<float>(0) = (float)Mcenter.x;
measurement.at<float>(1) = (float)Mcenter.y;
if (prediction.at<float>(0) > 0 && prediction.at<float>(0) > 0)
{
Point2f Pcenter = Point2f(prediction.at<float>(0), prediction.at<float>(0));
float Xoffset = Mcenter.x - Pcenter.x;
float Yoffset = Mcenter.y - Pcenter.y;
vector<Point>tempPoints;
for (int i = 0; i < 4; i++)
{
Point tempPoint = Point(ImgPoints[i].x - Xoffset, ImgPoints[i].y - Yoffset);
tempPoints.push_back(tempPoint);
}
for (int i = 0; i < 4; i++)
{
if (i == 3)
{
line(show, tempPoints[3], tempPoints[0], Scalar(200, 100, 200), 2);
break;
}
line(show, tempPoints[i], tempPoints[i + 1], Scalar(200, 100, 200), 2);
}
}
KF.correct(measurement);
}
void Armor::KalmanToZero()
{
measurement.at<float>(0) = 0.0;
measurement.at<float>(1) = 0.0;
KF.correct(measurement);
}
color.h
#pragma once
#include<iostream>
#include<opencv2/opencv.hpp>
#include"allprocess.h"
using namespace std;
using namespace cv;
class Color:public Process
{
public:
//直接使用
void ALLProcess(Mat& img, Mat &show,bool& ifsuccess);
//画出区域
int DrawRect(Mat& img, Mat& thresh);
};
color.cpp
#include "color.h"
//img:把检测出的框画在frame上
void Color::ALLProcess(Mat& img,Mat &show,bool& ifsuccess) //bool判断颜色检测是否成功
{
int rightnum = 0;//检测出的颜色数
Mat thresh;
Mat hsv;
img.copyTo(hsv);
cvtColor(hsv, hsv, COLOR_BGR2HSV);
Scalar ColorArr[6][2] =
{
//Scalar(64, 209, 2), Scalar(104, 249, 42), //16E554: 84 229 22(BGR)--前后20
//Scalar(147, 2, 209), Scalar(187, 42, 249), //E516A7: 167 22 229(BGR)--前后20
//Scalar(2, 64, 209), Scalar(42, 104, 249), //E55416: 22,84,229(BGR)--前后20
//Scalar(2, 209, 147), Scalar(42, 249, 187), //A7E516: 22,229,167(BGR)--前后20
//Scalar(209, 144, 2), Scalar(249, 184, 42), //16A7E5: 229,164,22(BGR)--前后20
//Scalar(209, 2, 64), Scalar(249, 42, 104), //5416E5: 229 22 84(BGR)--前后20
Scalar(55, 100, 100), Scalar(84, 255, 255), //16E554: 69,231,229
Scalar(144, 100, 100), Scalar(174, 255, 255), //E516A7: 159,231,229
Scalar(0, 100, 100), Scalar(24, 255, 255), //E55416: 9,231,229
Scalar(25, 100, 100), Scalar(54, 255, 255), //A7E516: 39,231,229
Scalar(85, 100, 100), Scalar(114, 255, 255), //16A7E5: 99,231,229
Scalar(115, 100, 100), Scalar(140, 255, 255), //5416E5: 129,231,229
//Scalar(59, 100, 100), Scalar(79, 255, 255), //16E554: 69,231,229
//Scalar(149, 100, 100), Scalar(169, 255, 255), //E516A7: 159,231,229
//Scalar(0, 100, 100), Scalar(19, 255, 255), //E55416: 9,231,229
//Scalar(29, 100, 100), Scalar(49, 255, 255), //A7E516: 39,231,229
//Scalar(89, 100, 100), Scalar(109, 255, 255), //16A7E5: 99,231,229
//Scalar(119, 100, 100), Scalar(139, 255, 255), //5416E5: 129,231,229
};
int a=0;
for (int i = 0; i < 6; i++)
{
inRange(hsv, ColorArr[i][0], ColorArr[i][1], thresh);
rightnum+= DrawRect(show, thresh);
}
if (rightnum)
{
ifsuccess = true;
}
else
{
ifsuccess = false;
}
}
int Color::DrawRect(Mat& img, Mat& thresh)
{
vector<vector<Point>>contours;
findContours(thresh, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);//CHAIN_APPROX_SIMPLE
if (contours.size() != 0)
{
int num=0;
//最大面积法防止检测出异常物体
double maxarea = 0;
for (int i = 0; i < contours.size(); i++)
{
double area = contourArea(contours[i]);
if (area > maxarea)
{
maxarea = area;
num = i;
}
}
double standard = img.size().width * img.size().height;//标准
if (maxarea > (standard / 100))
{
//标出区域
RotatedRect rect = minAreaRect(contours[num]);//矩形拟合
vector<Point2f>tangle;
rect.points(tangle);
for (int i = 0; i < 4; i++)
{
if (i == 3)
{
line(img, tangle[0], tangle[i], Scalar(255, 255, 0), 2);
break;
}
line(img, tangle[i], tangle[i + 1], Scalar(255, 255, 0), 2);
}
return 1;
}
else
{
return 0;
}
}
return 0;
}
figure.h
#pragma once
#include <stdio.h>
#include <time.h>
#include <opencv2/opencv.hpp>
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/ml/ml.hpp>
#include <io.h>
#include<string.h>
#include"allprocess.h"
using namespace std;
using namespace cv;
using namespace cv::ml;
class Figure:public Process
{
public:
void ALLProcess(Mat& frame);
//模型训练allprocess
void ModelTrain();
void access(Mat& img, vector<int>& train_label);
//图像处理
void dispose(Mat& img);
//SVM预测结果
int FigurePredict(Mat& img);
public:
string DataPath = "C:\\Users\\10949\\Desktop\\暂存箱\\校内赛\\data\\";
string ModelPath = "C:\\Users\\10949\\Desktop\\暂存箱\\校内赛\\data\\svm1_640_480.xml";
Size ImgSize = Size(100,100);
};
figure.cpp
#include"figure.h"
void Figure::ALLProcess(Mat& frame)
{
Mat framecopy;
frame.copyTo(framecopy);
GaussianBlur(framecopy, framecopy, Size(3, 3), 40, 40);
Mat process;
cvtColor(framecopy, process, COLOR_BGR2GRAY);
//二值化
threshold(process, process, 200, 255, THRESH_BINARY + THRESH_OTSU);
//闭运算去除黑点
Mat element = getStructuringElement(MORPH_RECT, Size(3, 3));
morphologyEx(process, process, MORPH_OPEN, element, Point(-1, -1), 3);
//morphologyEx(process, process, MORPH_CLOSE, element, Point(-1, -1), 3);
//漫水-改了
copyMakeBorder(process, process, 1, 1, 1, 1, BORDER_CONSTANT, Scalar(255));
Point seedpoint(0, 0);
Mat mask = Mat::zeros(process.rows + 2, process.cols + 2, CV_8UC1);
floodFill(process, mask, seedpoint, 0, 0, 0, 10, FLOODFILL_FIXED_RANGE);//漫黑水
//找轮廓-最大面积法
vector<vector<Point>>ALLContours;
vector<Point>target; //目标点
vector<Vec4i> Hierarchy; //轮廓关系
imshow("process", process);
findContours(process, ALLContours, Hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);//外轮廓
double maxarea = 0;
double num = 0;
for (int i = 0; i < ALLContours.size(); i++)//一筛:外层轮廓有无二层轮廓;二筛二层轮廓面积比例;三筛:外轮廓面积最大
{
if (Hierarchy[i][3] != -1 || Hierarchy[i][2] == -1) //筛不是外层轮廓 或 没有二层轮廓
{
continue;
}
double area = contourArea(ALLContours[i]);
if (contourArea(ALLContours[Hierarchy[i][2]]) < (area * 0.06)) //筛二层轮廓面积小于6%总面积
{
continue;
}
if (area > maxarea) //筛外轮廓面积最大
{
maxarea = area;
num = i;
}
}
double standard = framecopy.size().width * framecopy.size().height;//四筛:最大面积标准
if (maxarea > (standard / 100))
{
target = ALLContours[num];
//仿射变换
//法一:利用RotatedRect旋转
Rect OutRect = boundingRect(target); //挖出范围(最大矩形拟合)
Mat Out;
process(OutRect).copyTo(Out);
RotatedRect Rotate = minAreaRect(target); // 最小矩形拟合
Point Fcenter = Rotate.center; //frame中center
float RotatedAngle = 0; //旋转角度
if (Rotate.angle > 45) //大于45,定为左旋
{
RotatedAngle = Rotate.angle - 90;
}
else //小于45,定为右旋
{
RotatedAngle = Rotate.angle;
}
Point Origin = OutRect.tl(); //frame中Out原点
Point Center = Point(Fcenter.x - Origin.x, Fcenter.y - Origin.y); //out中center
Mat RotatedMat = getRotationMatrix2D(Center, RotatedAngle, 1); //旋转矩阵
warpAffine(Out, Out, RotatedMat, Out.size(), INTER_LINEAR + WARP_FILL_OUTLIERS, 0); //仿射变换
//二值化+扩大边界+漫水
threshold(Out, Out, 200, 255, THRESH_BINARY + THRESH_OTSU);
copyMakeBorder(Out, Out, 1, 1, 1, 1, BORDER_CONSTANT, Scalar(0));
floodFill(Out, Point(0, 0), 255, 0, 0, 10);
//缩放为标准大小
resize(Out, Out, Figure::ImgSize);
imshow("Out", Out);
//预测结果
int figurenum = FigurePredict(Out);
if (figurenum == 10)
{
cout << "无法检测数字" << endl;
}
else
{
cout << "SVM检测数字结果是" << num << endl;
}
cout << endl;
}
}
void Figure::ModelTrain()
{
Mat TrainImg;
vector<int>train_label;
access(TrainImg, train_label);
Mat train;
TrainImg.copyTo(train);
train.convertTo(train, CV_32FC1);
//模型训练
Ptr<SVM> svm = SVM::create();//创建一个svm对象
svm->setType(SVM::C_SVC);
svm->setKernel(SVM::LINEAR);
svm->setGamma(1);
svm->setDegree(0);
svm->setCoef0(0);
svm->setC(1);
svm->setNu(0);
svm->setP(0);
svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 1000, 1e-2));
cout << "开始进行训练..." << endl;
svm->train(train, cv::ml::SampleTypes::ROW_SAMPLE, train_label);
cout << "保存模型..." << endl;
svm->save(Figure::ModelPath.c_str());
}
void Figure::access(Mat& img, vector<int>& train_label)//标记和处理数据集
{
string fillroot = Figure::DataPath;//复制文件地址
for (int i = 0; i < 10; i++)
{
//尾加数字
char num = i + 48;
fillroot.push_back(num);
vector<string> files;
getFiles(fillroot, files);
for (int j = 0; j < files.size(); j++)
{
Mat in = imread(files[j].c_str());
dispose(in); //图像处理
resize(in, in, Figure::ImgSize); //改变图片规格
in = in.reshape(1, 1);
img.push_back(in);
train_label.push_back(i);
}
//尾删数字
fillroot.pop_back();
}
}
//图像处理
void Figure::dispose(Mat& img)
{
cvtColor(img, img, COLOR_BGR2GRAY);
threshold(img, img, 200, 255, THRESH_BINARY + THRESH_OTSU);//二值化-自适应阈值
}
//SVM预测结果
int Figure::FigurePredict(Mat& img)
{
if (img.size() != Figure::ImgSize)
{
return 10;//照片不符合规格
}
Ptr<SVM> svm = StatModel::load<SVM>(Figure::ModelPath.c_str());
img = img.reshape(1, 1);
img.convertTo(img, CV_32FC1);
int result = svm->predict(img);
return result;
}
main.cpp
#include"allprocess.h"
#include"color.h"
#include"figure.h"
#include"armor.h"
int main()
{
Figure figure;
Color color;
Armor armor;
armor.KalmanCreate();
VideoCapture video(1);
if (video.isOpened())
{
cout << "视频宽度:" << video.get(CAP_PROP_FRAME_WIDTH) << endl;
cout << "视频高度:" << video.get(CAP_PROP_FRAME_HEIGHT) << endl;
cout << "视频帧率:" << video.get(CAP_PROP_FPS) << endl;
cout << "视频总帧率:" << video.get(CAP_PROP_FRAME_COUNT) << endl;
}
else
{
cout << "请检查图像" << endl;
return -1;
}
while (1)
{
Mat frame;
video >> frame;
if (frame.empty())
{
break;
}
Mat show;
frame.copyTo(show);
//装甲板识别
armor.ALLProcess(frame, show);
//颜色识别
bool ifsuccess=false;
color.ALLProcess(frame, show, ifsuccess);
//数字识别(SVM)
if (ifsuccess)
{
figure.ALLProcess(frame);
}
imshow("frame", show);
waitKey(1000 / video.get(CAP_PROP_FPS));
}
return 0;
}
测试图片
颜色
装甲板
数字
数字加颜色
(在强光干扰下也能表现的很好)
获取训练集的cpp
#include <iostream>
#include "opencv2/video/tracking.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdio.h>
using namespace cv;
using namespace std;
static int a = 48;
static int b = 48;
static int c = 48;
void ALLProcess(Mat& frame, bool& iffull);
bool fillwrite(Mat& Out);
int main()
{
VideoCapture video(1);
if (video.isOpened())
{
cout << "视频宽度:" << video.get(CAP_PROP_FRAME_WIDTH) << endl;
cout << "视频高度:" << video.get(CAP_PROP_FRAME_HEIGHT) << endl;
cout << "视频帧率:" << video.get(CAP_PROP_FPS) << endl;
cout << "视频总帧率:" << video.get(CAP_PROP_FRAME_COUNT) << endl;
}
else
{
cout << "请检查图像" << endl;
return -1;
}
while (1)
{
Mat frame;
video >> frame;
if (frame.empty())
{
break;
}
bool iffull=true;
ALLProcess(frame,iffull);
if (!iffull)
{
break;
}
imshow("frame", frame);
waitKey(1000 / video.get(CAP_PROP_FPS));
}
return 0;
}
void ALLProcess(Mat& frame, bool& iffull)
{
Mat framecopy;
frame.copyTo(framecopy);
GaussianBlur(framecopy, framecopy, Size(3, 3), 40, 40);
Mat process;
cvtColor(framecopy, process, COLOR_BGR2GRAY);
//二值化
threshold(process, process, 200, 255, THRESH_BINARY + THRESH_OTSU);
//闭运算去除黑点
Mat element = getStructuringElement(MORPH_RECT, Size(3, 3));
//morphologyEx(process, process, MORPH_OPEN, element, Point(-1, -1), 3);
morphologyEx(process, process, MORPH_CLOSE, element, Point(-1, -1), 3);
//漫水-改了
copyMakeBorder(process, process, 1, 1, 1, 1, BORDER_CONSTANT, Scalar(255));
Point seedpoint(0, 0);
Mat mask = Mat::zeros(process.rows + 2, process.cols + 2, CV_8UC1);
floodFill(process, mask, seedpoint, 0, 0, 0, 10, FLOODFILL_FIXED_RANGE);//漫黑水
//找轮廓-最大面积法
vector<vector<Point>>ALLContours;
vector<Point>target; //目标点
vector<Vec4i> Hierarchy; //轮廓关系
findContours(process, ALLContours, Hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);//外轮廓
double maxarea = 0;
double num = 0;
for (int i = 0; i < ALLContours.size(); i++)//一筛:外层轮廓有无二层轮廓;二筛二层轮廓面积比例;三筛:外轮廓面积最大
{
if (Hierarchy[i][3] != -1 || Hierarchy[i][2] == -1) //筛不是外层轮廓 或 没有二层轮廓
{
continue;
}
double area = contourArea(ALLContours[i]);
if (contourArea(ALLContours[Hierarchy[i][2]]) < (area * 0.06)) //筛二层轮廓面积小于6%总面积
{
continue;
}
if (area > maxarea) //筛外轮廓面积最大
{
maxarea = area;
num = i;
}
}
double standard = framecopy.size().width * framecopy.size().height;//四筛:最大面积标准
if (maxarea > (standard / 100))
{
target = ALLContours[num];
//仿射变换
//法一:利用RotatedRect旋转
Rect OutRect = boundingRect(target); //挖出范围(最大矩形拟合)
Mat Out;
process(OutRect).copyTo(Out);
RotatedRect Rotate = minAreaRect(target); // 最小矩形拟合
Point Fcenter = Rotate.center; //frame中center
float RotatedAngle = 0; //旋转角度
if (Rotate.angle > 45) //大于45,定为左旋
{
RotatedAngle = Rotate.angle - 90;
}
else //小于45,定为右旋
{
RotatedAngle = Rotate.angle;
}
Point Origin = OutRect.tl(); //frame中Out原点
Point Center = Point(Fcenter.x - Origin.x, Fcenter.y - Origin.y); //out中center
Mat RotatedMat = getRotationMatrix2D(Center, RotatedAngle, 1); //旋转矩阵
warpAffine(Out, Out, RotatedMat, Out.size(), INTER_LINEAR + WARP_FILL_OUTLIERS, 0); //仿射变换
//二值化+扩大边界+漫水
threshold(Out, Out, 200, 255, THRESH_BINARY + THRESH_OTSU);
copyMakeBorder(Out, Out, 1, 1, 1, 1, BORDER_CONSTANT, Scalar(0));
floodFill(Out, Point(0, 0), 255, 0, 0, 10);
//缩放为标准大小
resize(Out, Out, Size(100, 100));
imshow("Out", Out);
iffull = fillwrite(Out);
}
}
bool fillwrite(Mat& Out)
{
string num;
if (c != 48)
{
num.push_back((char)c);
num.push_back((char)b);
}
else if (b != 48)
{
num.push_back((char)b);
}
num.push_back((char)a);
string name = "C:\\Users\\10949\\Desktop\\暂存箱\\校内赛\\data\\9\\.jpg";
name.insert(name.begin()+44, num.begin(), num.end());
imwrite(name.c_str(), Out);
if (a == 57)
{
if (b == 57)
{
if (c == 49)
{
cout << "满了" << endl;
return false;
}
else
{
c += 1;
b = 48;
a = 48;
}
}
else
{
b += 1;
a = 48;
}
}
else
{
a += 1;
}
return true;
}