图像形态学shrink——C实现

本文实现参考matlab实现方式,将m语言转换成C实现。参考:https://ww2.mathworks.cn/help/images/ref/bwmorph.html
matlab中这样调用BW = bwmorph(BW1,‘shrink’,n);
matlab中这样描述shrink操作:With n = Inf, shrinks objects to points. It removes pixels so that objects without holes shrink to a point, and objects with holes shrink to a connected ring halfway between each hole and the outer boundary. This option preserves the Euler number.也即是当输入操作次数为无限时,shrink操作会将衣服二值图中物体区域变成单个的像素点。分为两种情况,一种是区域没洞时,区域变成一个点;另一种是区域有洞时,区域变成一个连接的环;此操纵保持平面欧拉数不变。

例如,
无洞情形:

     0     0     0     0     0     0     0     0
     0     1     1     1     1     1     1     0
     0     1     1     1     1     1     1     0
     0     1     1     1     1     1     1     0
     0     1     1     1     1     1     1     0
     0     1     1     1     1     1     1     0
     0     1     1     1     1     1     1     0
     0     0     0     0     0     0     0     0

shrink操作之后变成:

     0     0     0     0     0     0     0     0
     0     0     0     0     0     0     0     0
     0     0     0     0     0     0     0     0
     0     0     0     0     0     0     0     0
     0     0     0     0     1     0     0     0
     0     0     0     0     0     0     0     0
     0     0     0     0     0     0     0     0
     0     0     0     0     0     0     0     0

有洞情形:

     0     0     0     0     0     0     0     0
     0     1     1     1     1     1     1     0
     0     1     1     1     1     1     1     0
     0     1     1     1     0     1     1     0
     0     1     1     1     1     1     1     0
     0     1     1     1     1     1     1     0
     0     1     1     1     1     1     1     0
     0     0     0     0     0     0     0     0

shrink操作之后变为:

     0     0     0     0     0     0     0     0
     0     0     0     0     1     0     0     0
     0     0     0     1     0     1     0     0
     0     0     1     0     0     0     1     0
     0     0     1     0     0     1     0     0
     0     0     0     1     1     0     0     0
     0     0     0     0     0     0     0     0
     0     0     0     0     0     0     0     0

matlab中的实现,已经进行整合:
首先获取shrink查找表:

 function lut = lutshrink
%LUTSHRINK Compute "shrink" look-up table.

%   Copyright 1993-2012 The MathWorks, Inc.  
    
%#codegen    

lut = logical([ ...
     0     0     0     0     0     0     0     0     0     0     0     0 ...
     0     0     0     0     0     1     1     1     1     0     1     1 ...
     1     1     1     1     0     0     1     1     0     0     0     0 ...
     0     0     0     0     0     0     0     0     0     0     0     0 ...
     1     0     1     1     1     0     1     1     0     0     1     1 ...
     0     0     1     1     0     0     0     0     0     0     0     0 ...
     0     0     0     0     0     0     0     0     1     0     0     0 ...
     0     0     0     0     1     1     1     1     0     0     1     1 ...
     0     0     0     0     0     0     0     0     0     0     0     0 ...
     0     0     0     0     0     0     0     0     0     0     0     0 ...
     0     0     1     1     0     0     1     1     0     0     0     0 ...
     0     0     0     0     0     0     0     0     0     0     0     0 ...
     1     0     0     0     0     0     0     0     1     1     1     1 ...
     0     0     1     1     0     0     0     0     0     0     0     0 ...
     0     0     0     0     0     0     0     0     1     0     1     1 ...
     1     0     1     1     1     1     0     0     1     1     0     0 ...
     0     0     0     0     0     0     0     0     0     0     0     0 ...
     0     0     0     0     1     0     0     0     0     0     0     0 ...
     1     1     1     1     0     0     1     1     0     0     0     0 ...
     0     0     0     0     0     0     0     0     0     0     0     0 ...
     1     0     1     1     1     0     1     1     1     1     0     0 ...
     1     1     0     0     0     0     0     0     0     0     0     0 ...
     0     0     0     0     0     0     0     0     1     0     0     0 ...
     0     0     0     0     0     0     0     0     0     0     0     0 ...
     0     0     0     0     0     0     0     0     0     0     0     0 ...
     0     0     0     0     1     0     1     1     1     0     1     1 ...
     0     0     1     1     0     0     1     1     0     0     0     0 ...
     0     0     0     0     0     0     0     0     0     0     0     0 ...
     0     0     0     0     0     0     0     0     0     0     0     0 ...
     0     0     0     0     0     0     0     0     0     0     0     0 ...
     0     0     0     0     0     0     0     0     0     0     0     0 ...
     0     0     0     0     0     0     1     1     0     0     1     1 ...
     0     0     0     0     0     0     0     0     0     0     0     0 ...
     0     0     0     0     1     0     0     0     0     0     0     0 ...
     1     1     1     1     0     0     1     1     0     0     0     0 ...
     0     0     0     0     0     0     0     0     0     0     0     0 ...
     1     0     1     1     1     0     1     1     1     1     0     0 ...
     1     1     0     0     0     0     0     0     0     0     0     0 ...
     0     0     0     0     0     0     0     0     1     0     0     0 ...
     0     0     0     0     1     1     1     1     0     0     1     1 ...
     0     0     0     0     0     0     0     0     0     0     0     0 ...
     0     0     0     0     1     0     1     1     1     0     1     1 ...
     1     1     0     0     1     1     0     0])';

