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)") } }
相关图像
遮罩图像:
当前生成的路径图像(包含多余边框):
解决方案
问题根源在于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 }
逻辑说明
- 获取图像的实际像素尺寸:因为Vision返回的
normalizedPath是基于0-1的归一化坐标,需要转换为实际像素尺寸进行对比 - 遍历单个轮廓:通过
contoursObservation.contours获取所有独立轮廓,而不是直接使用合并后的normalizedPath - 过滤边框:对比每个轮廓的实际边界与图像尺寸,排除差值极小的边框路径
- 合并有效轮廓:将剩下的内容轮廓合并为一个路径返回
内容的提问来源于stack exchange,提问作者Aarfeen Ahmad
相关产品推荐
相关产品推荐

