Python 让图像变卡通图

时间:2022-10-18 11:02:11

要创造卡通效果,我们需要注意两件事: 边缘和调色板,这就是照片和卡通的不同之处。为了调整这两个主要组成部分,我们将经历四个主要步骤:

  • 加载图像
  • 创建边缘
  • 减少调色板
  • 将边缘掩模与彩色图像结合

  1. 图片导入

import cv2
import numpy as np
img = cv2.imread('xingye.jpg')
cv2.imshow('origin', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

import cv2import numpy as npimg = cv2.imread('xingye.jpg')cv2.imshow('origin', img)cv2.waitKey(0)cv2.destroyAllWindows()

Python 让图像变卡通图


2. 创建边缘掩模

卡通效果强调图像边缘的宽度,使用 cv2.adaptiveThreshold()函数检测图像的边缘。


def edge_mask(img, line_size, blur_value):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray_blur = cv2.medianBlur(gray, blur_value)
edges = cv2.adaptiveThreshold(gray_blur, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, line_size, blur_value)
return edges

在上面函数中先将图像转换为灰度图像。然后,利用 cv2.medianBlur 函数对模糊灰度图像进行去噪处理。模糊值越大,图像中出现的黑噪声越少。然后,应用 adaptiveThreshold 函数,并定义边缘的线条大小。更大的线条尺寸意味着更宽的边缘,这将在图像中得到展示。


line_size = 7
blur_value = 7
edges = edge_mask(img, line_size, blur_value)
cv2.imshow('edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()


Python 让图像变卡通图


3. 减少调色板

照片和绘画的主要区别(就颜色而言)在于每张照片中不同颜色的数量,其中绘画的图像的颜色比照片少。因此,我们使用量化的方式来减少照片中的颜色数量。

为了实现这个图像量化,我们应用了 OpenCV 库提供的 K-Means 算法。为了使下一步更容易,我们可以定义 color_quantization 函数如下。


line_size = 7
blur_value = 7
edges = edge_mask(img, line_size, blur_value)
cv2.imshow('edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()

k 值是我们想要应用到图像的颜色数量




total_color = 9
img = color_quantization(img, total_color)


双边滤波器

做完图像量化后,我们可以通过使用双边滤波器来降低图像中的噪声。这会给图像带来一点模糊和锐度降低的效果。


blurred = cv2.bilateralFilter(img, d=7, sigmaColor=200, sigmaSpace=200)

调整以下三个参数:

  • d:每个像素邻域的直径
  • sigmaColor:颜色空间滤波器的sigma值。这个参数的值越大,就表明该像素邻域内有更宽广的颜色会被混合到一起,产生较大的半相等颜色区域。
  • sigmaSpace:坐标空间中滤波器的sigma值,坐标空间的标注方差。他的数值越大,意味着越远的像素会相互影响,从而使更大的区域足够相似的颜色获取相同的颜色。

4. 边缘掩模和彩色图像结合

最后一步是将我们前面创建的边缘掩模与经过颜色处理的图像结合起来。为此,可以使用 cv2.bitwise_and 函数






cartoon = cv2.bitwise_and(blurred, blurred, mask=edges)
cv2.imshow('cartoon', cartoon)
cv2.waitKey(0)
cv2.destroyAllWindows()

Python 让图像变卡通图


完整代码如下


import cv2
import numpy as np


def edge_mask(img, line_size, blur_value):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray_blur = cv2.medianBlur(gray, blur_value)
edges = cv2.adaptiveThreshold(gray_blur, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, line_size, blur_value)
return edges


img = cv2.imread('xingye.jpg')
cv2.imshow('origin', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

line_size = 7
blur_value = 7
edges = edge_mask(img, line_size, blur_value)
cv2.imshow('edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()


def color_quantization(img, k):
data = np.float32(img).reshape((-1, 3))

criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 20, 0.001)

ret, label, center = cv2.kmeans(data, k, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
center = np.uint8(center)
result = center[label.flatten()]
result = result.reshape(img.shape)
return result


total_color = 9
img = color_quantization(img, total_color)

blurred = cv2.bilateralFilter(img, d=7, sigmaColor=200, sigmaSpace=200)
cartoon = cv2.bitwise_and(blurred, blurred, mask=edges)

cv2.imshow('cartoon', cartoon)
cv2.waitKey(0)
cv2.destroyAllWindows()