人脸识别与美妆算法研究(二)基于opencv的图像风格转换

在每个人的手机相册功能中,都可以找到“滤镜”这一功能。这种滤镜就是简单的图像风格转换,本篇文章将使用OpenCV库对图像进行多种风格的处理,包括复古风格、油画和素描效果。同时,考虑到后续项目中的可调用性。这里先简单的将不同的风格转换方法封装起来,以备后续的进一步修改。

【注1】本篇文章同样以方圆圆的《人脸识别与美颜算法实战-基于python、机器学习与深度学习》一书为理论依据,在原书的代码中进行修改和优化!

【注2】本系列的所有代码,均在win11系统、python3.9、pycharm2023及以上版本中实现。若无具体要求,可根据开源代码中的requirments.txt文件进行相同配置!开源链接及后续项目的代码统一见文章最后!

1.  复古风格

复古风格处理是通过修改图像的颜色值来模拟老旧照片的效果。具体实现通过对每个像素点的RGB值进行线性变换来实现。这段代码对每个像素的RGB值进行线性变换来生成复古效果。通过特定的系数对原始的RGB值进行加权计算,并对结果进行裁剪以确保像素值在0到255之间。

def retro_style(img):
    img2 = img.copy()
    height, width, n = img.shape

    for i in range(height):
        for j in range(width):
            b, g, r = img[i, j]

            # 计算新的图像中的RGB值
            B = int(0.272 * r + 0.534 * g + 0.131 * b)
            G = int(0.349 * r + 0.686 * g + 0.168 * b)
            R = int(0.393 * r + 0.769 * g + 0.189 * b)

            # 约束图像像素值,防止溢出
            img2[i, j] = [max(0, min(B, 255)), max(0, min(G, 255)), max(0, min(R, 255))]

    return img2

在这里,可以使用NumPy进行矢量化操作,避免循环。同时,提前计算变换矩阵,简化计算过程。优化后的代码如下所示

import cv2
import numpy as np

def retro_style(img):
    # 计算变换矩阵
    transformation_matrix = np.array([[0.272, 0.534, 0.131],
                                      [0.349, 0.686, 0.168],
                                      [0.393, 0.769, 0.189]])

    # 使用矩阵变换进行复古处理
    img2 = np.dot(img[..., :3], transformation_matrix.T)
    
    # 约束图像像素值在0到255之间
    img2 = np.clip(img2, 0, 255).astype(np.uint8)

    return img2

def resize_image(img, max_size=800):
    height, width = img.shape[:2]
    if max(height, width) > max_size:
        scaling_factor = max_size / float(max(height, width))
        img = cv2.resize(img, None, fx=scaling_factor, fy=scaling_factor, interpolation=cv2.INTER_AREA)
    return img

