OpenCV implementation of SVM model training and classification

Article directory


1. Data preparation

In the OpenCV installation path, search for digits and you will get a picture with a size of 10002000 and 10 numbers from 0 to 9. There is a number every 5 lines, a total of 50 lines and a total of 5000 handwritten digits. , each number block size is 2020. Below, 0 and 1 among these numbers will be used as preparation data for binary classification. There are 500 0s and 500 1s.
Use the following code to prepare the image and create a folder in the writing path in advance:

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main()
{
    
    
	char ad[128] = {
    
     0 };
	int  filename = 0, filenum = 0;
	Mat img = imread("C:\\Users\\Administrator\\Desktop\\小李文献\\digits.png");
	Mat gray;
	cvtColor(img, gray, CV_BGR2GRAY);
	int b = 20;
	int m = gray.rows / b;   //原图为1000*2000
	int n = gray.cols / b;   //裁剪为5000个20*20的小图块

	for (int i = 0; i < m; i++)
	{
    
    
		int offsetRow = i * b;  //行上的偏移量
		if (i % 5 == 0 && i != 0)
		{
    
    
			filename++;
			filenum = 0;
		}
		for (int j = 0; j < n; j++)
		{
    
    
			int offsetCol = j * b; //列上的偏移量
			sprintf_s(ad, "C:\\Users\\Administrator\\Desktop\\小李文献\\data\\%d\\%d.jpg", filename, filenum++);
		
			//截取20*20的小块
			Mat tmp;
			gray(Range(offsetRow, offsetRow + b), Range(offsetCol, offsetCol + b)).copyTo(tmp);
			imwrite(ad, tmp);
		}
	}
	return 0;
}

Insert image description here
Finally, you can get this result:
Insert image description here
The organized binary data format is:

–D:
–data
–train_image
–0(400张)
–1(400张)
–test_image
–0(100张)
–1(100张)

It is also suitable for intercepting multiple folders. The code is the same, just more cout.

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main()
{
    
    
	char ad[128] = {
    
     0 };
	int  filename = 0, filenum = 0;
	Mat img = imread("H:\\opencv\\screen\\digits.png");
	Mat gray;
	cvtColor(img, gray, CV_BGR2GRAY);
	int b = 20;
	int m = gray.rows / b;   //原图为1000*2000
	int n = gray.cols / b;   //裁剪为5000个20*20的小图块
	cout << "m:" << m << endl;
	cout << "m:" << n << endl;
	cout << "截图中....." << endl;
	for (int i = 0; i < m; i++)
	{
    
    
		int offsetRow = i * b;  //行上的偏移量
		if (i % 5 == 0 && i != 0)
		{
    
    
			filename++;
			filenum = 0;
		}
		for (int j = 0; j < n; j++)
		{
    
    
			int offsetCol = j * b; //列上的偏移量
			sprintf_s(ad, "H:\\opencv\\screen\\data\\%d\\%d.jpg", filename, filenum++);

			//截取20*20的小块
			Mat tmp;
			gray(Range(offsetRow, offsetRow + b), Range(offsetCol, offsetCol + b)).copyTo(tmp);
			imwrite(ad, tmp);
		}
	}
	cout << "截图完毕" << endl;
	system("pause");
	return 0;
}

Insert image description here
There is image data in it

2. Model training

After the data preparation is completed, you can use the following code to train:

#include <stdio.h>  
#include <time.h>  
#include <opencv2/opencv.hpp>  
#include <opencv/cv.h>  
#include <iostream> 
#include <opencv2/core/core.hpp>  
#include <opencv2/highgui/highgui.hpp>  
#include <opencv2/ml/ml.hpp>  
#include <io.h>

using namespace std;
using namespace cv;
using namespace cv::ml;
void getFiles(string path, vector<string>& files);
void get_1(Mat& trainingImages, vector<int>& trainingLabels);
void get_0(Mat& trainingImages, vector<int>& trainingLabels);

int main()
{
    
    
	//获取训练数据
	Mat classes;
	Mat trainingData;
	Mat trainingImages;
	vector<int> trainingLabels;
	get_1(trainingImages, trainingLabels);
	get_0(trainingImages, trainingLabels);
	Mat(trainingImages).copyTo(trainingData);
	trainingData.convertTo(trainingData, CV_32FC1);
	Mat(trainingLabels).copyTo(classes);
	//配置SVM训练器参数
	
	/*CvSVMParams SVM_params;
	SVM_params.svm_type = CvSVM::C_SVC;
	SVM_params.kernel_type = CvSVM::LINEAR;
	SVM_params.degree = 0;
	SVM_params.gamma = 1;
	SVM_params.coef0 = 0;
	SVM_params.C = 1;
	SVM_params.nu = 0;
	SVM_params.p = 0;
	SVM_params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 1000, 0.01);*/

	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));//设置SVM训练时迭代终止条件  10的12次方
	//训练
	/*Ptr<TrainData> tData = TrainData::create(trainingData, ROW_SAMPLE, classes);
 	SVM_params->train(tData);*/  //这两行代码和下面一行代码等效
	cout << "开始进行训练..." << endl;
	svm->train(trainingData, cv::ml::SampleTypes::ROW_SAMPLE, classes);
	//保存模型
	cout << "保存模型..." << endl;
	svm->save("C:\\Users\\Administrator\\Desktop\\小李文献\\data\\svm.xml");
	cout << "训练好了!!!" << endl;
	getchar();
	return 0;
}
void 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)
	{
    
    
		do
		{
    
    
			if ((fileinfo.attrib &  _A_SUBDIR))
			{
    
    
				if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 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);

		_findclose(hFile);
	}
	
}
void get_1(Mat& trainingImages, vector<int>& trainingLabels)
{
    
    
	char * filePath = (char *) "C:\\Users\\Administrator\\Desktop\\小李文献\\data\\train_image\\1";
	vector<string> files;
	getFiles(filePath, files);
	int number = (int)files.size();
	for (int i = 0; i < number; i++)
	{
    
    
		Mat  SrcImage = imread(files[i].c_str());
		SrcImage = SrcImage.reshape(1, 1);
		trainingImages.push_back(SrcImage);
		trainingLabels.push_back(1);
	}
}
void get_0(Mat& trainingImages, vector<int>& trainingLabels)
{
    
    
	char * filePath = (char *)"C:\\Users\\Administrator\\Desktop\\小李文献\\data\\train_image\\0";
	vector<string> files;
	getFiles(filePath, files);
	int number = (int)files.size();
	for (int i = 0; i < number; i++)
	{
    
    
		Mat  SrcImage = imread(files[i].c_str());
		SrcImage = SrcImage.reshape(1, 1);
		trainingImages.push_back(SrcImage);
		trainingLabels.push_back(0);
	}
}

The entire training process can be divided into the following parts:

2.1 Data preparation

This routine defines three subroutines to implement data preparation:
getFiles() is used to traverse all files in the folder. You can refer to:
http://blog.csdn.net/chaipp0607/article/details/53914954
getBubble() is used to obtain pictures with bubbles and corresponding Labels. This routine Set Labels to 1.
getNoBubble() is used to obtain pictures without bubbles and their corresponding Labels. This routine sets Labels to 0.
getBubble() and getNoBubble() will obtain a picture and write the picture (feature) into the container, and then write the label into another container, thus ensuring There is a one-to-one correspondence between features and labels. push_back(0) or push_back(1) is actually the process of labeling.

trainingImages.push_back(SrcImage);
trainingLabels.push_back(0);

In the main function, copy the matrix containing features written by getBubble() and getNoBubble() to trainingData. Convert the vector container containing labels and copy it to trainingLabels. At this point, the data preparation work is completed. trainingData and trainingLabels are The data we want to train on.

	Mat classes;
	Mat trainingData;
	Mat trainingImages;
	vector<int> trainingLabels;
	getBubble(trainingImages, trainingLabels);
	getNoBubble(trainingImages, trainingLabels);
	Mat(trainingImages).copyTo(trainingData);
	trainingData.convertTo(trainingData, CV_32FC1);
	Mat(trainingLabels).copyTo(classes);

2.2 Feature extraction

In fact, feature extraction and data preparation are completed simultaneously. What we finally need to train are the features of positive and negative samples. In this routine, the feature extraction work is also completed in the getBubble() and getNoBubble() functions, but we simply and roughly use all the pixels of the entire image as features. Because we pay more attention to the entire training process, we choose the best Complete feature extraction work in a simple way. In addition, there are many ways to extract features, such as LBP, HOG, etc.

  SrcImage= SrcImage.reshape(1, 1);

We use the reshape() function to complete feature extraction. The prototype is as follows:

 Mat reshape(int cn, int rows=0) const;

You can see that the parameters of this function are very simple. cn is the new channel number. If cn = 0, it means that the channel number will not change. The parameter rows is the new number of rows. If rows = 0, it means that the number of rows will not change. The result of defining the parameter as reshape(1, 1) is that the matrix corresponding to the original image will be stretched into a one-row vector as a feature vector.

2.3 Parameter configuration

Parameter configuration is the core part of SVM. In Opencv, it is defined as a structure type, as follows:

struct CV_EXPORTS_W_MAP CvSVMParams
{
    
    
    CvSVMParams();
    CvSVMParams(  
    int svm_type, 
    int kernel_type,
    double degree, 
    double coef0,
    double Cvalue, 
    double p,
    CvMat* class_weights, 
    CvTermCriteria term_crit );
    CV_PROP_RW int         svm_type;
    CV_PROP_RW int         kernel_type;
    CV_PROP_RW double      degree; // for poly
    CV_PROP_RW double      gamma;  // for poly/rbf/sigmoid
    CV_PROP_RW double      coef0;  // for poly/sigmoid
    CV_PROP_RW double      C;  // for CV_SVM_C_SVC,       CV_SVM_EPS_SVR and CV_SVM_NU_SVR
    CV_PROP_RW double      nu; // for CV_SVM_NU_SVC, CV_SVM_ONE_CLASS, and CV_SVM_NU_SVR
    CV_PROP_RW double      p; // for CV_SVM_EPS_SVR
    CvMat*      class_weights; // for CV_SVM_C_SVC
    CV_PROP_RW CvTermCriteria term_crit; // termination criteria
};

