You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用cv2.findContours获取的轮廓粗糙,如何优化并实现拙劣绘画风格轮廓?

嘿,我完全get你想要的那种「拙劣手绘」质感的粗糙轮廓,同时还要先把原本的轮廓提取优化得更靠谱——咱们一步步来实现这个需求!

第一步:优化基础轮廓提取

你当前的代码里直接转灰度后就用cv2.findContours,但这个函数需要的是二值图像(纯黑纯白),而且原始轮廓可能有很多冗余点,先把这两步优化好:

  • 先做「灰度转换+阈值二值化」,生成干净的轮廓输入:

    # 假设你的原始图像是img
    gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    # 用Otsu自动阈值(适合对比度适中的图像),生成二值图
    _, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
    # 如果图像光照不均匀,换成自适应阈值更靠谱:
    # thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)
    
  • 对提取的轮廓做多边形逼近,去掉冗余点,让轮廓更简洁(也为后续的粗糙效果打基础):

    # 提取轮廓(OpenCV4.x返回两个值,3.x返回三个,这里兼容写法)
    contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    
    # 遍历轮廓做简化
    simplified_contours = []
    for cnt in contours:
        # epsilon是逼近精度,用轮廓周长的百分比控制:值越大,轮廓越粗糙棱角越明显
        epsilon = 0.02 * cv2.arcLength(cnt, True)
        approx = cv2.approxPolyDP(cnt, epsilon, True)
        simplified_contours.append(approx)
    
第二步:实现「拙劣手绘」的粗糙效果

现在我们要给干净的轮廓加上手绘的抖动、断笔、粗细变化感,核心思路是给轮廓点加随机偏移,再模拟手绘的不连贯线条:

1. 给轮廓点加随机抖动

让每个轮廓点都稍微偏移一点,模拟手绘时的手抖:

import random

def jitter_contour(cnt, jitter_amount=8):
    # 把轮廓转为二维数组
    cnt_np = cnt.reshape(-1, 2)
    # 给每个点加随机偏移
    jittered = cnt_np + np.random.randint(-jitter_amount, jitter_amount+1, cnt_np.shape)
    # 确保点不会超出图像范围
    jittered[:, 0] = np.clip(jittered[:, 0], 0, thresh.shape[1]-1)
    jittered[:, 1] = np.clip(jittered[:, 1], 0, thresh.shape[0]-1)
    # 转回OpenCV需要的轮廓格式
    return jittered.reshape(-1, 1, 2).astype(np.int32)

# 对简化后的轮廓做抖动处理
jittered_contours = [jitter_contour(approx) for approx in simplified_contours]

2. 模拟手绘风格的线条绘制

不用cv2.drawContours的规整线条,而是用随机粗细、随机断笔的方式逐段绘制:

blank_image = np.zeros((thresh.shape[0], thresh.shape[1], 3), np.uint8)

for cnt in jittered_contours:
    # 跳过太小的轮廓(过滤噪点,可选)
    if cv2.contourArea(cnt) < 100:
        continue
    # 逐段绘制轮廓线段
    for i in range(len(cnt)-1):
        start = tuple(cnt[i][0])
        end = tuple(cnt[i+1][0])
        # 随机线条粗细(模拟画笔压力变化)
        thickness = random.randint(2, 5)
        # 随机灰度值(不是纯白,模拟手绘的颜料不均匀)
        gray_val = random.randint(200, 255)
        # 5%概率不绘制,模拟手绘的断笔效果
        if random.random() > 0.05:
            cv2.line(blank_image, start, end, (gray_val, gray_val, gray_val), thickness)
    # 闭合轮廓(如果是闭合形状)
    if len(cnt) > 2:
        start = tuple(cnt[-1][0])
        end = tuple(cnt[0][0])
        if random.random() > 0.05:
            cv2.line(blank_image, start, end, (gray_val, gray_val, gray_val), random.randint(2,5))

# 显示结果
plt.imshow(blank_image)
plt.axis('off')
plt.show()
可调参数说明

你可以根据自己的图像需求调整这些参数,得到不同的粗糙效果:

  • epsilon:轮廓简化的精度,值越大轮廓越棱角分明
  • jitter_amount:轮廓点的抖动幅度,值越大手抖感越强
  • 随机断笔的概率(random.random() > 0.05):值越小,断笔越频繁
  • 线条粗细范围(randint(2,5)):范围越大,手绘的粗细变化越明显

内容的提问来源于stack exchange,提问作者Mitchell van Zuylen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:02:55