def retro_style_main(img_path):
    img = cv2.imread(img_path)
    retro_img = retro_style(img)
    resized_img = resize_image(retro_img)
    cv2.imshow("Retro Style Image", resized_img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

# 例如:
# retro_style_main("path_to_your_image.jpg")

实现效果:

图1  复古风格

2.  素描风格

下面这段代码将输入图像转换为素描风格的效果

def sketch_style(img, maxsize=800):
    height, width, _ = img.shape  # 获取图像的高度和宽度
    gray0 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 将图像转换为灰度图

    # 创建一个与原图像大小相同的黑色图像
    img2 = np.zeros((height, width), dtype='uint8')

    # 使用加权和的方法进行图像反转,创建类似负片效果的图像
    gray1 = cv2.addWeighted(gray0, -1, img2, 0, 255, 0)

    # 使用高斯模糊平滑图像,调整模糊内核大小和标准差
    gray1 = cv2.GaussianBlur(gray1, (15, 15), 0)

    # 增加对比度
    gray1 = cv2.convertScaleAbs(gray1, alpha=1.5, beta=0)

    # 将原始灰度图和模糊后的负片图像混合,调整混合权重
    dst = cv2.addWeighted(gray0, 0.7, gray1, 0.3, 0)

    # 反转颜色,使之变为白底黑画
    dst = cv2.bitwise_not(dst)

    # 调整输出图像大小并保持长宽比
    dst = resize_with_aspect_ratio(dst, maxsize)

    # 显示素描效果图像
    cv2.imshow('sketch_img', dst)


def sketch_style_main(img_path, max_size=800):
    # 读取输入图像
    img = cv2.imread(img_path)
    if img is None:
        print(f"Error: Unable to load image at {img_path}")
        return

    # 应用素描风格处理
    sketch_style(img, max_size)

    # 等待按键事件并关闭所有窗口
    cv2.waitKey(0)
    cv2.destroyAllWindows()

首先,通过 `sketch_style_main` 函数读取输入图像文件并检查是否成功读取,如果失败则输出错误信息并返回。接着调用 `sketch_style` 函数对图像进行处理。在 `sketch_style` 函数中,首先获取图像的高度和宽度,然后将图像转换为灰度图。接着创建一个与原图像大小相同的全黑图像,并使用加权和方法将灰度图像反转,生成一个类似负片效果的图像。对负片图像进行高斯模糊处理使图像变得更加平滑,并增加其对比度。然后将原始灰度图和模糊后的负片图像混合,调整权重使得效果更接近素描风格。再通过反转颜色将图像变为白底黑画,并调整输出图像的大小以保持长宽比。最后,显示处理后的素描效果图像,并在等待按键事件后关闭所有显示窗口。

实现效果:

图1  素描(黑底白画)

3.  油画风格

下面这段代码通过 oil_style_main 函数实现了将输入图像转换为油画风格并增强颜色的效果。首先,oil_style_main 函数读取输入图像文件,并检查图像是否成功读取,如果失败则输出错误信息并返回。接着调用 oil_style 函数,该函数通过遍历图像的每个像素,随机选择邻近像素的颜色值来生成油画效果图像。然后,调用 color_add 函数,该函数将油画效果图像转换为 PIL 图像并使用 ImageEnhance.Color 增强颜色,最终将图像转换回 OpenCV 格式。处理完毕后,oil_style_main 函数显示最终处理后的图像,并在等待按键事件后关闭所有显示窗口。优化后的代码确保只生成和显示最终的图像,不再创建中间图像文件。

def oil_style(img):
    height, width, n = img.shape
    output = np.zeros((height, width, n), dtype='uint8')
    for i in range(1, height - 1):
        for j in range(1, width - 1):
            rand_choice = random.randint(0, 2)
            if rand_choice == 0:
                output[i, j] = img[i + 1, j]
            elif rand_choice == 1:
                output[i, j] = img[i - 1, j]
            else:
                output[i, j] = img[i, j - 1]
    return output


def color_add(img):
    image = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    enhancer = ImageEnhance.Color(image)
    image_colored = enhancer.enhance(2.0)
    return cv2.cvtColor(np.array(image_colored), cv2.COLOR_RGB2BGR)


def oil_style_main(img_path, maxsize=800):
    img = cv2.imread(img_path)
    if img is None:
        print(f"Error: Unable to load image at {img_path}")
        return
    oil_img = oil_style(img)
    final_img = color_add(oil_img)

    final_img = resize_with_aspect_ratio(final_img, maxsize)

    cv2.imshow("Oil Painting Style Image", final_img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

运行效果:

图3  油画风格

4.  项目开源地址 

后续项目及本代码的更新地址如下所示,如有问题请私信或者评论区留言。https://github.com/damoshishen/FRBA 

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-12 19:20:02       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-12 19:20:02       71 阅读
  3. 在Django里面运行非项目文件

    2024-07-12 19:20:02       58 阅读
  4. Python语言-面向对象

    2024-07-12 19:20:02       69 阅读

热门阅读

  1. 开发需要的热门常用API

    2024-07-12 19:20:02       20 阅读
  2. sql server记录数据库表行数变化记录

    2024-07-12 19:20:02       22 阅读
  3. 小抄 20240711

    2024-07-12 19:20:02       20 阅读
  4. vscode 远程开发

    2024-07-12 19:20:02       17 阅读
  5. clean code-代码整洁之道 阅读笔记(第十六章)

    2024-07-12 19:20:02       16 阅读
  6. MySQL慢查询日志(Slow Query Log)

    2024-07-12 19:20:02       17 阅读
  7. ZCC5429 异步升压芯片

    2024-07-12 19:20:02       20 阅读
  8. 介绍一下docker的打包命令

    2024-07-12 19:20:02       21 阅读
  9. 华为OJ平台

    2024-07-12 19:20:02       18 阅读
  10. inline与nullptr

    2024-07-12 19:20:02       21 阅读
  11. ActiViz中的跟随者vtkFollower

    2024-07-12 19:20:02       22 阅读
  12. 常见的load_file()读取的敏感信息

    2024-07-12 19:20:02       21 阅读