You need to enable JavaScript to run this app. OpenCV膨胀操作消除形状间隙,如何匹配原轮廓尺寸
优惠活动
大模型
产品
解决方案
定价
更多

OpenCV膨胀操作消除形状间隙,如何匹配原轮廓尺寸

任意形状内部间隙/孔洞消除的实现问题

当前基础实现流程

  • 前置流程:对输入图像做阈值处理得到二值图
  • 缺陷消除逻辑:通过形态学膨胀操作打通形状内部的间隙、填充孔洞
  • 固有副作用:膨胀操作会带动形状整体外轮廓向外扩张,改变原形状的实际尺寸

核心待解决问题

  1. 轮廓尺寸精准对齐问题:现有“膨胀后再缩放回原尺寸”的尝试无法稳定实现轮廓匹配,需要要么找到能将膨胀处理得到的轮廓精准缩放至与原轮廓尺寸完全一致的方法,要么找到不改变原形状尺寸即可完成间隙消除的更优方案。
  2. 全间隙彻底清除的适配问题:固定参数的膨胀操作无法适配不同尺寸的形状,经常出现仅缩小间隙宽度、未彻底清除间隙的情况,需要实现可自适应不同形状尺寸、保证所有内部间隙都被彻底打通的逻辑。

现有自研方案的不稳定表现

当前尝试的实现路径为:先对二值图做膨胀处理,再对膨胀得到的轮廓做等比缩放得到最终轮廓。该方案的输出稳定性极差:

  • 部分输入图像场景下,缩放后的膨胀轮廓无法与原形状轮廓匹配
  • 配套验证测试中,输入含原始形状的图像时,错误输出为叠加了缩放后膨胀轮廓的图像,存在明显的轮廓错位问题
  • 无法实现对任意输入形状,处理后的轮廓完全贴合原形状边界的目标
  • 固定的缩放比例参数(代码中固定取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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.29 20:06:04