Exercise6--基于区域的分割


源码

之前的阈值分割是通过像素的特性分布为基础的阈值处理来完成的。而现在我们就来实现基于区域的分割。

区域增长

区域增长是指,根据预先定义的生长准则,将符合规则的像素组合为大区域的过程。

基本方法就是取一组“种子”点开始,从种子点四周开始检索,将符合生长准则的点都纳入种子点当中,直到种子四周都没有符合条件的像素点为止。

比如,生长准则为

那么,只要种子点四周的点与种子点的像素值相差在1以内,即纳入种子点之中。

那么计算方法就很简单了。

1,提取出图像$S(x,y)$的连通分量(即边界),然后,将所有属于边界的地方标记为1。

2,对输入的图像$f(x,y)$,根据输入的种子点的位置,进行8-联通种子点搜索,若符合条件$Q$,则纳入种子点当中,并标记。

3,根据标记出的值进行图像的输出。

在这里,我实现的例子

代码实现

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
int main(){

std::string path;
std::cin >> path;

auto data = ImageUtil::loadImageToGray(path);
auto seed = ImageUtil::loadImageToGray("bitmap/6.bmp");

BYTE *unRegion = new BYTE[data.width * data.height];
int *growQueX = new int[data.width * data.height];
int *growQueY = new int[data.width * data.height];
for (int i = 0; i < data.width * data.height; i++)
unRegion[i] = 0;

regionGrowWithSeed(seed, unRegion, growQueX, growQueY, 3, 0, 0, 1);
regionGrowWithSeed(seed, unRegion, growQueX, growQueY, 2, seed.width / 2, seed.height / 2, 2);
regionGrowWithSeed(seed, unRegion, growQueX, growQueY, 10, 328, 283 - 45, 3);


ImageUtil::IMGDATA newImg = seed;
newImg.rgbquad[1].rgbBlue = 255;
newImg.rgbquad[1].rgbGreen = 0;
newImg.rgbquad[1].rgbRed = 0;

newImg.rgbquad[2].rgbBlue = 0;
newImg.rgbquad[2].rgbGreen = 255;
newImg.rgbquad[2].rgbRed = 0;


newImg.rgbquad[3].rgbBlue = 0;
newImg.rgbquad[3].rgbGreen = 0;
newImg.rgbquad[3].rgbRed = 255;

data.fileHeader.bfOffBits = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER) + sizeof(RGBQUAD) * 4;
data.fileHeader.bfSize = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER) + sizeof(RGBQUAD) * 4 + data.infoHeader.biSizeImage;

data.infoHeader.biClrUsed = 4;


newImg.pImg = unRegion;
ImageUtil::outputImage(newImg, "bitmap/region_grow_with_seed.bmp");

return 0;
}

//采取8-联通的方式的广度优先搜索
void regionGrowWithSeed(const ImageUtil::IMGDATA& data,BYTE * unRegion,int *growQueX,int *growQueY, int threshold, int seedX, int seedY,int color)
{
int nDx[8] = { 0, 0,1,-1, 1,1,-1,-1 };
int nDy[8] = { 1,-1,0, 0,-1,1, 1,-1 };



int start = 0, end = 0;
growQueX[end] = seedX;
growQueY[end] = seedY;

while(start <= end)
{
const int currX = growQueX[start];
const int currY = growQueY[start];

for (int k = 0; k < 8; k++)
{
const int xx = currX + nDx[k];
const int yy = currY + nDy[k];

if(xx < data.width && xx >= 0 &&
yy < data.height && yy >= 0 &&
unRegion[yy * data.width + xx] == 0 &&
std::abs(data.pImg[yy * data.width + xx] - data.pImg[currY * data.width + currX]) < threshold)
{
end++;
growQueX[end] = xx;
growQueY[end] = yy;

unRegion[yy * data.width + xx] = color;

}
}

start++;
}

}

区域分裂与聚合

这种方法则是,不利用种子值,而是直接分裂一幅图像,然后再对合适的区域进行聚合得到合适的区域。

1,对图片进行分裂,分裂为左上,左下,右上,右下的四等分的区域

2,对图片的区域进行判断,求其平均灰度值,若灰度值不符合规则,则对该区域继续分裂(回到步骤一),否则,标记为true

