使用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
相关产品推荐
相关产品推荐

