图像是用一维坐标表示的,但是二维和三维绘图比较困难,所以我得用matlab。我可以不要用它,意思可以表达到位。
第一步:找到图像的局部最低点。方法有很多。你可以用一个内核去找,或者一个一个比较。实现起来并不难。
第二步:从最低点开始充水,水开始充互联网(形象的叫梯度法),其中那些最低点已经做了标记,不会被淹没,而那些中间点被淹没。
第三步:找到局部最高点,也就是图中位置3对应的两点。
步骤4:基于局部最小值和找到的局部最大值,可以分割图像。
分类图
模拟结果图
你觉得上面的方法很好很简单吗?然后看下图:
利用上面的步骤,第一步找到了三个点,然后第二步开始泛滥。所有三个点都被记录下来,并且发现了两个局部极大值。
这是我们想要的吗?
答案是否定的!我们不中间不需要最小值,因为它这只是一点噪音,我们不不需要如此仔细地分割图像。
缺陷暴露了吗?这不没关系。我们下面的opencv已经解决了这个问题。
模拟分类图
模拟结果图
Opencv改进分水岭算法;
针对以上问题,我们想的是,能否把这个小细节标记出来,让它不属于我们要找的最小点?
Opencv采用人工标记的方法对其进行了改进。我们标记了一些点,基于这些点来指导分水岭算法,效果很好!
例如,我们在上图中标记了两个三角形。第一步,我们找到三个局部极小点。第二步,淹没的时候三个点都被淹没了。但是如果中间的一个没有做标记,就淹死了(没有救生圈),另外两个点保留下来,这样才能达到我们想要的结果。
注意:这里的标记是用不同的标签做的,为了方便我用了同一个三角形。因为标记是用来分类的,所以不同的标记标注不同!这反映在下面的opencv程序中。
模拟分类图
模拟结果图
注意:具体实现没有完成,理解原理就可以用了。需要深入的时候,可以研究算法。当你用的比较浅,明白了原理,就要稍微改一下。它面试后完全没问题!哈哈~ ~
Opencv实现:
#包括
#包括
使用名称空间cv;
使用命名空间std
void water segment(input array _ src,OutputArray _dst,int nooffsegment);
int main(int argc,char** argv) {
mat input image=im read(coins.jpg );
断言(!input image . data);
Mat graImage,outputImage
利息抵消;
水分割(输入图像、输出图像、偏移图像);
wait key(0);
返回0;
}
void water segment(input array _ src,OutputArray _dst,int noOfSegment)
{
mat src=_ src . getmat();//dst=_ dst . getmat();
Mat灰度图像;
cvtColor(src,grayImage,CV _ bgr 2 gray);
threshold(灰度图像,灰度图像,0,255,THRESH _ BINARY | THRESH _ OTSU);
mat kernel=getStructuringElement(MORPH _ RECT,Size(9,9),Point(-1,-1));
morphologyEx(灰度图像,灰度图像,MORPH_CLOSE,内核);
distanceTransform(灰度图像,灰度图像,DIST_L2,MASK _面具_3,5);
normalize(grayImage,grayImage,0,1,NORM _ MINMAX);
grayImage.convertTo(grayImage,CV _ 8uc 1);
threshold(灰度图像,灰度图像,0,255,THRESH _ BINARY | THRESH _ OTSU);
morphologyEx(灰度图像,灰度图像,MORPH_CLOSE,内核);
向量轮廓;
向量层次结构;
Mat show image=Mat:zeros(gray image . size()、CV _ 32 SC1);
findContours(grayImage,Contours,hierarchy,RETR树,CHAIN_APPROX_SIMPLE,Point(-1,-1));
for(size _ t I=0;I contours . size();我)
{
//这里用static_cast(i 1)来区别标记分水岭。地区1、2、3。只有这样才能分。
drawContours(showImage,Contours,static_cast(i),Scalar:all(static_cast(i 1)),2);
}
mat k=getStructuringElement(MORPH _ RECT,Size(3,3),Point(-1,-1));
morphologyEx(src,src,MORPH_ERODE,k);
分水岭(src,show image);
//随机分配颜色
矢量颜色;
for(size _ t I=0;I轮廓。size();i ) {
int r=theRNG().制服(0,255);
int g=theRNG().制服(0,255);
int b=theRNG().制服(0,255);
颜色。push _ back(Vec3b((uchar)b,(uchar)g,(uchar)r));
}
//显示
Mat dst=Mat:zeros(显示图像。size()、CV _ 8uc 3);
int index=0;
for(int row=0;row showImage.rowsrow ) {
for(int col=0;col show image . cols coll){
index=showImage.at(row,col);
if(索引0索引=等高线。size()){
dst.at(row,col)=colors[index-1];
}
else if (index==-1)
{
dst.at(row,col)=Vec3b(255,255,255);
}
否则{
dst.at(row,col)=Vec3b(0,0,0);
}
}
}
}
分水岭合并代码:
void segMerge(Mat image,Mat segments,int numSeg)
{
向量样本;
int newNumSeg=numSeg
//初始化变量长度的矢量
for(size _ t I=0;我是纽姆塞格我)
{
垫样;
samples.push_back示例);
}
for(size _ t I=0;我段。行;我)
{
for(size _ t j=0;j segments.colsj)
{
int index=segments.at(i,j);
if (index=0 index=newNumSeg)//把同一个区域的点合并到一个垫子中
{
如果(!样本[索引]。数据)//数据为空不能合并,否则报错
{
样本[索引]=image(Rect(j,I,1,1));
}
else//按行合并
{
vconcat(samples[index],image(Rect(j,I,2,1)),samples[index]);
}
}
//if (index=0 index=newNumSeg)
//样本[索引]。push_back(image(Rect(j,I,1,1));
}
}
向量历史_基础
Mat hsv _ base
int h _ bins=35
int s _ bins=30
int histSize[2]={ h_bins,s _ bins };
float h_range[2]={ 0,256 };
float s_range[2]={ 0,180 };
const float* range[2]={ h_range,s _ range };
int channels[2]={ 0,1 };
Mat历史_基础
for(size _ t I=1;我numSeg我)
{
如果(样本【我】。dims 0)
{
颜色(样本[i],hsv_base,CV _ bgr 2 HSV);
calcHist(hsv_base,1,channels,Mat(),hist_base,2,histSize,range);
normalize(hist_base,hist_base,0,1,NORM _ MINMAX);
历史基础。push _ back(历史_基础);
}
其他
{
历史基础。push _ back(Mat());
}
}
双重相似度=0;
矢量合并;//是否合并的标志位
for(size _ t I=0;我有历史基础。size();我)
{
for(size _ t j=I 1;j历史_基础。size();j)
{
如果(!merged[j])//未合并的区域进行相似性判断
{
if (hist_bases[i]).dims 0 hist_bases[j].dims 0)//这里维数判断没必要,直接用个数据就可以了
{
相似度=compareHist(hist_bases[i],hist_bases[j],hist CMP _ BHATTACHARYYA);
如果(相似度0.8)
{
merged[j]=true;//被合并的区域标志位真实的
如果(我!=j)//这里没必要,我不可能等于j
{
newNumSeg-;//分割部分减少
for(size _ t p=0;p段。行;p)
{
for(size _ t k=0;k段. colsk)
{
int index=segments.at(p,k);
if (index==j) segments.at(p,k)=I;
}
}
}
}
}
}
}
}
numSeg=newNumSeg//返回合并之后的区域数量
}
标签:图像sizej