分水岭算法实现
原理:以后再补
代码:
void* Watershed(QImage &image,QImage &gradiant)
{
int width = image.width();
int height = image.height();
int all_size = height * width;
int Num;
int w,h;
int p_conn[8][2] = {{0,-1},{-1,-1},{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1}};
bool p_condition[8];
QVector<int> seedimage(all_size);
QVector<int> labelimage(all_size);
QVector<int> originalimage(all_size);
QVector<queue<Point>*> vque;
//QVector<int> vque(900 * all_size);
queue<Point> *pque;
queue<int> qqp;
queue<Point> qpt; //队列
//vector<int*>SeedCounts;//保持每个队列种子个数的数组
QVector<QVector<int>> seedcounts;
QVector<int> seed_array(256);
Point temp;
int x,y;
int currindex;
Num = 0;
for(y = 0 ;y<height; y++)
{
for(x = 0; x<width; x++)
{
currindex = y*width + x;
seedimage[currindex] = (qGray(image.pixel(x,y)) > 0) ? 255 : -1;
labelimage[currindex] = 0;
originalimage[currindex] = qGray(gradiant.pixel(x,y));
}
}
int i,j;
//初始连通盆地
for(i = 0 ;i<height; i++)
{
for(j = 0; j<width; j++)
{
currindex = i*width + j;
if(seedimage[currindex] == 255 && originalimage[currindex] == 0)
{
temp.x = j;
temp.y = i;
qpt.push(temp);
Num ++;
seedcounts.append(seed_array);
pque = new queue<Point>[256];
vque.push_back(pque);//加入到队列数组中,对应的是本标记号Num的
seedimage[currindex] = 127;
labelimage[currindex] = Num;
cout<<"Num"<<Num<<endl;
//种子
while(!qpt.empty())
{
temp = qpt.front(); //取种子
//保存取出来的种子
x = temp.x;
y = temp.y;
qpt.pop(); //删掉取出的种子
for(int k = 0; k<8; k++)
{
p_condition[k] = false;
h = y + p_conn[k][1];
w = x + p_conn[k][0];
currindex = h*width + w;
if(currindex>=0&&currindex<all_size)
{
if(seedimage[currindex] == 255 && originalimage[currindex] == 0)
{
temp.x = w;
temp.y = h;
qpt.push(temp);
seedimage[currindex] = 127 ;
labelimage[currindex] = Num ;
}else {
p_condition[k] = true;
}
}
}
//保存连通分量(盆地的所有点)
if(p_condition[0] || p_condition[1] || p_condition[2] || p_condition[3] || p_condition[4] || p_condition[5] || p_condition[6] || p_condition[7])
{
temp.x = x;
temp.y = y;
//Num-1 区的边界点,也就是Num-1盆地的周围的山的点与高度
//第一维度代表Num-1的盆地,第二维度代表的是灰度值的点集合,第三维度就是点空间坐标
//[区][灰度值队列].push就是相同灰度的坐标队列
currindex = y*width + x;
int grey = originalimage[currindex];
vque[Num-1][grey].push(temp);
seedcounts[Num-1][grey]++;//记录Num - 1区的每个灰度有多少个点,与上面对于
}
}//while
}//if
}//forj
}//fori
int P_4[4][2] = {{-1,0},{0,-1},{1,0},{0,1}};
int m,n;
bool actives;//在某一水位处,所有标记的种子生长完的标志
//int WaterLevel;
//淹没过程开始,水位从零开始上升,水位对应灰度级,采用四连通法
for(int WaterLevel = 0; WaterLevel < 256; WaterLevel++)//第二维,灰度值
{
actives = true;
while(actives)
{
actives=false;
for (int i = 0; i < Num; i++)//第一维,盆地区号
{
//对应的分水岭不为空集,i 表示区域号,waterlevel 表示灰阶
if(!vque[i][WaterLevel].empty())
{
actives = true;
while(seedcounts[i][WaterLevel]>0)
{
seedcounts[i][WaterLevel]--; //取出一个点,个数少一
temp=vque[i][WaterLevel].front(); //取出该区域的一个点,清空这个边缘点
//灰度级该像素已经处理掉了。
vque[i][WaterLevel].pop();
//当前的种子坐标
m = temp.x;
n = temp.y;
//四连通
for(int k = 0; k<4;k++)
{
h = n +P_4[k][0];
w = m +P_4[k][1];
currindex = h * width + w;
//不越界
if(currindex>=0 && currindex<all_size)
{
//不是盆地点
if(!labelimage[currindex])
{
temp.x=w;
temp.y=h;
labelimage[currindex]=i+1;//上方点标记为已淹没区域
if(originalimage[currindex]<=WaterLevel && seedimage[currindex] !=-1)//上方若为可生长点则加入当前队列,当前灰度的队列
{
vque[i][WaterLevel].push(temp);
//SeedCounts[i][WaterLevel]++;
}
else
{
vque[i][originalimage[currindex]].push(temp);
seedcounts[i][originalimage[currindex]]++;
}
}
}
}
}//while
seedcounts[i][WaterLevel]=vque[i][WaterLevel].size();
}//if
}//for j
}//for i
}//while
/**/
for(int i = 1;i<height-1; i++)
{
for(int j = 1; j<width-1;j++)
{
bool s = false,x_ = false,z = false,y_ = false;
int mu = i*width + j;
if(labelimage[mu] != labelimage[(i-1)*width +j])
s = true;
if(labelimage[mu] != labelimage[(i+1)*width + j])
x_ = true;
if(labelimage[mu] != labelimage[i*width + (j-1)])
z= true;
if(labelimage[mu] != labelimage[i*width + (j+1)])
y_ = true;
if(s || y_ ||x_ ||z)
{
image.setPixel(j,i,qRgb(255,0,0));
}
}
}
/**/
return 0;
}