So in the routine we define a structure variable to configure these parameters, and this variable is the fifth parameter of the train function in the CVSVM class. The parameters are explained below.
SVM_params.svm_type: Type of SVM:
C_SVC represents SVM classifier, C_SVR represents SVM regression
SVM_params.kernel_type: kernel Function type
Linear kernel LINEAR:
d(x,y)=(x,y)
Polynomial kernel POLY:< /span> d(x,y)= tanh( gamma*(x'y)+ coef0) sigmoid core SIGMOID: d(x,y)=exp(-gamma*|x-y|^2) Radial basis kernel RBF:
d(x,y)=(gamma*(x'y)+coef0)degree



SVM_params.degree: The parameter degree in the kernel function, for the polynomial kernel function;
SVM_params.gama: The parameter gamma in the kernel function, for the polynomial/RBF/SIGMOID kernel function;
SVM_params.coef0: Parameters in the kernel function, for polynomial/SIGMOID kernel function;
SVM_params.c: SVM optimal problem parameters, set C-SVC, Parameters of EPS_SVR and NU_SVR;
SVM_params.nu: SVM optimal problem parameters, set the parameters of NU_SVC, ONE_CLASS and NU_SVR;
SVM_params.p: SVM optimal Problem parameters, set the value of the loss function p in EPS_SVR.

2.4 Training model

CvSVM svm;
svm.train(trainingData, classes, Mat(), Mat(), SVM_params);

Through the above process, we have prepared the data to be trained and the parameters required for training. **In fact, it can be understood that this preparation is the process of preparing actual parameters for the svm.train() function. **Let’s take a look at the svm.train() function. Opencv encapsulates SVM into the CvSVM library. This library is based on the LIBSVM package developed by Professor Lin Chih-Jen of National Taiwan University and others. Due to space limitations, not all Paste the definition of the library, so the following code is just part of the data and functions in the CvSVM library:

class CV_EXPORTS_W CvSVM : public CvStatModel
{
    
    
public:
virtual bool train( 
  const CvMat* trainData, 
  const CvMat* responses,
  const CvMat* varIdx=0, 
  const CvMat* sampleIdx=0,
  CvSVMParams params=CvSVMParams() );
virtual float predict( 
  const CvMat* sample, 
  bool returnDFVal=false ) const;

We use the train function defined in the application class to complete the model training work.

2.5 Save model

svm.save("svm.xml");

There is only one line of code to save the model. Using the save() function, let's take a look at its definition:

    CV_WRAP virtual void save( const char* filename, const char* name=0 ) const;

This function is defined in the CvStatModel class. CvStatModel is the statistical model base class in the ML library. Other ML classes inherit from this class.

Summary: At this point we have completed the model training work. We can see that there is actually very little code actually used for training. The encapsulation of OpenCV’s support vector machine greatly reduces our programming work.
Insert image description here
Insert image description here
Insert image description here

3. Load the model to implement classification

#include <stdio.h>  
#include <time.h>  
#include <opencv2/opencv.hpp>  
#include <opencv/cv.h>  
#include <iostream> 
#include <opencv2/core/core.hpp>  
#include <opencv2/highgui/highgui.hpp>  
#include <opencv2/ml/ml.hpp>  
#include <io.h>

using namespace std;
using namespace cv;
using namespace cv::ml;
void getFiles(string path, vector<string>& files);

int main()
{
    
    
	int result0 = 0;
	int result1 = 0;
	char * filePath = (char *)"C:\\Users\\Administrator\\Desktop\\小李文献\\data\\test_image\\0";
	vector<string> files;
	getFiles(filePath, files);
	int number = (int)files.size();
	cout << number << endl;
	
	/*Ptr<SVM> svm = SVM::create();//创建一个svm对象
	svm->clear();
	string modelpath = "C:\\Users\\Administrator\\Desktop\\小李文献\\data\\svm.xml";
	FileStorage svm_fs(modelpath, FileStorage::READ);
	if (svm_fs.isOpened())
	{
		svm->load(modelpath.c_str());
	}*/    //这一段代码有点错误

	cv::Ptr<cv::ml::SVM> svm = cv::ml::StatModel::load<cv::ml::SVM>("C:\\Users\\Administrator\\Desktop\\小李文献\\data\\svm.xml");
	for (int i = 0; i < number; i++)
	{
    
    
		Mat inMat = imread(files[i].c_str());
		Mat p = inMat.reshape(1, 1);
		p.convertTo(p, CV_32FC1);
		int response = (int)svm->predict(p);
		
			if (response == 0)
			{
    
    
				result0++;
			}
		
		/*	 if (response == 1)
			{
				result1++;
			}*/
		
		
	}
	cout << result0 << endl;
	//cout << result1 << endl;

	getchar();//有system("pause")的作用
	return  0;
}
void getFiles(string path, vector<string>& files)
{
    
    
	long long  hFile = 0;  //long long  = intptr_t
	struct _finddata_t fileinfo;
	string p;
	if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)
	{
    
    
		do
		{
    
    
			if ((fileinfo.attrib &  _A_SUBDIR))
			{
    
    
				if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 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);
		_findclose(hFile);
	}
}

uses the predict() function to predict classification results. predict() is defined in the CVSVM class.
Upgraded version, can identify 2 respectively

#include <stdio.h>  
#include <time.h>  
#include <opencv2/opencv.hpp>  
#include <opencv/cv.h>  
#include <iostream> 
#include <opencv2/core/core.hpp>  
#include <opencv2/highgui/highgui.hpp>  
#include <opencv2/ml/ml.hpp>  
#include <io.h>

using namespace std;
using namespace cv;
using namespace cv::ml;
void getFiles(string path, vector<string>& files);

int main()
{
    
    
	int result0_0 = 0;
	int result0_1 = 0;
	int result1_0 = 0;
	int result1_1 = 0;
	char * filePath = (char *)"C:\\Users\\Administrator\\Desktop\\小李文献\\data\\test_image\\0";
	vector<string> files;
	getFiles(filePath, files);
	char * filePath1 = (char *)"C:\\Users\\Administrator\\Desktop\\小李文献\\data\\test_image\\1";
	vector<string> files1;
	getFiles(filePath1, files1);
	int number = (int)files.size();
	int number1 = (int)files1.size();
	cout << number << endl;
	cout << number1 << endl;
	
	cv::Ptr<cv::ml::SVM> svm = cv::ml::StatModel::load<cv::ml::SVM>("C:\\Users\\Administrator\\Desktop\\小李文献\\data\\svm.xml");
	//测试样本0
	for (int i = 0; i < number; i++)
	{
    
    
		Mat inMat = imread(files[i].c_str());
		Mat p = inMat.reshape(1, 1);
		p.convertTo(p, CV_32FC1);
		int response = (int)svm->predict(p);

		if (response == 0)
		 {
    
    
			result0_0++;
		  }
	    if (response == 1)
		 {
    
    
			result0_1++;
		  }
	}

	cout << "样本0-result0:" << result0_0 << endl;
	cout << "样本0-result1:" << result0_1 << endl;
	//测试样本11
	for (int i = 0; i < number1; i++)
	{
    
    
		Mat inMat1 = imread(files1[i].c_str());
		Mat p1 = inMat1.reshape(1, 1);
		p1.convertTo(p1, CV_32FC1);
		int response1 = (int)svm->predict(p1);

		if (response1 == 0)
		{
    
    
			result1_0++;
		}
		if (response1 == 1)
		{
    
    
			result1_1++;
		}
	}

	cout << "样本1-result0:" << result1_0 << endl;
	cout << "样本1-result1:" << result1_1 << endl;

	getchar();//有system("pause")的作用
	return  0;
}
void getFiles(string path, vector<string>& files)
{
    
    
	long long  hFile = 0;  //long long  = intptr_t
	struct _finddata_t fileinfo;
	string p;
	if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)
	{
    
    
		do
		{
    
    
			if ((fileinfo.attrib &  _A_SUBDIR))
			{
    
    
				if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 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);
		_findclose(hFile);
	}
}

Note:
1. Why should we establish three independent projects?
The main reason is that if they are written together, the amount of code will be relatively large and the logic will not be separated clearly. After running through the above code, you can change it at will.
2. Why is data preparation added?
There were comments before about the problem of data. After providing the data, the experiment will be smoother, because the code itself does not have much gold content, so the project can be run more smoothly and modified.
3. Some situations that may easily cause exceptions:
(1): Note that the generated .xml must be copied to the prediction project;
(2): Pay attention to whether the prepared data path and code are consistent;
(3): Pay attention to the training features and the test features;

4. OpenCV application reads file path and file name and processes images in batches

When applying OpenCV to test a large number of images, the images need to be read in batches and processed. This method was used to deal with this problem before: put the pictures to be processed into a folder, select them all and rename them 1, so that the system will automatically rename them all to 1(1), 1(2) , 1(3) and so on
Then use the following code to read the picture in:

for ( i=1;i<=624;i++)
	{
    
    
	sprintf_s(adr, "C:\Users\Administrator\Desktop\第二组截图\1 (%d).jpg",i);
	Mat g_SrcImage;
    g_SrcImage=imread(adr);
	printf("i=%d",i);
	}

This method is very troublesome. You need to rename it manually, and then determine the value in the loop based on the number of pictures in the folder. There is a simpler and more flexible method, which is to traverse the path, name and total number of all pictures in the folder.
The following implementation actually has nothing to do with OpenCV itself. It is a function implementation defined in the io.h header file provided by C++.
First give the definition of the function:

void listFiles(const char * dir, vector<string>& files);

You can see that the function does not return a value, but stores the traversed file information into a vector. The complete code is implemented as follows:

#include <iostream>
#include <io.h>
#include <vector>
#include <string>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

using namespace std;
using namespace cv;

void listFiles(const char * dir, vector<string>& files);

int main()
{
    
    
	string path = "C:\\Users\\Administrator\\Desktop\\小李文献\\data\\test_image\\0";
	vector<string> files;
	listFiles(path.c_str(), files);
	for (int i = 0; i < files.size(); i++)
	{
    
    
		cout << files[i] << endl;
		Mat SrcImage = imread(files[i]);
		namedWindow("show", 0);
		imshow("show", SrcImage);
		waitKey(10);
	}
	waitKey(0);
	return 0;
}
//目录中的所有图片(到每一级目录)
void listFiles(const char * dir, vector<string>& files)
{
    
    
	char dirNew[200];
	strcpy_s(dirNew, dir);
	strcat_s(dirNew, "\\*.*");    // 在目录后面加上"\\*.*"进行第一次搜索
	intptr_t handle;
	_finddata_t findData;
	handle = _findfirst(dirNew, &findData);
	if (handle == -1)        // 检查是否成功
		return;
	do
	{
    
    
		if (findData.attrib & _A_SUBDIR)
		{
    
    
			if (strcmp(findData.name, ".") == 0 || strcmp(findData.name, "..") == 0)
				continue;
			cout << findData.name << "\t<dir>\n";
			// 在目录后面加上"\\"和搜索到的目录名进行下一次搜索
			strcpy_s(dirNew, dir);
			strcat_s(dirNew, "\\");
			strcat_s(dirNew, findData.name);
			listFiles(dirNew, files);
		}
		else
			files.push_back(string(dir).append("\\").append(findData.name));
		cout << findData.name << "\t" << findData.size << " bytes.\n";
	} while (_findnext(handle, &findData) == 0);
	_findclose(handle);    // 关闭搜索句柄
}

Running results
Insert image description here
Notes:
1. As you can see in the above code, the listFiles function actually uses recursion, which means that this The function can not only find files in the directory, but also find files at each level of the directory. In most cases, there is no need to distinguish whether to traverse the directory or traverse the directory, because the directory is created by ourselves and needs to be traversed. The path is also entered by ourselves, so we can use this as a function to traverse files in the directory.
2. The above code has been tested on both x64 and x86 platforms. The reason why the x86 platform runs normally, the x64 compiles successfully, and runs abnormally is because the return type of _findfirst() is intptr_t and Non-long type, the data is lost when converting from "intptr_t" to "long", so when creating the handle, you need: intptr_t handle;
3. By the way, when using this function, there is a Suffix parameters, for example, if you want to access all .txt
files, you can set the suffix parameters to: *txt
. This can also be entered. The path is relevant. If the path does not end with \, then remember to add the suffix parameter. Of course, you can also modify this function.

5. Logical operators and bitwise operators

Logical AND (&&), logical OR (||), bitwise AND (&), bitwise OR (|), bitwise XOR (^), bitwise negation (~)

[1] Logical AND (&&)
The value of the expression on both sides of the operator is true, and the result of the operation is true, and in other cases, it is false.

[2] Logical OR (||)
The value of the expression on both sides of the operator is false, the result of the operation is false, and the other cases are true.

[3] Bitwise AND (&)
Calculation method:
The two numbers participating in the operation are converted to binary (0, 1 ), perform the AND operation. It is only taken as 1 when all the corresponding bits are 1, and it is 0 when there are 0s.

011 & 110

011
110
---
010

[4] Bitwise OR (|)
Calculation method:
The two numbers participating in the operation are converted to binary (0, 1) After that, perform an OR operation. It is 1 when there is a 1 in the corresponding bit and 0 when all 0s are present.

011 | 110

011
110
---
111

[5] Bitwise identical OR (⊙)
Calculation method:
The two numbers participating in the operation are converted to binary (0, 1 ), perform an XOR operation. It takes 1 only if the numbers on the corresponding bits are the same, and 0 if they are not the same.

011110

011
110
---
010

[6] Bitwise XOR (^) xor
Calculation method:
The two numbers participating in the operation are converted into binary (0, 1), perform XOR operation. It takes 1 only if the numbers on the corresponding bits are different, and 0 if they are the same.

011 ^ 110

011
110
---
101

[7] Bitwise negation (~)
Calculation method:
The two numbers participating in the operation are converted to binary (0, 1 ), 0 becomes 1, and 1 becomes 0.
~(010) = 101

【8】优先级
not>and>xor>or

6. The function of getchar()

[1] Let the program stay at this step until it receives a message from the keyboard.
Add getchar() at the end of the program to prevent the program from exiting immediately, followed by system("pause"); has the same function. Maybe when you run it with ctrl + F5 after writing the code, without adding getchar(); the program will not exit immediately. Of course, the compiler has this function. However, if you open the code with an .exe file from the debug folder, there is no getchar() or system("pause"); the program will flash and disappear, maybe in a few tenths of a second. getchar(); lets the program stay at this step , until it receives a message from the keyboard.

[2] The role of the buffer
In the middle of two consecutive input statements from the keyboard. This is a bit interesting. I will use a piece of code to demonstrate it below
Insert image description here
I commented out getchar() in this code, take a look at the running results

Insert image description here
Do you see anything? The following gets(str2) sentence is executed directly. It ends before I enter the characters.

Look at the program with getchar() added
Insert image description here
Insert image description here
Do you see the difference? gets(str2) is not executed directly here, I will type the command below

Insert image description here
This is the running result I want.

So why does this happen? I also checked some information online. Here is my summary:

When input from the keyboard, the characters entered by the keyboard will be saved in the buffer. When the keyboard presses enter, the buffer is cleared and the contents of the buffer are written to the target. For example, the target of my code is str, that is The list I input from the keyboard is written into the str array. What else is there in the buffer at this time? To be precise, there is another character 'enter' in the buffer at this time.

If getchar() is not added, the buffer will write the character 'enter' into gets(str2), and the program will end directly like the above. But if getchar() is added; it will eat the character in the buffer. 'enter' character, then the buffer really has nothing. gets(str2) waits for the buffer to write content, then the program will execute as follows

6. Severity Code Description Project File Line Display Suppressed Status Error C4996 ‘strcat‘: This function or variable may be unsafe. Consider using

Solution: Change this sentence strcat(des, src); to strcat_s(des, src);

Reason: Because strcat(des, src); is unsafe to write like this. If this program is executed dynamically, the program is not sure whether the des character array is large enough. If it is really overlooked, the first If the character array is defined smaller than the second character array, buffer overflow will occur when the program runs. Once it overflows, the originally useful data may be overwritten. This is a very dangerous behavior.
So the compiler very smartly tells us that it is unsafe to write like this and we need to use the strcat_s(des, src) function instead. This function is provided by Microsoft and does not support cross-platform. The -s type is provided by Microsoft. It will let you specify the size of the buffer. It will judge whether the second character array is really put together and whether the space of the first character array is enough. If it is not enough, it will call the police directly. , it will not cause buffer overflow.

7. OpenCV3: Understanding the meaning of channel and bit depth

7.1 Matrix data type

– CV_<bit_depth>(S|U|F)C<number_of_channels>
S = signed integer U = unsigned integer F = floating point

CV_8UC1 refers to an 8-bit unsigned integer single-channel matrix,
CV_32FC2 refers to a 32-bit floating point dual-channel matrix
CV_8UC1 CV_8SC1 CV_16U C1 CV_16SC1
CV_8UC2 CV_8SC2 CV_16UC2 CV_16SC2
CV_8UC3 CV_8SC3 CV_16UC3 CV_16SC3
CV_8UC4 CV_8SC4 CV_16UC4 CV_16SC4
CV_32SC1 CV_32FC1 CV_64FC1
CV_32SC2 CV_32FC2 CV_64FC2
CV_32SC3 CV_32FC3 CV_64FC3
CV_32SC4 CV_32FC4 CV_64FC4

Among them, the channel indicates how many numbers each point can store, similar to how each pixel in the RGB color image has three values, that is, three channels.
The depth in the picture indicates how many bits each value is stored in. It is a matter of accuracy. Generally, the picture is 8 bits, so the depth is 8.

1–bit_depth—the number of bits—represents 8bite, 16bites, 32bites, 64bites—for example—for example,
If you create a storage now—grayscale image Mat object, the size of this image is 100 in width and 100 in height. Then, there are 10,000 pixels in this
grayscale image, and each pixel occupies 100% of the memory space. The space size is 8bit, 8 bits – so it corresponds to
CV_8
2–S|U|F–S–represents—signed int—signed Integer
U-represents-unsigned int-unsigned int
F-represents-float---------single-precision floating-point type a> 3-RGB image with Alpha channel-Yes-4 channel Image 2-RGB color image---------Yes-3 channel image 1–grayscale picture–grayImg—is a single-channel image
3–C<number_of_channels>----represents the number of channels of a picture, for example:


7.2 Summary of opencv cv::Mat data type

When using OpenCV in the following two scenarios, we must know the data type of the matrix elements in advance:

When using the at method to access data elements, you must specify the data type
When doing numerical operations, such as whether it is integer division or floating point division.
The object of the cv::Mat class has a member function type() used to return the data type of the matrix element,

The return value is of type int. Different return values ​​represent different types. The specific correspondence is as follows:
Insert image description here
C1, C2, C3, C4 in the header refer to the channel (Channel) number, for example:

Grayscale images have only 1 channel, which is C1;

The RGB color image in JPEG format has 3 channels and is C3

In addition to the RGB 3 channels, the color image in PNG format also has a transparency channel, so it is C4.

If it is just to clarify the data type before numerical calculation, then you can see it here

If you want to use the at method to access data elements, then you need the following step

Because taking a single channel as an example, the at method accepts data types such as uchar, not CV_8U.

With the number of channels and the data type of each channel known, the data types assigned to the at method are as shown in the following table:
Insert image description here
Now, you can use at to access the image of pixels:

    cv::Vec3b vec3b = img.at<cv::Vec3b>(0,0);
    uchar vec3b0 = img.at<cv::Vec3b>(0,0)[0];
    uchar vec3b1 = img.at<cv::Vec3b>(0,0)[1];
    uchar vec3b2 = img.at<cv::Vec3b>(0,0)[2];
    std::cout<<"vec3b = "<<vec3b<<std::endl;
    std::cout<<"vec3b0 = "<<(int)vec3b0<<std::endl;
    std::cout<<"vec3b1 = "<<(int)vec3b1<<std::endl;
    std::cout<<"vec3b2 = "<<(int)vec3b2<<std::endl;

The above data types and value ranges

Insert image description here
Definition of Vec class:

template<typename _Tp, int n> class Vec : public Matx<_Tp, n, 1> {
    
    ...};

typedef Vec<uchar, 2> Vec2b;
typedef Vec<uchar, 3> Vec3b;
typedef Vec<uchar, 4> Vec4b;

typedef Vec<short, 2> Vec2s;
typedef Vec<short, 3> Vec3s;
typedef Vec<short, 4> Vec4s;

typedef Vec<int, 2> Vec2i;
typedef Vec<int, 3> Vec3i;
typedef Vec<int, 4> Vec4i;

typedef Vec<float, 2> Vec2f;
typedef Vec<float, 3> Vec3f;
typedef Vec<float, 4> Vec4f;
typedef Vec<float, 6> Vec6f;

typedef Vec<double, 2> Vec2d;
typedef Vec<double, 3> Vec3d;

typedef Vec<double, 4> Vec4d;
typedef Vec<double, 6> Vec6d;

8. Understanding the channel of Mat

  1. Knowledge Points
    tips1: The number of channels of an image is N, which means that there are N numbers at each pixel. An a×b N-channel image has an image matrix. It is actually a numerical matrix of b rows and N×a columns.

The channels of images in OpenCV can be 1, 2, 3 and 4. The common ones are 1-channel and 3-channel, while 2-channel and 4-channel are less common.

  1通道的是灰度图。

  3通道的是彩色图像,比如RGB图像。

  4通道的图像是RGBA,是RGB加上一个A通道,也叫alpha通道,表示透明度。PNG图像是一种典型的4通道图像。alpha通道可以赋值0到1,或者0到255,表示透明到不透明。

  2通道的图像是RGB555和RGB565。2通道图在程序处理中会用到,如傅里叶变换,可能会用到,一个通道为实数,一个通道为虚数,主要是编程方便。RGB555是16位的,2个字节,5+6+5,第一字节的前5位是R,后三位+第二字节是G,第二字节后5位是B,可见对原图像进行压缩了。

tips2: imshow() is used to display images in OpenCV. As long as the data matrix of Mat meets the requirements of the image, imshow can be used to display it. It seems that the second channel is not possible. . . If it exceeds 4 channels, it is no longer an image, and imshow() cannot display it.

tips3: When imshow() displays a single-channel image, it must be a grayscale image. If we want to display the red R component, we should still display it as a three-channel image, but the G and B channels must be assigned values ​​of 0 or 255.

tips4: Use split() for channel decomposition and merg() for channel synthesis. Both functions are special cases of mixchannel().

9. opencv3 automatically generates a txt file from the image path in the folder

Convenient for opencv to traverse and process images

#include<iostream>
#include<vector>
#include<io.h>
#include<fstream>

using namespace std;

ofstream ofs("C:\\Users\\Administrator\\Desktop\\小李文献\\data\\test_image\\img_pow_sta2.txt", ios::out);  //ofstream ofs  ofs可以随便起名
vector<int> number;         

int num = 0;

void 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)
	{
    
    
		do
		{
    
    
			//如果是目录,迭代之  
			//如果不是,加入列表  
			if ((fileinfo.attrib &  _A_SUBDIR))
			{
    
    
				if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0) {
    
    
					getFiles(p.assign(path).append("\\").append(fileinfo.name), files);
					num++;
				}
			}
			else
			{
    
    
				files.push_back(p.assign(path).append("\\").append(fileinfo.name));
				number.push_back(num);
			}
		} while (_findnext(hFile, &fileinfo) == 0);
		_findclose(hFile);
	}
}

int main() {
    
    
	char* filepath = (char*)"C:\\Users\\Administrator\\Desktop\\小李文献\\data\\test_image\\0";
	vector<string> files;
	getFiles(filepath, files);
	//char str[30];
	int size = (int)files.size();
	for (int i = 1; i < size; i++) {
    
    
		ofs << files[i].c_str();
		ofs << " ";
		//off << number[i];
		ofs << "\n";
	}
	ofs.close();
	return 0;
}

Insert image description here

9.1opencv3.x traverses folders to read images

#include <opencv2\opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main(){
    
    
    //用void glob(String pattern, std::vector<String>& result, bool recursive = false);当recursive为false时,仅仅遍历指定文件夹内符合模式的文件,当recursive为true时,会同时遍历指定文件夹的子文件夹
    //pattern要绝对路径 其它测试有问题
    string pattern = "D:/Acodes/data/*.jpg";
    //cout << pattern << endl;
    vector<Mat> images;
    // 必须cv的String
    vector<String> fn;
    glob(pattern, fn, false);
    size_t count = fn.size();
    cout << count << endl;
    for (int i = 0; i < count; i++){
    
    
        images.push_back(imread(fn[i]));
        imshow("jaj", images[i]);
        waitKey(10);
    }
    return 1;
}

9.2 OpenCV: glob traverses all images in a folder

The procedure is as follows:

#include <opencv2\opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
    
    
    //要绝对路径
    string path = "D:\\data\\*.bmp";
    cout << path << endl;
    vector<Mat> images;
    // 必须cv的String
    vector<String> fn;
    glob(path, fn, false);
    size_t count = fn.size();
    cout << count << endl;
    for (int i = 0; i < count; i++)
    {
    
    
        images.push_back(imread(fn[i]));
        imshow("pic", images[i]);
        waitKey(10);
    }
    return 0;
}

