赞
踩
在OpenCV的安装路径下,搜索digits,可以得到一张图片,图片大小为10002000,有0-9的10个数字,每5行为一个数字,总共50行,共有5000个手写数字,每个数字块大小为2020。 下面将把这些数字中的0和1作为二分类的准备数据。其中0有500张,1有500张。
用下面的代码将图片准备好,在写入路径提前建立好文件夹:
#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; }
最后可以得到这样的结果:
组织的二分类数据形式为:
–D:
–data
–train_image
–0(400张)
–1(400张)
–test_image
–0(100张)
–1(100张)
同时也适合截取多个文件夹,代码一样,多点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; }
里面有图片数据
数据准备完成之后,就可以用下面的代码训练了:
#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); } }
整个训练过程可以分为一下几个部分:
该例程中一个定义了三个子程序用来实现数据准备工作:
getFiles()用来遍历文件夹下所有文件,可以参考:
http://blog.csdn.net/chaipp0607/article/details/53914954
getBubble()用来获取有气泡的图片和与其对应的Labels,该例程将Labels定为1。
getNoBubble()用来获取没有气泡的图片与其对应的Labels,该例程将Labels定为0。
getBubble()与getNoBubble()将获取一张图片后会将图片(特征)写入到容器中,紧接着会将标签写入另一个容器中,这样就保证了特征和标签是一一对应的关系push_back(0)或者push_back(1)其实就是我们贴标签的过程。
trainingImages.push_back(SrcImage);
trainingLabels.push_back(0);
在主函数中,将getBubble()与getNoBubble()写好的包含特征的矩阵拷贝给trainingData,将包含标签的vector容器进行类型转换后拷贝到trainingLabels里,至此,数据准备工作完成,trainingData与trainingLabels就是我们要训练的数据。
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);
其实特征提取和数据的准备是同步完成的,我们最后要训练的也是正负样本的特征。本例程中同样在getBubble()与getNoBubble()函数中完成特征提取工作,只是我们简单粗暴将整个图的所有像素作为了特征,因为我们关注更多的是整个的训练过程,所以选择了最简单的方式完成特征提取工作,除此中外,特征提取的方式有很多,比如LBP,HOG等等。
SrcImage= SrcImage.reshape(1, 1);
我们利用reshape()函数完成特征提取,原型如下:
Mat reshape(int cn, int rows=0) const;
可以看到该函数的参数非常简单,cn为新的通道数,如果cn = 0,表示通道数不会改变。参数rows为新的行数,如果rows = 0,表示行数不会改变。我们将参数定义为reshape(1, 1)的结果就是原图像对应的矩阵将被拉伸成一个一行的向量,作为特征向量。
参数配置是SVM的核心部分,在Opencv中它被定义成一个结构体类型,如下:
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 };
所以在例程中我们定义了一个结构体变量用来配置这些参数,而这个变量也就是CVSVM类中train函数的第五个参数,下面对参数进行说明。
SVM_params.svm_type :SVM的类型:
C_SVC表示SVM分类器,C_SVR表示SVM回归
SVM_params.kernel_type:核函数类型
线性核LINEAR:
d(x,y)=(x,y)
多项式核POLY:
d(x,y)=(gamma*(x’y)+coef0)degree
径向基核RBF:
d(x,y)=exp(-gamma*|x-y|^2)
sigmoid核SIGMOID:
d(x,y)= tanh(gamma*(x’y)+ coef0)
SVM_params.degree:核函数中的参数degree,针对多项式核函数;
SVM_params.gama:核函数中的参数gamma,针对多项式/RBF/SIGMOID核函数;
SVM_params.coef0:核函数中的参数,针对多项式/SIGMOID核函数;
SVM_params.c:SVM最优问题参数,设置C-SVC,EPS_SVR和NU_SVR的参数;
SVM_params.nu:SVM最优问题参数,设置NU_SVC, ONE_CLASS 和NU_SVR的参数;
SVM_params.p:SVM最优问题参数,设置EPS_SVR 中损失函数p的值.
CvSVM svm;
svm.train(trainingData, classes, Mat(), Mat(), SVM_params);
通过上面的过程,我们准备好了待训练的数据和训练需要的参数,**其实可以理解为这个准备工作就是在为svm.train()函数准备实参的过程。**来看一下svm.train()函数,Opencv将SVM封装成CvSVM库,这个库是基于台湾大学林智仁(Lin Chih-Jen)教授等人开发的LIBSVM封装的,由于篇幅限制,不再全部粘贴库的定义,所以一下代码只是CvSVM库中的一部分数据和函数:
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;
我们就是应用类中定义的train函数完成模型训练工作。
svm.save("svm.xml");
保存模型只有一行代码,利用save()函数,我们看下它的定义:
CV_WRAP virtual void save( const char* filename, const char* name=0 ) const;
该函数被定义在CvStatModel类中,CvStatModel是ML库中的统计模型基类,其他 ML 类都是从这个类中继承。
总结:到这里我们就完成了模型训练工作,可以看到真正用于训练的代码其实很少,OpenCV最支持向量机的封装极大地降低了我们的编程工作。
#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); } }
用到predict()函数用来预测分类结果,predict()被定义在CVSVM类中。
升级版,可以分别识别2个
#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); } }
注意:
1.为什么要建立三个独立的工程呢?
主要是考虑写在一起话,代码量会比较大,逻辑没有分开清晰,当跑通上面的代码之后,就可以随意的改了。
2.为什么加上数据准备?
之前有评论说道数据的问题,提供数据后实验能更顺利一些,因为本身代码没有什么含金量,这样可以更顺利的运行起来工程,并修改它。
3.一些容易引起异常的情况:
(1):注意生成的.xml记得拷贝到预测工程下;
(2):注意准备好数据路径和代码是不是一致;
(3):注意训练的特征要和测试的特征一致;
在应用OpenCV大量测试图片时,需要对图片批量的读入并进行处理。之前处理这个问题时是使用这种方法:把待处理的图片放到一个文件夹内,全选它们然后重命名1,这样系统会自动给他们全部重命名为1(1),1(2),1(3)等等等
然后用下面的代码把图片读进来:
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);
}
这种方法很麻烦,需要手动重命名一遍,然后根据文件夹下的图片个数确定循环中的值。有一种更简便并且灵活性更高的方法,就是遍历文件夹内所有图片的路径,名称和总个数。
下面这种实现方式其实和OpenCV本身没什么关系了,是一种应用C++提供的io.h头文件中定义的函数实现。
先给出函数的定义:
void listFiles(const char * dir, vector<string>& files);
可以看到函数没有返回值,而是将遍历到的文件信息存储到vector中,完整的代码实现如下:
#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); // 关闭搜索句柄 }
运行结果
备注:
1.在上面的代码中可以看到,listFiles函数其实在利用递归,这意味着,这个函数不仅仅可以找目录中的文件,还可以找到目录下每一层的文件,在大多数情况下并不需要区分是遍历目录下还是遍历目录中,因为目录是我们自己创建的,要遍历的路径也是自己输入,所以我们完全可以把这个当做遍历目录中文件的函数来用。
2.上述代码在x64,x86平台上都测试通过,之所以出现x86平台运行正常,x64编译通过,运行出现异常,是因为_findfirst()返回类型为intptr_t而非long型,从“intptr_t”转换到“long”丢失了数据,所以创建句柄时需要:intptr_t handle;
3.顺便提醒一下,在使用这个函数的时候,有一个后缀参数,例如希望访问到所有的 .txt
文件,那么在使用的时候,可以把后缀参数设置为: *txt
这个也有输入的path有关系,如果path不是以\结尾,那么后缀参数记得加上,当然修改这个函数也行。
逻辑与(&&)、逻辑或(||)、按位与(&)、按位或(|)、按位异或(^)、按位取反(~)
【1】逻辑与(&&)
运算符两边的表达式的值都为true运算结果为true, 其余情况为false。
【2】逻辑或(||)
运算符两边的表达式的值都为false运算结果为false, 其余情况为true。
【3】按位与(&)
计算方法:
参加运算的两个数,换算为二进制(0、1)后,进行与运算。只有当 相应位上全部为1时取1, 存在0时为0。
011 & 110
011
110
---
010
【4】按位或(|)
计算方法:
参加运算的两个数,换算为二进制(0、1)后,进行或运算。只要当 相应位上存在1时取1, 全部为0时为0。
011 | 110
011
110
---
111
【5】按位同或(⊙)
计算方法:
参加运算的两个数,换算为二进制(0、1)后,进行异或运算。只有当 相应位上的数字相同时取1, 不相同为0。
011 ⊙ 110
011
110
---
010
【6】按位异或(^) xor
计算方法:
参加运算的两个数,换算为二进制(0、1)后,进行异或运算。只有当 相应位上的数字不相同时取1, 相同为0。
011 ^ 110
011
110
---
101
【7】按位取反(~)
计算方法:
参加运算的两个数,换算为二进制(0、1)后, 0变1, 1变0。
~(010) = 101
【8】优先级
not>and>xor>or
【1】让程序停留在这一步,直到它从键盘接收到消息.
在程序末尾加getchar(),用来让程序不会立即退出,跟system(“pause”);是一样的功能.可能你在写完代码后用ctrl + F5运行时,不加getchar();程序也不会立即退出,这是当然的,编译器有这个功能.不过如果你从debug文件夹下用.exe文件打开代码,没有getchar()或system(“pause”);程序会闪一下就消失,可能就零点几秒.getchar();让程序停留在这一步,直到它从键盘接收到消息。
【2】缓冲区的作用
在两次连续从键盘输入语句中间.这个就有点意思了,下面我用一段代码演示
这段代码里getchar()被我注释掉了,看一下运行结果
看出什么了吗?下面那句gets(str2)直接执行了,我还没有输入字符,它就执行结束了.
看一下加了getchar()的程序
看到差异了吗?这里gets(str2)直接没有执行,下面我键入命令
这才是我想要的运行结果.
那么为什么会出现这种情况呢,我自己也上网查了些许资料,以下为本人总结:
当从键盘输入时,键盘输入的字符会保存在缓冲区,当键盘按下enter建时,缓冲区被清空,缓冲区的内容被写入目标内,比如我这段代码的目标就是str,即我从键盘输入的list被写入str数组里,这个时候缓冲区还有什么呢?准确的说,这时缓冲区里还有一个字符’enter’,
如果不加getchar(),缓冲区会把’enter’这个字符写进gets(str2),这时程序就会像上面那样,直接结束.而加了getchar();它会吃了缓冲区里的’enter’字符,这时候缓冲区才是真的什么都没有,gets(str2)等待缓冲区写入内容,这时程序才会像下面那样执行
解决方法: 把这句话strcat(des, src);改为strcat_s(des, src);
原因: 因为strcat(des, src);这样写不安全,如果这个程序动态的去执行的话,程序不确定des这个字符数组够不够大,如果真的疏忽了,把第一个字符数组定义的比第二个字符数组小,这样程序运行起来,就会发生缓冲区溢出,一旦溢出,就可能把本来有用的数据给覆盖了,这是一种非常危险的行为。
所以编译器非常智能的告诉我们,这样写不安全,要用strcat_s(des, src)这个函数来代替,这个函数是微软提供的,是不支持跨平台的,-s的这种都是微软提供的,他会让你指定缓冲区的大小,他会判断第二个字符数组如果真的拼上去,第一个字符数组的空间够不够,不够的话就直接报警了,他不会发生缓冲区溢出这种情况。
– CV_<bit_depth>(S|U|F)C<number_of_channels>
S = 符号整型 U = 无符号整型 F = 浮点型
CV_8UC1 是指一个8位无符号整型单通道矩阵,
CV_32FC2是指一个32位浮点型双通道矩阵
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
其中,通道表示每个点能存放多少个数,类似于RGB彩色图中的每个像素点有三个值,即三通道的。
图片中的深度表示每个值由多少位来存储,是一个精度问题,一般图片是8bit(位)的,则深度是8.
1–bit_depth—比特数—代表8bite,16bites,32bites,64bites—举个例子吧–比如说,如
如果你现在创建了一个存储–灰度图片的Mat对象,这个图像的大小为宽100,高100,那么,现在这张
灰度图片中有10000个像素点,它每一个像素点在内存空间所占的空间大小是8bite,8位–所以它对
应的就是CV_8
2–S|U|F–S–代表—signed int—有符号整形
U–代表–unsigned int–无符号整形
F–代表–float---------单精度浮点型
3–C<number_of_channels>----代表—一张图片的通道数,比如:
1–灰度图片–grayImg—是–单通道图像
2–RGB彩色图像---------是–3通道图像
3–带Alph通道的RGB图像–是–4通道图像
在以下两个场景中使用OpenCV时,我们必须事先知道矩阵元素的数据类型:
使用 at 方法访问数据元素的时候要指明数据类型
做数值运算的时候,比如究竟是整数除法还是浮点数除法。
cv::Mat 类的对象有一个成员函数type()用来返回矩阵元素的数据类型,
返回值是 int 类型,不同的返回值代表不同的类型,具体对应关系如下所示:
表头的 C1, C2, C3, C4 指的是通道(Channel)数,例如:
灰度图像只有 1 个通道,是 C1;
JPEG格式 的 RGB 彩色图像就是 3 个通道,是 C3
PNG 格式的彩色图像除了 RGB 3个通道外,还有一个透明度通道,所以是 C4。
如果仅仅是为了在数值计算前明确数据类型,那么看到这里就可以了
如果是要使用 at 方法访问数据元素,那么还需要下面一步
因为以单通道为例,at 方法接受的是 uchar 这样的数据类型,而非 CV_8U。
在已知通道数和每个通道数据类型的情况下,指定给 at 方法的数据类型如下表所示:
现在,就可以使用at来访问图像的像素了:
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;
上述数据类型以及取值范围
Vec类的定义:
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;
OpenCV中图像的通道可以是1、2、3和4。其中常见的是1通道和3通道,2通道和4通道不常见。
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: OpenCV中用imshow( )来显示图像,只要Mat的数据矩阵符合图像的要求,就可以用imshow来显示。二通道好像不可以。。。超过了4通道,就不是图像了,imshow( )也显示不了。
tips3: imshow( )显示单通道图像时一定是灰度图,如果我们想显示红色的R分量,还是应该按三通道图像显示,只不过G和B通道要赋值成0或255.
tips4: 通道分解用split( ),通道合成用merg( ),这俩函数都是mixchannel( )的特例。
便于opencv遍历处理图像
#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; }
#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; }
程序如下:
#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; }
【注】:
//用void glob(String pattern, std::vector& result, bool recursive = false);
//当recursive为false时,仅仅遍历指定文件夹内符合模式的文件,当recursive为true时,会同时遍历指定文件夹的子文件夹
由于glob遍历图像名称不是按顺序进行遍历的;
在读取图像序列的时候经常要按顺序读取,如在多目标跟踪中;
这时可以sort进行排序;
//获取文件夹下所有图像名称, // 图像名称按升序排列 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; }
文中所利用知识点:
1. cv::String cv::format(const char* fmg,...) 用于给要存储的照片指定路径。
2. void glob(String pattern, std::vector<String>& result, bool recursive = false); 用于遍历指定路径下的文件。
代码演示
此代码的目的如下:从一个文件夹中读取后缀为 .jpg的图片,将其存入到另一个指定文件夹,并按顺序命名。
剩余判断,或者输出其他信息,自己可以在代码上进行添加即可。
#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; }
函数讲解
void glob(String pattern, std::vector<String>& result, bool recursive = false);
参数: pattern 是要遍历的路径; result 是要存放的结果,
recursive 这个参数默认为false,此时glob函数只会查找pattern路径下的的匹配类型的文件,不会匹配其他类型及子文件夹。如果为true的话,会遍历子文件。举个例子如下:
string path2 = "E:/test/*.jpg"
vector<String> result;
glob(path2, result, false);
//只能输出path2路径下的图片名,并且不会遍历子文件
vector<String> result2;
glob(path2, result2, true);
//输出path2路径下的图片名,并且会遍历子文件。
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,...)
format 函数类似于 sprintf 函数。都是用来处理字符串的格式化。
但注意,%s 并不能处理 string类型,因为读入的地址 可能是对象的地址,并不是字符串的首地址,在vs调试是这个原因。 因此在处理字符串格式的时候,只能处理 c 拥有的格式。( string str = “hanhan”; printf("%s",str);这样是不对的。可以这样输出:printf("%s\n",str.c_str());)
int sprintf(char* str, const char* format,...);
成功后,将返回写入的字符总数。此计数不包括自动附加在字符串末尾的其他空字符。
失败时,将返回负数。
#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
目的:opencv读取txt文件,并将txt文件赋值给Mat矩阵
方法:利用fstream类来完成
// 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文件显示
2222.txt显示
【注意】:1.空格辨认数据
2.mat数据数据根据设置补全,例如Mat Ky2_Data = Mat::zeros(100, 6, CV_32FC1);这行代码代表是生成100X6的矩阵,里面填充0,所以即使文件是4列,它会读取第二行
升级
//将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);
}
}
//#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); }
//#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; }
【说明】1.写入数据方法1打印结果(考虑了通道没问题)
2.写入数据方法2打印结果(未考虑了通道有问题)
写到文本会出现问题,修改如下
// 写入数据方法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);
}
#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;
}
#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); }
对于存储一个矩阵的文本文件中的行数,我们可以很轻易的用一个getline函数得到,代码在下面,但是如何得到列数了,是把它一行一行的读进来,然后再写代码分解吗?太麻烦了把,现在想到了一个简便的方法,我们知道文本文件的末尾有一个\n作为换行符,而元素与元素之间有一个空格相隔,所以我们每读取一个数字, 就判断后面一个符号是不是换行符,这样我们就能得到一行的列数. peek 和 get 函数都可以实现, peek只是检查,不会让流前进,而get会让流前进.
#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; }
【2】方法2的txt文件的行数
#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; }
【注意】:将第一种获取方法与opencv与Mat矩阵结合,就可以自适应知道txt文件的行列数。
#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; }
#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); }
结果
【注意1】:这个需要改编
【注意2】:2048表示维度,代码的背景是:一张人脸可以用2048维特征来表示
// 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"); }
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
)
自动训练函数的参数注释(13个)
前5个参数参考构造函数的参数注释。 k_fold:
交叉验证参数。训练集被分成k_fold的自子集。其中一个子集是用来测试模型,其他子集则成为训练集。所以,SVM算法复杂度是执行k_fold的次数。
*Grid: (6个)对应的SVM迭代网格参数。
balanced: 如果是true则这是一个2类分类问题。这将会创建更多的平衡交叉验证子集。
自动训练函数的使用说明
这个方法根据CvSVMParams中的最佳参数C, gamma, p, nu, coef0, degree自动训练SVM模型。
参数被认为是最佳的交叉验证,其测试集预估错误最小。
如果没有需要优化的参数,相应的网格步骤应该被设置为小于或等于1的值。例如,为了避免gamma的优化,设置gamma_grid.step =
0,gamma_grid.min_val, gamma_grid.max_val 为任意数值。所以params.gamma
由gamma得出。
最后,如果参数优化是必需的,但是相应的网格却不确定,你可能需要调用函数CvSVM::get_default_grid(),创建一个网格。例如,对于gamma,调用CvSVM::get_default_grid(CvSVM::GAMMA)。
该函数为分类运行 (params.svm_type=CvSVM::C_SVC 或者
params.svm_type=CvSVM::NU_SVC) 和为回归运行 (params.svm_type=CvSVM::EPS_SVR
或者
params.svm_type=CvSVM::NU_SVR)效果一样好。如果params.svm_type=CvSVM::ONE_CLASS,没有优化,并指定执行一般的SVM。
这里需要注意的是,对于需要的优化的参数虽然train_auto可以自动选择最优值,但在代码中也要先赋初始值,要不然编译能通过,但运行时会报错。
下面是示例代码
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);
用上面的代码的就可以自动训练并优化参数。最后,若想查看优化后的参数值,可以使用CvSVM::get_params()函数来获得优化后的CvSVMParams。下面是示例代码:
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);
Load(‘路径\xx.txt’)
%加载txt文件,加载成功后,在Workspace中出现与该txt文件同名的变量。
%注意:若txt文件名中有“-”字符,则Workspace中变量名中相应字符变为“_”
Save(‘路径\xx.mat’,‘变量名’)
例:
load('D:\matlabprogram\test-1.txt')
save('D:\matlabprogram\test-1.mat','test_1')
(1)不考虑转换后txt文件中数据格式
Load(‘路径\xx.mat’)
Save(‘路径\xx.txt’,‘变量名’,’-ASCII’)
Save函数可用到的文件格式选项如下:
load('路径\collect001.mat')
save('路径\collect001.txt','collect001','-ASCII')
【注意】:上下.mat文件和.txt文件名应该相同
(2)考虑转换后txt文件中数据格式
【方法1】
当前路径下
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);
打开imu.txt文件,存储效果如下图所示,
【方法2】还没成功
//把矩阵 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 ) ;
【方法3】还没成功
改成用load函数和fprintf函数相结合的方法,一行一行的读进来,然后写到对应的TXT文件中去。(因为我最终要的TXT文件需要与之前的文件名一致,所以会多一些比较细节的操作)
%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
寻找图像最大值最小值的函数 minMaxLoc() 函数
minMaxLoc() 函数原型
void cv::minMaxLoc(InputArray src, double * minVal, double * maxVal=0,
Point * minLoc=0,Point * maxLoc=0,InputArray mask = noArray())
其中,src为需要寻找最大值和最小值的图像或者矩阵,要求必须是单通道;minVal:图像或矩阵的最小值;maxVal:图像或矩阵的最大值;minLoc:图像或矩阵的最小值在矩阵中的坐标;maxLoc:图像或矩阵的最大值在矩阵中的坐标;mask:掩膜,用于设置在图像或矩阵中的指定区域寻找最值。
minMaxLoc() 函数输出最值的位置为按行扫描从左到右第一次检测到最值的位置,同时输入参数时一定
Point 数据类型:该数据类型用于表示图像的像素坐标,水平方向为x轴,垂直方向为y轴,Point(x,y)。针对二维坐标数据类型,定义了整型坐标 cv::Point2i(或者cv::Point)、double类型坐标cv::Point2d、浮点型坐标cv::Point2f。对于三维坐标类型定义与二维坐标数据类型相似,只需要将 2 改成 3 即可。对于坐标中 x、y 轴具体数据,可以通过变量属性进行访问,例如:Point.x 可以读取坐标的 x 轴数据。
src为需要寻找最大值和最小值的图像或者矩阵,要求必须是单通道,对于多通道矩阵数据,需要用 cv::Mat::reshape() 将多通道变成单通道,或者分别寻找每个通道的最值,然后进行比较。
cv::Mat::reshape() 函数原型
Mat cv::Mat::reshape(int cn, int rows = 0)
其中 cn:转换后矩阵的通道数;rows:转换后矩阵的行数。如果参数为0,则转换后行数与转化前行数相同。
列还是原来的
综合示例:
#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; }
运行结果:mat矩阵是从0开始记录的
图像的平均值表示图像整体的亮暗程度,平均值越大,则图像整体越亮。标准差表示图中明暗变化的对比程度,标准差越大,表示图像中明暗变化越明显。
mean()函数原型
cv::Scalar cv::mean(InputArray src, InputArray mask= noArray())
其中,src:待求平均值的图像矩阵(通道数可以位1~4)。mask:掩模,用于标记求取那些区域的平均值。
该函数求取每个通道的平均值。该函数返回值是一个cv::Scalar 类型的变量,函数的返回值有 4 位,分别表示输入图像的4 个通道的平均值,如果输入图像只有一个通道,那么返回值的后 3 位都是0.可以通过 cv::Scalar[n] 查看第 n 个通道的平均值。
meanStdDev()函数原型
void cv::meanStdDev(InputArray src, OutputArray mean, OutputArray stddev,
InputArray mask =noArray())
其中,src:待求平均值的图像矩阵;mean:图像每个通道的平均值,参数为Mat类型变量;
stddev:图像每个通道的标准差,参数为Mat类型变量;mask:掩模,用于标记求取那些区域的平均值和标准差。该函数没有返回值。图像的均值和标准差输出在函数第二个参数和第三个参数中。
综合示例:包括提取某一列
#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; }
运行结果
升级,循环提取某一列
#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; }
输出结果:
原始Mat格式数据:
cv::Mat A = Mat::zeros(4, 5, CV_32F);【4行5列,高4宽5】
1、提取行
函数:Mat::rowRange(int startrow, int endrow)
例:提取第0~2行(包括第2行)
cv::Mat B = A.rowRange(0, 3).clone() ;
2、提取列
函数:Mat::colRange(int startcol, int endcol)
例:提取第2~4列(包括第4列)
cv::Mat C = A.colRange(2, 5).clone() ;
注意,rowRange(start,end)与colRange(start,end)均包括左边界,不包括右边界。
3、copyTo()函数
Mat c = Mat::zeros(3, 5, CV_32F);
Mat a = Mat::ones(3, 6, CV_32F);
1)将c的第1列赋值给a
c.col(0).copyTo(a.col(0));
2)将c的1-5列赋值给a
c.copyTo(a.colRange(1, 6));
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。