北交图像处理与机器学习
在此记录一下自己的学习过程
灰度直方图反映了图像灰度的分布(统计)特征
计算公式:
h ( r k ) = n k h(r_{k})=n_{k} h(rk)=nk
r k 为 灰 度 级 , n 为 该 灰 度 级 的 像 素 个 数 r_{k}为灰度级,n为该灰度级的像素个数 rk为灰度级,n为该灰度级的像素个数
之后我们来写这部分的代码:
首先有初始化:
// 计算图像直方图
int histFlag;
int hist[256]; //存储图像直方图,256灰度级
void histCompute(BYTE*, int, int);//计算图像直方图函数
添加好事件处理函数,调用求取直方图函数,更新窗口,显示直方图
//计算图像直方图
void CMFCApplication1View::HistCompute()
{
// TODO: 在此添加命令处理程序代码
histCompute(image, width, height);
histFlag = 1;
OnInitialUpdate();
CRect ClientRect;
GetClientRect(&ClientRect);
InvalidateRect(&ClientRect);
}
计算直方图部分:
void CMFCApplication1View::histCompute(BYTE*image, int width, int height)
{
//计算直方图
int n;
for (n = 0; n < 256; n++)
{
hist[n] = 0;
}
int i, j;
BYTE gray;
for (i = 0; i < height; i++)
{
for (j = 0; j < width; j++)
{
gray = image[i * width + j];
//根据定义来求就好
hist[gray]++;
}
}
}
H A ( D A ) H_{A}\left( D_{A}\right) HA(DA)和 H B ( D B ) H_{B}\left( D_{B}\right) HB(DB)分布代表均衡前后图像直方图,改变后使每个灰度级拥有相同的像素个数,即
H B ( D B ) = 常 数 = A 0 D m H_{B}\left( D_{B}\right)=常数=\dfrac{A_{0}}{D_{m}} HB(DB)=常数=DmA0
通过积分得到:
D B = D m A 0 ⋅ ∑ 0 D A H A ( D A ) D_{B}=\dfrac{D_{m}}{A_{0}}\cdot \sum ^{DA}_{0}H_{A}\left( D_{A}\right) DB=A0Dm⋅0∑DAHA(DA)
其中, D m D_{m} Dm代表灰度级, A 0 A_{0} A0代表图像像素总数
这种技术可应用于人脸识别中
我们来研究一下代码,共有3步:
void CMFCApplication1View::hisEqualiz(BYTE* image, int w, int h, BYTE* outImg)
{
//直方图均衡
//计算输入图像直方图
int his[256];
int n,i,j;
for (n = 0; n < 256; n++)
{
his[n] = 0;
}
for (i = 0; i < h; i++)
for (j = 0; j < w; j++)
his[image[i * w + j]]++;
//计算像素新的灰度级
for (n = 1; n < 256; n++)
his[n] += his[n - 1];
BYTE gray[256];
float cons;
cons = 255.0 / his[255];
for (n = 0; n < 256; n++)
gray[n] = (BYTE)(cons * his[n]);
//新灰度级替换原灰度级
for (i = 0; i < h; i++)
for (j = 0; j < w; j++)
outImg[i * w + j]=gray[image[i * w + j]];
}
首先是卷积运算:
int CMFCApplication1View::convolution(int* operatr, BYTE* block)
{
int value;
int i, j;
value = 0;
//卷积运算
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++)
value += operatr[i * 3 + j] * block[i * 3 + j];
return value;
}
然后均值滤波:
void CMFCApplication1View::meanFilter(BYTE* image, int width, int heigth, BYTE* outImg)
{
//均值滤波
int smth[9];
int i, j, m, n;
BYTE block[9];
int value;
for (i = 0; i < 9; i++)
smth[i] = 1;
for (i = 0; i < height; i++)
{
for (j = 0; j < width; j++)
{
if (i == 0 || j == 0 || i == height - 1 || j == width - 1)
outImg[i * width + j] = 0;
else
{
for (m = -1; m < 2; m++)
for (n = -1; n < 2; n++)
block[(m + 1) * 3 + n + 1] = image[(i + m) * width + j + n];
value = convolution(smth, block);
outImg[i * width + j] = BYTE(value / 9.);
}
}
}
}
高斯滤波:
void CMFCApplication1View::gaussian(BYTE* image, int width, int heigth, BYTE* outImg)
{
//高斯滤波
int smth[9];
int i, j, m, n;
BYTE block[9];
int value;
smth[0] = 1; smth[4] = 4;
smth[1] = 2; smth[5] = 2;
smth[2] = 1; smth[6] = 1;
smth[3] = 2; smth[7] = 2;
smth[8] = 1;
for (i = 0; i < height; i++)
{
for (j = 0; j < width; j++)
{
if (i == 0 || j == 0 || i == height - 1 || j == width - 1)
outImg[i * width + j] = 0;
else
{
for (m = -1; m < 2; m++)
for (n = -1; n < 2; n++)
block[(m + 1) * 3 + n + 1] = image[(i + m) * width + j + n];
value = convolution(smth, block);
outImg[i * width + j] = BYTE(value / 16.);
}
}
}
}
中值滤波函数:
void CMFCApplication1View::midFindFiltering(BYTE* image, int width, int heigth, BYTE* outImg)
{
//中值滤波
int i, j, m, n;
BYTE block[9];
int value;
int blockNum = 9;
for (i = 0; i < height; i++)
{
for (j = 0; j < width; j++)
{
if (i == 0 || j == 0 || i == height - 1 || j == width - 1)
outImg[i * width + j] = 0;
else
{
for (m = -1; m < 2; m++)
for (n = -1; n < 2; n++)
block[(m + 1) * 3 + n + 1] = image[(i + m) * width + j + n];
value = MidValueFind(blockNum, block);
outImg[i * width + j] = value;
}
}
}
}
O ( n 2 ) O(n^2) O(n2)的排序函数,有兴趣推荐改成 O ( l o g n ) O(logn) O(logn):
int CMFCApplication1View::MidValueFind(int num, BYTE* d)
{
int value;
int i, j;
int temp;
for (i = 0; i < num - 1; i++)
for (j = i + 1; j < num; j++)
{
if (d[i] < d[j])
{
temp = d[i];
d[i] = d[j];
d[j] = temp;
}
}
return d[num / 2];
}
void CMFCApplication1View::erosion(BYTE* image, int w, int h, BYTE* outImg)
{
int rept;
//腐蚀
memcpy(outImg, image, sizeof(BYTE) * width * height);
int i, j;
int m, n;
BYTE flag;
for (rept = 0; rept < 3; rept++)//多次腐蚀
{
for (i = 1; i < h - 1; i++)
{
for (j = 1; j < w - 1; j++)
{
if (image[i * w + j] == 255)//找到一个图形点
{
flag = 0;
for (m = -1; m < 2; m++)
{
for (n = -1; n < 2; n++)
{
if (image[(i + m) * w + j + n] == 0)
{
flag++;//3x3邻域包含多少个背景点
break;
}
}
}
if (flag > 2)
outImg[i * w + j] = 0;//该图形点设为背景点
}
}
}
}
memcpy(image, outImg, sizeof(BYTE) * width * height);
}
将与物体接触所有背景点合并到该物体中,使边界向外部扩张的过程,可以用来填补物体中的空洞
void CMFCApplication1View::dilation(BYTE* image, int w, int h, BYTE* outImg)
{
int rept;
//膨胀
memcpy(outImg, image, sizeof(BYTE) * width * height);
int i, j;
int m, n;
BYTE flag;
for (rept = 0; rept < 3; rept++)//多次膨胀
{
for (i = 1; i < h - 1; i++)
{
for (j = 1; j < w - 1; j++)
{
if (image[i * w + j] == 0)//找到一个背景点(gray=0)
{
flag = 0;
for (m = -1; m < 2; m++)
{
for (n = -1; n < 2; n++)
{
if (image[(i + m) * w + j + n] == 255)
{
flag++;//3x3邻域包含多少个图形点
}
}
}
if (flag > 1)
outImg[i * w + j] = 255;//该图形点设为图形点
}
}
}
}
memcpy(image, outImg, sizeof(BYTE) * width * height);
}
先腐蚀后膨胀,能够消除图像区域外的小白点(噪声)
先膨胀后腐蚀,能够消除图像区域内的小黑点(噪声)
开闭运算可以保持物体原有大小,一个是消除物体外部噪声(开运算)的,另一个是增强物体之间连接点(闭运算)的
【北交】图像处理与机器学习课件和代码地址:
传送门
我这个项目可以在百度网盘下载:
传送门
提取码:
cccc