cv项目:颜色检测,装甲板识别、解算位置和角度、预测位移,数字识别

前言:本来想随便发发代码用来保存的,但是后来想想算了还是整理一下,项目很基础,资料给的很全了复制代码就可以复现出来的,图片放后面了,模型可以自己训练,想要多大要多大,后文有获取数据集的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;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值