Python+OpenCV:手撸简易图像风格迁移,让照片一键换装
还在羡慕别人的照片色彩鲜明、风格独特?想让自己的照片也拥有大师级的质感?别再苦苦P图啦!今天,就带你用Python和OpenCV,手撸一个简易版的图像风格迁移程序,让你的照片也能一键“换装”!
什么是图像风格迁移?
简单来说,图像风格迁移就是将一张图片的风格(例如色彩、纹理)应用到另一张图片的内容上,生成一张既保留了内容图片的结构,又具有风格图片特点的新图片。听起来是不是很酷?
虽然现在有很多深度学习的风格迁移算法,效果惊艳,但对于咱们这种“手撸党”来说,先从简单的入手,理解基本原理才是王道。所以,我们今天选择一种比较基础但有效的风格迁移方法:基于直方图匹配的风格迁移。
原理:直方图匹配
直方图匹配,顾名思义,就是让两张图片的直方图尽可能相似。在图像处理中,直方图可以反映图像的色彩分布情况。通过调整目标图片的像素值,使其直方图与风格图片的直方图尽可能接近,从而实现风格迁移。
核心思想:
- 颜色空间转换: 将图片从RGB颜色空间转换到LAB颜色空间。LAB颜色空间将色彩分解为亮度(L)和两个颜色分量(A和B),更符合人眼对色彩的感知。
- 计算统计量: 分别计算风格图片和目标图片在LAB颜色空间三个通道上的均值和标准差。
- 直方图匹配: 根据风格图片的均值和标准差,调整目标图片的像素值,使其在每个通道上的统计量与风格图片尽可能接近。
准备工作
在开始之前,你需要确保已经安装了以下库:
- 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.jpg和target.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实现一个简易的图像风格迁移程序。虽然这种方法比较基础,但它可以帮助我们理解图像风格迁移的基本原理。希望你能通过这个小项目,掌握更多的图像处理技巧,创造出更惊艳的作品!快去试试吧,让你的照片也拥有独特的风格!