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

Swift/SwiftUI提取透明背景图像轮廓路径:去除图像边框

如何排除Vision框架轮廓检测中的图像边框路径?

我正在使用Vision框架处理遮罩图像和带透明背景的图像,目标是提取图像内容的轮廓路径。当前实现能返回所需轮廓,但会附带代表图像边框的额外路径,需要修改代码仅保留实际内容的轮廓。

现有代码实现

轮廓检测函数

private func detectContours(image: UIImage) async throws -> CGPath? {
// Convert UIImage to CIImage
guard let ciImage = CIImage(image: image) else {
    return nil
}

// Set up the contour detection request
var request = DetectContoursRequest()
request.contrastAdjustment = 1.5
request.contrastPivot = nil

// Perform the contour detection request
let contoursObservations = try await request.perform(
    on: ciImage,
    orientation: .downMirrored
)

// Retrieve the detected contours as a path object
let contours = contoursObservations.normalizedPath

return contours
}

调用方式

Task {
    do {
        contours = try await detectContours(image: maskedImage)
    } catch {
        print("Error detecting contours: \(error)")
    }
}

相关图像

遮罩图像:
mask image

当前生成的路径图像(包含多余边框):
path image

解决方案

问题根源在于Vision的DetectContoursRequest默认会将图像的像素边界(即使是透明背景的边缘)识别为轮廓。我们可以通过过滤单个轮廓的边界尺寸来排除边框,具体修改如下:

修改后的轮廓检测函数

private func detectContours(image: UIImage) async throws -> CGPath? {
    guard let ciImage = CIImage(image: image),
          let cgImage = image.cgImage else {
        return nil
    }
    // 获取图像实际像素尺寸
    let imageSize = CGSize(width: cgImage.width, height: cgImage.height)
    
    var request = DetectContoursRequest()
    request.contrastAdjustment = 1.5
    request.contrastPivot = nil
    
    let contoursObservation = try await request.perform(
        on: ciImage,
        orientation: .downMirrored
    )
    
    // 过滤掉与图像尺寸几乎一致的边框路径
    let filteredContours = contoursObservation.contours.filter { contour in
        let normalizedBounds = contour.normalizedPath.boundingBoxOfPath
        // 转换为实际像素坐标的边界
        let actualBounds = CGRect(
            x: normalizedBounds.origin.x * imageSize.width,
            y: normalizedBounds.origin.y * imageSize.height,
            width: normalizedBounds.width * imageSize.width,
            height: normalizedBounds.height * imageSize.height
        )
        // 若轮廓宽高与图像尺寸差值小于1像素,判定为边框并排除
        let widthDiff = abs(actualBounds.width - imageSize.width)
        let heightDiff = abs(actualBounds.height - imageSize.height)
        return widthDiff > 1 || heightDiff > 1
    }
    
    // 将过滤后的轮廓合并为单个路径返回
    let combinedPath = CGMutablePath()
    filteredContours.forEach { contour in
        combinedPath.addPath(contour.normalizedPath)
    }
    
    return combinedPath.isEmpty ? nil : combinedPath
}

逻辑说明

  1. 获取图像的实际像素尺寸:因为Vision返回的normalizedPath是基于0-1的归一化坐标,需要转换为实际像素尺寸进行对比
  2. 遍历单个轮廓:通过contoursObservation.contours获取所有独立轮廓,而不是直接使用合并后的normalizedPath
  3. 过滤边框:对比每个轮廓的实际边界与图像尺寸,排除差值极小的边框路径
  4. 合并有效轮廓:将剩下的内容轮廓合并为一个路径返回

内容的提问来源于stack exchange,提问作者Aarfeen Ahmad

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 17:39:55