You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Swift中如何快速将OpenCV生成的UIImage轮廓图白色像素改为红色并合并

嘿,我来帮你搞定这个需求!你想要的红轮廓+白填充效果完全不用逐像素遍历,不管是用Core Image做快速颜色替换,还是直接在OpenCV里生成红色轮廓图,都能高效实现,下面给你两种靠谱方案:

方案1:用Core Image快速转换轮廓颜色+正确合并图像

这个方案不需要碰OpenCV的底层处理,直接在UIImage层面用Core Image的GPU加速滤镜完成,速度很快。

第一步:把轮廓图的白色像素转成红色(背景透明)

我们先把白色轮廓提取为蒙版,再用红色填充蒙版区域,这样得到的是红色轮廓+透明背景的图,方便后续合并:

import CoreImage

func convertWhiteOutlineToRed(_ outlineImage: UIImage) -> UIImage? {
    guard let ciImage = CIImage(image: outlineImage),
          let maskFilter = CIFilter(name: "CIMaskToAlpha") else {
        return nil
    }
    
    // 把白色轮廓转成alpha蒙版(白色区域alpha=1,其他=0)
    maskFilter.setValue(ciImage, forKey: kCIInputImageKey)
    guard let alphaMask = maskFilter.outputImage else { return nil }
    
    // 生成纯红色背景图
    let redColor = CIColor(red: 1.0, green: 0.0, blue: 0.0)
    let redGenerator = CIFilter(name: "CIConstantColorGenerator")!
    redGenerator.setValue(redColor, forKey: kCIInputColorKey)
    guard let redBackground = redGenerator.outputImage else { return nil }
    
    // 用蒙版把红色背景裁剪成轮廓形状
    let blendFilter = CIFilter(name: "CIBlendWithMask")!
    blendFilter.setValue(redBackground, forKey: kCIInputImageKey)
    blendFilter.setValue(ciImage, forKey: kCIInputBackgroundImageKey)
    blendFilter.setValue(alphaMask, forKey: kCIInputMaskImageKey)
    
    // 输出并转成UIImage(用GPU加速的CIContext)
    guard let outputCI = blendFilter.outputImage else { return nil }
    let ciContext = CIContext(options: [.useSoftwareRenderer: false])
    guard let cgImage = ciContext.createCGImage(outputCI, from: outputCI.extent) else { return nil }
    return UIImage(cgImage: cgImage, scale: outlineImage.scale, orientation: outlineImage.imageOrientation)
}

第二步:正确合并填充图和红色轮廓图

现在红色轮廓图是透明背景,直接叠加在白色填充图上即可,完全不会出现颜色混合:

func mergeFillAndRedOutline(fillImage: UIImage, outlineImage: UIImage) -> UIImage? {
    guard let redOutline = convertWhiteOutlineToRed(outlineImage) else { return nil }
    
    // 用原图的scale和透明度设置上下文,避免模糊
    UIGraphicsBeginImageContextWithOptions(fillImage.size, false, fillImage.scale)
    defer { UIGraphicsEndImageContext() }
    
    let drawRect = CGRect(origin: .zero, size: fillImage.size)
    // 先画白色填充图
    fillImage.draw(in: drawRect)
    // 再画红色轮廓图(透明背景,直接叠加)
    redOutline.draw(in: drawRect, blendMode: .normal, alpha: 1.0)
    
    return UIGraphicsGetImageFromCurrentImageContext()
}
方案2:直接用OpenCV生成红色轮廓图

如果你希望在OpenCV处理阶段就直接得到红色轮廓图,省去后续UIImage转换的步骤,效率会更高。假设你已经有了单通道的轮廓Mat(白色为255,黑色为0):

import OpenCV

func createRedOutlineMat(from contourMat: Mat) -> Mat {
    let rows = contourMat.rows()
    let cols = contourMat.cols()
    
    // 创建BGRA格式的Mat,初始全透明(alpha=0)
    var redOutlineMat = Mat(rows: rows, cols: cols, type: CvType.CV_8UC4)
    redOutlineMat.setTo(Scalar(0, 0, 0, 0)) // BGRA顺序:蓝、绿、红、透明度
    
    // 找到所有白色像素(值为255),设置为红色(B=0, G=0, R=255, A=255)
    let whiteMask = contourMat == 255
    redOutlineMat.setTo(Scalar(0, 0, 255, 255), mask: whiteMask)
    
    return redOutlineMat
}

// 转成UIImage后再合并
let redOutlineImage = MatToUIImage(createRedOutlineMat(from: yourContourMat))
let finalImage = mergeFillAndRedOutline(fillImage: yourFillImage, outlineImage: redOutlineImage)

为什么之前的代码会出现粉色?

你之前用alpha: 0.5的混合模式,相当于把白色轮廓和下面的白色填充做了半透明叠加,两种白色混合后就会偏粉。现在改成透明背景的红色轮廓,直接叠加就不会有颜色混合的问题,完美实现红轮廓+白填充的效果。

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

火山引擎 最新活动