完成一次shrink操作,一次shrink操作为4次查表迭代,第一次处理奇数行,奇数列,注意matlab中以1作为行号或者列号的开始,第二次处理偶数行列,第三次奇数行、偶数列,第4次偶数行奇数列;

 % First subiteration
        m   = bwlookup(bw, table);
        sub = bw & ~m;
        bw(1:2:end,1:2:end) = sub(1:2:end,1:2:end);
        
        % Second subiteration
        m   = bwlookup(bw, table);
        sub = bw & ~m;
        bw(2:2:end,2:2:end) = sub(2:2:end,2:2:end);
        
        % Third subiteration
        m   = bwlookup(bw, table);
        sub = bw & ~m;
        bw(1:2:end,2:2:end) = sub(1:2:end,2:2:end);
        
        % Fourth subiteration
        m   = bwlookup(bw, table);
        sub = bw & ~m;
        bw(2:2:end,1:2:end) = sub(2:2:end,1:2:end);

从下面的matlab可以看出如果本次shrink的结果与上次的结果相同,或者shrink操作达到指定的次数,结束循环。

    while (~done)
        lastbw = bw;
        bw     = applylut(bw, lut);//调用一次shrink操作;
        done   = ((iter >= n) | isequal(lastbw, bw));
        iter   = iter + 1;
    end

C实现:

shrink查找表,直接从matlab中拷贝过来,加上逗号,中括号改为花括号,C的数组形式:

static int shrink_table[] = {
	0, 0,  0,  0, 0,   0, 0,  0, 0,  0,  0, 0,
	0, 0,  0,  0, 0,   1, 1,  1, 1,  0,  1, 1,
	1, 1,  1,  1, 0,   0, 1,  1, 0,  0,  0, 0,
	0, 0,  0,  0, 0,   0, 0,  0, 0,  0,  0, 0,
	1, 0,  1,  1, 1,   0, 1,  1, 0,  0,  1, 1,
	0, 0,  1,  1, 0,   0, 0,  0, 0,  0,  0, 0,
	0, 0,  0,  0, 0,   0, 0,  0, 1,  0,  0, 0,
	0, 0,  0,  0, 1,   1, 1,  1, 0,  0,  1, 1,
	0, 0,  0,  0, 0,   0, 0,  0, 0,  0,  0, 0,
	0, 0,  0,  0, 0,   0, 0,  0, 0,  0,  0, 0,
	0, 0,  1,  1, 0,   0, 1,  1, 0,  0,  0, 0,
	0, 0,  0,  0, 0,   0, 0,  0, 0,  0,  0, 0,
	1, 0,  0,  0, 0,   0, 0,  0, 1,  1,  1, 1,
	0, 0,  1,  1, 0,   0, 0,  0, 0,  0,  0, 0,
	0, 0,  0,  0, 0,   0, 0,  0, 1,  0,  1, 1,
	1, 0,  1,  1, 1,   1, 0,  0, 1,  1,  0, 0,
	0, 0,  0,  0, 0,   0, 0,  0, 0,  0,  0, 0,
	0, 0,  0,  0, 1,   0, 0,  0, 0,  0,  0, 0,
	1, 1,  1,  1, 0,   0, 1,  1, 0,  0,  0, 0,
	0, 0,  0,  0, 0,   0, 0,  0, 0,  0,  0, 0,
	1, 0,  1,  1, 1,   0, 1,  1, 1,  1,  0, 0,
	1, 1,  0,  0, 0,   0, 0,  0, 0,  0,  0, 0,
	0, 0,  0,  0, 0,   0, 0,  0, 1,  0,  0, 0,
	0, 0,  0,  0, 0,   0, 0,  0, 0,  0,  0, 0,
	0, 0,  0,  0, 0,   0, 0,  0, 0,  0,  0, 0,
	0, 0,  0,  0, 1,   0, 1,  1, 1,  0,  1, 1,
	0, 0,  1,  1, 0,   0, 1,  1, 0,  0,  0, 0,
	0, 0,  0,  0, 0,   0, 0,  0, 0,  0,  0, 0,
	0, 0,  0,  0, 0,   0, 0,  0, 0,  0,  0, 0,
	0, 0,  0,  0, 0,   0, 0,  0, 0,  0,  0, 0,
	0, 0,  0,  0, 0,   0, 0,  0, 0,  0,  0, 0,
	0, 0,  0,  0, 0,   0, 1,  1, 0,  0,  1, 1,
	0, 0,  0,  0, 0,   0, 0,  0, 0,  0,  0, 0,
	0, 0,  0,  0, 1,   0, 0,  0, 0,  0,  0, 0,
	1, 1,  1,  1, 0,   0, 1,  1, 0,  0,  0, 0,
	0, 0,  0,  0, 0,   0, 0,  0, 0,  0,  0, 0,
	1, 0,  1,  1, 1,   0, 1,  1, 1,  1,  0, 0,
	1, 1,  0,  0, 0,   0, 0,  0, 0,  0,  0, 0,
	0, 0,  0,  0, 0,   0, 0,  0, 1,  0,  0, 0,
	0, 0,  0,  0, 1,   1, 1,  1, 0,  0,  1, 1,
	0, 0,  0,  0, 0,   0, 0,  0, 0,  0,  0, 0,
	0, 0,  0,  0, 1,   0, 1,  1, 1,  0,  1, 1,
	1, 1,  0,  0, 1,   1, 0,  0
};

