by HPC_ZY
由于剧情,需要纯C(不用三方库)实现图像算法。但作为一名MATLAB老用户,刚学会了内存分配,顾开始实现图像算法,在此总结。
今后会持续更新MATLAB函数的C实现,欢迎交流,共同学习。
.
注:本文不讲原理,假设已经理解形态学原理,或通过以下文章学习
https://blog.csdn.net/weixin_42462804/article/details/84039380
.
一、结构元
- 类型定义
typedef struct
{
int* x; // 值为1的元素行坐标
int* y; // 值为1的元素列坐标
int len; // 值为1的元素数量
}StrElem;
- 自定义结构元
如上图结构元,参数分别为
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;
- 标准结构元
这里只写两种,‘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()。
调用方式与结果
// im是double**
double** im3 = imopen2(im, 512, 512, 10, 1);
double** im4 = imclose2(im, 512, 512, 10, 1);
五、其他
- 上述函数名结尾都有“2”,因为我们是对二维图像进行处理。对应的还有三维形态学操作,如 imopen3()。
- 后续可能更新灰度形态学或三维形态学
- 对应MATLAB代码
se = strel('diamond', 10);
im1 = imerode(im,se);
im2 = imdilate(im,se);
im3 = imopen(im,se);
im4 = imclose(im,se);