[Note]:
//Use void glob(String pattern, std::vector& result, bool recursive = false);
/ /When recursive is false, only the files matching the pattern in the specified folder will be traversed. When recursive is true, the subfolders of the specified folder will also be traversed
Since the glob traversal image name is not based on It is traversed sequentially;
When reading image sequences, it is often necessary to read them in sequence, such as in multi-target tracking;
At this time, sort can be performed ;

//获取文件夹下所有图像名称,
// 图像名称按升序排列
int imageNameLists(string filePath, vector<string>& nameArr)
{
    
    
    vector<cv::String> fn;
    cv::glob(filePath, fn, false);
    size_t count = fn.size();
    if (count==0)
    {
    
    
        cout << "file " << filePath << " not  exits"<<endl;
        return -1;
    }
    for (int i = 0; i < count; ++i)
    {
    
    
        //1.获取不带路径的文件名,000001.jpg
        string::size_type iPos = fn[i].find_last_of('/') + 1;
        string filename = fn[i].substr(iPos, fn[i].length() - iPos);
        //cout << filename << endl;
        //2.获取不带后缀的文件名,000001
        string name = filename.substr(0, filename.rfind("."));
        //cout << name << endl;
        nameArr.emplace_back(name);
    }
    sort(nameArr.begin(), nameArr.end(),
         [](string a, string b) {
    
    return stoi(a) < stoi(b); });
    return 0;
}

