服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C++实现分水岭算法(Watershed Algorithm)

C++实现分水岭算法(Watershed Algorithm)

2021-06-18 15:00nagao_kagetora C/C++

这篇文章主要为大家详细介绍了C++实现分水岭算法Watershed Algorithm,具有一定的参考价值,感兴趣的小伙伴们可以参考一 下

分水岭分割方法(Watershed Segmentation),是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭。分水岭的概念和形成可以通过模拟浸入过程来说明。在每一个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响域慢慢向外扩展,在两个集水盆汇合处构筑大坝,即形成分水岭。
  分水岭的计算过程是一个迭代标注过程。分水岭比较经典的计算方法是L. Vincent提出的。在该算法中,分水岭计算分两个步骤,一个是排序过程,一个是淹没过程。首先对每个像素的灰度级进行从低到高排序,然后在从低到高实现淹没过程中,对每一个局部极小值在h阶高度的影响域采用先进先出(FIFO)结构进行判断及标注。
  分水岭变换得到的是输入图像的集水盆图像,集水盆之间的边界点,即为分水岭。显然,分水岭表示的是输入图像极大值点。因此,为得到图像的边缘信息,通常把梯度图像作为输入图像,即:
  grad(f(x,y))=((f(x-1,y)-f(x+1,y))^2 + (f(x,y-1)-f(x,y+1))^2)^0.5
  式中,f(x,y)表示原始图像,grad(.)表示梯度运算。
  分水岭算法对微弱边缘具有良好的响应,图像中的噪声、物体表面细微的灰度变化,都会产生过度分割的现象。但同时应当看出,分水岭算法对微弱边缘具有良好的响应,是得到封闭连续边缘的保证的。另外,分水岭算法所得到的封闭的集水盆,为分析图像的区域特征提供了可能。
  为消除分水岭算法产生的过度分割,通常可以采用两种处理方法,一是利用先验知识去除无关边缘信息。二是修改梯度函数使得集水盆只响应想要探测的目标。
  为降低分水岭算法产生的过度分割,通常要对梯度函数进行修改,一个简单的方法是对梯度图像进行阈值处理,以消除灰度的微小变化产生的过度分割。即:
  g(x,y)=max(grad(f(x,y)),gθ)
  式中,gθ表示阈值。
  程序可采用方法:用阈值限制梯度图像以达到消除灰度值的微小变化产生的过度分割,获得适量的区域,再对这些区域的边缘点的灰度级进行从低到高排序,然后在从低到高实现淹没的过程,梯度图像用Sobel算子计算获得。对梯度图像进行阈值处理时,选取合适的阈值对最终分割的图像有很大影响,因此阈值的选取是图像分割效果好坏的一个关键。缺点:实际图像中可能含有微弱的边缘,灰度变化的数值差别不是特别明显,选取阈值过大可能会消去这些微弱边缘。

  下面用C++实现分水岭算法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
#define _USE_MATH_DEFINES
 
#include <cstddef>
#include <cstdlib>
#include <cstring>
#include <climits>
#include <cfloat>
#include <ctime>
#include <cmath>
#include <cassert>
#include <vector>
#include <stack>
#include <queue>
 
using namespace std;
 
 
 
typedef void              GVVoid;
typedef bool              GVBoolean;
typedef char              GVChar;
typedef unsigned char          GVByte;
typedef short              GVInt16;
typedef unsigned short         GVUInt16;
typedef int               GVInt32;
typedef unsigned int          GVUInt32;
typedef long long            GVInt64;
typedef unsigned long long       GVUInt64;
typedef float              GVFloat32;
typedef double             GVFloat64;
 
