我是看着这个学习的,在此推荐一下:

北交图像处理与机器学习

在此记录一下自己的学习过程
在这里插入图片描述

1.项目代码

图像处理与机器学习课件和代码地址:传送门

含完整代码的项目可在github下载:

https://github.com/chenshunpeng/Impro_MLearning

ps.

  • 项目第一次运行可能会卡,第二次就好了
  • 如果项目需要加载.dll的符号,直接取消即可(即禁用后序符号加载)

2.第一章 基本概念

2.1.直方图(code)

在这里插入图片描述

数字图像:以空间位置 ( x , y ) (x,y) (x,y) 为自变量的二维函数 f ( x , y ) f(x,y) f(x,y)

直方图定义:

  • 不同灰度级分布构成不同图像
  • 统计灰度级出现的次数(概率)
  • 直方图表征了图像中灰度级分布特性

灰度直方图反映了图像灰度的分布(统计)特征

  • 灰度级的函数
  • 具有该灰度级的像素个数

计算公式:

h ( r k ) = n k h(r_{k})=n_{k} h(rk)=nk
r k 为灰度级 , n 为该灰度级的像素个数 r_{k}为灰度级,n为该灰度级的像素个数 rk为灰度级,n为该灰度级的像素个数
在这里插入图片描述
在这里插入图片描述

且有灰度直方图累加: ∑ k = 0 L − 1 h ( r k ) = ∑ k = 0 L − 1 n k = N ( 图像中像素的总个数 ) \sum ^{L-1}_{k=0}h\left( r_{k}\right) =\sum ^{L-1}_{k=0}n_{k}=N\left( 图像中像素的总个数\right) k=0L1h(rk)=k=0L1nk=N(图像中像素的总个数)

之后我们来写这部分的代码:

首先有初始化:

// 计算图像直方图
int histFlag;
int hist[256]; //存储图像直方图,256灰度级
void histCompute(BYTE*, int, int);//计算图像直方图函数

添加好事件处理函数,调用求取直方图函数,更新窗口,显示直方图

//计算图像直方图
void CMFCApplication1View::HistCompute(){
	// TODO: 完成histCompute函数设计

	//求取直方图的函数
	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]++;
		}
	}
}

如果想方便切换图片,可以修改路径,之后用键盘右上角的page downpage up键即可实现切换图片(当然也可以不改,用鼠标切换也很方便):

在这里插入图片描述

最后成功显示出结果:

在这里插入图片描述
核心代码位置:
在这里插入图片描述

3.第二章 图像增强

在这里插入图片描述
在这里插入图片描述
首先进行灰度变换: D B = f ( D A ) D_{B}=f(D_{A}) DB=f(DA)

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 ) = H A ( D A ) f ′ ( D A ) H_{B}\left( D_{B}\right)=\dfrac{H_{A}\left( D_{A}\right)}{f'\left( D_{A}\right) } HB(DB)=f(DA)HA(DA)

即灰度变换后图像直方图是变换前直方图与变换函数导数之比

3.1.直方图均衡(code)

  • 直方图均衡化处理之后,原来比较少像素的灰度会被分配到别的灰度去,像素相对集中, 处理后灰度范围变大,对比度变大,清晰度变大,所以能有效增强图像
  • 这种方法通常用来增加许多图像的局部对比度,尤其是当图像的有用数据的对比度相当接近的时候,通过这种方法,亮度可以更好地在直方图上分布
  • 可以用于增强局部的对比度而不影响整体的对比度,直方图均衡化通过有效地扩展常用的亮度来实现这种功能

改变后使每个灰度级拥有相同的像素个数,即出现
H B ( D B ) = H A ( D A ) f ′ ( D A ) = 常数 = A 0 D m H_{B}\left( D_{B}\right)=\dfrac{H_{A}\left( D_{A}\right)}{f'\left( D_{A}\right) }=常数=\dfrac{A_{0}}{D_{m}} HB(DB)=f(DA)HA(DA)=常数=DmA0

其中, D m D_{m} Dm代表灰度级, A 0 A_{0} A0 代表 D m D_{m} Dm这个灰度级上的 图像像素总数

通过积分最终得到:
D B = D m A 0 ⋅ ∑ 0 D A H A ( D A ) D_{B}=\dfrac{D_{m}}{A_{0}}\cdot \sum ^{D_{A}}_{0}H_{A}\left( D_{A}\right) DB=A0Dm0DAHA(DA)

这种技术可应用于人脸识别

我们来研究一下代码,共有3步:

  1. 计算输入图像直方图
  2. 计算像素新的灰度级
  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]];
}

其中注意一下BYTE* image的数据结构定义(在minwindef.h中):
在这里插入图片描述

成功显示出结果:

在这里插入图片描述
在这里插入图片描述

核心代码位置:

在这里插入图片描述

3.2.卷积运算

这部分是看着这个学习的,在此推荐一下:

信号与系统(MOOK,北邮)

首先了解最基本的名词:

在这里插入图片描述

之后需要了解单位冲激函数和单位阶跃函数(一些奇异信号):

在这里插入图片描述
关系如下:
在这里插入图片描述
之后引入卷积

τ = − ∞ \tau =-\infty τ= ∞ \infty e ( t ) e(t) e(t)可表示为许多窄脉冲的叠加