9.3C++ OpenCV reads all images in the folder and renames them (format(), glob())

Knowledge points used in the article:

1.  cv::String cv::format(const char* fmg,...)  用于给要存储的照片指定路径。

2.  void glob(String pattern, std::vector<String>& result, bool recursive = false);  用于遍历指定路径下的文件。

code demo

The purpose of this code is as follows: read pictures with the suffix .jpg from a folder, save them to another specified folder, and name them in order.

You can add the remaining judgment or output other information to the code yourself.

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
 
int main()
{
    
    
    string yuanPath = "E:/test/*.jpg";
    string mudiPath = "E:/target";
 
    vector<string> result;
    glob(yuanPath, result, false); //从文件夹中读取图片路径名
 
    for (int i = 0; i < result.size(); i++) {
    
    
 
        string tarpath = mudiPath + format("/img%03d.jpg", i);//设置新的路径
 
        Mat img = imread(result[i], -1);//从路径读取图片,以不变的格式读取
        imwrite(tarpath, img);//以新路径存储图片
    }
 
    cout << "finish" << endl;
    return 0;
}

Function explanation

 void glob(String pattern, std::vector<String>& result, bool recursive = false);

Parameters: pattern is the path to be traversed; result is the result to be stored.

The recursive parameter defaults to false. At this time, the glob function will only search for files of matching types in the pattern path, and will not match other types and subfolders. If true, subfiles will be traversed. An example is as follows:

string path2 = "E:/test/*.jpg"
 
vector<String> result;
glob(path2, result, false); 
//只能输出path2路径下的图片名,并且不会遍历子文件
 
vector<String> result2;
glob(path2, result2, true); 
//输出path2路径下的图片名,并且会遍历子文件。

Insert image description here
result2 为{”E:/test\3.jpg“,”E:/test\311.jpg”,”E:/test\31111.jpg”,”E:/test\3111111.jpg”,”E:/test\311111111.jpg”,”E:/test\31111111111.jpg”,”E:/test\3111111111111.jpg”,”E:/test\target\img000.jpg”,”E:/test\target\img001.jpg”,”E:/test\target\img002.jpg”,”E:/test\target\img003.jpg”,”E:/test\target\img004.jpg”,”E:/test\target\img005.jpg”,”E:/test\target\img006.jpg”}

result 的{”E:/test\3.jpg“,”E:/test\311.jpg”,”E:/test\31111.jpg”,”E:/test\3111111.jpg”,”E: /test\311111111.jpg”,”E:/test\31111111111.jpg”,”E:/test\3111111111111.jpg”}

  cv::String cv::format(const char* fmg,...) 

The format function is similar to the sprintf function. They are all used to process string formatting.

But note that %s cannot handle the string type, because the read address may be the address of the object, not the first address of the string. This is the reason for debugging in vs. Therefore, when processing string formats, only the formats owned by c can be processed. (string str = “hanhan”; printf("%s",str); This is incorrect. You can output it like this: printf("%s\n",str.c_str());)

int sprintf(char* str, const char* format,...);

Upon success, the total number of characters written will be returned. This count does not include additional null characters that are automatically appended to the end of the string.
On failure, a negative number will be returned.

#include <cstdio>
 
int main ()
{
    
    
  char buffer [50];
  int n, a=5, b=3;
  n=sprintf (buffer, "%d plus %d is %d", a, b, a+b);
  printf ("[%s] is a string %d chars long\n",buffer,n);
  return 0;
}
//[5 plus 3 is 8] is a string 13 chars long

10. Opencv reads txt file

10.1opencv reads the txt file and assigns it to a Mat matrix

Purpose: opencv reads the txt file and assigns the txt file to the Mat matrix
Method: Use the fstream class to complete

// vv.cpp : 定义控制台应用程序的入口点。
//
//#include "stdafx.h"  

#include <stdio.h>  
#include <cv.h>  
#include "cvaux.h" //必须引此头文件  
#include "cxcore.h"
#include <iostream>  
#include <fstream>  
#include "opencv2/core/core.hpp"  
#include "opencv2/highgui/highgui.hpp"   
using namespace std;
using namespace cv;

int main(int argc, char** argv)
{
    
    
	fstream file1, file2;//创建文件流对象
	file1.open("H:\\code-practice\\file_practice\\特征提取加分类\\action1\\collect001.txt");
	file2.open("H:\\code-practice\\file_practice\\opencv读txt\\2222.txt");
	Mat Ky1_Data = Mat::zeros(100, 6, CV_32FC1);//创建Mat类矩阵,定义初始化值全部是0,矩阵大小和txt一致
	Mat Ky2_Data = Mat::zeros(100, 6, CV_32FC1);//同理

	//将txt文件数据写入到Data矩阵中
	for (int i = 0; i < 100; i++)
	{
    
    
		for (int j = 0; j < 6; j++)
		{
    
    
			file1 >> Ky1_Data.at<float>(i, j);
			//file2 >> Ky2_Data.at<float>(i, j);
		}
	}
	for (int i = 0; i < 100; i++)
	{
    
    
		for (int j = 0; j < 6; j++)
		{
    
    
			//file1 >> Ky1_Data.at<float>(i, j);
			file2 >> Ky2_Data.at<float>(i, j);
		}
	}
	cout << "矩阵1的数据输出为:" << endl;
	cout << Ky1_Data << endl;
	cout << endl;
	cout << "矩阵2的数据输出为:" << endl;
	cout << Ky2_Data << endl;
	waitKey(0);
	return 0;
}

