0%

基于区域的图像分割——区域生长

​ 区域生长是一种基于区域的分割方式,从初始种子点出发,检查相邻像素点,判断是否需要加入该区域。

mountains.jpgresult.jpg

算法

​ 令 f(x, y) 表示一个输入图像阵列;S(x, y) 表示一个种子阵列,阵列中种子点位置处为1,其他位置处为0;Q 表示在每个位置 (x, y) 处所用的属性。假设阵列 fS 的尺寸相同。基于8连接的一个基本区域生长算法说明如下:

  1. S(x, y) 中寻找所有连通分量,并把每个连通分量腐蚀成一个像素;把找到的所有这种像素标记为1,把 S 中的所有其他像素标记为0;
  2. 在坐标对 (x, y) 处形成图像 fQ:如果输入图像在该坐标处满足给定的属性 Q,则令 fQ(x, y) = 1,否则令 fQ(x, y) = 0
  3. g 是这样形成的图像:即把 fQ 中为8连通种子点的所有1值点,添加到 S 中的每个种子点;
  4. 用不同的区域标记标记出 g 中的每个连通分量。这就是由区域生长得到的分割图像。

例子

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
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;


/**
区域生长
img为原图像,result为经区域生长后的图像,seed为初始种子点,threshold为阈值
输入种子点超出范围返回false
*/
bool regionGrowing(Mat img, Mat &result, Point2i seed, int threshold){
result = Mat::zeros(img.size(), CV_8UC1); //背景全部设置为黑
// 输入种子点超出范围返回false
if (seed.x < 0 || seed.y < 0 || seed.y > img.rows-1 || seed.x > img.cols-1) {
return false;
}

vector<Point2i> seeds; //种子点集
seeds.push_back(seed); //压入初始种子点
result.ptr<uchar>(seed.y)[seed.x] = 255; //种子点设置为白
int growDirections[8][2] = {{-1,-1}, {0,-1}, {1,-1}, {-1,0}, {1,0}, {-1,1}, {0,1}, {1,1}}; //生长方向

// 开始生长
while (!seeds.empty()) {
// 取出一个种子点作为现在循环的初始种子点
Point2i seed_current = seeds.back();
seeds.pop_back();

// 遍历各生长方向的邻点
for (int i = 0; i < 8; i++) {
Point2i neighborPoint = {seed_current.x + growDirections[i][0], seed_current.y + growDirections[i][1]}; //邻点
if (neighborPoint.x < 0 || neighborPoint.y < 0 || neighborPoint.x > img.cols-1 || neighborPoint.y > img.rows-1) { //邻点超出范围
continue;
}
if ((result.ptr<uchar>(neighborPoint.y)[neighborPoint.x] == 0) && abs(img.ptr<uchar>(neighborPoint.y)[neighborPoint.x] - img.ptr<uchar>(seed.y)[seed.x]) < threshold) {
// 设置为种子点
result.ptr<uchar>(neighborPoint.y)[neighborPoint.x] = 255; //设置为白色
seeds.push_back(neighborPoint); //压入种子集
}
}
}

return true;
}

int main() {
Mat img, result;
img = imread("mountains.jpg", IMREAD_GRAYSCALE); //从文件中加载灰度图像

// 读取图片失败,则停止
if (img.empty()) {
printf("读取图像文件失败");
system("pause");
return -1;
}

// 输入初始种子点
cout << "Select a point as seed from [" << img.rows-1 << ", " << img.cols-1 << "]" << endl;
int seed_row, seed_col;
cout << "row";
cin >> seed_row;
cout << "col";
cin >> seed_col;
Point2i seed;
seed.x = seed_col;
seed.y = seed_row;

// 区域生长
int threshold = 50;
regionGrowing(img, result, seed, threshold); //区域生长

// 图像显示
imshow("img",img);
imshow("result",result);
imwrite("result.jpg", result);
waitKey(); //等待键值输入

return 0;
}

结果

原图像:

mountains.jpg

经区域生长后的图像(取原种子点为(0, 0)):

result.jpg