基于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