collet001.txt file display
Insert image description here
2222.txt display
[Note]: 1. Space identification data
2. The mat data is completed according to the settings, for example, Mat Ky2_Data = Mat::zeros(100, 6, CV_32FC1); This line of code represents the generation of a 100X6 matrix, filled with 0s, so even if the file has 4 columns, it will read Take the second line
Insert image description here
upgrade

	//将txt文件数据写入到Data矩阵中
	for (int i = 0; i < Ky1_Data.rows; i++)
	{
    
    
		for (int j = 0; j < Ky1_Data.cols; j++)
		{
    
    
			file1 >> Ky1_Data.at<float>(i, j);
			//file2 >> Ky2_Data.at<float>(i, j);
		}
	}

Insert image description here

10.2 Write mat image data into txt file

//#include <iterator>
//#include <vector>
#include<opencv2\opencv.hpp>  
#include<core/core.hpp>    
#include<highgui/highgui.hpp>     
#include<cv.h>
#include <iostream>
#include <fstream>

using namespace std;
using namespace cv;
/*
* 功能 : 将 Mat 数据写入到 .txt 文件
* 函数 : WriteData
* 访问 : public
* 返回 : -1:打开文件失败;0:写入数据成功;1:矩阵为空
*
* 参数 : fileName	[in]	文件名
* 参数 : matData	[in]	矩阵数据
*/
int WriteData(string fileName, Mat& matData)
{
    
    
	int retVal = 0;

	// 检查矩阵是否为空
		if (matData.empty())
	{
    
    
		cout << "矩阵为空" << endl;
		retVal = 1;
		return (retVal);
	}

	// 打开文件
	ofstream outFile(fileName.c_str(), ios_base::out);	//按新建或覆盖方式写入
	if (!outFile.is_open())
	{
    
    
		cout << "打开文件失败" << endl;
		retVal = -1;
		return (retVal);
	}

	// 写入数据方法1
//	for (int i = 0; i < matData.rows; i++)
//	{
    
    
//		uchar* pixelPtr = matData.ptr<uchar>(i);            //获取矩阵每行首地址指针  
//		for (int j = 0; j < matData.cols*matData.channels(); j++)
//		{
    
    
//			int data = pixelPtr[j];
//			outFile << data << "\t";  //每列数据用 tab 隔开
//		}
//		outFile << endl;  //换行
//	}
//	return (retVal);
//}

// 写入数据方法2
	for (int r = 0; r < matData.rows; r++)
	{
    
    
		for (int c = 0; c < matData.cols; c++)
		{
    
    
			int data = matData.at<uchar>(r, c);	//读取数据,at<type> - type 是矩阵元素的具体数据格式
			outFile << data << "\t";	//每列数据用 tab 隔开
		}
		outFile << endl;	//换行
	}

	return (retVal);
}

int main(int argc, char* argv[])
{
    
    

	Mat scr = imread("H:\\code-practice\\file_practice\\opencv读txt\\1111.png");
	WriteData("H:\\code-practice\\file_practice\\opencv读txt\\4333.txt", scr);
}

10.3 Write mat matrix data into txt file

//#include <iterator>
//#include <vector>
#include<opencv2\opencv.hpp>  
#include<core/core.hpp>    
#include<highgui/highgui.hpp>     
#include<cv.h>
#include <iostream>
#include <fstream>

using namespace std;
using namespace cv;
/*
* 功能 : 将 Mat 数据写入到 .txt 文件
* 函数 : WriteData
* 访问 : public
* 返回 : -1:打开文件失败;0:写入数据成功;1:矩阵为空
*
* 参数 : fileName	[in]	文件名
* 参数 : matData	[in]	矩阵数据
*/
int WriteData(string fileName, Mat& matData)
{
    
    
	int retVal = 0;

	// 检查矩阵是否为空
		if (matData.empty())
	{
    
    
		cout << "矩阵为空" << endl;
		retVal = 1;
		return (retVal);
	}

	// 打开文件
	ofstream outFile(fileName.c_str(), ios_base::out);	//按新建或覆盖方式写入
	if (!outFile.is_open())
	{
    
    
		cout << "打开文件失败" << endl;
		retVal = -1;
		return (retVal);
	}

	// 写入数据方法1
//	for (int i = 0; i < matData.rows; i++)
//	{
    
    
//		uchar* pixelPtr = matData.ptr<uchar>(i);            //获取矩阵每行首地址指针  
//		for (int j = 0; j < matData.cols*matData.channels(); j++)
//		{
    
    
//			int data = pixelPtr[j];
//			outFile << data << "\t";  //每列数据用 tab 隔开
//		}
//		outFile << endl;  //换行
//	}
//	return (retVal);
//}

// 写入数据方法2
	for (int r = 0; r < matData.rows; r++)
	{
    
    
		for (int c = 0; c < matData.cols; c++)//这里只考虑单通道
		{
    
    
			int data = matData.at<uchar>(r, c);	//读取数据,at<type> - type 是矩阵元素的具体数据格式
			outFile << data << "\t";	//每列数据用 tab 隔开
		}
		outFile << endl;	//换行
	}

	return (retVal);
}

int main(int argc, char* argv[])
{
    
    

	Mat scr = Mat::ones(4, 4, CV_8UC1);
	
	WriteData("H:\\code-practice\\file_practice\\opencv读txt\\63331.txt", scr);

	Mat scr1 = Mat::ones(4, 4, CV_8UC2);

	WriteData("H:\\code-practice\\file_practice\\opencv读txt\\63332.txt", scr1);
	Mat scr2 = Mat::ones(4, 4, CV_8UC3);

	WriteData("H:\\code-practice\\file_practice\\opencv读txt\\63333.txt", scr2);
	cout << scr << endl;
	cout << scr1 << endl;
	cout << scr2 << endl;
	getchar();
	return 0;
}

[Explanation] 1. Write data method 1 and print the result (no problem with the channel)
Insert image description here
2. Write data method 2 and print the result (no problem with the channel)
Insert image description here
There will be problems when writing text, please modify it as follows

// 写入数据方法2
	for (int r = 0; r < matData.rows; r++)
	{
    
    
		for (int c = 0; c < matData.cols*matData.channels(); c++)
		{
    
    

			int data = matData.at<uchar>(r, c);	//读取数据,at<type> - type 是矩阵元素的具体数据格式
			outFile << data<< "\t";
			//outFile << data << "\t";	//每列数据用 tab 隔开
		}
		outFile << endl;	//换行
	}

	return (retVal);
}

10.4 VS prints out the version information of opencv

#include<iostream>
#include<opencv2/opencv.hpp>

int main()
{
    
    
	std::cout << "OpenCV version : " << CV_VERSION << std::endl;
	std::cout << "Major version : " << CV_MAJOR_VERSION << std::endl;
	std::cout << "Minor version : " << CV_MINOR_VERSION << std::endl;
	std::cout << "Subminor version : " << CV_SUBMINOR_VERSION << std::endl;

	system("pause");
	return 0;
}

10.5 A little test of SVM

#include <opencv2/opencv.hpp>
#include <opencv2/ml.hpp>
#include<iostream>
#include <cv.h>  
#include "cvaux.h" //必须引此头文件  
#include "cxcore.h"
#include <iostream>  
#include <fstream>  
using namespace std;
using namespace cv;
using namespace cv::ml;
int main(int, char**)
{
    
    
	//视觉表示的数据
	int width = 512, height = 512;
	Mat image = Mat::zeros(height, width, CV_8UC3);
	//设置训练数据
	int labels[4] = {
    
     1, -1, -1, -1 };
	//float trainingData[4][2] = { {501, 10}, {255, 10}, {501, 255}, {10, 501} };
	

	fstream file1;
    file1.open("E:\\code\\c++\\Project10\\Project10\\111.txt");
    Mat Ky1_Data = Mat::zeros(4, 2, CV_32FC1);//创建Mat类矩阵,定义初始化值全部是0,矩阵大小和txt一致
		//将txt文件数据写入到Data矩阵中
	for (int i = 0; i < 4; i++)
	{
    
    
		for (int j = 0; j < 2; j++)
		{
    
    
		file1 >> Ky1_Data.at<float>(i, j);
			//file2 >> Ky2_Data.at<float>(i, j);
		}
	}

	//Mat trainingDataMat(4, 2, CV_32FC1, trainingData);
	//cout << trainingDataMat << endl;
	//Mat trainingDataMat(4, 2, CV_32FC1, Ky1_Data);
	Mat trainingDataMat = Ky1_Data;
	cout << Ky1_Data << endl;
	Mat labelsMat(4, 1, CV_32SC1, labels);//4行1列
	//训练SVM
	Ptr<SVM> svm = SVM::create();//创建一个svm对象
	svm->setType(SVM::C_SVC); //设置SVM公式类型
	svm->setKernel(SVM::LINEAR);//设置SVM核函数类型
	svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));//设置SVM训练时迭代终止条件
	Ptr<TrainData> train_data = TrainData::create(trainingDataMat, ROW_SAMPLE, labelsMat); //创建训练集
	svm->train(train_data); //参数默认
	//svm->train(trainingDataMat, ROW_SAMPLE, labelsMat);//训练数据
	//显示SVM的决策区域
	Vec3b green(0, 255, 0), blue(255, 0, 0);
	for (int i = 0; i < image.rows; ++i)
		for (int j = 0; j < image.cols; ++j)
		{
    
    
			Mat sampleMat = (Mat_<float>(1, 2) << j, i);//蓝绿赋值
			float response = svm->predict(sampleMat);
			if (response == 1)
				image.at<Vec3b>(i, j) = green;
			else if (response == -1)
				image.at<Vec3b>(i, j) = blue;
		}
	//显示训练数据
	int thickness = -1;//-1表示实心
	int lineType = 8;
	circle(image, Point(501, 10), 5, Scalar(0, 0, 0), thickness, lineType);//半径为5
	circle(image, Point(255, 10), 5, Scalar(255, 255, 255), thickness, lineType);
	circle(image, Point(501, 255), 5, Scalar(255, 255, 255), thickness, lineType);
	circle(image, Point(10, 501), 5, Scalar(255, 255, 255), thickness, lineType);
	//显示支持向量
	thickness = 2;
	lineType = 8;
	Mat sv = svm->getUncompressedSupportVectors();
	//cout << sv << endl;//输出结果:[501,10; 255,10; 501,255]为什么???
	for (int i = 0; i < sv.rows; ++i)
	{
    
    
		const float* v = sv.ptr<float>(i);//指向矩阵sv的第i行
		circle(image, Point((int)v[0], (int)v[1]), 6, Scalar(128, 128, 128), thickness, lineType);//灰色,半径为6
	}
	imwrite("result.png", image);        //保存图像
	imshow("SVM Simple Example", image); //显示图像
	waitKey(0);
}

