You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于Python与OpenCV优化图片卡通漫画风格效果的技术咨询

解决卡通风格转换中色彩过渡过平滑的问题

我之前做类似的图片卡通化效果时,也碰到过一模一样的问题——普通模糊处理会把明暗交界的细节过度平滑,保留了太多中间色调,导致输出的画面更像柔化的照片,而不是我们想要的色块分明、边界锐利的卡通风格。针对这个问题,咱们可以从三个核心方向调整代码:

1. 用双边滤波替代普通高斯模糊,保留边缘的同时平滑区域

普通高斯模糊会无差别地模糊所有区域,包括边缘,这直接导致明暗过渡被抹掉。而双边滤波(cv2.bilateralFilter())能区分边缘和纯色区域:它会平滑颜色相近的区域,但保留颜色突变的边缘,完美适配卡通化的需求。

示例代码替换:

# 替换原代码中的高斯模糊
# blurred = cv2.GaussianBlur(img, (5,5), 0)

# 使用双边滤波
blurred = cv2.bilateralFilter(img, d=9, sigmaColor=75, sigmaSpace=75)
  • d:滤波核的直径,数值越大平滑效果越强
  • sigmaColor:颜色空间的标准差,数值越大,允许更多颜色被归为一类
  • sigmaSpace:坐标空间的标准差,数值越大,允许更远的像素参与平滑

2. 加入色彩量化,直接减少颜色数量

模糊后画面还有过多色调的核心原因是保留了太多相近的颜色,我们可以用K均值聚类把相似颜色合并成大块色块,从根源上减少过渡色。

这里是一个可复用的色彩量化函数:

import numpy as np
import cv2

def color_quantization(image, num_colors=8):
    # 将图像转换为K均值需要的二维数组格式
    pixel_data = image.reshape((-1, 3))
    pixel_data = np.float32(pixel_data)
    
    # K均值的终止条件:迭代20次或误差小于0.001时停止
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 20, 0.001)
    # 执行K均值聚类
    _, labels, centers = cv2.kmeans(pixel_data, num_colors, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
    
    # 将聚类后的颜色转换回uint8格式,并重塑为原图像尺寸
    centers = np.uint8(centers)
    quantized_image = centers[labels.flatten()]
    return quantized_image.reshape(image.shape)

你可以根据效果调整num_colors(建议8-16之间),数值越小,色块越大,卡通感越强。

3. 叠加锐利边缘,强化卡通的边界感

卡通风格的关键是清晰的轮廓,我们可以用自适应阈值提取图像边缘,然后把边缘叠加到色彩量化后的画面上,让边界更突出。

示例代码片段:

# 从原图像提取边缘(避免模糊后的边缘丢失)
gray_img = cv2.cvtColor(original_img, cv2.COLOR_BGR2GRAY)
# 自适应阈值提取边缘,比固定阈值更适配不同亮度区域
edges = cv2.adaptiveThreshold(
    gray_img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, blockSize=9, C=2
)
# 把边缘图转换为彩色格式,方便和量化后的图像融合
edges = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)

# 融合边缘和量化后的图像
cartoon_result = cv2.bitwise_and(quantized_image, edges)

整合后的完整工作流

把上面的步骤整合起来,最终的卡通化函数大概是这样:

import numpy as np
import cv2

def color_quantization(image, num_colors=8):
    pixel_data = image.reshape((-1, 3))
    pixel_data = np.float32(pixel_data)
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 20, 0.001)
    _, labels, centers = cv2.kmeans(pixel_data, num_colors, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
    centers = np.uint8(centers)
    quantized_image = centers[labels.flatten()]
    return quantized_image.reshape(image.shape)

def cartoonize_image(original_img):
    # 1. 双边滤波平滑区域,保留边缘
    blurred = cv2.bilateralFilter(original_img, d=9, sigmaColor=75, sigmaSpace=75)
    # 2. 色彩量化减少颜色数
    quantized = color_quantization(blurred, num_colors=8)
    # 3. 提取边缘
    gray = cv2.cvtColor(original_img, cv2.COLOR_BGR2GRAY)
    edges = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, blockSize=9, C=2)
    edges = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)
    # 4. 融合边缘与量化图像
    return cv2.bitwise_and(quantized, edges)

# 测试调用
img = cv2.imread("your_image.jpg")
cartoon = cartoonize_image(img)
cv2.imwrite("cartoon_result.jpg", cartoon)

这样调整后,你会发现画面的明暗过渡大幅减少,色块更清晰,完全符合卡通漫画的风格。

内容的提问来源于stack exchange,提问作者Megan Caithlyn

火山引擎 最新活动