const GVBoolean GV_TRUE         = true;
const GVBoolean GV_FALSE        = false;
const GVByte              GV_BYTE_MAX = UCHAR_MAX;
const GVInt32              GV_INT32_MAX = INT_MAX;
const GVInt32              GV_INT32_MIX = INT_MIN;
const GVInt64              GV_INT64_MAX = LLONG_MAX;
const GVInt64              GV_INT64_MIN = LLONG_MIN;
const GVFloat32 GV_FLOAT32_MAX     = FLT_MAX;
const GVFloat32 GV_FLOAT32_MIN     = FLT_MIN;
const GVFloat64 GV_FLOAT64_MAX     = DBL_MAX;
const GVFloat64 GV_FLOAT64_MIN     = DBL_MIN;
 
class                  GVPoint;
 
 
 
class GVPoint {
 
public:
  GVInt32       x;
  GVInt32       y;
 
public:
  GVPoint() : x(0), y(0) { }
  GVPoint(const GVPoint &obj) : x(obj.x), y(obj.y) { }
  GVPoint(GVInt32 x, GVInt32 y) : x(x), y(y) { }
 
public:
  GVBoolean operator ==(const GVPoint &right) const {
    return ((x == right.x) && (y == right.y));
  }
  GVBoolean operator !=(const GVPoint &right) const {
    return (!(x == right.x) || !(y == right.y));
  }
};
 
 
 
/*
 * <Parameter>
 *   <image> image data;
 *   <width> image width;
 *   <height> image height;
 *   <label out> image of labeled watersheds.
 */
