OPENCV GrabCut程序详细分析.docx
- 文档编号:9705942
- 上传时间:2023-02-05
- 格式:DOCX
- 页数:27
- 大小:23.32KB
OPENCV GrabCut程序详细分析.docx
《OPENCV GrabCut程序详细分析.docx》由会员分享,可在线阅读,更多相关《OPENCV GrabCut程序详细分析.docx(27页珍藏版)》请在冰豆网上搜索。
OPENCVGrabCut程序详细分析
一、GrabCut函数使用
在OpenCV的源码目录的samples的文件夹下,有grabCut的使用例程,请参考:
opencv\samples\cpp\grabcut.cpp。
而grabCut函数的API说明如下:
voidcv:
:
grabCut(InputArray_img,InputOutputArray_mask,Rectrect,
InputOutputArray_bgdModel,InputOutputArray_fgdModel,
intiterCount,intmode)
二、GrabCut源码解读
其中源码包含了gcgraph.hpp这个构建图和maxflow/mincut算法的实现文件,这个文件暂时没有解读,后面再更新了。
[cpp]viewplaincopyprint?
#include"precomp.hpp"
#include"gcgraph.hpp"
#include
usingnamespacecv;
classGMM
{
public:
staticconstintcomponentsCount=5;
GMM(Mat&_model);
doubleoperator()(constVec3dcolor)const;
doubleoperator()(intci,constVec3dcolor)const;
intwhichComponent(constVec3dcolor)const;
voidinitLearning();
voidaddSample(intci,constVec3dcolor);
voidendLearning();
private:
voidcalcInverseCovAndDeterm(intci);
Matmodel;
double*coefs;
double*mean;
double*cov;
doubleinverseCovs[componentsCount][3][3];//协方差的逆矩阵
doublecovDeterms[componentsCount];//协方差的行列式
doublesums[componentsCount][3];
doubleprods[componentsCount][3][3];
intsampleCounts[componentsCount];
inttotalSampleCount;
};
//背景和前景各有一个对应的GMM(混合高斯模型)
GMM:
:
GMM(Mat&_model)
{
//一个像素的(唯一对应)高斯模型的参数个数或者说一个高斯模型的参数个数
//一个像素RGB三个通道值,故3个均值,3*3个协方差,共用一个权值
constintmodelSize=3+9+1;
if(_model.empty())
{
//一个GMM共有componentsCount个高斯模型,一个高斯模型有modelSize个模型参数
_model.create(1,modelSize*componentsCount,CV_64FC1);
_model.setTo(Scalar(0));
}
elseif((_model.type()!
=CV_64FC1)||(_model.rows!
=1)||(_model.cols!
=modelSize*componentsCount))
CV_Error(CV_StsBadArg,"_modelmusthaveCV_64FC1type,rows==1andcols==13*componentsCount");
model=_model;
//注意这些模型参数的存储方式:
先排完componentsCount个coefs,再3*componentsCount个mean。
//再3*3*componentsCount个cov。
coefs=model.ptr<double>(0);//GMM的每个像素的高斯模型的权值变量起始存储指针
mean=coefs+componentsCount;//均值变量起始存储指针
cov=mean+3*componentsCount;//协方差变量起始存储指针
for(intci=0;ci if(coefs[ci]>0) //计算GMM中第ci个高斯模型的协方差的逆Inverse和行列式Determinant //为了后面计算每个像素属于该高斯模型的概率(也就是数据能量项) calcInverseCovAndDeterm(ci); } //计算一个像素(由color=(B,G,R)三维double型向量来表示)属于这个GMM混合高斯模型的概率。 //也就是把这个像素像素属于componentsCount个高斯模型的概率与对应的权值相乘再相加, //具体见论文的公式(10)。 结果从res返回。 //这个相当于计算Gibbs能量的第一个能量项(取负后)。 doubleGMM: : operator()(constVec3dcolor)const { doubleres=0; for(intci=0;ci res+=coefs[ci]*(*this)(ci,color); returnres; } //计算一个像素(由color=(B,G,R)三维double型向量来表示)属于第ci个高斯模型的概率。 //具体过程,即高阶的高斯密度模型计算式,具体见论文的公式(10)。 结果从res返回 doubleGMM: : operator()(intci,constVec3dcolor)const { doubleres=0; if(coefs[ci]>0) { CV_Assert(covDeterms[ci]>std: : numeric_limits<double>: : epsilon()); Vec3ddiff=color; double*m=mean+3*ci; diff[0]-=m[0];diff[1]-=m[1];diff[2]-=m[2]; doublemult=diff[0]*(diff[0]*inverseCovs[ci][0][0]+diff[1]*inverseCovs[ci][1][0]+diff[2]*inverseCovs[ci][2][0]) +diff[1]*(diff[0]*inverseCovs[ci][0][1]+diff[1]*inverseCovs[ci][1][1]+diff[2]*inverseCovs[ci][2][1]) +diff[2]*(diff[0]*inverseCovs[ci][0][2]+diff[1]*inverseCovs[ci][1][2]+diff[2]*inverseCovs[ci][2][2]); res=1.0f/sqrt(covDeterms[ci])*exp(-0.5f*mult); } returnres; } //返回这个像素最有可能属于GMM中的哪个高斯模型(概率最大的那个) intGMM: : whichComponent(constVec3dcolor)const { intk=0; doublemax=0; for(intci=0;ci { doublep=(*this)(ci,color); if(p>max) { k=ci;//找到概率最大的那个,或者说计算结果最大的那个 max=p; } } returnk; } //GMM参数学习前的初始化,主要是对要求和的变量置零 voidGMM: : initLearning() { for(intci=0;ci { sums[ci][0]=sums[ci][1]=sums[ci][2]=0; prods[ci][0][0]=prods[ci][0][1]=prods[ci][0][2]=0; prods[ci][1][0]=prods[ci][1][1]=prods[ci][1][2]=0; prods[ci][2][0]=prods[ci][2][1]=prods[ci][2][2]=0; sampleCounts[ci]=0; } totalSampleCount=0; } //增加样本,即为前景或者背景GMM的第ci个高斯模型的像素集(这个像素集是来用估 //计计算这个高斯模型的参数的)增加样本像素。 计算加入color这个像素后,像素集 //中所有像素的RGB三个通道的和sums(用来计算均值),还有它的prods(用来计算协方差), //并且记录这个像素集的像素个数和总的像素个数(用来计算这个高斯模型的权值)。 voidGMM: : addSample(intci,constVec3dcolor) { sums[ci][0]+=color[0];sums[ci][1]+=color[1];sums[ci][2]+=color[2]; prods[ci][0][0]+=color[0]*color[0];prods[ci][0][1]+=color[0]*color[1];prods[ci][0][2]+=color[0]*color[2]; prods[ci][1][0]+=color[1]*color[0];prods[ci][1][1]+=color[1]*color[1];prods[ci][1][2]+=color[1]*color[2]; prods[ci][2][0]+=color[2]*color[0];prods[ci][2][1]+=color[2]*color[1];prods[ci][2][2]+=color[2]*color[2]; sampleCounts[ci]++; totalSampleCount++; } //从图像数据中学习GMM的参数: 每一个高斯分量的权值、均值和协方差矩阵; //这里相当于论文中“Iterativeminimisation”的step2 voidGMM: : endLearning() { constdoublevariance=0.01; for(intci=0;ci { intn=sampleCounts[ci];//第ci个高斯模型的样本像素个数 if(n==0) coefs[ci]=0; else { //计算第ci个高斯模型的权值系数 coefs[ci]=(double)n/totalSampleCount; //计算第ci个高斯模型的均值 double*m=mean+3*ci; m[0]=sums[ci][0]/n;m[1]=sums[ci][1]/n;m[2]=sums[ci][2]/n; //计算第ci个高斯模型的协方差 double*c=cov+9*ci; c[0]=prods[ci][0][0]/n-m[0]*m[0];c[1]=prods[ci][0][1]/n-m[0]*m[1];c[2]=prods[ci][0][2]/n-m[0]*m[2]; c[3]=prods[ci][1][0]/n-m[1]*m[0];c[4]=prods[ci][1][1]/n-m[1]*m[1];c[5]=prods[ci][1][2]/n-m[1]*m[2]; c[6]=prods[ci][2][0]/n-m[2]*m[0];c[7]=prods[ci][2][1]/n-m[2]*m[1];c[8]=prods[ci][2][2]/n-m[2]*m[2]; //计算第ci个高斯模型的协方差的行列式 doubledtrm=c[0]*(c[4]*c[8]-c[5]*c[7])-c[1]*(c[3]*c[8]-c[5]*c[6])+c[2]*(c[3]*c[7]-c[4]*c[6]); if(dtrm<=std: : numeric_limits<double>: : epsilon()) { //相当于如果行列式小于等于0,(对角线元素)增加白噪声,避免其变 //为退化(降秩)协方差矩阵(不存在逆矩阵,但后面的计算需要计算逆矩阵)。 //Addsthewhitenoisetoavoidsingularcovariancematrix. c[0]+=variance; c[4]+=variance; c[8]+=variance; } //计算第ci个高斯模型的协方差的逆Inverse和行列式Determinant calcInverseCovAndDeterm(ci); } } } //计算协方差的逆Inverse和行列式Determinant voidGMM: : calcInverseCovAndDeterm(intci) { if(coefs[ci]>0) { //取第ci个高斯模型的协方差的起始指针 double*c=cov+9*ci; doubledtrm= covDeterms[ci]=c[0]*(c[4]*c[8]-c[5]*c[7])-c[1]*(c[3]*c[8]-c[5]*c[6]) +c[2]*(c[3]*c[7]-c[4]*c[6]); //在C++中,每一种内置的数据类型都拥有不同的属性,使用库可以获 //得这些基本数据类型的数值属性。 因为浮点算法的截断,所以使得,当a=2, //b=3时10*a/b==20/b不成立。 那怎么办呢? //这个小正数(epsilon)常量就来了,小正数通常为可用给定数据类型的 //大于1的最小值与1之差来表示。 若dtrm结果不大于小正数,那么它几乎为零。 //所以下式保证dtrm>0,即行列式的计算正确(协方差对称正定,故行列式大于0)。 CV_Assert(dtrm>std: : numeric_limits<double>: : epsilon()); //三阶方阵的求逆 inverseCovs[ci][0][0]=(c[4]*c[8]-c[5]*c[7])/dtrm; inverseCovs[ci][1][0]=-(c[3]*c[8]-c[5]*c[6])/dtrm; inverseCovs[ci][2][0]=(c[3]*c[7]-c[4]*c[6])/dtrm; inverseCovs[ci][0][1]=-(c[1]*c[8]-c[2]*c[7])/dtrm; inverseCovs[ci][1][1]=(c[0]*c[8]-c[2]*c[6])/dtrm; inverseCovs[ci][2][1]=-(c[0]*c[7]-c[1]*c[6])/dtrm; inverseCovs[ci][0][2]=(c[1]*c[5]-c[2]*c[4])/dtrm; inverseCovs[ci][1][2]=-(c[0]*c[5]-c[2]*c[3])/dtrm; inverseCovs[ci][2][2]=(c[0]*c[4]-c[1]*c[3])/dtrm; } } //计算beta,也就是Gibbs能量项中的第二项(平滑项)中的指数项的beta,用来调整 //高或者低对比度时,两个邻域像素的差别的影响的,例如在低对比度时,两个邻域 //像素的差别可能就会比较小,这时候需要乘以一个较大的beta来放大这个差别, //在高对比度时,则需要缩小本身就比较大的差别。 //所以我们需要分析整幅图像的对比度来确定参数beta,具体的见论文公式(5)。 staticdoublecalcBeta(constMat&img) { doublebeta=0; for(inty=0;y { for(intx=0;x { //计算四个方向邻域两像素的差别,也就是欧式距离或者说二阶范数 //(当所有像素都算完后,就相当于计算八邻域的像素差了) Vec3dcolor=img.at(y,x); if(x>0)//left>0的判断是为了避免在图像边界的时候还计算,导致越界 { Vec3ddiff=color-(Vec3d)img.at(y,x-1); beta+=diff.dot(diff);//矩阵的点乘,也就是各个元素平方的和 } if(y>0&&x>0)//upleft { Vec3ddiff=color-(Vec3d)img.at(y-1,x-1); beta+=diff.dot(diff); } if(y>0)//up { Vec3ddiff=color-(Vec3d)img.at(y-1,x); beta+=diff.dot(diff); } if(y>0&&x//upright { Vec3ddiff=color-(Vec3d)img.at(y-1,x+1); beta+=diff.dot(diff); } } } if(beta<=std: : numeric_limits<double>: : epsilon()) beta=0; else beta=1.f/(2*beta/(4*img.cols*img.rows-3*img.cols-3*img.rows+2));//论文公式(5) returnbeta; } //计算图每个非端点顶点(也就是每个像素作为图的一个顶点,不包括源点s和汇点t)与邻域顶点 //的边的权值。 由于是无向图,我们计算的是八邻域,那么对于一个顶点,我们计算四个方向就行, //在其他的顶点计算的时候,会把剩余那四个方向的权值计算出来。 这样整个图算完后,每个顶点 //与八邻域的顶点的边的权值就都计算出来了。 //这个相当于计算Gibbs能量的第二个能量项(平滑项),具体见论文中公式(4) staticvoidcalcNWeights(constMat&img,Mat&leftW,Mat&upleftW,Mat&upW, Mat&uprightW,doublebeta,doublegamma) { //gammaDivSqrt2相当于公式(4)中的gamma*dis(i,j)^(-1),那么可以知道, //当i和j是垂直或者水平关系时,dis(i,j)=1,当是对角关系时,dis(i,j)=sqrt(2.0f)。 //具体计算时,看下面就明白了 constdoublegammaDivSqrt2=gamma/std: : sqrt(2.0f); //每个方向的边的权值通过一个和图大小相等的Mat来保存 leftW.create(img.rows,img.cols,CV_64FC1); upleftW.create(img.rows,img.cols,CV_64FC1); upW.create(img.rows,img.cols,CV_64FC1); uprightW.create(img.rows,img.cols,CV_64FC1); for(inty=0;y { for(intx=0;x { Vec3dcolor=img.at(y,x); if(x-1>=0)//left//避免图的边界 { Vec3ddiff=color-(Vec3d)img.at(y,x-1); leftW.at<double>(y,x)=gamma*exp(-beta*diff.dot(diff)); } else leftW.at<double>(y,x)=0; if(x-1>=0&&y-1>=0)//upleft { Vec3ddiff=color-(Vec3d)img.at(y-1,x-1); upleftW.at<double>(y,x)=gammaDivSqrt2*exp(-beta*diff.dot(diff)); } else upleftW.at<double>(y,x)=0; if(y-1>=0)//up { Vec3ddiff=color-(Vec3d)img.at(y-1,x); upW.a
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- OPENCV GrabCut程序详细分析 GrabCut 程序 详细 分析