从内存缓冲区中创建二值图像,相关数据结果可以参考前面的博客,

static uint8_2d* uint8_2d_create_from_data(uint8_t* data, int rows, int cols)
{
	uint8_2d* res = NULL;
	if (!data)return NULL;
	res = create_uint8_2d(rows, cols);
	if (!res)
	{
		return NULL;
	}
	memcpy(res->data, data, rows*cols);
	return res;

判断两幅二值图像是否相等,需要二值图像的所有点相等:

static int uint8_2d_equal(uint8_2d* a, uint8_2d* b)
{
	int res = 0;
	if (!a || !b)return res;
	int rows, cols;
	int i, j;
	uint8_t** a_arr;
	uint8_t** b_arr;
	assert(a->cols == b->cols && a->rows == b->rows);
	rows = a->rows;
	cols = b->cols;
	a_arr = a->arr;
	b_arr = b->arr;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			if (a_arr[i][j] != b_arr[i][j])
			{
				return res;
			}
		}
	}
	res = 1;
	return res;
}

拷贝图像:

static void uint8_2d_copy(uint8_2d* src, uint8_2d* dst)
{
	if (!src || !dst)return;
	uint8_t** src_arr;
	uint8_t** dst_arr;
	int rows, cols;
	int i;
	assert(src->cols == dst->cols && src->rows == dst->rows);
	rows = src->rows;
	cols = src->cols;
	src_arr = src->arr;
	dst_arr = dst->arr;
	for (i = 0; i < rows; i++)
	{
		memcpy(dst_arr[i], src_arr[i], cols);
	}
}

两幅图像相与:

static void uint8_2d_and(uint8_2d* a, uint8_2d* b, uint8_2d* res)
{
	if (!a || !b || !res)return;
	int rows, cols;
	int i, j;
	uint8_t** a_arr;
	uint8_t** b_arr;
	uint8_t** res_arr;
	assert(a->cols == b->cols && a->rows == b->rows);
	rows = a->rows;
	cols = b->cols;
	a_arr = a->arr;
	b_arr = b->arr;
	res_arr = res->arr;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			res_arr[i][j] = a_arr[i][j] & ~b_arr[i][j];
		}
	}
}

1/4尺寸图像拷贝,拷贝的是奇数行或偶数行,需要指定开始的行号或列号,隔行隔列拷贝

