任务要求
1.找出图像各几何图像的边长或半径;获取个图象边长或半径;获取图像形状
2.用PCA降维 将二维数组降到一维
平台
在opencv下 使用的c++代码
原理,思路
第一题: 用拟合曲线求出角点,角点的个数代表了每个图形,角点坐标可以求出中心坐标
第二题: 运用线性代数转化
1、对所有样本进行中心化(去均值操作);
2、计算样本的协方差矩阵;
3、对协方差矩阵做特征值分解;
4、取最大的特征值所对应的特征向量;
5、将原样本矩阵与投影矩阵相乘: X*V即为降维后数据集X’ ;
6、输出:降维后的数据集X’
7、还原降维压缩的矩阵:Y = X’ * V^T
8、输出还原矩阵Y
代码
第一题
下面展示一些 内联代码片
。
下面展示一些 内联代码片
。
头文件
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
//定义全局变量
Mat src; Mat src_gray;
int thresh = 100;
int max_thresh = 255;
/// Function header
void thresh_callback(int, void*);
/** @function main */
int main(int argc, char** argv)
{
// 加载源图像
src = imread("D:/QT Projects/csdn_mode2/1.png");
/*
* 输入原图
* 输出灰度图
*/
cvtColor(src, src_gray, COLOR_BGRA2GRAY);
/*
* 输入灰度图
* 输出原图片为高斯滤波图 卷积核为3*3
*/
GaussianBlur(src_gray, src_gray, Size(3, 3), 5);
//调用
thresh_callback(0, 0);
//等待键盘事件响应,否者一直等待
waitKey(0);
return(0);
}
/** @function thresh_callback */
void thresh_callback(int, void*)
{
//给src_copy复制src的值并分配内存空间
Mat src_copy = src.clone();
//创建threshold_output用于后续的曲线拟合
Mat threshold_output;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
/*
* 输入灰度图
* 输出二值图像
*/
threshold(src_gray, threshold_output, thresh, 255, THRESH_BINARY);
/*
* 输入二值图
* 输出二维数组 contours
*/
findContours(threshold_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
/*Mat drawing = Mat::zeros(threshold_output.size(), CV_8UC3);*/
//曲线拟合
vector<vector<Point> >poly(contours.size());
for (int i = 0; i < contours.size(); i++)
{
/*
* 输入曲线contours
* 输出折线poly
*/
approxPolyDP(Mat(contours[i]), poly[i], 5,true);
}
//遍历poly数组将每一个图像的角点坐标相加 除以角点个数 即为几何中心的坐标
for(int i=1;i<4;i++){
//x,y为几何中心的对应最表 n代表角点的个数 flag用于控制后面的判断
int x=0;
int y=0;
int n=0;
int flag=1;
for(int j=0;j<poly[i].size();j++){
//每个int j 这个循环结束后 x,y代表的都是角点对应轴的坐标和, n就是角点个数
x+=poly[i][j].x;
y+=poly[i][j].y;
n++;
}
//坐标和除以个数 即为几何中心对应的坐标
x/=n;
y/=n;
/* circle()函数
* 输入几何中心的坐标点(Point类)
* 输出 以几何中心为圆心 半径为3,颜色为黑色的小圆点
*/
circle(src,Point(x,y),3,Scalar(0),1,8,0);
//将数值类型的 x y坐标转化为string类
string x1=to_string(x);
string y1=to_string(y);
/*
* 输入目标图像,要输出的文字串,坐标点
* 输出在对应点point处字体为FONT_HERSHEY_SIMPLEX,黑色的一行文字
*/
putText(src,"("+x1+","+y1+")",Point(x,y-30),FONT_HERSHEY_SIMPLEX,1,Scalar(0),1,4,false);
/*
* 接下来的三个if: 遍历poly的同时,每一次循环通过poly[i]的大小判断边数多少 六边形等于6 五角星等于10 圆则更多;
* flag用于标志本次循环是否完成了判断(已经完成为0 待完成为1) 减少判断次数
*/
//六边形判断
if(poly[i].size()==6 && flag==1){
//用两点间距离公式求解边长(两个点为角点)
double side_len=sqrt(pow(poly[i][0].x-poly[i][1].x,2)+pow(poly[i][0].y-poly[i][1].y,2));
string b=to_string(side_len);
//打印边长(side_Length)
putText(src,"side_Length= "+b,Point(x,y),FONT_HERSHEY_SIMPLEX,0.8,Scalar(0),1,4,false);
flag=0;
//打印图像所属类型
putText(src,"hexagon",Point(x,y+side_len),FONT_HERSHEY_SIMPLEX,0.8,Scalar(0),1,4,false);
}
//五角星判断
if(poly[i].size()==10 && flag==1){
//用两点间距离公式求解边长(两个点为角点)
double side_len=sqrt(pow(poly[i][0].x-poly[i][1].x,2)+pow(poly[i][0].y-poly[i][1].y,2));
string b=to_string(side_len);
//打印边长(side_Length)
putText(src,"side_Length= "+b,Point(x,y),FONT_HERSHEY_SIMPLEX,0.8,Scalar(0),1,4,false);
flag=0;
//打印图像所属类型
putText(src,"five_Star",Point(x,y+side_len),FONT_HERSHEY_SIMPLEX,0.8,Scalar(0),1,4,false);
}
//圆的判断
if(poly[i].size()>10 && flag==1){
//用两点间距离公式求解半径(一个是拟合出来的圆边上的一点 一个是圆心)
double side_len=sqrt(pow(poly[i][0].x-x,2)+pow(poly[i][0].y-y,2));
string b=to_string(side_len);
//打印半径radius
putText(src,"radius="+b,Point(x,y),FONT_HERSHEY_SIMPLEX,0.8,Scalar(0),1,4,false);
flag=0;
//打印图像所属类型
putText(src,"circle",Point(x+50,y+side_len),FONT_HERSHEY_SIMPLEX,0.8,Scalar(0),1,4,false);
}
}
// 把结果显示在窗体
imshow("src", src);
}
第二题
下面展示一些 内联代码片
。
头文件
#include<Eigen>
#include<Eigenvalues>
#include <opencv2/opencv.hpp>
#include<opencv2/core/eigen.hpp>
/*
* 最好在release模式下运行
*/
int main()
{
int m=5; //数组的行
int n=2; //数组的列
//初始化数组
double matrix[5][2]={{2.3,2.9},{0.5,0.3},{1.6,1.4},{0.9,0.8},{1.8,1.1}};
//求每个维度的均值向量 (不同向量的同一个维度 所有数值的平均值) 创建了长度为2的一位数组E来保存均值向量
vector<double>E(2);
for( int j=0 ; j<2 ; j++ ){
double sum=0;
for( int i=0 ; i<m ; i++ ){
//对同一个维度的值累加到sum
sum+=matrix[i][j];
}
//求得均值向量
E[j]=sum/m;
}
//中心化矩阵 初始化倒置矩阵
vector<vector<float>>centerMatrix(m,vector<float>(n));
vector<vector<float>>centerMatrixT(n,vector<float>(m));
for( int i=0 ; i<m ; i++ ){
for( int j=0 ; j<n ; j++ ){
//中心化矩阵(对应的矩阵值减去该维度上的均值向量)
centerMatrix[i][j]=matrix[i][j]-E[j];
//初始化初始矩阵的倒置矩阵
centerMatrixT[j][i]=matrix[i][j];
}
}
//计算协方差矩阵 covMatrix = 1 / (m-1) * matrixT * matrix
Mat covMatrix(2,2,CV_32FC1);
for ( int i=0 ; i<n ; i++ )
for ( int j=0 ; j<n ; j++)
for ( int k=0 ; k<m ; k++ ){
//运用矩阵乘法的原理 直接累加得到对应协方差矩阵值
covMatrix.at<float>(i,j)+=centerMatrixT[i][k] * centerMatrix[k][j];
covMatrix.at<float>(i,j)/=(m-1.0);
}
//val保存特征值 vec保存特征向量 MatrixXd 创建大小不定的double类矩阵
MatrixXd val,vec;
Matrix2d R_matrix;
/*
* 输入Mat类型矩阵
* 输出Matrix类矩阵
*/
cv2eigen(covMatrix,R_matrix);
//计算特征值和特征向量,使用selfadjont按照对阵矩阵的算法去计算,可以让产生的vec和val按照有序排列
SelfAdjointEigenSolver<MatrixXd> eig(R_matrix);
//协方差矩阵的特征值
val=eig.eigenvalues();
//协方差矩阵的特征向量
vec=eig.eigenvectors();
//特征向量的矩阵转化到Mat矩阵里面
Mat feature_Mat(3,3,CV_32FC1);
/*
* 输入Matrix类型矩阵
* 输出Mat类型矩阵
*/
eigen2cv(vec,feature_Mat);
//将原矩阵放到以特征向量为基底的空间下面 创建final矩阵储存结果
Mat final(1,5,CV_32FC1);
//去最大特征向量的倒置与原矩阵相乘
for( int i=0 ; i<1 ; i++ ){
for( int j=0 ; j<5 ; j++ ){
final.at<float>(i,j)=(float)(feature_Mat.at<float>(0,1)*centerMatrixT[0][j]+ //利用矩阵乘法的性质(这里本该是feature_Mat的倒置矩阵来做乘法的 我这里用的是原矩阵 所有下标值应该翻过来)
feature_Mat.at<float>(1,1)*centerMatrixT[1][j]);
}
}
//输出最终结果
cout<<"Final_Data:"<<final;
}