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

iOS中使用vImageFloodFill_ARGB8888实现UIImage泛洪填充遇异常

使用Accelerate框架实现UIImage泛洪填充时结果异常的问题

我尝试用Accelerate框架(尚未完全理解)为UIImage实现泛洪填充功能,编写了UIImage扩展,但执行结果不可控:当UIImage为单色图时结果符合预期,但若使用相册图片调用floodFill方法,结果会混乱。

以下是当前使用的代码:

import Accelerate
import Accelerate.vImage
import Foundation
import CoreGraphics

extension UIImage {
    
    func floodFill(seedPoint: CGPoint, replacementColor: UIColor) -> UIImage? {
        guard var buffer = self.toVImage() else {
            return nil
        }
               
        guard var maskBuffer = createMaskBuffer(width: Int(buffer.width), height: Int(buffer.height)) else {
            return nil
        }

        let replacementRGBA = replacementColor.toRGBA()
       
        var cgImageFormatPlanarF = vImage_CGImageFormat(
            bitsPerComponent: 32,
            bitsPerPixel: 32,
            colorSpace: CGColorSpaceCreateDeviceGray(),
            bitmapInfo: CGBitmapInfo(
                rawValue: kCGBitmapByteOrder32Host.rawValue |
                CGBitmapInfo.floatComponents.rawValue |
                CGImageAlphaInfo.none.rawValue),
            renderingIntent: .defaultIntent)!
       
        let bufferPlanarF = try! vImage.PixelBuffer<vImage.PlanarF>(
            cgImage: self.cgImage!,
            cgImageFormat: &cgImageFormatPlanarF)
        bufferPlanarF.colorThreshold(
            0.5,
            destination: bufferPlanarF)
        let bufferRGB8 = vImage.PixelBuffer<vImage.Interleaved8x4>(
            planarBuffers: [bufferPlanarF, bufferPlanarF, bufferPlanarF, bufferPlanarF])
       
        // 创建RGBA组件的连续内存块
        var rgbaComponents: [UInt8] = [replacementRGBA.0, replacementRGBA.1, replacementRGBA.2, replacementRGBA.3]

        bufferRGB8.withUnsafePointerToVImageBuffer { src in
            vImageFloodFill_ARGB8888(src, nil,
                                     UInt(seedPoint.x), UInt(seedPoint.y),
                                     &rgbaComponents, 8, 0)
           
        }
 

        
        let cgImageFormatRGB8 = vImage_CGImageFormat(
            bitsPerComponent: 8,
            bitsPerPixel: 8 * 4,
            colorSpace: CGColorSpaceCreateDeviceRGB(),
            bitmapInfo: CGBitmapInfo(
                rawValue: CGImageAlphaInfo.noneSkipFirst.rawValue),
            renderingIntent: .defaultIntent)!
       
        guard let result = bufferRGB8.makeCGImage(cgImageFormat: cgImageFormatRGB8) else{
            return nil
        }
        return UIImage(cgImage: result)
    }
    
    private func toVImage() -> vImage_Buffer? {
        guard let cgImage = self.cgImage else {
            return nil
        }
        
        guard let colorSpace = cgImage.colorSpace else {
            return nil
        }
        
        var format = vImage_CGImageFormat(bitsPerComponent: 8,
                                      bitsPerPixel: 8 * 4,
                                      colorSpace: CGColorSpaceCreateDeviceRGB(),
                                      bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.noneSkipLast.rawValue))!
        
        var buffer = vImage_Buffer()
        
        let error = vImageBuffer_InitWithCGImage(&buffer, &format, nil, cgImage, vImage_Flags(kvImageNoFlags))
        
        guard error == kvImageNoError else {
            print("创建vImage缓冲区出错: \(error)")
            return nil
        }
        
        return buffer
    }


    private func vImageToCGImage(buffer: vImage_Buffer) -> CGImage? {

        guard let context = CGContext(data: buffer.data,
                                      width: Int(buffer.width),
                                      height: Int(buffer.height),
                                      bitsPerComponent: 8,
                                      bytesPerRow: buffer.rowBytes,
                                      space: CGColorSpaceCreateDeviceRGB(),
                                      bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue).rawValue) else {
            return nil
        }

        return context.makeImage()
    }
    
    private func createMaskBuffer(width: Int, height: Int) -> vImage_Buffer? {

        let bytesPerPixel = 4
        let bytesPerRow = width * bytesPerPixel
        let byteCount = bytesPerRow * height
        let maskData = UnsafeMutablePointer<UInt8>.allocate(capacity: byteCount)
        

        var maskBuffer = vImage_Buffer(data: maskData, height: vImagePixelCount(height), width: vImagePixelCount(width), rowBytes: bytesPerRow)

        memset(maskBuffer.data, 0, byteCount)
        
        return maskBuffer
    }
    
}

extension UIColor {
    func toRGBA() -> (UInt8, UInt8, UInt8, UInt8) {
        var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0
        getRed(&red, green: &green, blue: &blue, alpha: &alpha)
        
        return (UInt8(red * 255), UInt8(green * 255), UInt8(blue * 255), UInt8(alpha * 255))
    }
}

我尝试了多种传递替换颜色的方式,是不是我在做一件不可能的事?怀疑vImageFloodFill_ARGB8888并非为实际泛洪填充设计,仅适用于空白图像。

编辑补充:添加了结果示例图,同时更新了代码块,该代码直接基于Apple官方文档编写。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 14:40:27