static void uint8_2d_copy_quarter_data(uint8_2d* src, uint8_2d* dst, int rs, int cs)
{
	if (!src || !dst)return;
	uint8_t** src_arr;
	uint8_t** dst_arr;
	int rows, cols;
	int i, j;
	assert(src->cols == dst->cols && src->rows == dst->rows);
	rows = src->rows;
	cols = src->cols;
	src_arr = src->arr;
	dst_arr = dst->arr;
	for (i = rs; i < rows; i+=2)
	{
		for (j = cs; j < cols; j+=2)
		{
			dst_arr[i][j] = src_arr[i][j];
		}
	}

查找表处理,图像的某点8邻域,权重值:

8      5      2
7      4      1
6      3      0

这里需要加上当前像素,考虑的是当前像素点加上8邻域的8个点,一个9个点的加权和,得到一个小于等于511的数,所以查找表由512个数构成,这样做的好处是可以节省比较的次数,将3x3的模板匹配换成了比较数字,提升算法执行效率。

static void shrink_look_up(uint8_2d* img, uint8_2d* res)
{
	if (!img || !res)return;
	int rows, cols;
	int i, j;
	int index;
	uint8_t** arr;
	uint8_t** res_arr;
	rows = img->rows;
	cols = img->cols;

	rows--;
	cols--;
	assert(rows > 1 && cols > 1);
	arr = img->arr;
	res_arr = res->arr;
	for (i = 1; i < rows; i++)
	{
		for (j = 1; j < cols; j++)
		{
			index = (arr[i - 1][j - 1] << 8) + (arr[i][j - 1] << 7) +\
					(arr[i + 1][j - 1] << 6) + (arr[i - 1][j] << 5) + \
					(arr[i][j] << 4) + (arr[i + 1][j] << 3) + \
					(arr[i - 1][j + 1] << 2) + (arr[i][j + 1] << 1) + \
					arr[i + 1][j + 1];
			assert(index < 512);
			res_arr[i][j] = shrink_table[index];
		}
	}
}

整合前面的方法:

void shrink(uint8_2d* img, int n)
{
	int iter = 0;
	int done = 0;
	uint8_2d* m = NULL;
	uint8_2d* sub = NULL;
	uint8_2d* last_img = NULL;
	if (!img)return;
	int rows, cols;
	rows = img->rows;
	cols = img->cols;
	m = create_uint8_2d(rows, cols);
	sub = create_uint8_2d(rows, cols);
	last_img = create_uint8_2d(rows, cols);
	if (!m || !sub)
	{
		destroy_uint8_2d(&m);
		destroy_uint8_2d(&sub);
		destroy_uint8_2d(&last_img);
		return;
	}
	while (!done)
	{
	   // 一次shrink操作,4次查找表处理
		uint8_2d_copy(img, last_img);

		shrink_look_up(img, m);
		//printf_uint8_2d(m);
		uint8_2d_and(img, m, sub);
		//printf_uint8_2d(sub);
		uint8_2d_copy_quarter_data(sub, img, 0, 0);
		//printf_uint8_2d(img);

		shrink_look_up(img, m);
		uint8_2d_and(img, m, sub);
		uint8_2d_copy_quarter_data(sub, img, 1, 1);

		shrink_look_up(img, m);
		uint8_2d_and(img, m, sub);
		uint8_2d_copy_quarter_data(sub, img, 0, 1);

		shrink_look_up(img, m);
		uint8_2d_and(img, m, sub);
		uint8_2d_copy_quarter_data(sub, img, 1, 0);

		//printf_uint8_2d(img);
		//达到设定次数或者当前二值图与上一次shrink操作结果相同,结束shrink操作
		if (iter++ >= n || uint8_2d_equal(last_img, img))
			done = 1;
	}
}

测试:

void main()
{
#define ROWS 8
#define COLS 8
	uint8_t data[] = {
		0, 0, 0, 0, 0, 0, 0, 0,
		0, 1, 1, 1, 1, 1, 1, 0,
		0, 1, 1, 1, 1, 1, 1, 0,
		0, 1, 1, 1, 0, 1, 1, 0,
		0, 1, 1, 1, 1, 1, 1, 0,
		0, 1, 1, 1, 1, 1, 1, 0,
		0, 1, 1, 1, 1, 1, 1, 0,
		0, 0, 0, 0, 0, 0, 0, 0
	};
	uint8_2d* img = uint8_2d_create_from_data(&data[0], ROWS, COLS);
	if (!img)return;
	shrink(img, INT_MAX);
	printf_uint8_2d(img);
}

你可能感兴趣的:(图像处理,C++)