GVVoid gvWatershed(
    const GVByte *image,
    GVInt32 width,
    GVInt32 height,
    GVInt32 *label)
{
 
  // Local constants
  const GVInt32 WSHD = 0;
  const GVInt32 INIT = -1;
  const GVInt32 MASK = -2;
  const GVPoint FICT_PIXEL = GVPoint(~0, ~0);
 
  // Image statistics and sorting
  GVInt32 size = width * height;
  GVInt32 *image_stat = new GVInt32[GV_BYTE_MAX + 1];
  GVInt32 *image_space = new GVInt32[GV_BYTE_MAX + 1];
  GVPoint *image_sort = new GVPoint[size];
  ::memset(image_stat, 0, sizeof (GVInt32) * (GV_BYTE_MAX + 1));
  ::memset(image_space, 0, sizeof (GVInt32) * (GV_BYTE_MAX + 1));
  ::memset(image_sort, 0, sizeof (GVPoint) * size);
  for (GVInt32 i = 0; !(i == size); ++i) {
    image_stat[image[i]]++;
  }
  for (GVInt32 i = 0; !(i == GV_BYTE_MAX); ++i) {
    image_stat[i + 1] += image_stat[i];
  }
  for (GVInt32 i = 0; !(i == height); ++i) {
    for (GVInt32 j = 0; !(j == width); ++j) {
      GVByte space = image[i * width + j];
      GVInt32 index = image_stat[space] - (++image_space[space]);
      image_sort[index].x = j;
      image_sort[index].y = i;
    }
  }
  for (GVInt32 i = GV_BYTE_MAX; !(i == 0); --i) {
    image_stat[i] -= image_stat[i - 1];
  }
 
  // Watershed algorithm
  GVPoint *head = image_sort;
  GVInt32 space = 0;
  GVInt32 *dist = new GVInt32[size];
  GVInt32 dist_cnt = 0;
  GVInt32 label_cnt = 0;
  std::queue<GVPoint> queue;
  ::memset(dist, 0, sizeof (GVInt32) * size);
  ::memset(label, ~0, sizeof (GVInt32) * size);
  for (GVInt32 h = 0; !(h == (GV_BYTE_MAX + 1)); ++h) {
    head += space;
    space = image_stat[h];
    for (GVInt32 i = 0; !(i == space); ++i) {
      GVInt32 index = head[i].y * width + head[i].x;
      GVInt32 index_l = ((head[i].x - 1) < 0) ? -1 : ((head[i].x - 1) + head[i].y * width);
      GVInt32 index_r = !((head[i].x + 1) > width) ? -1 : ((head[i].x + 1) + head[i].y * width);
      GVInt32 index_t = ((head[i].y - 1) < 0) ? -1 : (head[i].x + (head[i].y - 1) * width);
      GVInt32 index_b = !((head[i].y + 1) > height) ? -1 : (head[i].x + (head[i].y + 1) * width);
      label[index] = MASK;
      if (    (!(index_l < 0) && !(label[index_l] < WSHD))
          || (!(index_r < 0) && !(label[index_r] < WSHD))
          || (!(index_t < 0) && !(label[index_t] < WSHD))
          || (!(index_b < 0) && !(label[index_b] < WSHD))) {
        dist[index] = 1;
        queue.push(head[i]);
      }
    }
    dist_cnt = 1;
    queue.push(FICT_PIXEL);
    while (GV_TRUE) {
      GVPoint top = queue.front();
      GVInt32 index = top.y * width + top.x;
      GVInt32 index_l = ((top.x - 1) < 0) ? -1 : ((top.x - 1) + top.y * width);
      GVInt32 index_r = !((top.x + 1) > width) ? -1 : ((top.x + 1) + top.y * width);
      GVInt32 index_t = ((top.y - 1) < 0) ? -1 : (top.x + (top.y - 1) * width);
      GVInt32 index_b = !((top.y + 1) > height) ? -1 : (top.x + (top.y + 1) * width);
      queue.pop();
      if (top == FICT_PIXEL) {
        if (queue.empty()) break;
        else {
          ++dist_cnt;
          queue.push(FICT_PIXEL);
          top = queue.front();
          queue.pop();
        }
      }
      if (!(index_l < 0)) {
        if ((dist[index_l] < dist_cnt) && !(label[index_l] < WSHD)) {
          if (label[index_l] > WSHD) {
            if ((label[index] == MASK) || (label[index] = WSHD)) label[index] = label[index_l];
            else if (!(label[index] == label[index_l])) label[index] = WSHD;
          } else if (label[index] == MASK) {
            label[index] = WSHD;
          }
        } else if ((label[index_l] == MASK) && (dist[index_l] == 0)) {
          dist[index_l] = dist_cnt + 1;
          queue.push(GVPoint(top.x - 1, top.y));
        }
      }
      if (!(index_r < 0)) {
        if ((dist[index_r] < dist_cnt) && !(label[index_r] < WSHD)) {
          if (label[index_r] > WSHD) {
            if ((label[index] == MASK) || (label[index] = WSHD)) label[index] = label[index_r];
            else if (!(label[index] == label[index_r])) label[index] = WSHD;
          } else if (label[index] == MASK) {
            label[index] = WSHD;
          }
        } else if ((label[index_r] == MASK) && (dist[index_r] == 0)) {
          dist[index_r] = dist_cnt + 1;
          queue.push(GVPoint(top.x + 1, top.y));
        }
      }
      if (!(index_t < 0)) {
        if ((dist[index_t] < dist_cnt) && !(label[index_t] < WSHD)) {
          if (label[index_t] > WSHD) {
            if ((label[index] == MASK) || (label[index] = WSHD)) label[index] = label[index_t];
            else if (!(label[index] == label[index_t])) label[index] = WSHD;
          } else if (label[index] == MASK) {
            label[index] = WSHD;
          }
        } else if ((label[index_t] == MASK) && (dist[index_t] == 0)) {
          dist[index_t] = dist_cnt + 1;
          queue.push(GVPoint(top.x, top.y - 1));
        }
      }
      if (!(index_b < 0)) {
        if ((dist[index_b] < dist_cnt) && !(label[index_b] < WSHD)) {
          if (label[index_b] > WSHD) {
            if ((label[index] == MASK) || (label[index] = WSHD)) label[index] = label[index_b];
            else if (!(label[index] == label[index_b])) label[index] = WSHD;
          } else if (label[index] == MASK) {
            label[index] = WSHD;
          }
        } else if ((label[index_b] == MASK) && (dist[index_b] == 0)) {
          dist[index_b] = dist_cnt + 1;
          queue.push(GVPoint(top.x, top.y + 1));
        }
      }
    }
    for (GVInt32 i = 0; !(i == space); ++i) {
      GVInt32 index = head[i].y * width + head[i].x;
      dist[index] = 0;
      if (label[index] == MASK) {
        label_cnt++;
        label[index] = label_cnt;
        queue.push(head[i]);
        while (!queue.empty()) {
          GVPoint top = queue.front();
          GVInt32 index_l = ((top.x - 1) < 0) ? -1 : ((top.x - 1) + top.y * width);
          GVInt32 index_r = !((top.x + 1) > width) ? -1 : ((top.x + 1) + top.y * width);
          GVInt32 index_t = ((top.y - 1) < 0) ? -1 : (top.x + (top.y - 1) * width);
          GVInt32 index_b = !((top.y + 1) > height) ? -1 : (top.x + (top.y + 1) * width);
          queue.pop();
          if (!(index_l < 0) && (label[index_l] == MASK)) {
            queue.push(GVPoint(top.x - 1, top.y));
            label[index_l] = label_cnt;
          }
          if (!(index_r < 0) && (label[index_r] == MASK)) {
            queue.push(GVPoint(top.x + 1, top.y));
            label[index_r] = label_cnt;
          }
          if (!(index_t < 0) && (label[index_t] == MASK)) {
            queue.push(GVPoint(top.x, top.y - 1));
            label[index_t] = label_cnt;
          }
          if (!(index_b < 0) && (label[index_b] == MASK)) {
            queue.push(GVPoint(top.x, top.y + 1));
            label[index_b] = label_cnt;
          }
        }
      }
    }
  }
 
  // Release resources
  delete[] image_stat;
  delete[] image_space;
  delete[] image_sort;
  delete[] dist;
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:http://blog.csdn.net/nagao_kagetora/article/details/5995925

延伸 · 阅读

精彩推荐
  • C/C++深入C++拷贝构造函数的总结详解

    深入C++拷贝构造函数的总结详解

    本篇文章是对C++中拷贝构造函数进行了总结与介绍。需要的朋友参考下...

    C++教程网5182020-11-30
  • C/C++关于C语言中E-R图的详解

    关于C语言中E-R图的详解

    今天小编就为大家分享一篇关于关于C语言中E-R图的详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看...

    Struggler095962021-07-12
  • C/C++c/c++实现获取域名的IP地址

    c/c++实现获取域名的IP地址

    本文给大家汇总介绍了使用c/c++实现获取域名的IP地址的几种方法以及这些方法的核心函数gethostbyname的详细用法,非常的实用,有需要的小伙伴可以参考下...

    C++教程网10262021-03-16
  • C/C++OpenCV实现拼接图像的简单方法

    OpenCV实现拼接图像的简单方法

    这篇文章主要为大家详细介绍了OpenCV实现拼接图像的简单方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    iteye_183805102021-07-29
  • C/C++C语言main函数的三种形式实例详解

    C语言main函数的三种形式实例详解

    这篇文章主要介绍了 C语言main函数的三种形式实例详解的相关资料,需要的朋友可以参考下...

    ieearth6912021-05-16
  • C/C++C语言实现双人五子棋游戏

    C语言实现双人五子棋游戏

    这篇文章主要为大家详细介绍了C语言实现双人五子棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    两片空白7312021-11-12
  • C/C++c/c++内存分配大小实例讲解

    c/c++内存分配大小实例讲解

    在本篇文章里小编给大家整理了一篇关于c/c++内存分配大小实例讲解内容,有需要的朋友们可以跟着学习参考下。...

    jihite5172022-02-22
  • C/C++使用C++制作简单的web服务器(续)

    使用C++制作简单的web服务器(续)

    本文承接上文《使用C++制作简单的web服务器》,把web服务器做的功能稍微强大些,主要增加的功能是从文件中读取网页并返回给客户端,而不是把网页代码...

    C++教程网5492021-02-22