Bootstrap

数字信号与图像处理课程设计——(图像几何变换、图像增强、面部调节)

设计内容

在这里插入图片描述

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)。
在这里插入图片描述
在这里插入图片描述

  1. 根据旋转角度和旋转中心获取旋转矩阵
  2. 根据旋转矩阵进行仿射变换,即可实现任意角度和任意中心的旋转效果。
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. 去雾

本部分主要使用了两种方法进行尝试。

  1. 自适应色阶图像处理算法
  2. 暗通道去雾算法
    效果: 原图(左)自动色阶处理算法(中)暗通道去雾算法(右)在这里插入图片描述

4. 人像调节

人脸检测
人脸检测主要是人脸各部位关键点检测,本次实验使用开源的机器学习库——dlib,分别使用开源的68特征点预测模型和81特征点预测模型识别人脸关键特征点,并取得了较为良好的效果。
下图为人脸68特征点模型检测效果:
在这里插入图片描述
人脸81特征点模型识别效果:
在这里插入图片描述

4.1 皮肤美化

主要效果是:磨皮,淡斑
使用到的技术:双边滤波,导向滤波
导向滤波又称引导滤波,通过一张引导图片反映边缘、物体等信息,对输入图像进行滤波处理,使输出图像的内容由输入图像决定,但纹理与引导图片相似。
导向滤波(右)与双边滤波(中)处理效果对比:
在这里插入图片描述通过调节导向滤波的各项参数,最终实现的人脸美化效果如下图:美颜前(左)磨皮美颜后(右)。
在这里插入图片描述

4.2 瘦脸

使用Interactive Image Warping局部平移算法实现瘦脸效果。
处理前(左)和瘦脸特效处理后(右):
在这里插入图片描述

总结

本次课程设计主要聚焦图像增强算法的实现,工程量比较大。前后花费了一周左右的时间,具备一定的挑战性。整个开发过程中遇到的首要难题是相关参考资料较少,相对的碎片化,特别是一些所采用的算法公式准确性有待进一步考究,以及编程能力仍存在明显的欠缺,所实现算法的性能仍有很大的优化空间。对本次课程设计的完成度并不满意,最后没能真正的实现前后端整合,代码封装仍有待优化。希望后续总结完善各部分功能,深入到其背后的算法公式、数学原理,尝试对已完成工作做出系统性的整合,最后发一篇博客记录整个实践过程中的学到的知识、心得体会。

ps: 补档……

;