在这里插入图片描述

将任意信号表示为时移单位冲激信号的加权叠加:

在这里插入图片描述

利用卷积求系统的零状态响应:

在这里插入图片描述
在这里插入图片描述

卷积的物理意义:

在这里插入图片描述
在这里插入图片描述
总结一下,公式即:
f ( t ) = f 1 ( t ) ∗ f 2 ( t ) = ∫ − ∞ ∞ f 1 ( τ ) f 2 ( t − τ ) d τ f\left( t\right) =f_{1}\left( t\right) \ast f_{2}\left( t\right) =\int _{-\infty }^{\infty }f_{1}\left( \tau\right) f_{2}\left( t-\tau \right) d\tau f(t)=f1(t)f2(t)=f1(τ)f2(tτ)dτ

3.3.低通滤波

低通滤波的显著作用:实现图像平滑

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

代码部分:

我们用到的卷积公式:
y ( j , i ) = h ( j , i ) ∗ j ( j , i ) = ∑ m ∑ n h ( m , n ) x ( j + m , i + n ) y(j,i)=h(j,i)\ast j(j,i)=\sum _{m}\sum _{n}h(m,n)x(j+m,i+n) y(j,i)=h(j,i)j(j,i)=mnh(m,n)x(j+m,i+n)

点对点相乘的代码:

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;
}

3.3.1.均值滤波(code)

y ( j , i ) = ∑ m ∑ n h ( m , n ) x ( j + m , i + n ) = 1 9 ∑ m = − 1 1 ∑ n = − 1 1 x ( j + m , i + n ) \begin{aligned}y(j,i)=\sum _{m}\sum _{n}h(m,n)x(j+m,i+n)\\ =\dfrac{1}{9}\sum ^{1}_{m=-1}\sum ^{1}_{n=-1}x(j+m,i+n)\end{aligned} y(j,i)=mnh(m,n)x(j+m,i+n)=91m=11n=11x(j+m,i+n)

注意 b l o c k block block 的大小设为9就可以了,省空间

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++){
			//最外面这一圈不做卷积运算了,即外圈灰度级直接置0
			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.);
			}
		}
	}
}

Additive_Noised_Image.raw
在这里插入图片描述
Pulse_Noised_Image.raw
在这里插入图片描述

3.3.2.高斯滤波(code)

在这里插入图片描述

高斯滤波的归一化因子是16,因此除以16就行

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.);
			}

		}
	}
}

Additive_Noised_Image.raw
在这里插入图片描述
Pulse_Noised_Image.raw
在这里插入图片描述

附上直方图均衡:

Additive_Noised_Image.raw
在这里插入图片描述
Pulse_Noised_Image.raw
在这里插入图片描述

3.4.中值滤波(code)

为了在去除噪声的过程中、不模糊边缘,可以采用中值滤波:

在这里插入图片描述
在这里插入图片描述
代码部分:

中值滤波函数:

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 ( n l o g n ) O(nlogn) O(nlogn)

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];
}

中值滤波:

Additive_Noised_Image.raw
在这里插入图片描述

Pulse_Noised_Image.raw
在这里插入图片描述

3.5.高通滤波

高通滤波的显著作用:实现图像锐化
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

4.第三章 数学形态学处理

4.1.腐蚀(code)

  • 一种消除边界点,使边界向内部收缩的过程,用来消除小且无意义的物体
  • 图形点(gray=255)在其3x3邻域,只要有若干个背景点,则该点设为背景点(0)。

在这里插入图片描述

  • 我们选择的是2值图像,即像素大小只有0(背景点)和255(图形点)
  • 图像最外圈像素点不予处理

代码实现:

void CMFCApplication1View::erosion(BYTE* image, int w, int h, BYTE* outImg){
	int rept;
	//多次腐蚀,把结果图像image拷贝至outImg
	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);
}

腐蚀结果:

在这里插入图片描述

4.2.膨胀(code)

将与物体接触所有背景点合并到该物体中,使边界向外部扩张的过程,可以用来填补物体中的空洞

在这里插入图片描述
与腐蚀不同的是,我们关注的是图像的背景点,如果背景点周围有一定个数的图形点,我们就将这个背景点设为图形点

代码实现:

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);
}

膨胀:

在这里插入图片描述

4.3.开运算

先腐蚀后膨胀,能够消除图像区域 小白点(噪声)

在这里插入图片描述

4.4.闭运算

先膨胀后腐蚀,能够消除图像区域 小黑点(噪声)

在这里插入图片描述
开闭运算可以保持物体原有大小,一个是消除物体外部噪声(开运算)的,另一个是增强物体之间连接点(闭运算)的

5.第四章 图像分割

5.1.基于阈值的方法

在这里插入图片描述
在这里插入图片描述

5.2.基于边缘的方法

在这里插入图片描述
在这里插入图片描述

5.3.基于区域的方法

在这里插入图片描述

在这里插入图片描述

5.4.基于学习的方法

在这里插入图片描述

6.总结

在这里插入图片描述
数字图像处理需要认真的分析,本文涉及的算法都很简单,而且自己动手调参就可以看到变化,也易于解释(比如在腐蚀运算中,把邻域背景点判断范围从大于2放大为大于1)

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