图像分割概述
图像分割就是把图像细分为构成它的对象或子区域,这些区域是互不相交的,每个区域都满足特定区域的一致性。分割的程度主要取决于人们想要解决的问题,当感兴趣的区域或对象已经被区分出来,分割就算完成。图像分割是图像处理中的重要问题,也是计算机视觉研究中的一个经典难题。计算机视觉中的图像理解包括目标检测、特征提取和目标识别等,都依赖于分割的质量。
目前,图像分割算法一般是围绕亮度值的两个基本特性设计的:不连续性和相似性。亮度值的不连续性的应用途径主要是基于像素点特性(如灰度值)的不连续变化分割图像,如最常用的边缘检测。而利用亮度值的相似性可以形成一套机制,即依据事先指定的准则将图像分割为相似的区域。一些实例包括门限处理、区域分离、区域生长和聚类等。而采用模糊C均值聚类及其扩展算法进行图像分割的好处是避免了阈值的设定问题,聚类的过程不需要人工干预,只需输入预想的分类数目即可实现自动化的图像分割。
1、灰度共生矩阵
原理
灰度共生矩阵(GLDM)的统计方法是20世纪70年代初由R.Haralick等人提出的,它是在假定图像中各像素间的空间分布关系包含了图像纹理信息的前提下,提出的具有广泛性的纹理分析方法。
灰度共生矩阵被定义为从灰度为i的像素点出发,离开某个固定位置的点上灰度值为的概率,所有估计的值可以表示成一个矩阵的形式,以此被称为灰度共生矩阵。对于纹理变化缓慢的图像,其灰度共生矩阵对角线上的数值较大;而对于纹理变化较快的图像,其灰度共生矩阵对角线上的数值较小,对角线两侧的值较大。由于灰度共生矩阵的数据量较大,一般不直接作为区分纹理的特征,而是基于它构建的一些统计量作为纹理分类特征。Haralick曾提出了14种基于灰度共生矩阵计算出来的统计量:即:能量、熵、对比度、均匀性、相关性、方差、和平均、和方差、和熵、差方差、差平均、差熵、相关信息测度以及最大相关系数。
上图显示了如何求解灰度共生矩阵,以(1,1)点为例GLCM(1,1)值为1说明只有一对灰度为1的像素水平相邻。GLCM(1,2)值为2,是因为有两对灰度为1和2的像素水平相邻。
GLCM表其实就是所有像素可能的组合,比如,GLCM(1,1)就是I中像素值为1和1的组合,GLCM(4,5)就是I中像素4和像素5的组合,GLCM(i,j)的值呢就是I中像素为i,像素为j的有有多少和相邻的成对点。这个相邻有个规则:就是f(x,y),f(x+a,y+b)相邻,就是只有x相隔a的单位,y相隔b个单位,我们认为是相邻的。
上图所示为:a=1,b=0,也就是f(x,y)和f(x+1,y+0)相邻。
于是就有了:
a=1,b=0 时我们就说水平相邻:也就是0度的时候
a=1,b=1 时我们就说对角相邻,也就是45度的时候
a=-1,b=1时 即135度
其他角度类似。
在a=1,b=0时:GLCM(1,1)=1;其实就是I中有几个1和1相邻(1个)(按上面的规则)GLCM(1,2)=2,几个1和2相邻(2个)。
常用的统计模型
- 角二阶矩(Angular Second Moment, ASM)
角二阶矩又称能量,是图像灰度分布均匀程度和纹理粗细的一个度量,反映了图像灰度分布均匀程度和纹理粗细度。当图像纹理均一规则时,能量值较大;反之灰度共生矩阵的元素值相近,能量值较小。
$$
A S M=\sum_{i} \sum_{j} P(i, j)^{2}
$$
- 熵(Entropy, ENT)
熵度量了图像包含信息量的随机性,表现了图像的复杂程度。当共生矩阵中所有值均相等或者像素值表现出最大的随机性时,熵最大。
$$
E N T=-\sum_{i} \sum_{j} P(i, j) \log (P(i, j))
$$
- 对比度
对比度反应了图像的清晰度和纹理的沟纹深浅。纹理越清晰反差越大对比度也就越大。
$$
\operatorname{Con}=\sum_{i} \sum_{j}(i-j)^{2} P(i, j)
$$
- 反差分矩阵(Inverse Differential Moment, IDM)
反差分矩阵又称逆方差,反映了纹理的清晰程度和规则程度,纹理清晰、规律性较强、易于描述的,值较大。
$$
I D M=\sum_{i} \sum_{j} \frac{P(i, j)}{1+(i-j)^{2}}
$$
代码
glcm.h
#ifndef BLIND_GLCM_H
#define BLIND_GLCM_H
#include
#include "math.h"
using namespace cv;
using namespace std;
// 灰度等级
// Gray Level (Choose in 4/8/16)
enum GrayLevel
{
GRAY_4,
GRAY_8,
GRAY_16
};
// 灰度统计方向
// Gray Value Statistical Direction
// (Choose in 0°, 45°, 90°, 135°)
enum GrayDirection
{
DIR_0,
DIR_45,
DIR_90,
DIR_135
};
// 彩色图中的指定通道
// Point out R, G, B Channel of a Image
enum RGBChannel
{
CHANNEL_R,
CHANNEL_G,
CHANNEL_B
};
// 纹理特征值结构体
// struct including Texture Eigenvalues
struct TextureEValues
{
// 能量
float energy;
// 对比度
float contrast;
// 相关度
float homogenity;
// 熵
float entropy;
};
class GLCM
{
public:
// 从彩色通道中提取一个通道
// Extract a channel from RGB Image
void getOneChannel(Mat src, Mat& dstChannel, RGBChannel channel = CHANNEL_R);
// 将灰度图中的所有像素值量级化,可以被量化为4/8/16个等级
// Magnitude all pixels of Gray Image, and Magnitude Level can be chosen in 4/8/16;
void GrayMagnitude(Mat src, Mat& dst, GrayLevel level = GRAY_8);
// 计算一个矩阵窗口中,按照某个方向统计的灰度共生矩阵
// Calculate the GLCM of one Mat Window according to one Statistical Direction.
void CalcuOneGLCM(Mat src, Mat &dst, int src_i, int src_j, int size, GrayLevel level = GRAY_8, GrayDirection direct = DIR_0);
// 矩阵的归一化,将矩阵所有元素与矩阵中所有元素之和作除运算,得到概率矩阵
// Normalize the Martix, make all pixels of Mat divided by the sum of all pixels of Mat, then get Probability Matrix.
void NormalizeMat(Mat src, Mat& dst);
// 计算单个窗口矩阵的图像纹理特征值,包括能量、对比度、相关度、熵
// Calculate Texture Eigenvalues of One Window Mat, which is including Energy, Contrast, Homogenity, Entropy.
void CalcuOneTextureEValue(Mat src, TextureEValues& EValue, bool ToCheckMat = false);
// 计算全图的图像纹理特征值,包括能量、对比度、相关度、熵
// Calculate Texture Eigenvalues of One Window Mat, which is including Energy, Contrast, Homogenity, Entropy.
void CalcuTextureEValue(Mat src, TextureEValues& EValue,
int size = 5, GrayLevel level = GRAY_8);
// 计算整幅图像的纹理特征
void CalcuTextureImages(Mat src, Mat& imgEnergy, Mat& imgContrast, Mat& imgHomogenity, Mat& imgEntropy,
int size = 5, GrayLevel level = GRAY_8, bool ToAdjustImg = false);
};
#endif //BLIND_GLCM_H
glcm.cpp
#include "glcm.h"
void GLCM::getOneChannel(Mat src, Mat& dstChannel, RGBChannel channel)
{
// 若输入图像已经是灰度图,则直接输出
if(src.channels() == 1)
dstChannel = src;
vector bgr;
// 分离图像
split(src, bgr);
switch(channel)
{
case CHANNEL_B: dstChannel = bgr[0]; break;
case CHANNEL_G: dstChannel = bgr[1]; break;
case CHANNEL_R: dstChannel = bgr[2]; break;
default:
cout<<"ERROR in getOneChannel(): No Such Channel."<(j);
uchar* output = dst.ptr(j);
for(int i = 0; i < tmp.cols; i++)
{
switch(level)
{
case GRAY_4:
output[i] = cv::saturate_cast(current[i] / 64);
break;
case GRAY_8:
output[i] = cv::saturate_cast(current[i] / 32);
break;
case GRAY_16:
output[i] = cv::saturate_cast(current[i] / 16);
break;
default:
cout<<"ERROR in GrayMagnitude(): No Such GrayLevel."< src.rows
|| src_j + (size/2) + 1 > src.cols
|| src_i < (size/2)
|| src_j < (size/2))
{
size = 3;
if(src_i <= size/2)
{
if(src_j <= size/2)
srcCut = Mat(src, Range(0, 3), Range(0, 3));
else if(src_j + (size/2) + 1 > src.cols)
srcCut = Mat(src, Range(0, 3), Range(src.cols - 3, src.cols));
else
srcCut = Mat(src, Range(0, 3), Range(src_j - size/2, src_j + size/2 + 1));
}
else if(src_i >= src.rows - size/2)
{
if(src_j <= size/2)
srcCut = Mat(src, Range(src.rows - 3, src.rows), Range(0, 3));
else if(src_j + (size/2) + 1 > src.cols)
srcCut = Mat(src, Range(src.rows - 3, src.rows), Range(src.cols - 3, src.cols));
else
srcCut = Mat(src, Range(src.rows - 3, src.rows), Range(src_j - size/2, src_j + size/2 + 1));
}
else if(src_j <= size/2)
{
if(src_i <= size/2)
srcCut = Mat(src, Range(0, 3), Range(0, 3));
else if(src_i + (size/2) + 1 > src.rows)
srcCut = Mat(src, Range(src.rows - 3, src.rows), Range(0, 3));
else
srcCut = Mat(src, Range(src_i - size/2, src_i + size/2 + 1), Range(0, 3));
}
else if(src_j >= src.cols - size/2)
{
if(src_i <= size/2)
srcCut = Mat(src, Range(0, 3), Range(src.cols - 3, src.cols));
else if(src_i + (size/2) + 1 > src.rows)
srcCut = Mat(src, Range(src.rows - 3, src.rows), Range(src.cols - 3, src.cols));
else
srcCut = Mat(src, Range(src_i - size/2, src_i + size/2 + 1), Range(src.cols - 3, src.cols));
}
else
srcCut = Mat(src, Range(src_i - size/2, src_i + size/2 + 1), Range(src_j - size/2, src_j + size/2 + 1));
}
else
srcCut = Mat(src, Range(src_i - size/2, src_i + size/2 + 1), Range(src_j - size/2, src_j + size/2 + 1));
// 根据灰度等级初始化灰度共生矩阵
// Initialize GLCM according Gray Level
switch(level)
{
case GRAY_4:
{
glcm = Mat_(4, 4);
for(int i = 0; i < 4; i++)
for(int j = 0; j < 4; j++)
glcm.at(j, i) = 0;
break;
}
case GRAY_8:
{
glcm = Mat_(8, 8);
for(int i = 0; i < 8; i++)
for(int j = 0; j < 8; j++)
glcm.at(j, i) = 0;
break;
}
case GRAY_16:
{
glcm = Mat_(16, 16);
for(int i = 0; i < 16; i++)
for(int j = 0; j < 16; j++)
glcm.at(j, i) = 0;
break;
}
default:
cout<<"ERROR in CalcuOneGLCM(): No Such Gray Level."<(srcCut.at(j, i), srcCut.at(j+1, i))++;
break;
case DIR_45:
for(int i = 0; i < srcCut.rows - 1; i++)
for(int j = 0; j < srcCut.cols - 1; j++)
glcm.at(srcCut.at(j, i), srcCut.at(j+1, i+1))++;
break;
case DIR_90:
for(int i = 0; i < srcCut.rows - 1; i++)
for(int j = 0; j < srcCut.cols; j++)
glcm.at(srcCut.at(j, i), srcCut.at(j, i+1))++;
break;
case DIR_135:
for(int i = 1; i < srcCut.rows; i++)
for(int j = 0; j < srcCut.cols - 1; j++)
glcm.at(srcCut.at(j, i), srcCut.at(j+1, i-1))++;
break;
default:
cout<<"ERROR in CalcuOneGLCM(): No such Direct."<(j, i);
if(sum == 0) sum = 1;
for(int i = 0; i < tmp.rows; i++)
for(int j = 0; j < tmp.cols; j++)
tmp.at(j, i) /= sum;
tmp.copyTo(dst);
}
/*===================================================================
* 函数名:CalcuOneTextureEValue
* 说明:计算单个窗口矩阵的图像纹理特征值,包括能量、对比度、相关度、熵
* 参数:
* Mat src: 源矩阵,窗口矩阵
* TextureEValues& EValue: 纹理特征值变量
* bool ToCheckMat: 检查输入矩阵是否为概率矩阵
* 返回值:void
*------------------------------------------------------------------
* Function: CalcuOneTextureEValue
*
* Summary:
* Calculate Texture Eigenvalues of the Window Mat, which is including
* Energy, Contrast, Homogenity, Entropy.
*
* Arguments:
* Mat src - source Matrix (Window Mat)
* TextureEValues& EValue - Texture Eigenvalues
* bool ToCheckMat - to check input Mat is Probability Mat or not
*
* Returns:
* void
=====================================================================
*/
void GLCM::CalcuOneTextureEValue(Mat src, TextureEValues& EValue, bool ToCheckMat)
{
if(ToCheckMat)
{
float sum = 0;
for(int i = 0; i < src.rows; i++)
for(int j = 0; j < src.cols; j++)
sum += src.at(j, i);
if(sum < 0.99 || sum > 1.01)
{
cout<<"ERROR in CalcuOneTextureEValue(): Sum of the Mat is not equal to 1.00."<(j, i), 2);
EValue.contrast += (powf((i - j), 2) * src.at(j, i) );
EValue.homogenity += (src.at(j, i) / (1 + fabs((float)(i - j))) );
if(src.at(j, i) != 0)
EValue.entropy -= (src.at(j, i) * log10(src.at(j, i)) );
}
}
/*===================================================================
* 函数名:CalcuTextureEValue
* 说明:计算全图的图像纹理特征值,包括能量、对比度、相关度、熵
* 参数:
* Mat src: 源矩阵,窗口矩阵
* TextureEValues& EValue: 输出目标,全图的纹理特征值变量
* int size: 窗口尺寸(仅支持5*5, 7*7)
* GrayLevel level: 灰度等级
* 返回值:void
*------------------------------------------------------------------
* Function: CalcuOneTextureEValue
*
* Summary:
* Calculate Texture Eigenvalues of One Window Mat, which is including
* Energy, Contrast, Homogenity, Entropy.
*
* Arguments:
* Mat src - source Matrix (Window Mat)
* TextureEValues& EValue - Output Dst: Texture Eigenvalues of the Whole Image
* int size - size of Mat Window (only support 5*5, 7*7)
* GrayLevel level - Destination image's Gray Level (choose in 4/8/16)
*
* Returns:
* void
=====================================================================
*/
void GLCM::CalcuTextureEValue(Mat src, TextureEValues& EValue, int size, GrayLevel level)
{
// 原图像的灰度图
// Gray Image of the Source Image
Mat imgGray;
// 窗口矩阵
// Window Matrix
Mat glcm_win;
// 归一化后的概率矩阵
// Probability Matrix after Normalizing
Mat glcm_norm;
// 纹理特征值缓存变量
// Texture Eigenvalues temp variable
TextureEValues EValue_temp;
// 初始化目标纹理特征值
// Init Dst Texture Eigenvalues
EValue.contrast = 0; EValue.energy = 0; EValue.entropy = 0; EValue.homogenity = 0;
// 检查输入图像是否为单通道图像,如果不是,则转换其格式
// Check if Input Image is Single Channel Image or not, IF it's Single Channel Image, then Convert its Format to Gray Image.
if(src.channels() != 1)
cvtColor(src, imgGray, CV_BGR2GRAY);
else
src.copyTo(imgGray);
for(int i = 0; i < imgGray.rows; i++)
{
for(int j = 0; j < imgGray.cols; j++)
{
// 计算所有统计方向的灰度共生矩阵与对应的特征值,并累加至缓存变量中
// Calculate All Statistical Direction's GLCM and Eigenvalues, then accumulate into temp variables
float energy, contrast, homogenity, entropy;
energy = contrast = homogenity = entropy = 0;
CalcuOneGLCM(imgGray, glcm_win, i, j, size, level, DIR_0);
NormalizeMat(glcm_win, glcm_norm);
CalcuOneTextureEValue(glcm_norm, EValue_temp, false);
energy += EValue_temp.energy; contrast += EValue_temp.contrast;
homogenity += EValue_temp.homogenity; entropy += EValue_temp.entropy;
CalcuOneGLCM(imgGray, glcm_win, i, j, size, level, DIR_45);
NormalizeMat(glcm_win, glcm_norm);
CalcuOneTextureEValue(glcm_norm, EValue_temp, false);
energy += EValue_temp.energy; contrast += EValue_temp.contrast;
homogenity += EValue_temp.homogenity; entropy += EValue_temp.entropy;
CalcuOneGLCM(imgGray, glcm_win, i, j, size, level, DIR_90);
NormalizeMat(glcm_win, glcm_norm);
CalcuOneTextureEValue(glcm_norm, EValue_temp, false);
energy += EValue_temp.energy; contrast += EValue_temp.contrast;
homogenity += EValue_temp.homogenity; entropy += EValue_temp.entropy;
CalcuOneGLCM(imgGray, glcm_win, i, j, size, level, DIR_135);
NormalizeMat(glcm_win, glcm_norm);
CalcuOneTextureEValue(glcm_norm, EValue_temp, false);
energy += EValue_temp.energy; contrast += EValue_temp.contrast;
homogenity += EValue_temp.homogenity; entropy += EValue_temp.entropy;
// 将所有方向计算得到的特征值平均化,得到的值即可消除统计方向影响
// average Eigenvalues of all Statistical Directions, then the average value has eliminated the effect of Statistical Directions
energy /= 4; contrast /= 4;
homogenity /= 4; entropy /= 4;
// 累加当前单个窗口的纹理特征值,作为整个图像的纹理特征值
// Accumulate Texture Eigenvalues of Current Window, then make the Sum as Texture Eigenvalues of the Whole Image
EValue.contrast += contrast;
EValue.energy += energy;
EValue.entropy += entropy;
EValue.homogenity += homogenity;
}
}
}
/*===================================================================
* 函数名:CalcuTextureImages
* 说明:计算整幅图像的纹理特征,并将结果输出到相应矩阵中
* 参数:
* Mat src: 原图像
* Mat& imgEnergy: 目标能量矩阵
* Mat& imgContrast: 目标对比度矩阵
* Mat& imgHomogenity: 目标相关度矩阵
* Mat& imgEntropy: 目标熵矩阵
* int size: 窗口尺寸(仅支持5*5, 7*7)
* GrayLevel level: 灰度等级
* bool ToAdjustImg: 是否调整输出的纹理特征图像
* 返回值:void
*------------------------------------------------------------------
* Function: CalcuTextureImages
*
* Summary:
* Calculate Texture Features of the whole Image, and output the result
* into Martixs.
*
* Arguments:
* Mat src - source Image
* Mat& imgEnergy - Destination Mat, Energy Matrix
* Mat& imgContrast - Destination Mat, Contrast Matrix
* Mat& imgHomogenity - Destination Mat, Homogenity Matrix
* Mat& imgEntropy - Destination Mat, Entropy Matrix
* int size - size of Mat Window (only support 5*5, 7*7)
* GrayLevel level - Destination image's Gray Level (choose in 4/8/16)
* bool ToAdjustImg: to Adjust output Texture Feature Images or not
*
* Returns:
* void
=====================================================================
*/
void GLCM::CalcuTextureImages(Mat src, Mat& imgEnergy, Mat& imgContrast, Mat& imgHomogenity, Mat& imgEntropy,
int size, GrayLevel level, bool ToAdjustImg)
{
// 窗口矩阵
// Window Matrix
Mat glcm_win;
// 归一化后的概率矩阵
// Probability Matrix after Normalizing
Mat glcm_norm;
// 纹理特征值缓存变量
// Texture Eigenvalues temp varialbe
TextureEValues EValue;
imgEnergy.create(src.size(), CV_32FC1);
imgContrast.create(src.size(), CV_32FC1);
imgHomogenity.create(src.size(), CV_32FC1);
imgEntropy.create(src.size(), CV_32FC1);
for(int i = 0; i < src.rows; i++)
{
float* energyData = imgEnergy.ptr(i);
float* contrastData = imgContrast.ptr(i);
float* homogenityData = imgHomogenity.ptr(i);
float* entropyData = imgEntropy.ptr(i);
for(int j = 0; j < src.cols; j++)
{
// 计算所有统计方向的灰度共生矩阵与对应的特征值,并累加至缓存变量中
// Calculate All Statistical Direction's GLCM and Eigenvalues, then accumulate into temp variables
float energy, contrast, homogenity, entropy;
energy = contrast = homogenity = entropy = 0;
CalcuOneGLCM(src, glcm_win, i, j, size, level, DIR_0);
NormalizeMat(glcm_win, glcm_norm);
CalcuOneTextureEValue(glcm_norm, EValue, false);
energy += EValue.energy; contrast += EValue.contrast;
homogenity += EValue.homogenity; entropy += EValue.entropy;
CalcuOneGLCM(src, glcm_win, i, j, size, level, DIR_45);
NormalizeMat(glcm_win, glcm_norm);
CalcuOneTextureEValue(glcm_norm, EValue, false);
energy += EValue.energy; contrast += EValue.contrast;
homogenity += EValue.homogenity; entropy += EValue.entropy;
CalcuOneGLCM(src, glcm_win, i, j, size, level, DIR_90);
NormalizeMat(glcm_win, glcm_norm);
CalcuOneTextureEValue(glcm_norm, EValue, false);
energy += EValue.energy; contrast += EValue.contrast;
homogenity += EValue.homogenity; entropy += EValue.entropy;
CalcuOneGLCM(src, glcm_win, i, j, size, level, DIR_135);
NormalizeMat(glcm_win, glcm_norm);
CalcuOneTextureEValue(glcm_norm, EValue, false);
energy += EValue.energy; contrast += EValue.contrast;
homogenity += EValue.homogenity; entropy += EValue.entropy;
// 将所有方向计算得到的特征值平均化,得到的值即可消除统计方向影响
// average Eigenvalues of all Statistical Directions, then the average value has eliminated the effect of Statistical Directions
energy /= 4; contrast /= 4;
homogenity /= 4; entropy /= 4;
energyData[j] = energy;
contrastData[j] = contrast;
homogenityData[j] = homogenity;
entropyData[j] = entropy;
}
}
// 调整输出特征图像,类型由CV_32FC1改为CV_8UC1,取值范围0--255
// Adjust output Texture Feature Images, Change its type from CV_32FC1 to CV_8UC1, Change its value range as 0--255
if(ToAdjustImg)
{
cv::normalize(imgEnergy, imgEnergy, 0, 255, NORM_MINMAX);
cv::normalize(imgContrast, imgContrast, 0, 255, NORM_MINMAX);
cv::normalize(imgEntropy, imgEntropy, 0, 255, NORM_MINMAX);
cv::normalize(imgHomogenity, imgHomogenity, 0, 255, NORM_MINMAX);
imgEnergy.convertTo(imgEnergy, CV_8UC1);
imgContrast.convertTo(imgContrast, CV_8UC1);
imgEntropy.convertTo(imgEntropy, CV_8UC1);
imgHomogenity.convertTo(imgHomogenity, CV_8UC1);
}
}
main.cpp
#include
#include"glcm.h"
using namespace std;
using namespace cv;
int main() {
char key;
Mat img;
GLCM glcm;
TextureEValues EValues;
// 程序运行时间统计变量
// the Time Statistical Variable of Program Running Time
double time;
double start;
// 纹理特征值矩阵
// the Matrixs of Texture Features
Mat imgEnergy, imgContrast, imgHomogenity, imgEntropy;
// 读取图像
// Read a Image
img = imread("/home/litchi/PycharmProjects/blindsidewalk/1.png");
Mat dstChannel;
glcm.getOneChannel(img, dstChannel, CHANNEL_B);
// 灰度量化,并统计运算时间
// Magnitude Gray Image, and calculate program running time
start = static_cast(getTickCount());
glcm.GrayMagnitude(dstChannel, dstChannel, GRAY_8);
time = ((double)getTickCount() - start) / getTickFrequency() * 1000;
cout << "Time of Magnitude Gray Image: " << time << "ms" <(getTickCount());
glcm.CalcuTextureImages(dstChannel, imgEnergy, imgContrast, imgHomogenity, imgEntropy, 5, GRAY_8, true);
time = ((double)getTickCount() - start) / getTickFrequency() * 1000;
cout << "Time of Generate the whole Image's Calculate Texture Features Image: " << time << "ms" << endl<(getTickCount());
glcm.CalcuTextureEValue(dstChannel, EValues, 5, GRAY_8);
time = ((double)getTickCount() - start) / getTickFrequency() * 1000;
cout << "Time of Calculate Texture Features of the whole Image: " << time << "ms" << endl<
CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(blind)
set(CMAKE_CXX_STANDARD 14)
set(OpenCV_DIR /usr/local/share/OpenCV)
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
link_directories(${OpenCV_LIBS})
add_executable(blind main.cpp glcm.h glcm.cpp)
target_link_libraries(blind
${OpenCV_LIBS}
)
2、模糊C均值聚类(FCM Fuzzy C-Means)
原理
FCM算法是一种基于划分的聚类算法,它的思想就是使得被划分到同一簇的对象之间相似度最大,而不同簇之间的相似度最小。k均值聚类的实现中,把每个样本划分到单一的类别中,亦即是每个样本只能属于一种类别,不能属于多种类别。这样的划分,称为硬划分。为了解决硬划分所带来的问题,因此有了称为软划分的聚类算法,这一类算法中,每个样本不再只能属于一种类别,而是对于每个样本,都有对应的隶属度数组,数组里的每一个元素代表该样本属于某种类别的程度。而该样本的隶属度数组中的总值等于1。
推导
假定数据集为X,如果把这些数据划分成c类的话,那么对应的就有c个类中心为C,每个样本j属于某一类i的隶属度为$u_{i j}$,那么定义一个FCM目标函数及其约束条件如下所示:
$$
J=\sum_{i=1}^{c} \sum_{j=1}^{n} u_{i j}^{m}\left|x_{j}-c_{i}\right|^{2}
$$
$$
\sum_{i=1}^{c} u_{i j}=1, j=1,2 \ldots, n
$$
看一下目标函数而知,由相应样本的隶属度与该样本到各个类中心的距离相乘组成的,m是一个隶属度的因子,个人理解为属于样本的轻缓程度,就像$x^{2}$与$x^{3}$这种一样。约束条件是一个样本属于所有类的隶属度之和要为1。观察目标函数可以发现,其中的变量有$u_{i j}$、$c_{i}$并且还有约束条件,那么如何求这个目标函数的极值呢?
这里首先采用拉格朗日乘数法将约束条件拿到目标函数中去,前面加上系数,并把约束条件的所有j展开,那么目标函数变成下列所示:
$$
J=\sum_{i=1}^{c} \sum_{j=1}^{n} u_{i j}^{m}\left|x_{j}-c_{i}\right|^{2}+\lambda_{1}\left(\sum_{i=1}^{c} u_{i 1}-1\right)+\ldots+\lambda_{j}\left(\sum_{i=1}^{c} u_{i j}-1\right)+\ldots+\lambda_{n}\left(\sum_{i=n}^{c} u_{i n}-1\right) )
$$
在约束条件下,可以求得目标函数取最小值时相应的隶属度矩阵和聚类中心。通常,该最小值用极小值代替,因此分别对各变量求偏导,并令偏导数为0,联立并解出更新后的模糊隶属度和聚类中心,如下公式
$$
u_{i j}=\frac{1}{\sum_{k}^{c}\left[d^{2}\left(x_{j^{\prime}}, v_{i}\right) / d^{2}\left(x_{j^{\prime}} v_{k}\right)\right]^{\frac{2}{m-1}}}
$$
$$
v_{i}=\frac{\sum_{j=1}^{n} u_{i j}^{m} x_{j}}{\sum_{j=1}^{n} u_{i j}^{m}}, \forall i, j, \quad i=1,2,3, \ldots, c \text { and } j=1,2,3, \ldots, n
$$