11. C++ reads the matrix in the text file and obtains the number of rows and columns of the matrix

For the number of rows in a text file that stores a matrix, we can easily use a getline function to get it. The code is below, but how to get the number of columns? Should we read it in row by row and then write code to decompose it? It's too troublesome. Now I think of a simple method. We know that there is a \n at the end of the text file as a newline character, and there is a space between elements. So every time we read a number, we judge the following one. The symbol is a newline character, so that we can get the number of columns in a row. Both the peek and get functions can be implemented. Peek only checks and will not make the stream advance, while get will make the stream advance.

#include <stdio.h>  
#include <cv.h>  
#include "cvaux.h" //必须引此头文件  
#include "cxcore.h"
#include <iostream>  
#include <fstream>  
#include "opencv2/core/core.hpp"  
#include "opencv2/highgui/highgui.hpp"   
using namespace std;
using namespace cv;

int getFileColumns(const char * fileName) {
    
    
	ifstream fileStream;
	fileStream.open(fileName, std::ios::_Nocreate);
	double tmp;
	char c;
	int count = 0;
	for (int i = 0; i < 10000; i++) {
    
    
		fileStream >> tmp;
		++count;
		c = fileStream.peek();
		if ('\n' == c)
		{
    
    
			break;
		}
	}
	fileStream.close();
	return count;
}

int getFileRows(const char *fileName) {
    
    
	ifstream fileStream;
	string tmp;
	int count = 0;
	fileStream.open(fileName);
	if (fileStream) {
    
    
		while (getline(fileStream, tmp, '\n')) {
    
    
			count++;
		}
		fileStream.close();
	}
	return count;
}



int main(int argc, char** argv)
{
    
    
	cout << getFileColumns("H:\\code-practice\\file_practice\\特征提取加分类\\action1\\collect001.txt") << endl;
	cout << getFileRows("H:\\code-practice\\file_practice\\特征提取加分类\\action1\\collect001.txt") << endl;
	system("pause");
	return 0;
}

Insert image description here
【2】The number of lines in the txt file of method 2

#include <stdio.h>  
#include <cv.h>  
#include "cvaux.h" //必须引此头文件  
#include "cxcore.h"
#include <iostream>  
#include <fstream>  
#include "opencv2/core/core.hpp"  
#include "opencv2/highgui/highgui.hpp"   
using namespace std;
using namespace cv;

int main(int argc, char** argv)
{
    
    
	fstream file1;//创建文件流对象
	file1.open("H:\\code-practice\\file_practice\\特征提取加分类\\action1\\collect001.txt");
	char c;
	int T0 = 0;//T0是txt文件的行数
	while (file1.get(c))
	{
    
    
		if (c == '\n')
			T0++;
	}
	cout << T0 << endl;
	file1.close();
	file1.open("H:\\code-practice\\file_practice\\特征提取加分类\\action1\\collect001.txt");
	Mat Ky1_Data = Mat::zeros(T0, 6, CV_32FC1);//创建Mat类矩阵,定义初始化值全部是0,矩阵大小和txt一致
	

	//将txt文件数据写入到Data矩阵中
	for (int i = 0; i < T0; i++)
	{
    
    
		for (int j = 0; j < Ky1_Data.cols; j++)
		{
    
    
			file1 >> Ky1_Data.at<float>(i, j);
			//file2 >> Ky2_Data.at<float>(i, j);
		}
	}

	cout << "矩阵1的数据输出为:" << endl;
	cout << Ky1_Data << endl;
	cout << endl;
	
	file1.close();
	
	waitKey(0);
	return 0;
}

[Note]: Combining the first acquisition method with opencv and Mat matrix, you can adaptively know the number of rows and columns of the txt file.

#include <stdio.h>  
#include <cv.h>  
#include "cvaux.h" //必须引此头文件  
#include "cxcore.h"
#include <iostream>  
#include <fstream>  
#include "opencv2/core/core.hpp"  
#include "opencv2/highgui/highgui.hpp"   
using namespace std;
using namespace cv;
int getFileColumns(const char * fileName);
int getFileRows(const char * fileName);


int main(int argc, char** argv)
{
    
    
	int gCol = getFileColumns("H:\\code-practice\\file_practice\\特征提取加分类\\action1\\collect001.txt");
	int gRow = getFileRows("H:\\code-practice\\file_practice\\特征提取加分类\\action1\\collect001.txt");
	cout << gCol << endl;
	cout << gRow << endl;
	fstream file1, file2;//创建文件流对象
	file1.open("H:\\code-practice\\file_practice\\特征提取加分类\\action1\\collect001.txt");
	
	Mat Ky1_Data = Mat::zeros(gRow, gCol, CV_32FC1);//创建Mat类矩阵,定义初始化值全部是0,矩阵大小和txt一致
	
	//将txt文件数据写入到Data矩阵中
	for (int i = 0; i < Ky1_Data.rows; i++)
	{
    
    
		for (int j = 0; j < Ky1_Data.cols; j++)
		{
    
    
			file1 >> Ky1_Data.at<float>(i, j);
			//file2 >> Ky2_Data.at<float>(i, j);
		}
	}

	cout << "矩阵1的数据输出为:" << endl;
	cout << Ky1_Data << endl;
	system("pause");
	//waitKey(0);
	return 0;
}

//获取txt文件的列数
int getFileColumns(const char * fileName) {
    
    
	ifstream fileStream;
	fileStream.open(fileName, std::ios::_Nocreate);
	double tmp;
	char c;
	int count = 0;
	for (int i = 0; i < 10000; i++) {
    
    
		fileStream >> tmp;
		++count;
		c = fileStream.peek();
		if ('\n' == c)
		{
    
    
			break;
		}
	}
	fileStream.close();
	return count;
}

//获取txt文件的行数
int getFileRows(const char *fileName) {
    
    
	ifstream fileStream;
	string tmp;
	int count = 0;
	fileStream.open(fileName);
	if (fileStream) {
    
    
		while (getline(fileStream, tmp, '\n')) {
    
    
			count++;
		}
		fileStream.close();
	}
	return count;
}

12. opencv multi-classification

12.1 opencv simple multi-classification

Insert image description here
Insert image description here

#include<opencv2\opencv.hpp> 

using namespace std;
using namespace cv;
using namespace cv::ml;

int main()
{
    
    

	//训练需要用到的数据
	int 标签[4] = {
    
     1, 2, 3, 4 };
	float 训练数据[4][2] = {
    
     {
    
     31, 12 },{
    
     65, 220 },{
    
     440, 350 },{
    
     400, 400 } };
	//转为Mat以调用
	Mat 训练Mat(4, 2, CV_32FC1, 训练数据);
	Mat 标签label(4, 1, CV_32SC1, 标签);
	//训练的初始化
	Ptr<SVM> svm = SVM::create();
	svm->setType(SVM::C_SVC);
	svm->setKernel(SVM::LINEAR);
	svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));
	//开始训练
	svm->train(训练Mat, ROW_SAMPLE, 标签label);

	//-----------无关紧要的美工的部分-----------------------	
	//----其实对每个像素点的坐标也进行了分类----------------
	int= 512,= 512;
	Mat 演示图片 = Mat::zeros(,, CV_8UC3);
	Vec3b green(0, 255, 0), blue(255, 0, 0), red(0, 0, 255), black(0, 0, 0);
	for (int i = 0; i < 演示图片.rows; ++i)
		for (int j = 0; j < 演示图片.cols; ++j)
		{
    
    
			Mat sampleMat = (Mat_<float>(1, 2) << j, i);
			float response = svm->predict(sampleMat);

			if (response == 1)
				演示图片.at<Vec3b>(i, j) = green;
			else if (response == 2)
				演示图片.at<Vec3b>(i, j) = blue;
			else if (response == 3)
				演示图片.at<Vec3b>(i, j) = red;
			else if (response == 4)
				演示图片.at<Vec3b>(i, j) = black;
		}
	//--------把初始化训练的点画进图片------------
	int thickness = -1;
	int lineType = 8;
	for (int 画点 = 0; 画点 < sizeof(标签) / sizeof(int); 画点++) {
    
    
		circle(演示图片, Point(训练数据[画点][0], 训练数据[画点][1]), 10, Scalar(255, 255, 255), thickness, -1);
	}
	// 把 support vectors  cout粗来看看……
	Mat sv = svm->getSupportVectors();
	cout << "Support Vectors为:" << endl;
	for (int i = 0; i < sv.rows; ++i)
	{
    
    
		const float* v = sv.ptr<float>(i);
		cout << v[0] << " " << v[1] << endl;
	}

	//测试测试

	Mat 结果;
	float teatData[2][2] = {
    
     {
    
     20, 11 },{
    
     310, 411 } };
	Mat query(2, 2, CV_32FC1, teatData);

	svm->predict(query, 结果);
	cout << "分类结果为:" << endl;
	cout << 结果;
	imshow("SVM显示", 演示图片);
	waitKey(-1);
}

result
Insert image description here

12.2 opencv complex multi-classification

[Note 1]: This needs to be adapted
[Note 2]: 2048 represents dimension. The background of the code is: a face can be represented by 2048-dimensional features

// svm_test.cpp : 定义控制台应用程序的入口点。
//
 
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/ml/ml.hpp>
 
using namespace cv;
using namespace std;
 
#define skyface_API	extern __declspec(dllexport)
skyface_API int sex_detect(vector<float> &feats, const char* modpth);
 
Mat traindata(string path, int num)
{
    
    
	vector<vector<float>> data(num, vector<float>(2048, 0));
	ifstream ifs;
	ifs.open(path);
	for (int i = 0; i < num; i++)
	{
    
    
		for (int j = 0; j < 2048; j++)
		{
    
    
			ifs >> data[i][j];
		}
	}
	ifs.close();
	Mat class_n_data(data.size(), data.at(0).size(), CV_32FC1);
	for (int i = 0; i < data.size(); i++)
		for (int j = 0; j < data.at(0).size(); j++)
			class_n_data.at<float>(i, j) = data.at(i).at(j);
	return class_n_data;
}
 
