C语言二值图的腐蚀膨胀及开闭运算


by HPC_ZY


由于剧情,需要纯C(不用三方库)实现图像算法。但作为一名MATLAB老用户,刚学会了内存分配,顾开始实现图像算法,在此总结。
今后会持续更新MATLAB函数的C实现,欢迎交流,共同学习。

.
注:本文不讲原理,假设已经理解形态学原理,或通过以下文章学习
https://blog.csdn.net/weixin_42462804/article/details/84039380
.


一、结构元

  1. 类型定义
typedef struct
{
	int* x;  // 值为1的元素行坐标
	int* y;  // 值为1的元素列坐标
	int len;  // 值为1的元素数量
}StrElem;
  1. 自定义结构元

如上图结构元,参数分别为

StrElem se1,se2, se3;
se1.x = {0, 0, 1};
se1.y = {0, 1, 0};
se1.len =3;

se2.x = {-1, 0, 0, 0, 1};
se2.y = {0, -1, 0, 1, 0};
se2.len = 5;

se3.x = {0, 1, 2};
se3.y = {0, 0, 0};
se3.len = 3;
  1. 标准结构元
    这里只写两种,‘square’,‘diamond’
StrElem strel(int r, int type)
{
	StrElem se;
	switch(type)
	{
	case 1: // diamond
		se.len = (r*2 + 1)*(r*2 + 1);
		se.x = (int*)malloc(se.len*sizeof(int));
		se.y = (int*)malloc(se.len*sizeof(int));
		int idx = 0;
		for (int i = -r; i <= r; i++){
			for (int j = -r; j <= r; j++){
				if (abs(i) + abs(j) <= r){
					se.x[idx] = i;
					se.y[idx] = j;
					idx++;
				}
			}
		}
		break;
	case 2: // square
		se.len = 2*r*(r + 1) + 1;
		se.x = (int*)malloc(se.len*sizeof(int));
		se.y = (int*)malloc(se.len*sizeof(int));
		int idx = 0;
		for (int i = -r; i <= r; i++){
			for (int j = -r; j <= r; j++){
				se.x[idx] = i;
				se.y[idx] = j;
				idx++;
			}
		}
		break;
	}
	return se;
}

调用方式与结果

StrElem se1 = strel(2, 1);
StrElem se2 = strel(3, 2);

在这里插入图片描述


二、腐蚀

腐蚀相对简单,因为不用考虑图像边缘的问题

// 输出:图像  输入:原始图像,行,列,结构元半径,结构元类型
double** imerode2(double** in, int Rows, int Cols, int r, int type){
	// 初始化
	double** out = NULL;
	out = (double**)malloc(Rows * sizeof(double *));
	for (int i = 0; i < Rows; i++)
		out[i] = (double*)malloc(Cols * sizeof(double));
	for (int i = 0; i < Rows; i++)
		for (int j = 0; j < Cols; j++)
			out[i][j] = 0;
	// 腐蚀
	StrElem se;
	se = strel(r, type);
	for (int i = r; i < Rows-r; i++){	
		for (int j = r; j < Cols-r; j++){
			for (int k = 0; k < se.len; k++){
				if (in[i + se.x[k]][j + se.y[k]] < 1)
					goto p; // 判断是否整个结构元在目标内
			}
			out[i][j] = 1;
		p:;
		}
	}
	return out;
}

调用方式与结果

double** im1 = imerode2(im, 512, 512, 10, 1);

在这里插入图片描述

三、膨胀

膨胀的时候,目标可能会超出原图边界,所以要扩充原图

double** imdilate2(double** in, int Rows, int Cols, int r, int type){
	// 扩展图初始化
	double** tmpin = NULL, **tmpout = NULL;
	int tmpR = Rows + 2*r, tmpC = Cols + 2*r;
	tmpin = (double**)malloc(tmpR * sizeof(double *));
	tmpout = (double**)malloc(tmpR * sizeof(double *));
	for (int i = 0; i < tmpR; i++){
		tmpin [i] = (double*)malloc(tmpC * sizeof(double));
		tmpout [i] = (double*)malloc(tmpC * sizeof(double));
	}
	for (int i = 0; i < tmpR; i++)
		for (int j = 0; j < tmpC ; j++){
			tmpin [i][j] = 0;
			tmpout [i][j] = 0;
		}
	for (int i = 0; i < Rows; i++)
		for (int j = 0; j < Cols; j++)
			tmpin [i+r][j+r] = in[i][j];
	// 输出初始化
	double** out = NULL;
	out = (double**)malloc(Rows * sizeof(double *));
	for (int i = 0; i < Rows; i++)
		out[i] = (double*)malloc(Cols * sizeof(double));
	for (int i = 0; i < Rows; i++)
		for (int j = 0; j < Cols; j++)
			out[i][j] = 0;
	// 膨胀
	StrElem se;
	se = strel(r, type);
	for (int i = r; i < tmpR- r; i++)
		for (int j = r; j < tmpC - r; j++)
			if (tmpin[i][j]>0)
				for (int k = 0; k < se.len; k++)
					tmpout[i + se.x[k]][j + se.y[k]] = 1;
	// 截取赋值(保持与原图同尺寸)
	for (int i = 0; i < Rows; i++)
		for (int j = 0; j < Cols; j++)
			out[i][j] = tmpout [i+r][j+r];

    // free(别忘了释放)
    for (int i = 0; i < tmpR; i++){
        free(tmpin[i]);
        free(tmpout[i]);
    }
    free(tmpin);
    free(tmpout);
    
	return out;
}

调用方式与结果

double** im2 = imdilate2(im, 512, 512, 10, 1);

在这里插入图片描述

四、开闭运算

由于:
开运算 = 腐蚀+膨胀
闭运算 = 膨胀+腐蚀
这里就不列出代码了,大家可组合上述代码完成imopen2(),imclose2()。

调用方式与结果

double** im3 = imopen2(im, 512, 512, 10, 1);
double** im4 = imclose2(im, 512, 512, 10, 1);

五、其他

  1. 上述函数名结尾都有“2”,因为我们是对二维图像进行处理。对应的还有三维形态学操作,如 imopen3()。
  2. 后续可能更新灰度形态学或三维形态学
  3. 对应MATLAB代码
se = strel('diamond', 10);
im1 = imerode(im,se);
im2 = imdilate(im,se);
im3 = imopen(im,se);
im4 = imclose(im,se);

猜你喜欢

转载自blog.csdn.net/xsz591541060/article/details/91415052