3,对分裂完成的图片进行聚合,遍历分裂区域

4,若两个分裂区域是相邻的,则合并之

这样,就可以完成对一个图片的分裂了。

代码实现

这里的实现方式与上面的区域增长其实是差不多的。只是多出了一个对分裂出来的区域进行标记的数据结构而已。

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
struct Region
{
int wBeginIndex = 0, wEndIndex = 0, hBeginIndex = 0, hEndIndex = 0;
bool Q = false;
};


ImageUtil::IMGDATA reginGrowWithoutSeed(ImageUtil::IMGDATA data,int threadhold)
{
Region origin;
origin.wBeginIndex = 0;
origin.wEndIndex = data.width;
origin.hBeginIndex = 0;
origin.hEndIndex = data.height;

std::vector<Region> qTList;
std::queue<Region> queRegion;

Region * rList = new Region[4];

queRegion.push(origin);
int start = 0, end = 0;
while(start <= end)
{
const Region curRegion = queRegion.front();
queRegion.pop();
//if (!curRegion.Q)
Region * rQList = splitRegion(curRegion, rList);

for(int i =0;i < 4;i++)
{
if(rQList != nullptr)
{
if (getAver(data, rList[i]) > threadhold)
{
queRegion.push(rList[i]);
end++;
}
else
qTList.push_back(rList[i]);

}


}

start++;
}

delete[] rList;

BYTE *byte = new BYTE[data.width * data.height];
memset(byte, 0, data.width * data.height);
for(auto& r : qTList)
{
for (int i = r.hBeginIndex; i < r.hEndIndex; i++)
{
for (int j = r.wBeginIndex; j < r.wEndIndex; j++)
{
byte[i * data.width + j] = 1;
}
}
}

data.pImg = byte;
return data;
}

//分裂区域
Region* splitRegion(const Region& r,Region *alloc)
{

if (r.wEndIndex - r.wBeginIndex <= 1 || r.hEndIndex - r.hBeginIndex <= 1)
return nullptr;

alloc[0].wBeginIndex = r.wBeginIndex;
alloc[0].wEndIndex = r.wBeginIndex + (r.wEndIndex - r.wBeginIndex) / 2;
alloc[0].hBeginIndex = r.hBeginIndex;
alloc[0].hEndIndex = r.hBeginIndex + (r.hEndIndex - r.hBeginIndex) / 2;

alloc[1].wBeginIndex = r.wBeginIndex + (r.wEndIndex - r.wBeginIndex) / 2;
alloc[1].wEndIndex = r.wEndIndex;
alloc[1].hBeginIndex = r.hBeginIndex;
alloc[1].hEndIndex = r.hBeginIndex + (r.hEndIndex - r.hBeginIndex) / 2;

alloc[2].wBeginIndex = r.wBeginIndex;
alloc[2].wEndIndex = r.wBeginIndex + (r.wEndIndex - r.wBeginIndex) / 2;
alloc[2].hBeginIndex = r.hBeginIndex + (r.hEndIndex - r.hBeginIndex) / 2;
alloc[2].hEndIndex = r.hEndIndex;

alloc[3].wBeginIndex = r.wBeginIndex + (r.wEndIndex - r.wBeginIndex) / 2;
alloc[3].wEndIndex = r.wEndIndex;
alloc[3].hBeginIndex = r.hBeginIndex + (r.hEndIndex - r.hBeginIndex) / 2;
alloc[3].hEndIndex = r.hEndIndex;

return alloc;
}

//计算区域的平均灰度值
double getAver(const ImageUtil::IMGDATA& data, const Region& r)
{
int count = 0, result = 0;
for(int i = r.hBeginIndex;i < r.hEndIndex;i++)
{
for(int j = r.wBeginIndex;j<r.wEndIndex;j++)
{
result += data[i][j];
count++;
}
}

return static_cast<double>(result) / count;
}
  • 本文作者: ShinyGX
  • 本文链接: https://ShinyGX.github.io/posts/f25a7903/
  • 版权声明: 本博客所有文章除特别声明外,均采用 https://creativecommons.org/licenses/by-nc-sa/3.0/ 许可协议。转载请注明出处!
0%