匹配分为两个阶段:第一阶段,在HSV空间下对S、V通道进行匹配,H通道不变。会有些许色片。(对于HSV空间的理解,此处不足解释,自己查阅资料看)
第二阶段,将HSV空间下做好的匹配图转换到RGB空间下,在对R、G、B三通道分别进行匹配。这样就完成了。
过程中(指导老师提供了算法思想),遇到问题冷静思考解决,一步步去测试(直方图、均值、方差、映射系数、映射矩阵。。。)
自己碰到的麻烦:变量名的误写,最终的映射代码逻辑写反了(这个逻辑读者一定要注意)。
以下只给出HSV空间下的匹配代码(RGB空间用到的匹配思想是一样的,读者自行学习编写):
#include <iostream>
#include <iomanip>
#include "match.h"
using namespace std;
Mat firstMatch(Mat &src, Mat &tar)
{
//创建三通道的hsv图像
Mat srcHSV(src.rows,src.cols,CV_8UC3);
Mat tarHSV(tar.rows,tar.cols,CV_8UC3);
//将rgb图像转换成hsv图像
cvtColor(src,srcHSV,CV_BGR2HSV);
cvtColor(tar,tarHSV,CV_BGR2HSV);
//显示转换后的图像
//namedWindow("srcHSV",0);//定义显示窗口
//imshow("srcHSV",srcHSV );
//namedWindow("tarHSV",0);//定义显示窗口
//imshow("tarHSV",tarHSV );
//通道分离
vector<Mat> imgSrc(srcHSV.channels());
vector<Mat> imgTar(tarHSV.channels());//通过imgSrc、imgTar的[]这种数组形式来访问各通道分量
//提取各通道数据
split(srcHSV,imgSrc);
split(tarHSV, imgTar);//将通道分量保存在
//显示各通道分量
//namedWindow("srcH",0);//定义显示窗口
//imshow("srcH",imgSrc[0] );
//namedWindow("srcS",0);//定义显示窗口
//imshow("srcS",imgSrc[1]);
//namedWindow("srcV",0);//定义显示窗口
//imshow("srcV",imgSrc[2]);
//namedWindow("tarH",0);//定义显示窗口
//imshow("tarH", imgTar[0] );
//namedWindow("tarS",0);//定义显示窗口
//imshow("tarS", imgTar[1]);
//namedWindow("tarV",0);//定义显示窗口
//imshow("tarV", imgTar[2] );
//计算各通道的统计直方图
double srcHisto[2][256] = {0};
double tarHisto[2][256] = {0};
//calmageHist(srcHisto,imgSrc);
//计算source图的S、V通道的直方图
for(int level = 1; level < 3; level++)
{
for(int k = 0; k < 256; k++)
{
double count = 0; //统计k灰度级所对应的像素个数
for(int i = 0; i < imgSrc[level].rows; i++)
{
for(int j = 0; j<imgSrc[level].cols; j++)
{
unsigned char* data=imgSrc[level].ptr<unsigned char>(i);
int size=data[j];
if(size==k)
{
count++;
}
}
}
srcHisto[level-1][k] = count/(imgSrc[level].rows*imgSrc[level].cols);
cout<<fixed<<setprecision(6)<<srcHisto[level-1][k]<<" ";//保留6位小数,
}
cout<<endl;
}
//计算target图的S、V通道的直方图
for(int level = 1; level < 3; level++)
{
for(int k = 0; k < 256; k++)
{
double count = 0; //统计k灰度级所对应的像素个数
for(int i = 0; i < imgTar[level].rows; i++)
{
unsigned char* data=imgTar[level].ptr<unsigned char>(i);
for(int j = 0; j<imgTar[level].cols; j++)
{
int size=data[j];
if(size==k)
{
count++;
}
}
}
tarHisto[level-1][k] = count/(imgTar[level].rows*imgTar[level].cols);
cout<<fixed<<setprecision(6)<<tarHisto[level-1][k]<<" ";//保留6位小数,
}
cout<<endl;
}
//计算均值和方差
double SMean[2]={0};
double VMean[2]={0};
for(int j=0;j<=255;j++)
{
SMean[0]=SMean[0]+srcHisto[0][j]*j;
VMean[0]=VMean[0]+srcHisto[1][j]*j;
SMean[1]=SMean[1]+tarHisto[0][j]*j;
VMean[1]=VMean[1]+tarHisto[1][j]*j;
}
cout<<"source's mean="<<fixed<<setprecision(6)<<SMean[0]<<endl;
cout<<"source'v mean="<<fixed<<setprecision(6)<<VMean[0]<<endl;
cout<<"target's mean="<<fixed<<setprecision(6)<<SMean[1]<<endl;
cout<<"target's mean="<<fixed<<setprecision(6)<<VMean[1]<<endl;
double SStd[2] = {0};
double VStd[2] = {0};
for(int j = 0; j<256; j++)
{
SStd[0]+=(j-SMean[0])*(j-SMean[0])*srcHisto[0][j];
SStd[1]+=(j-SMean[1])*(j-SMean[1])*tarHisto[0][j];
VStd[0]+=(j-VMean[0])*(j-VMean[0])*srcHisto[1][j];
VStd[1]+=(j-VMean[1])*(j-VMean[1])*tarHisto[1][j];
}
cout<<"source's std="<<fixed<<setprecision(6)<<SStd[0]<<endl;
cout<<"source'v std="<<fixed<<setprecision(6)<<VStd[0]<<endl;
cout<<"target's std="<<fixed<<setprecision(6)<<SStd[1]<<endl;
cout<<"target's std="<<fixed<<setprecision(6)<<VStd[1]<<endl;
//计算映射矩阵
double HistoMap[2][256]={0};
double s=0,sstep=0.03,diffstd,minstd=9999;
int lev=0;
for(int k = 0; k < 300; k++)
{
s+=sstep;
//移动坐标
for(int j = 0; j<256; j++)
{
HistoMap[0][j] = (j-SMean[0])*s+SMean[1];
if(HistoMap[0][j]<0){
HistoMap[0][j]=0;
}
if(HistoMap[0][j]>255)
{
HistoMap[0][j]=255;
}
}
//计算中间图的方差,找出与SStd[1]相差最小的方差以及其对应的系数k
double MidStd = 0;
for(int j = 0; j < 256; j++)
{
MidStd+= (HistoMap[0][j]-SMean[1])*(HistoMap[0][j]-SMean[1])*srcHisto[0][j];
}
diffstd=MidStd-SStd[1];
if(fabs(diffstd)<minstd)
{
minstd=fabs(diffstd);
lev=k;
}
}
cout<<"lev ="<<lev<<endl;
s=sstep*lev;
for(int j=0;j<256;j++)
{
HistoMap[0][j]=(j-SMean[0])*s+SMean[1];
if(HistoMap[0][j]<0)
HistoMap[0][j]=0;
if(HistoMap[0][j]>255)
HistoMap[0][j]=255;
}
for(int i=0;i<256;i++)
{
cout<<fixed<<setprecision(6)<<HistoMap[0][i]<<ends;
}
cout<<endl;
for(int i=0;i<imgSrc[1].rows;i++)
{
unsigned char* data=imgSrc[1].ptr<unsigned char>(i);
for(int j=0;j<imgSrc[1].cols;j++)
{
int size=data[j];
for(int k=0;k<256;k++)
{
if(size==k)
{
data[j] = (int)HistoMap[0][k];
}
}
}
}//之前错在逻辑写反了,应该将for(int k=0;k<256;k++)写在里面
//namedWindow("MatchS",0);
//imshow("MatchS",imgSrc[1]);
//计算映射矩阵HistoMap_V,通过选择系数构建y=kx+b,一次线性映射函数
double sV=0,sstepV=0.03,diffstdV,minstdV=9999;
int levV=0;
for(int k=0;k<300;k++)
{
sV=sV+sstepV;
//移动坐标
for(int j=0;j<256;j++)
{
HistoMap[1][j]=(j-VMean[0])*sV+VMean[1];
if(HistoMap[1][j]<0){
HistoMap[1][j]=0;
}
if(HistoMap[1][j]>255)
{
HistoMap[1][j]=255;
}
}
//计算中间图的方差,找出与TarS_Std相差最小的方差以及其对应的系数k
double MidStd=0;
for(int j=0;j<256;j++)
{
MidStd=MidStd+(HistoMap[1][j]-VMean[1])*(HistoMap[1][j]-VMean[1])*srcHisto[1][j];
}
diffstdV=MidStd-VStd[1];
if(fabs(diffstdV)<minstdV)
{
minstdV=fabs(diffstdV);
levV=k;
}
}
cout<<"levv="<<levV<<endl;
//求出中间图的最终HistoV_Map[]
sV=sstepV*levV;
for(int j=0;j<256;j++)
{
HistoMap[1][j]=(j-VMean[0])*sV+VMean[1];
if(HistoMap[1][j]<0)
HistoMap[1][j]=0;
if(HistoMap[1][j]>255)
HistoMap[1][j]=255;
}
for(int i=0;i<256;i++)
{
cout<<fixed<<setprecision(6)<<HistoMap[1][i]<<ends;
}
cout<<endl;
for(int i=0;i<imgSrc[2].rows;i++)
{
unsigned char* data=imgSrc[2].ptr<unsigned char>(i);
for(int j=0;j<imgSrc[2].cols;j++)
{
int size = data[j];
for(int k=0; k<256;k++)
{
if(size==k)
{
data[j] = (int)HistoMap[1][k];
}
}
}
}
//namedWindow("MatchV",0);
//imshow("MatchV",imgSrc[2]);
//合成
Mat mergeHSV(src.rows,src.cols,CV_8UC3);
merge(imgSrc,mergeHSV);
namedWindow("mergeHSV",0);//定义显示窗口
imshow("mergeHSV", mergeHSV);
//转换
Mat matchHSV(src.rows,src.cols,CV_8UC3);
cvtColor(mergeHSV,matchHSV,CV_HSV2BGR);
//namedWindow("matchHSV",0);//定义显示窗口
//imshow("matchHSV",matchHSV );//验证正确
return matchHSV;
}