数字信号与图像处理课程设计
设计内容
1.图像几何变换
1.1图像平移
仿射变换:在向量空间中进行一次线性变换(乘以一个矩阵)和一次平移(加上一个向量),变换到另一个向量空间的过程。
使用opencv
中的warpAffine()
函数来实现仿射变换:
def translation(img, tx, ty):
rows, cols, channels = img.shape
move = np.float32([[1, 0, tx], [0, 1, ty]])
dst = cv2.warpAffine(img, move, (cols, rows))
return dst
效果如下图:
1.2图像旋转
围绕图像中心点旋转一定的角度,旋转通常也会改变图像的大小。
点P(x0, y0)绕原点逆时针旋转角度θ到点P1(x1, y1)。
- 根据旋转角度和旋转中心获取旋转矩阵
- 根据旋转矩阵进行仿射变换,即可实现任意角度和任意中心的旋转效果。
def rotate(img, yaw):
rows, cols, channels = img.shape
# getRotationMatrix2D有三个参数,第一个为旋转中心,第二个为旋转角度,第三个为缩放比例
M = cv2.getRotationMatrix2D((cols / 2, rows / 2), yaw, 1)
dst = cv2.warpAffine(img, M, (cols, rows))
return dst
如下图所示:逆时针旋转(中)和顺时针旋转(右)
1.3图像剪切
图像剪切通常是指获取矩形框选区域下的图像。原理较为简单,只需确认矩形框的左上角顶点与右下角顶点坐标,对原图像进行区域分割即可。
def cut(img, x_lt, y_lt, x_rb, y_rb):
return img[x_lt:x_rb, y_lt:y_rb]
1.4图像缩放
# 缩放系数 k ∈ [0, 2]
def imgScale(img, k):
# 输入k [0, 100]
k = k / 50
if k < 1:
# 缩小
dst = cv2.resize(img, (int(k * img.shape[1]), int(k * img.shape[0])), interpolation=cv2.INTER_AREA)
else:
# 放大
dst = cv2.resize(img, (int(k * img.shape[1]), int(k * img.shape[0])), interpolation=cv2.INTER_LINEAR)
return dst
2.图像增强
图像的线性变换操作:
其中参数a表示图像对比度变化,b表示图像亮度变化。
当a<0时,图像变换代表反转操作,如a=-1、b=255,这是常见的8位灰度图像的反转操作设置参数;
当|a| > 1时,图像变换代表对比度增加操作;
当|a| < 1时,图像变换代表对比度减少操作。
当b>0时,表示图像变换操作是亮度增加操作;
b<0时,表示图像变换操作是亮度减少操作。
2.1对比度调节
def adjust_contrast(img, contrast_factor):
contrast_factor = np.float32(contrast_factor + 20) / 50.0
table = np.array([(i - 74) * contrast_factor + 74 for i in range(0, 256)]).clip(0, 255).astype('uint8')
if img.shape[2] == 1:
return cv2.LUT(img, table)[:, :, np.newaxis]
else:
return cv2.LUT(img, table)
提高对比度(中)和降低对比度(右)
适当降低对比度,图像变得昏暗,明暗分布较为均匀。
适当提高对比度,光线强弱对比度更直观,视觉冲击力更强。
2.2亮度调节
# 亮度调节 参数 b: 0~100
def lightness(img, b):
# b ∈ [0, 100] -> [-1.0, 1.0]
b = np.float32(b - 50) / 180.0
b = b * 255
res = img.astype(np.float32)
res = res + b
res = np.where(res > 255, 255, res)
res = np.where(res < 0, 0, res)
res = res.astype(np.uint8)
return res
提高亮度(中)和降低亮度(右)。
2.3光感
在体验醒图app中的光感调节功能后,与亮度调节的直观差别是色彩保留度上,光感调节不是整体图片的色彩提亮,而是增强了图像中的光线。
思路:以原图像的像素值归一化后为权重,相比亮度调节,光感调节增加的常量乘以原像素值的权重。
# 图像光感调节 参数 a: 0~100
# a ∈ [0, 100] ->
def lightSense(img, a):
a = np.float32(a - 50) / 80.0
a = a * 255
res = img.astype(np.float32)
res = res + a * res / 255.0
res = np.where(res > 255, 255, res)
res = np.where(res < 0, 0, res)
res = res.astype(np.uint8)
return res
适当提高图像光感参数:
与之对比,适当提高图像亮度的效果图:
2.4曝光度调节
在摄影上,曝光(英语:Exposure)是指摄影的过程中允许进入镜头照在感光媒体(胶片相机的底片或是数字照相机的图像传感器)上的光量。“曝光”可以经由光圈,快门和感光媒体的感光度的组合来控制。
其中N是光圈(f值);t是曝光时间(快门),单位秒。
在感光度100时,曝光值与亮度的关系为:
由曝光值的定义,通过查阅相关文章,找到一个计算曝光值的算法,即通过点处理算法,各像素点乘以2^EV。
# 曝光度调节 参数 b: 0~100
def exposure(img, b):
# b ∈ [0, 100] -> [-2.5, 2.5]
b = np.float32(b - 50) / 20.0
print('b = %.2f' % b)
res = img.astype(np.float32)
res = res * pow(2, b)
res = np.where(res > 255, 255, res)
res = res.astype(np.uint8)
return res
原图片如下:
提高曝光度参数b为83后效果:
曝光参数b增加至92(过曝):
降低曝光度系数,b调节为10:
2.5饱和度调整
该部分参考photoshop中的饱和度调整算法。对每个像素点的有关处理,伪代码如下:
- 调整参数nparameter,取值范围为[-100, 100],对其归一化后为`increment∈[-1,1]`。
rgbMax = max(max(R[i,j], G[i,j]), B[i,j])
rgbMin = min(min(R[i,j], G[i,j]), B[i,j])
delta = (rgbMax - rgbMin) / 255
if delta = 0: 不调整 continue
val = (rgbMax + rgbMin) / 255
L = val / 2 (HSL中的L, 亮度)
饱和度 S = delta / val (L<0.5)
delta / (2 - val) (L>=0.5)
if increment>=0:
if increment+S>=1: alpha = S
else: alpha = 1-increment
alpha = 1/alpha-1
newRGB[i,j] = RGB[i,j] + (RGB[i,j] - L * 255) * alpha
else:
alpha = increment
newRGB[i,j] = L * 255 + (RGB[i,j] - L * 255) * (1+alpha)
使用Python复现,源代码如下:
# 饱和度调整算法 输入参数increment,[-100,100] 归一化为 [-1,1]
@jit
def saturation(img, increment):
increment = np.float32(increment - 50) / 50.0
res = img.astype(np.float64)
for i in range(img.shape[0]):
for j in range(img.shape[1]):
val_max = max(res[i, j, 0], max(res[i, j, 1], res[i, j, 2]))
val_min = min(res[i, j, 0], min(res[i, j, 1], res[i, j, 2]))
delta = (val_max - val_min) / 255
if delta == 0:
continue
val = (val_max + val_min) / 255
L = val / 2
if L < 0.5:
S = delta / val
else:
S = delta / (2 - val)
if increment >= 0:
if increment + S > 1:
alpha = S
else:
alpha = 1 - increment
alpha = 1 / alpha - 1
res[i, j, 2] = res[i, j, 2] + (res[i, j, 2] - L * 255) * alpha
res[i, j, 1] = res[i, j, 1] + (res[i, j, 1] - L * 255) * alpha
res[i, j, 0] = res[i, j, 0] + (res[i, j, 0] - L * 255) * alpha
else:
alpha = increment
res[i, j, 2] = L * 255 + (res[i, j, 2] - L * 255) * (1 + alpha)
res[i, j, 1] = L * 255 + (res[i, j, 1] - L * 255) * (1 + alpha)
res[i, j, 0] = L * 255 + (res[i, j, 0] - L * 255) * (1 + alpha)
res = res.astype(np.uint8)
return res
原图片:
提高饱和度系数,增量increment值为88:
降低饱和度,增量increment减小至29:
2.6 直方图均衡化
# 直方图均衡化
def equalize(img):
dst = img.copy()
rows, cols, channels = dst.shape
for i in range(channels):
dst[:, :, i] = cv2.equalizeHist(dst[:, :, i])
return dst
原图(左)与直方图均衡化(右)效果对比示例一:
原图(左)与直方图均衡化(右)效果对比示例二:
2.7 HSL调节(色相、饱和度、亮度)
设(r,g,b)分别是一个颜色的红、绿和蓝坐标,它们的值是在0到1之间的实数。设max等价于r, g和b中的最大者。设min等于这些值中的最小者。要找到在HSL空间中的(h, s, l)值,这里的 h ∈ [0,360)是角度的色相角,而s,l ∈ [0,1]是饱和度和亮度,计算为:
从HSL到RGB的转换
给定HSL空间中的(h, s, l)值定义的一个颜色,带有h在指示色相角度的值域[0,360)中,分别表示饱和度和亮度的s和/在值域[0.1中,相应在RGB空间中的(r, g,b)三原色,带有分别对应于红色、绿色和蓝色的 r,g和b也在值域[0.1]中,它们可计算为:
首先,如果S=0,则结果的颜色是非彩色的、或灰色的。在这个特殊情况,r, g和b都等子I。注意h的值在这种情况下是未定义的。当s≠0的时候,可以使用下列过程:
对每个颜色向量Color = (ColorR, ColorG, ColorB) = (r, g, b).
实现代码:
# 图像HSL调节
# cf_h 范围[0, 200] 对应[0, 2]
# cf_s, cf_l 范围[0, 200] 对应 [0, 2]
# 整个调用过程:
# 对于一个img
# 1. hslImg = convertToHSL(img)
# 2. dst = HSL(hslImg, cf_h, cf_s, cf_l)
# 3. dst = convertToBGR(dst)
@jit
def HSL(img, cf_h, cf_s, cf_l):
# cf_h 范围[0, 200] 对应[0, 2]
# cf_s, cf_l 范围[0, 200] 对应 [0, 2]
# h_new = h * cf_h
cf_h = (cf_h + 10) / 100.0
cf_s = (cf_s + 10) / 100.0
cf_l = (cf_l + 20) / 100.0
rows, cols, channels = img.shape
res = np.zeros(img.shape)
for i in range(rows):
for j in range(cols):
res[i, j, 0] = np.float32(cf_h) * img[i, j, 0]
if res[i, j, 0] > 360.0:
res[i, j, 0] = 360.0
res[i, j, 1] = np.float32(cf_s) * img[i, j, 1]
if res[i, j, 1] > 1.0:
res[i, j, 1] = 1.0
res[i, j, 2] = np.float32(cf_l) * img[i, j, 2]
if res[i, j, 2] > 1.0:
res[i, j, 2] = 1.0
return res
# 图像RGB空间转HSL
@jit
def convertToHSL(img):
rows, cols, channels = img.shape
res = np.zeros(img.shape)
for i in range(rows):
for j in range(cols):
b_val = np.float32(img[i, j, 0]) / 255.0
g_val = np.float32(img[i, j, 1]) / 255.0
r_val = np.float32(img[i, j, 2]) / 255.0
max_val = max(b_val, max(g_val, r_val))
min_val = min(b_val, min(g_val, r_val))
# H [0, 360)
H = 0
S = 0
L = 0
if max_val == min_val:
H = 0
elif max_val == r_val and g_val >= b_val:
H = np.float32(60) * (g_val - b_val) / (max_val - min_val)
elif max_val == r_val and g_val < b_val:
H = np.float32(60) * (g_val - b_val) / (max_val - min_val) + 360
elif max_val == g_val:
H = np.float32(60) * (b_val - r_val) / (max_val - min_val) + 120
elif max_val == b_val:
H = np.float32(60) * (r_val - g_val) / (max_val - min_val) + 240
# L [0, 1]
L = np.float32(0.5) * (max_val + min_val)
# S [0, 1]
if L == 0 or max_val == min_val:
S = 0
elif 0 < L <= 0.5:
S = (max_val - min_val) / (2.0 * L)
elif L > 0.5:
S = (max_val - min_val) / (2.0 - 2.0 * L)
res[i, j, 0] = H
res[i, j, 1] = S
res[i, j, 2] = L
return res
# 图像HSL空间转RGB
@jit
def convertToBGR(img):
rows, cols, channels = img.shape
res = np.zeros(img.shape, dtype=np.float32)
for i in range(rows):
for j in range(cols):
h, s, l = img[i, j, :]
if s == 0:
res[i, j, 0] = l
res[i, j, 1] = l
res[i, j, 2] = l
continue
if l < 0.5:
q = l * (1.0 + s)
else:
q = l + s - (l * s)
p = 2 * l - q
hk = h / 360.0
tC = np.array([hk - 1.0 / 3.0, hk, hk + 1.0 / 3.0], dtype=np.float32)
tC = np.where(tC < 0, tC + 1.0, tC)
tC = np.where(tC > 1, tC - 1.0, tC)
for tt in range(3):
if tC[tt] < 1.0 / 6.0:
temp_val = p + ((q - p) * 6.0 * tC[tt])
elif tC[tt] < 1.0 / 2.0:
temp_val = q
elif tC[tt] < 2.0 / 3.0:
temp_val = p + ((q - p) * 6.0 * (2.0 / 3.0 - tC[tt]))
else:
temp_val = p
res[i, j, tt] = temp_val
res = res * np.float32(255.0)
res = res.astype(np.uint8)
return res
原图像如下:
适当增强饱和度与亮度,H值调低,整体色调表现为偏红色。
HSL中的H值提高,图像色调偏绿色。
2.8 锐化处理
图像锐化的目的是使模糊图像变得清晰,方法大致分为两类:微分法、高频加重滤波法。其中微分法可以分为梯度法、Sobel算法法、Laplace算子法。
本次实验使用拉普拉斯算子法,实验效果如下图对比所示,图像边缘轮廓更突出。
原图(左)和锐化处理(右)效果对比:
2.9 平滑处理
图像在获取、传输的过程中,可能会受到干扰的影响,会产生噪声,噪声是一种出错了的信号,噪声会造成图像粗糙,需要我们对图像进行平滑处理。图像去噪是一种信号滤波的方法,目的就是为了保留有用的信号。
平滑滤波对图像的低频分量增强,同时会消弱高频分量。用于消除图像中的随机噪声,起到平滑作用。
本次实验采用均值滤波,实验效果如下所示:原图(左)和均值滤波处理后(右)效果对比图。
2.10 色温调节
定量的以开尔文温度(K)来表示色彩。它是开尔文通过黑体这一理想光源,在不同温度下所发出的光线的颜色特性来定义的。
原图:
降低色温(左)和提高色温(右)效果图对比
HSV调节(色调、饱和度、明度)
RGB->HSV,各分量比例放缩->RGB
3. 去雾
本部分主要使用了两种方法进行尝试。
- 自适应色阶图像处理算法
- 暗通道去雾算法
效果: 原图(左)自动色阶处理算法(中)暗通道去雾算法(右)
4. 人像调节
人脸检测
人脸检测主要是人脸各部位关键点检测,本次实验使用开源的机器学习库——dlib,分别使用开源的68特征点预测模型和81特征点预测模型识别人脸关键特征点,并取得了较为良好的效果。
下图为人脸68特征点模型检测效果:
人脸81特征点模型识别效果:
4.1 皮肤美化
主要效果是:磨皮,淡斑
使用到的技术:双边滤波,导向滤波
导向滤波又称引导滤波,通过一张引导图片反映边缘、物体等信息,对输入图像进行滤波处理,使输出图像的内容由输入图像决定,但纹理与引导图片相似。
导向滤波(右)与双边滤波(中)处理效果对比:
通过调节导向滤波的各项参数,最终实现的人脸美化效果如下图:美颜前(左)磨皮美颜后(右)。
4.2 瘦脸
使用Interactive Image Warping
局部平移算法实现瘦脸效果。
处理前(左)和瘦脸特效处理后(右):
总结
本次课程设计主要聚焦图像增强算法的实现,工程量比较大。前后花费了一周左右的时间,具备一定的挑战性。整个开发过程中遇到的首要难题是相关参考资料较少,相对的碎片化,特别是一些所采用的算法公式准确性有待进一步考究,以及编程能力仍存在明显的欠缺,所实现算法的性能仍有很大的优化空间。对本次课程设计的完成度并不满意,最后没能真正的实现前后端整合,代码封装仍有待优化。希望后续总结完善各部分功能,深入到其背后的算法公式、数学原理,尝试对已完成工作做出系统性的整合,最后发一篇博客记录整个实践过程中的学到的知识、心得体会。
ps: 补档……