OpenCV膨胀操作消除形状间隙,如何匹配原轮廓尺寸
任意形状内部间隙/孔洞消除的实现问题
当前基础实现流程
- 前置流程:对输入图像做阈值处理得到二值图
- 缺陷消除逻辑:通过形态学膨胀操作打通形状内部的间隙、填充孔洞
- 固有副作用:膨胀操作会带动形状整体外轮廓向外扩张,改变原形状的实际尺寸
核心待解决问题
- 轮廓尺寸精准对齐问题:现有“膨胀后再缩放回原尺寸”的尝试无法稳定实现轮廓匹配,需要要么找到能将膨胀处理得到的轮廓精准缩放至与原轮廓尺寸完全一致的方法,要么找到不改变原形状尺寸即可完成间隙消除的更优方案。
- 全间隙彻底清除的适配问题:固定参数的膨胀操作无法适配不同尺寸的形状,经常出现仅缩小间隙宽度、未彻底清除间隙的情况,需要实现可自适应不同形状尺寸、保证所有内部间隙都被彻底打通的逻辑。
现有自研方案的不稳定表现
当前尝试的实现路径为:先对二值图做膨胀处理,再对膨胀得到的轮廓做等比缩放得到最终轮廓。该方案的输出稳定性极差:
- 部分输入图像场景下,缩放后的膨胀轮廓无法与原形状轮廓匹配
- 配套验证测试中,输入含原始形状的图像时,错误输出为叠加了缩放后膨胀轮廓的图像,存在明显的轮廓错位问题
- 无法实现对任意输入形状,处理后的轮廓完全贴合原形状边界的目标
- 固定的缩放比例参数(代码中固定取0.95)不具备泛化能力,换不同尺寸的形状时匹配效果完全失效
已实现的相关代码
import sys from pathlib import Path from PIL import Image from PIL import ImageCms from PIL import ImageFile ImageFile.LOAD_TRUNCATED_IMAGES = True Image.MAX_IMAGE_PIXELS = 99999999999999 import numpy as np import cv2 import numpy img_path = 'input.png' def cmyk_to_rgb(cmyk_img): img = Image.open(cmyk_img) if img.mode == "CMYK": img = ImageCms.profileToProfile(img, "Color Profiles\\USWebCoatedSWOP.icc", "Color Profiles\\sRGB_Color_Space_Profile.icm", outputMode="RGB") return cv2.cvtColor(numpy.array(img), cv2.COLOR_RGB2BGR) def cv_threshold(img, thresh=254, maxval=255, type=cv2.THRESH_BINARY_INV): if len(img.shape) == 3: img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) threshed = cv2.threshold(img, thresh, maxval, type)[1] return threshed def find_contours(img, to_gray=None): kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11,11)) morphed = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel) contours = cv2.findContours(morphed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) return contours[-2] def mask_from_contours(ref_img, contours): mask = numpy.zeros(ref_img.shape, numpy.uint8) mask = cv2.drawContours(mask, contours, -1, (255,255,255), 24) return cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY) def dilate_mask(mask, kernel_size=10): kernel = numpy.ones((kernel_size,kernel_size), numpy.uint8) dilated = cv2.dilate(mask, kernel, iterations=1) return dilated def draw_contours(src_img, contours): canvas = cv2.drawContours(src_img.copy(), contours, 0, (0,255,0), 24) x, y, w, h = cv2.boundingRect(contours[-1]) cv2.rectangle(canvas, (x,y), (x+w,y+h), (0,0,255), 2) return canvas def scale_contour(cnt, scale): M = cv2.moments(cnt) cx = int(M['m10']/M['m00']) cy = int(M['m01']/M['m00']) cnt_norm = cnt - [cx, cy] cnt_scaled = cnt_norm * scale cnt_scaled = cnt_scaled + [cx, cy] cnt_scaled = cnt_scaled.astype(np.int32) return cnt_scaled #GET ORIGINAL CONTOURS orig_img = cmyk_to_rgb(str(img_path)) orig_threshed = cv_threshold(orig_img, 254,255, type=cv2.THRESH_BINARY_INV) orig_contours= find_contours(orig_threshed) orig_mask = mask_from_contours(orig_img, orig_contours) orig_output = draw_contours(orig_img, orig_contours) #GET DILATED CONTOURS dilated_mask = dilate_mask(orig_mask, 80) dilated_contours= find_contours(dilated_mask) dilated_output = draw_contours(orig_img, dilated_contours) #Just to observe dilation effect RGBimage = cv2.cvtColor(dilated_output, cv2.COLOR_BGR2RGB) pilImage = Image.fromarray(RGBimage) pilImage.save('dilation.png', dpi=(300,300)) # SCALE DILATED CONTOURS cnt_scaled = scale_contour(dilated_contours[0], 0.95) im_copy = orig_img.copy() cv2.drawContours(im_copy, [cnt_scaled], -1, (50, 50, 50), 24) RGBimage = cv2.cvtColor(im_copy, cv2.COLOR_BGR2RGB) pilImage = Image.fromarray(RGBimage) #Output pilImage.save('output.png', dpi=(300,300))
内容的提问来源于stack exchange,提问作者lorelayb
相关产品推荐
相关产品推荐

