详细原理见《数字图像处理》3.8
这里作简要说明:
为了用模糊术语表示“平滑区”,我们考虑中心领域处像素和中心像素的灰度差。
如下图所示,我们将一个像素8个相邻的像素,各减去这个像素的值,即,di=di-z5
现我们使用以下规则来判断输出像素是白色还是黑色
其中“0”(ZE),白色(WH),黑色(BL)都是模糊集合,它们的隶属度函数如下:
其中,“0”集合(ZE)的隶属度函数为
假设现在有如下领域:
则可计算其领域di值如下(根据图1):
根据式1计算各di属于“0”集合的隶属度u(di)如下:
于是d2属于“0”且d4属于“0”的隶属度为min(u(d2),u(d4)) = 0.78;
同理有:
条件1:d2属于“0”且d6属于“0”的隶属度为wh1=0.78
条件2:d6属于“0”且d8属于“0”的隶属度为wh2=0.78
条件3:d8属于“0”且d4属于“0”的隶属度为wh3=0.87
条件4:d4属于“0”且d2属于“0”的隶属度为wh4=0.78
由于前四个条件只需满足一个条件,输出即为“白色”(WH),故现用or语句将前四个条件连接,假设其为条件a:
wh=max(wh1,wh2,wh3,wh4)=0.87
条件b:前四个条件都不满足:
bl=1-wh=0.13
因为输出灰度属于“白色”的隶属度函数u(v)如下(见图3,WH线):
于是,条件A:同时满足前四个条件,并且输出灰度为“白色”,的输出为
Q(1)=min(wh,u(v)),如下图:
同理,条件B:满足条件5,且输出灰度属于“黑色”,的输出为:
Q(2)=min(bl,u(v)),如下图:
于是,全部输出为条件A或者条件B,即
Q = max(Q(1),Q(2)),如下图:
最后,我们对输出进行去模糊,即最终输出V0:
其中,v = 0,1,2.....L-1,即K=L-1
将图10代入式2得
v0=174.405
VS2013+OpenCV3.0代码如下:
/*
***************************************************************************************************************************************
-----------------------------------------------作者:我三食堂不服----------------------------------------------------------------------
***************************************************************************************************************************************
*/
#include
#include
#include
using namespace std;
using namespace cv;
/*
//模糊集合的边缘检测
//isUnify - 是否标定(仅在位深度为8时有效),false - 梯度值小于0时置0,大于255时置为255,true - 梯度值拉伸到0 ~ 255内
*/
int FuzzyEdge(const Mat &input, Mat &out, bool isUnify = false);
int main(int argc, char** argv)
{
Mat im_source; //原图
Mat im_pro1; //处理后的图片
Mat im_pro2; //处理后的图片
//载入原图(灰度图方式)
im_source = imread("3-59_a.tif", IMREAD_GRAYSCALE);
//图片处理
FuzzyEdge(im_source, im_pro1);
FuzzyEdge(im_source, im_pro2, true);
//创建窗口
namedWindow("【原图】");
namedWindow("【处理后1】");
namedWindow("【处理后2】");
//显示图片
imshow("【原图】", im_source);
imshow("【处理后1】", im_pro1);
imshow("【处理后2】", im_pro2);
//保存图片
imwrite("3-59_b.tif", im_pro1);
imwrite("3-59_c.tif", im_pro2);
//等待键盘操作
cvWaitKey(0);
return 0;
}
/*
//模糊集合的边缘检测
//isUnify - 是否标定(仅在位深度为8时有效),false - 梯度值小于0时置0,大于255时置为255,true - 梯度值拉伸到0 ~ 255内
*/
int FuzzyEdge(const Mat &input, Mat &out, bool isUnify)
{
double kernel[4]; // 领域灰度差\隶属度
double WH[256], BL[256]; // 颜色隶属度函数
double wh1, wh2, wh3, wh4, wh, bl; // 各条件对应白色(黑色)隶属度
double Q, MQ; // 输出模糊集合的隶属度、隶属度总和
Mat extend; // 对输入图像进行拓展
Mat out_f; // 输出图像
uchar* data_extend; // 拓展的图像像素数据
uchar* data_out; // 输出图片像素数据
long double data; // 灰度值
int h{ input.rows }; // 图片高
int w{ input.cols }; // 图片宽
int w2{ w + 2 }; // 拓展图片宽
int h2{ h + 2 }; // 拓展图片高
int u, v, i; // 循环变量
//参数输入错误
if (h < 3 || w < 3)
return 0;
//拓展图像
copyMakeBorder(input, extend, 1, 1, 1, 1, BORDER_REFLECT_101);
data_extend = extend.data;
//输出图像初始化
out.release();
out.create(h, w, input.type());
out_f.create(h, w, CV_32FC1);
data_out = out.data;
//颜色隶属度函数初始化
for (i = 0; i < 256; i++)
{
if (i < 204)
BL[i] = (204.0 - i) / 204.0;
else
BL[i] = 0;
if (i>51)
WH[i] = (i - 51.0) / 204.0;
else
WH[i] = 0;
}
//进行边缘增强
for (v = 1; v < h2 - 1; v++)
for (u = 1; u < w2 - 1; u++)
{
//计算灰度差(即d2,d4,d6,d8)
kernel[0] = data_extend[(v - 1)*w2 + u] - data_extend[v*w2 + u];
kernel[1] = data_extend[(v)*w2 + u - 1] - data_extend[v*w2 + u];
kernel[2] = data_extend[(v)*w2 + u + 1] - data_extend[v*w2 + u];
kernel[3] = data_extend[(v + 1)*w2 + u] - data_extend[v*w2 + u];
//计算d2,d4,d6,d8四个灰度差属于
//“灰度差为0”集合
//的隶属度
for (i = 0; i < 3; i++)
{
kernel[i] /= 255;
kernel[i] = exp(-40 * kernel[i] * kernel[i]);
}
//计算各像素属于
//“di和dj同时为0”集合(i,j=2、4、6、8)
//的隶属度
wh1 = min(kernel[0], kernel[1]);
wh2 = min(kernel[0], kernel[2]);
wh3 = min(kernel[3], kernel[1]);
wh4 = min(kernel[3], kernel[2]);
//计算各像素属于
//“d2,d4,d6,d8全为0”集合
//的隶属度
wh = max(max(wh1, wh2), max(wh3, wh4));
//计算各像素属于
//“满足第5个条件”集合
//的隶属度
bl = 1 - wh;
//计算输出灰度(重心)
data = 0;
MQ = 0;
for (i = 0; i < 256; i++)
{
Q = max(min(BL[i], bl), min(WH[i], wh));
data += i*Q;
MQ += Q;
}
data /= MQ;
out_f.at(v - 1, u - 1) = (float)data;
}
//图像格式转换
if (isUnify)
{
double max, min, size, k;
minMaxIdx(out_f, &min, &max);
size = max - min;
k = 255 / size;
for (v = 0; v < h; v++)
for (u = 0; u < w; u++)
{
data_out[v*w + u] = (uchar)(k*(out_f.at(v, u) - min));
}
}
else
{
for (v = 0; v < h; v++)
for (u = 0; u < w; u++)
{
data = out_f.at(v, u);
if (data < 0)
data = 0;
else if (data>255)
data = 255;
data_out[v*w + u] = (uchar)data;
}
}
return 0;
}