WEBKT

Python+OpenCV:手撸简易图像风格迁移,让照片一键换装

153 0 0 0

还在羡慕别人的照片色彩鲜明、风格独特?想让自己的照片也拥有大师级的质感?别再苦苦P图啦!今天,就带你用Python和OpenCV,手撸一个简易版的图像风格迁移程序,让你的照片也能一键“换装”!

什么是图像风格迁移?

简单来说,图像风格迁移就是将一张图片的风格(例如色彩、纹理)应用到另一张图片的内容上,生成一张既保留了内容图片的结构,又具有风格图片特点的新图片。听起来是不是很酷?

虽然现在有很多深度学习的风格迁移算法,效果惊艳,但对于咱们这种“手撸党”来说,先从简单的入手,理解基本原理才是王道。所以,我们今天选择一种比较基础但有效的风格迁移方法:基于直方图匹配的风格迁移

原理:直方图匹配

直方图匹配,顾名思义,就是让两张图片的直方图尽可能相似。在图像处理中,直方图可以反映图像的色彩分布情况。通过调整目标图片的像素值,使其直方图与风格图片的直方图尽可能接近,从而实现风格迁移。

核心思想:

  1. 颜色空间转换: 将图片从RGB颜色空间转换到LAB颜色空间。LAB颜色空间将色彩分解为亮度(L)和两个颜色分量(A和B),更符合人眼对色彩的感知。
  2. 计算统计量: 分别计算风格图片和目标图片在LAB颜色空间三个通道上的均值和标准差。
  3. 直方图匹配: 根据风格图片的均值和标准差,调整目标图片的像素值,使其在每个通道上的统计量与风格图片尽可能接近。

准备工作

在开始之前,你需要确保已经安装了以下库:

  • Python: 建议使用Python 3.6及以上版本。
  • OpenCV: pip install opencv-python
  • NumPy: pip install numpy

代码实现

接下来,我们一步一步地实现图像风格迁移程序。

1. 加载图片

首先,我们需要加载风格图片和目标图片。

import cv2
import numpy as np

def load_image(path):
    img = cv2.imread(path)
    if img is None:
        print(f"Error: Could not load image at {path}")
        exit()
    return img

style_img = load_image('style.jpg')  # 风格图片
target_img = load_image('target.jpg') # 目标图片

请确保style.jpgtarget.jpg存在于你的程序目录下,或者替换成你自己的图片路径。

2. 颜色空间转换

将图片从RGB颜色空间转换到LAB颜色空间。

def preprocess_image(img):
    img = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
    return img

style_img = preprocess_image(style_img)
target_img = preprocess_image(target_img)

3. 计算统计量

分别计算风格图片和目标图片在LAB颜色空间三个通道上的均值和标准差。

def calculate_statistics(img):
    mean = np.mean(img, axis=(0, 1))
    std = np.std(img, axis=(0, 1))
    return mean, std

style_mean, style_std = calculate_statistics(style_img)
target_mean, target_std = calculate_statistics(target_img)

4. 直方图匹配

核心步骤!根据风格图片的均值和标准差,调整目标图片的像素值。

def match_histograms(target_img, style_mean, style_std, target_mean, target_std):
    height, width, channels = target_img.shape
    matched_img = np.zeros(target_img.shape, dtype=np.float64)

    for i in range(channels):
        channel = target_img[:, :, i].astype(np.float64)
        channel = (channel - target_mean[i]) * (style_std[i] / target_std[i]) + style_mean[i]
        matched_img[:, :, i] = channel

    matched_img = np.clip(matched_img, 0, 255).astype(np.uint8)
    return matched_img

matched_img = match_histograms(target_img, style_mean, style_std, target_mean, target_std)

这段代码的关键在于,它对目标图片的每个像素值都进行了调整,使其更接近风格图片的色彩分布。

5. 颜色空间转换(返回RGB)

将图片从LAB颜色空间转换回RGB颜色空间,方便显示和保存。

def postprocess_image(img):
    img = cv2.cvtColor(img, cv2.COLOR_LAB2BGR)
    return img

matched_img = postprocess_image(matched_img)

6. 显示和保存结果

最后,我们将结果显示出来,并保存到本地。

cv2.imshow('Style Image', cv2.cvtColor(cv2.imread('style.jpg'), cv2.COLOR_BGR2RGB))
cv2.imshow('Target Image', cv2.cvtColor(cv2.imread('target.jpg'), cv2.COLOR_BGR2RGB))
cv2.imshow('Matched Image', cv2.cvtColor(matched_img, cv2.COLOR_BGR2RGB))

cv2.imwrite('matched.jpg', matched_img)

cv2.waitKey(0)
cv2.destroyAllWindows()

完整代码

import cv2
import numpy as np

def load_image(path):
    img = cv2.imread(path)
    if img is None:
        print(f"Error: Could not load image at {path}")
        exit()
    return img

def preprocess_image(img):
    img = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
    return img

def calculate_statistics(img):
    mean = np.mean(img, axis=(0, 1))
    std = np.std(img, axis=(0, 1))
    return mean, std

def match_histograms(target_img, style_mean, style_std, target_mean, target_std):
    height, width, channels = target_img.shape
    matched_img = np.zeros(target_img.shape, dtype=np.float64)

    for i in range(channels):
        channel = target_img[:, :, i].astype(np.float64)
        channel = (channel - target_mean[i]) * (style_std[i] / target_std[i]) + style_mean[i]
        matched_img[:, :, i] = channel

    matched_img = np.clip(matched_img, 0, 255).astype(np.uint8)
    return matched_img

def postprocess_image(img):
    img = cv2.cvtColor(img, cv2.COLOR_LAB2BGR)
    return img

style_img = load_image('style.jpg')  # 风格图片
target_img = load_image('target.jpg') # 目标图片

style_img = preprocess_image(style_img)
target_img = preprocess_image(target_img)

style_mean, style_std = calculate_statistics(style_img)
target_mean, target_std = calculate_statistics(target_img)

matched_img = match_histograms(target_img, style_mean, style_std, target_mean, target_std)

matched_img = postprocess_image(matched_img)

cv2.imshow('Style Image', cv2.cvtColor(cv2.imread('style.jpg'), cv2.COLOR_BGR2RGB))
cv2.imshow('Target Image', cv2.cvtColor(cv2.imread('target.jpg'), cv2.COLOR_BGR2RGB))
cv2.imshow('Matched Image', cv2.cvtColor(matched_img, cv2.COLOR_BGR2RGB))

cv2.imwrite('matched.jpg', matched_img)

cv2.waitKey(0)
cv2.destroyAllWindows()

运行结果

运行程序后,你会看到三个窗口:风格图片、目标图片和风格迁移后的图片。同时,程序也会将风格迁移后的图片保存到matched.jpg文件中。

优化和扩展

  • 更高级的颜色空间: 可以尝试使用其他的颜色空间,例如YCrCb,可能会获得更好的效果。
  • 局部风格迁移: 可以将图片分成多个区域,对每个区域进行单独的风格迁移,从而实现更精细的控制。
  • 深度学习风格迁移: 学习更高级的深度学习风格迁移算法,例如使用卷积神经网络(CNN)来实现风格迁移。

总结

通过这篇文章,我们学习了如何使用Python和OpenCV实现一个简易的图像风格迁移程序。虽然这种方法比较基础,但它可以帮助我们理解图像风格迁移的基本原理。希望你能通过这个小项目,掌握更多的图像处理技巧,创造出更惊艳的作品!快去试试吧,让你的照片也拥有独特的风格!

图像魔法师 PythonOpenCV图像风格迁移

评论点评