Mat get_traindata3(Mat class1, Mat class2, Mat class3)
{
    
    
	Mat traindata(class1.rows + class2.rows + class3.rows , 2048, CV_32FC1);
	Mat tmp = traindata.rowRange(0, class1.rows);
	class1.copyTo(tmp);
	tmp = traindata.rowRange(class1.rows, class1.rows + class2.rows);
	class2.copyTo(tmp);
	tmp = traindata.rowRange(class1.rows + class2.rows, class1.rows + class2.rows + class3.rows);
	class3.copyTo(tmp);
	cout << "获取到训练数据!" << endl;
	return traindata;
}
 
Mat get_labels3(Mat class1, Mat class2, Mat class3)
{
    
    
	Mat labels(class1.rows + class2.rows + class3.rows , 1, CV_32FC1);
	labels.rowRange(0, class1.rows).setTo(1);
	labels.rowRange(class1.rows, class1.rows + class2.rows).setTo(2);
	labels.rowRange(class1.rows + class2.rows, class1.rows + class2.rows + class3.rows).setTo(3);
	return labels;
}
 
 
void trainSVM(Mat traindata, Mat labels, string modelpth)
{
    
    
	//------------------------ 2. Set up the support vector machines parameters --------------------
	CvSVMParams params;
	params.svm_type = SVM::C_SVC;
	params.C = 0.1;
	params.kernel_type = SVM::LINEAR;
	params.term_crit = TermCriteria(CV_TERMCRIT_ITER, (int)1e7, 1e-6);
	//------------------------ 3. Train the svm ----------------------------------------------------
	cout << "Starting training process" << endl;
	CvSVM svm;
	svm.train(traindata, labels, Mat(), Mat(), params);
	cout << "Finished training process" << endl;
	svm.save("../data/model_AGE.txt");
}
 
int sex_detect(vector<float> &feats, const char* modpth)
{
    
    
	CvSVM SVM;
	SVM.load(modpth);
	int i;
	float* testdata = new float[2048];
	for (int i = 0; i < 2048; i++)
	{
    
    
		testdata[i] = feats[i];
	}
	Mat test = Mat(1, 2048, CV_32FC1, testdata);
	float result = SVM.predict(test);
	delete[] testdata;
	return result;
}
int main()
{
    
    
 
  //int labels[3]=[class1,class2,class3];
  
	Mat class1 = traindata("../data/feats_left.txt",40);
	Mat class2 = traindata("../data/feats_right.txt",36);
	Mat class3 = traindata("../data/feats_pos.txt",48);
	
	//Mat traindata = get_traindata(class1, class2);
	//Mat labels = get_labels(class1, class2);
	
	Mat traindata = get_traindata3(class1, class2, class3);
	Mat labels = get_labels3(class1, class2, class3);
	
	trainSVM(traindata, labels, "*");
	CvSVM SVM;
	SVM.load("../data/model_AGE.txt");
	ifstream ifs;
	float testdata[2048];
	ifs.open("../data/feats_test.txt");
		for (int i = 0; i < 2048; i++)
		{
    
    
			ifs >> testdata[i];
		}
		Mat test = Mat(1, 2048, CV_32FC1, testdata);
		float result = SVM.predict(test);
		if (result == 1)
			cout << "左偏30度" << endl;
		else if (result == 2)
			cout<< "右偏30度" <<endl;
		else if (result == 3)
		  cout<< "正脸" <<endl;
 
	ifs.close();
	system("pause");
}

13. opencv trainAuto usage

Insert image description here

13.1 Function prototype of train_auto

C++: bool CvSVM::train_auto(const Mat& trainData,

const Mat& responses, 

const Mat& varIdx, 

const Mat& sampleIdx, 

CvSVMParams params,

int k_fold=10, 

CvParamGrid Cgrid=CvSVM::get_default_grid(CvSVM::C), CvParamGrid gammaGrid=CvSVM::get_default_grid(CvSVM::GAMMA), CvParamGrid pGrid=CvSVM::get_default_grid(CvSVM::P), CvParamGrid nuGrid=CvSVM::get_default_grid(CvSVM::NU), CvParamGrid coeffGrid=CvSVM::get_default_grid(CvSVM::COEF), CvParamGrid degreeGrid=CvSVM::get_default_grid(CvSVM::DEGREE), 
bool balanced=false
)

Parameter annotations for automatic training functions (13)

The first five parameters refer to the parameter comments of the constructor. k_fold:
Cross-validation parameters. The training set is divided into self-subsets of k_fold. One subset is used to test the model, and the other subsets become the training set. Therefore, the SVM algorithm complexity is the number of times k_fold is executed.
*Grid: (6) corresponding SVM iteration grid parameters.
balanced: If true then this is a 2-category classification problem. This will create more balanced cross-validation subsets.

Instructions for using the automatic training function

This method automatically trains the SVM model based on the best parameters C, gamma, p, nu, coef0, degree in CvSVMParams.
parameters are considered optimal for cross-validation, with the smallest test set prediction error.
If there are no parameters to be optimized, the corresponding mesh step should be set to a value less than or equal to 1. For example, to avoid gamma optimization, set gamma_grid.step =
0, gamma_grid.min_val, and gamma_grid.max_val to any values. So params.gamma
is derived from gamma.
Finally, if parameter optimization is required but the corresponding grid is uncertain, you may need to call the function CvSVM::get_default_grid() to create a grid. For example, for gamma, call CvSVM::get_default_grid(CvSVM::GAMMA).
This function is a classification run (params.svm_type=CvSVM::C_SVC or
params.svm_type=CvSVM::NU_SVC) and a regression run (params.svm_type =CvSVM::EPS_SVR
or
params.svm_type=CvSVM::NU_SVR) works equally well. If params.svm_type=CvSVM::ONE_CLASS, no optimization is performed and a normal SVM is specified.
What needs to be noted here is that although train_auto can automatically select the optimal value for the required optimization parameters, the initial value must be assigned first in the code, otherwise the compilation will pass, but when running An error will be reported.
The following is the sample code

	CvSVMParams param;  
	param.svm_type = CvSVM::EPS_SVR;  
	param.kernel_type = CvSVM::RBF;  
	param.C = 1;  //给参数赋初始值
	param.p = 5e-3;  //给参数赋初始值
	param.gamma = 0.01;  //给参数赋初始值
	param.term_crit = cvTermCriteria(CV_TERMCRIT_EPS, 100, 5e-3); 
	//对不用的参数step设为0
	CvParamGrid nuGrid = CvParamGrid(1,1,0.0);
	CvParamGrid coeffGrid = CvParamGrid(1,1,0.0);
	CvParamGrid degreeGrid = CvParamGrid(1,1,0.0);
 
	CvSVM regressor;
	regressor.train_auto(PCA_training,tr_label,NULL,NULL,param,
		10,
		regressor.get_default_grid(CvSVM::C),
		regressor.get_default_grid(CvSVM::GAMMA),
		regressor.get_default_grid(CvSVM::P),
		nuGrid,
		coeffGrid,
		degreeGrid);

Using the above code, you can automatically train and optimize parameters. Finally, if you want to view the optimized parameter values, you can use the CvSVM::get_params() function to obtain the optimized CvSVMParams. Here is the sample code:

CvSVMParams params_re = regressor.get_params();
	regressor.save("training_srv.xml");
	float C = params_re.C;
	float P = params_re.p;
	float gamma = params_re.gamma;
	printf("\nParms: C = %f, P = %f,gamma = %f \n",C,P,gamma);

14. Mutual conversion between mat files and txt files

14.1xx.txt converted to xx.mat

Load(‘path\xx.txt’)

%Load the txt file. After the loading is successful, a variable with the same name as the txt file will appear in the Workspace.

%Note: If there is a "-" character in the txt file name, the corresponding character in the variable name in Workspace will become "_"

Save(‘path\xx.mat’, ‘variable name’)

example:

load('D:\matlabprogram\test-1.txt'save('D:\matlabprogram\test-1.mat','test_1')

14.2xx.mat converted to xx.txt

(1) The data format in the converted txt file is not considered

Load(‘path\xx.mat’)

Save('path\xx.txt','variable name','-ASCII')

The file format options available for the Save function are as follows:

load('路径\collect001.mat')
save('路径\collect001.txt','collect001','-ASCII')

[Note]: The upper and lower .mat files and .txt file names should be the same

(2) Consider the data format in the converted txt file
[Method 1]
Under the current path
Insert image description here

raw=load('collect001.mat');%.mat文件的数据读入raw中

%%将IMU的陀螺和加计数据存储到变量imu中(北-东-地 转为 东-北-天)
imu(:,1) = raw.collect001(:,1);
imu(:,2) = raw.collect001(:,2);
imu(:,3) = raw.collect001(:,3);
imu(:,4) = raw.collect001(:,4);
imu(:,5) = raw.collect001(:,5);
imu(:,6) = raw.collect001(:,6);

fid=fopen('imu.txt','w'); %打开txt文件,如果没有会自动创建

len = length(imu); 
for k=1:1:len  %开始逐行存储
    fprintf(fid,' %f %f %f %f %f %f\n',imu(k,1),imu(k,2),imu(k,3),imu(k,4),imu(k,5),imu(k,6));
end
fclose(fid);

Open the imu.txt file, the storage effect is as shown below,
Insert image description here
[Method 2] has not been successful yet

//把矩阵 matrix 保存成任意后缀的文件
//转换成 .txt 举例:mat2txt( 'filename.txt', data );
//转换成 .corr 举例:mat2txt( 'filename.corr',data );

function back = mat2txt( file_Name, matrix ) 
fop = fopen( file_Name, 'wt' );
[M,N] = size(matrix);
for m = 1:M
    for n = 1:N
        fprintf( fop, ' %s', mat2str( matrix(m,n) ) );
    end
    fprintf(fop, '\n' );
end
back = fclose( fop ) ;

[Method 3] has not been successful yet
Change to a method that combines the load function and the fprintf function, reading it line by line, and then writing it to the corresponding TXT file. (Because the TXT file I want in the end needs to have the same name as the previous file, there will be more detailed operations)

%topFolder表示.mat文件的上层目录
%outputFolder表示最终输出TXT文件的上层目录

function flag = mat2txt(topFolder, outputFolder)
%获取所有的.mat文件
AllFile = strcat(topFolder,'\*.','mat');
files = dir(AllFile);
len =length(files);
for i = 1:len
    fileName = files(i).name;
    %载入相应的mat文件
    loadFile = load(strcat(topFolder,fileName));
    %创建输出的TXT文件,windows中如果想用‘\n’来表示换行,需要使用'wt'读写模式
    outputFile = fopen(strcat(outputFolder,strrep(fileName,'.mat',''),'.txt'),'wt');
    %向txt文件中写数据
    %dataVariable表示.mat文件中含有的字段名
    %由于字段不同数据格式可能不同,所以一次只支持一个字段,根据自己的需要进行修改
    [m,n] = size(loadFile.dataVariable);
    for j = 1:m
        for k =1:n
            fprintf(outputFile, '%d ',loadFile.dataVariable(j,k));
            end
        fprintf(outputFile,'\n');
    end
    flag = fclose(outputFile);
end
end

15. Calculate the maximum, minimum, mean and variance of image pixels in OpenCv

15.1 Find the maximum and minimum values ​​of image pixels

The function minMaxLoc() function that finds the maximum and minimum values ​​of the image

minMaxLoc() function prototype

void cv::minMaxLoc(InputArray src, double * minVal, double * maxVal=0,
	Point * minLoc=0,Point * maxLoc=0,InputArray mask = noArray())

Among them, src is the image or matrix that needs to find the maximum and minimum values, and it must be a single channel; minVal: the minimum value of the image or matrix; maxVal: the maximum value of the image or matrix; minLoc: the minimum value of the image or matrix in the matrix coordinates in; maxLoc: the coordinates of the maximum value of the image or matrix in the matrix; mask: mask, used to set the specified area in the image or matrix to find the maximum value.

The position of the minMaxLoc() function outputs the maximum value is the position where the maximum value is first detected from left to right by row scanning. It must be certain when inputting parameters at the same time.

Point data type: This data type is used to represent the pixel coordinates of the image. The horizontal direction is the x-axis, the vertical direction is the y-axis, Point (x, y). For the two-dimensional coordinate data type, integer coordinates cv::Point2i (or cv::Point), double type coordinates cv::Point2d, and floating point coordinates cv::Point2f are defined. The definition of three-dimensional coordinate type is similar to that of two-dimensional coordinate data type. You only need to change 2 to 3. The specific data of the x and y axes in the coordinates can be accessed through variable attributes. For example: Point.x can read the x-axis data of the coordinates.

src is an image or matrix that needs to find the maximum and minimum values. It must be a single channel. For multi-channel matrix data, you need to use cv::Mat::reshape() to turn multi-channel into a single channel, or search for each channel separately. The maximum value of the channel is then compared.

cv::Mat::reshape() function prototype

Mat cv::Mat::reshape(int cn, int rows = 0)

where cn: the number of channels of the converted matrix; rows: the number of rows of the converted matrix. If the parameter is 0, the number of rows after conversion is the same as the number of rows before conversion.
The columns are still the same
Comprehensive example:

#include <opencv2\opencv.hpp>
#include <iostream>
#include <vector>

using namespace std;
using namespace cv;

int main()
{
    
    
	system("color F0");  //更改输出界面颜色
	float a[12] = {
    
     1, 2, 3, 4, 5, 10, 6, 7, 8, 9, 10, 0 };
	Mat img = Mat(3, 4, CV_32FC1, a);  //单通道矩阵
	Mat imgs = Mat(2, 3, CV_32FC2, a);  //多通道矩阵
	double minVal, maxVal;  //用于存放矩阵中的最大值和最小值
	Point minIdx, maxIdx; // 用于存放矩阵中的最大值和最小值在矩阵中的位置
	cout << img << endl;
	cout << imgs << endl;
		/*寻找单通道矩阵中的最值*/
		minMaxLoc(img, &minVal, &maxVal, &minIdx, &maxIdx);
	cout << "img中最大值是:" << maxVal << "  " << "在矩阵中的位置:" << maxIdx << endl;
	cout << "img中最小值是:" << minVal << "  " << "在矩阵中的位置:" << minIdx << endl;

	/*寻找多通道矩阵中的最值*/
	Mat imgs_re = imgs.reshape(1, 4);  //将多通道矩阵变成单通道矩阵
	cout << imgs_re << endl;
	minMaxLoc(imgs_re, &minVal, &maxVal, &minIdx, &maxIdx);
	cout << "imgs中最大值是:" << maxVal << "  " << "在矩阵中的位置:" << maxIdx << endl;
	cout << "imgs中最小值是:" << minVal << "  " << "在矩阵中的位置:" << minIdx << endl;
	return 0;
}

Running results: The mat matrix is ​​recorded starting from 0
Insert image description here

15.2 Calculate the mean and standard deviation of an image

The average value of the image represents the brightness and darkness of the overall image. The greater the average value, the brighter the overall image. The standard deviation represents the contrast degree of light and dark changes in the image. The larger the standard deviation, the more obvious the light and dark changes in the image.

mean() function prototype

cv::Scalar cv::mean(InputArray src, InputArray mask= noArray())

Among them, src: the image matrix to be averaged (the number of channels can be 1~4). mask: Mask, used to mark the average of those areas.

This function finds the average value of each channel. The return value of this function is a cv::Scalar type variable. The return value of the function has 4 digits, which represent the average of the 4 channels of the input image. If the input image has only one channel, the last 3 digits of the return value are 0. You can view the average value of the nth channel through cv::Scalar[n].
meanStdDev() function prototype

void cv::meanStdDev(InputArray src, OutputArray mean, OutputArray stddev, 
                        InputArray mask =noArray())

Among them, src: the image matrix to be averaged; mean: the average value of each channel of the image, and the parameter is a Mat type variable;

stddev: the standard deviation of each channel of the image, the parameter is a Mat type variable; mask: mask, used to mark the average and standard deviation of those areas. This function has no return value. The mean and standard deviation of the image are output in the second and third parameters of the function.

Comprehensive example: including extracting a column

#include <opencv2\opencv.hpp>
#include <iostream>
#include <vector>

using namespace std;
using namespace cv;

int main()
{
    
    
	system("color F0");  //更改输出界面颜色
	float a[12] = {
    
     1, 2, 3, 4, 5, 10, 6, 7, 8, 9, 10, 0 };
	Mat img = Mat(3, 4, CV_32FC1, a);  //单通道矩阵
	Mat imgs = Mat(2, 3, CV_32FC2, a);  //多通道矩阵
	Mat A = Mat::zeros(3, 1, CV_32FC1);
	img.col(0).copyTo(A.col(0));//提取某一列
	cout << "/* 用meanStdDev同时求取图像的均值和标准差 */" << endl;
	Scalar myMean;
	Scalar myMean1;
	myMean = mean(imgs);

	myMean1 = mean(A);
	
	cout << "imgs均值=" << myMean << endl;
	cout << "img第一列均值=" << myMean1 << endl; //提取某一列平均值
	cout << "imgs第一个通道的均值=" << myMean[0] << "    "
		<< "imgs第二个通道的均值=" << myMean[1] << endl << endl;

	cout << "/* 用meanStdDev同时求取图像的均值和标准差 */" << endl;
	Mat myMeanMat, myStddevMat;

	meanStdDev(img, myMeanMat, myStddevMat);//可以同时输出2个参数
	cout << "img均值=" << myMeanMat << "    " << endl;
	cout << "img标准差=" << myStddevMat << endl << endl;
	meanStdDev(imgs, myMeanMat, myStddevMat);
	cout << "imgs均值=" << myMeanMat << "    " << endl << endl;
	cout << "imgs标准差=" << myStddevMat << endl;
	return 0;
}

Run results
Insert image description here
Upgrade, loop to extract a certain column

#include <opencv2\opencv.hpp>
#include <iostream>
#include <vector>

using namespace std;
using namespace cv;

int main()
{
    
    
	system("color F0");  //更改输出界面颜色
	float a[12] = {
    
     1, 2, 3, 4, 5, 10, 6, 7, 8, 9, 10, 0 };
	Mat img = Mat(3, 4, CV_32FC1, a);  //单通道矩阵
	Mat imgs = Mat(2, 3, CV_32FC2, a);  //多通道矩阵
	Mat A = Mat::zeros(3, 1, CV_32FC1);
	//img.col(0).copyTo(A.col(0));//提取某一列
	cout << "/* 用meanStdDev同时求取图像的均值和标准差 */" << endl;
	Scalar myMean;
	Scalar myMean1;
	myMean = mean(imgs);
	//循环提取img Mat矩阵每一列
	for (int i = 0; i < 4; i++)
	{
    
    
		img.col(i).copyTo(A.col(0));//提取某一列
		myMean1 = mean(A);
		cout << "img每一列均值=" << myMean1 << endl; //提取某一列平均值
	}
	
	cout << "imgs均值=" << myMean << endl;
	//cout << "img第一列均值=" << myMean1 << endl; //提取某一列平均值
	cout << "imgs第一个通道的均值=" << myMean[0] << "    "
		<< "imgs第二个通道的均值=" << myMean[1] << endl << endl;

	cout << "/* 用meanStdDev同时求取图像的均值和标准差 */" << endl;
	Mat myMeanMat, myStddevMat;

	meanStdDev(img, myMeanMat, myStddevMat);//可以同时输出2个参数
	cout << "img均值=" << myMeanMat << "    " << endl;
	cout << "img标准差=" << myStddevMat << endl << endl;
	meanStdDev(imgs, myMeanMat, myStddevMat);
	cout << "imgs均值=" << myMeanMat << "    " << endl << endl;
	cout << "imgs标准差=" << myStddevMat << endl;
	return 0;
}

Output result:
Insert image description here

15.3opencv extracts certain rows and columns in Mat

Original Mat format data:

cv::Mat A = Mat::zeros(4, 5, CV_32F);45列,高45

1. Extract rows
Function: Mat::rowRange(int startrow, int endrow)
Example: Extract rows 0~2 (including Line 2)

cv::Mat B = A.rowRange(0, 3).clone() ;

2. Extract columns
Function: Mat::colRange(int startcol, int endcol)
Example: Extract columns 2~4 (including Column 4)

cv::Mat C = A.colRange(2, 5).clone() ;

Note that rowRange(start,end) and colRange(start,end) both include the left boundary and do not include the right boundary.
3. copyTo() function

Mat c = Mat::zeros(3, 5, CV_32F);
Mat a = Mat::ones(3, 6, CV_32F);

1) Assign the first column of c to a

c.col(0).copyTo(a.col(0));

2) Assign columns 1-5 of c to a

c.copyTo(a.colRange(1, 6));

Guess you like

Origin blog.csdn.net/weixin_50624597/article/details/124259032