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

Swift4:如何用滤镜修改黑白图像颜色?优化逐像素遍历慢问题

Great question—逐像素遍历确实是图像处理里的性能大坑,尤其是图像尺寸大的时候,每次调用getPixel和setPixel都会带来额外的方法调用开销,慢得离谱。下面给你几个更高效的实现方案,以及Swift 4里用滤镜快速修改黑白图像颜色的方法:

高效替代逐像素遍历的实现方式

方案1:直接操作CGContext像素缓冲区

这种方式跳过了单个像素的get/set方法,直接操作内存中的像素数据,效率比原代码高很多,适合大多数场景:

import UIKit

func convertBlackWhiteImage(_ image: UIImage, darkColor: UIColor, lightColor: UIColor) -> UIImage? {
    guard let cgImage = image.cgImage else { return nil }
    let width = cgImage.width
    let height = cgImage.height
    let bytesPerPixel = 4
    let bytesPerRow = bytesPerPixel * width
    let bitsPerComponent = 8
    
    // 创建位图上下文
    guard let context = CGContext(data: nil,
                                  width: width,
                                  height: height,
                                  bitsPerComponent: bitsPerComponent,
                                  bytesPerRow: bytesPerRow,
                                  space: CGColorSpaceCreateDeviceRGB(),
                                  bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) else {
        return nil
    }
    
    // 将原图像绘制到上下文
    context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height))
    
    // 获取像素缓冲区指针
    guard let pixelBuffer = context.data else { return nil }
    let pixels = pixelBuffer.bindMemory(to: UInt32.self, capacity: width * height)
    
    // 把目标颜色转换为RGBA格式的UInt32值
    let darkRGBA = darkColor.rgbaValue
    let lightRGBA = lightColor.rgbaValue
    
    // 批量处理像素
    for i in 0..<(width * height) {
        let pixel = pixels[i]
        // 提取红色通道(黑白图RGB通道值一致,取任意一个即可)
        let red = (pixel >> 16) & 0xFF
        pixels[i] = red > 200 ? darkRGBA : lightRGBA
    }
    
    // 生成新图像
    guard let newCGImage = context.makeImage() else { return nil }
    return UIImage(cgImage: newCGImage)
}

// 给UIColor扩展,方便获取RGBA的UInt32值
extension UIColor {
    var rgbaValue: UInt32 {
        var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0
        self.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
        let r = UInt32(red * 255) << 16
        let g = UInt32(green * 255) << 8
        let b = UInt32(blue * 255)
        let a = UInt32(alpha * 255) << 24
        return a | r | g | b
    }
}

方案2:用Accelerate框架的vImage实现极致性能

如果处理的是超大尺寸图像(比如4K以上),可以用苹果的Accelerate框架,它会利用CPU的向量指令(比如NEON)做批量处理,速度比普通Swift循环快数倍:

import Accelerate
import UIKit

func convertBlackWhiteImageWithAccelerate(_ image: UIImage, darkColor: UIColor, lightColor: UIColor) -> UIImage? {
    guard let cgImage = image.cgImage else { return nil }
    
    // 定义vImage的图像格式
    var format = vImage_CGImageFormat(bitsPerComponent: 8,
                                      bitsPerPixel: 32,
                                      colorSpace: nil,
                                      bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue),
                                      version: 0,
                                      decode: nil,
                                      renderingIntent: .defaultIntent)
    var sourceBuffer = vImage_Buffer()
    defer { sourceBuffer.data.deallocate() }
    
    // 将CGImage转换为vImage缓冲区
    let error = vImageBuffer_InitWithCGImage(&sourceBuffer,
                                             &format,
                                             nil,
                                             cgImage,
                                             vImage_Flags(kvImageNoFlags))
    guard error == kvImageNoError else { return nil }
    
    // 分配目标缓冲区
    var destinationBuffer = vImage_Buffer()
    defer { destinationBuffer.data.deallocate() }
    guard vImageBuffer_Init(&destinationBuffer,
                            sourceBuffer.height,
                            sourceBuffer.width,
                            format.bitsPerPixel,
                            vImage_Flags(kvImageNoFlags)) == kvImageNoError else {
        return nil
    }
    
    // 准备目标颜色的组件值
    guard let darkComponents = darkColor.cgColor.components,
          let lightComponents = lightColor.cgColor.components else { return nil }
    
    // 创建颜色查找表:每个灰度值对应目标颜色
    var lookupTable = [Pixel_8x4](repeating: Pixel_8x4(a: 0, r: 0, g: 0, b: 0), count: 256)
    for i in 0..<256 {
        if i > 200 {
            lookupTable[i] = Pixel_8x4(a: UInt8(darkComponents[3] * 255),
                                       r: UInt8(darkComponents[0] * 255),
                                       g: UInt8(darkComponents[1] * 255),
                                       b: UInt8(darkComponents[2] * 255))
        } else {
            lookupTable[i] = Pixel_8x4(a: UInt8(lightComponents[3] * 255),
                                       r: UInt8(lightComponents[0] * 255),
                                       g: UInt8(lightComponents[1] * 255),
                                       b: UInt8(lightComponents[2] * 255))
        }
    }
    
    // 应用查找表,批量转换像素
    vImageLookupTable_PlanarToARGB8888(&sourceBuffer,
                                        &destinationBuffer,
                                        &lookupTable,
                                        vImage_Flags(kvImageNoFlags))
    
    // 将vImage缓冲区转换为CGImage
    guard let resultCGImage = vImageCreateCGImageFromBuffer(&destinationBuffer,
                                                           &format,
                                                           nil,
                                                           nil,
                                                           vImage_Flags(kvImageNoFlags),
                                                           nil)?.takeRetainedValue() else {
        return nil
    }
    
    return UIImage(cgImage: resultCGImage)
}

// 匹配vImage的ARGB像素结构
struct Pixel_8x4 {
    var a: UInt8
    var r: UInt8
    var g: UInt8
    var b: UInt8
}

Swift 4中用滤镜修改黑白图像颜色

Core Image框架提供了现成的滤镜,代码简洁且利用GPU加速,性能拉满,适合不需要自定义复杂逻辑的场景:

方法1:用CIFalseColor快速替换明暗颜色

这个滤镜专门用来把图像的暗部和亮部分别替换成指定颜色,完美匹配你的需求:

import CoreImage
import UIKit

func convertBlackWhiteImageWithFalseColor(_ image: UIImage, darkColor: UIColor, lightColor: UIColor) -> UIImage? {
    guard let ciImage = CIImage(image: image) else { return nil }
    
    // 初始化FalseColor滤镜
    let falseColorFilter = CIFilter(name: "CIFalseColor")!
    falseColorFilter.setValue(ciImage, forKey: kCIInputImageKey)
    // 替换亮部颜色(对应你原代码中red>200的部分)
    falseColorFilter.setValue(CIColor(cgColor: darkColor.cgColor), forKey: kCIInputColor0Key)
    // 替换暗部颜色(对应原代码中red<=200的部分)
    falseColorFilter.setValue(CIColor(cgColor: lightColor.cgColor), forKey: kCIInputColor1Key)
    
    guard let outputCI = falseColorFilter.outputImage else { return nil }
    
    // 创建GPU加速的CI上下文
    let context = CIContext(options: [.useSoftwareRenderer: false])
    guard let outputCG = context.createCGImage(outputCI, from: outputCI.extent) else { return nil }
    
    return UIImage(cgImage: outputCG)
}

方法2:结合阈值滤镜+颜色映射(适合非纯黑白的原图)

如果你的原图不是严格的黑白图,先通过阈值滤镜把它转成纯黑白,再用颜色映射替换:

import CoreImage
import UIKit

func convertBlackWhiteImageWithThresholdAndMap(_ image: UIImage, darkColor: UIColor, lightColor: UIColor) -> UIImage? {
    guard let ciImage = CIImage(image: image) else { return nil }
    
    // 第一步:用阈值滤镜把图像转成纯黑白
    let thresholdFilter = CIFilter(name: "CIColorThreshold")!
    thresholdFilter.setValue(ciImage, forKey: kCIInputImageKey)
    thresholdFilter.setValue(200/255.0, forKey: kCIInputThresholdKey) // 对应原代码的red>200
    guard let thresholdedImage = thresholdFilter.outputImage else { return nil }
    
    // 第二步:创建渐变映射图,用来替换颜色
    let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(),
                              colors: [lightColor.cgColor, darkColor.cgColor] as CFArray,
                              locations: [0, 1])!
    let gradientImage = UIGraphicsImageRenderer(size: CGSize(width: 256, height: 1)).image { context in
        gradient.draw(in: context.cgContext,
                      startPoint: CGPoint(x: 0, y: 0),
                      endPoint: CGPoint(x: 256, y: 0))
    }
    
    // 第三步:用颜色映射滤镜替换颜色
    let colorMapFilter = CIFilter(name: "CIColorMap")!
    colorMapFilter.setValue(thresholdedImage, forKey: kCIInputImageKey)
    colorMapFilter.setValue(CIImage(image: gradientImage), forKey: kCIInputGradientImageKey)
    
    guard let outputCI = colorMapFilter.outputImage else { return nil }
    
    let context = CIContext(options: [.useSoftwareRenderer: false])
    guard let outputCG = context.createCGImage(outputCI, from: outputCI.extent) else { return nil }
    
    return UIImage(cgImage: outputCG)
}

总结

  • 追求极致性能(超大图像):选Accelerate的vImage方案;
  • 追求代码简洁+高性能:选Core Image滤镜方案;
  • 自定义逻辑较多:选CGContext直接操作像素的方案。

内容的提问来源于stack exchange,提问作者Hafiz Shoaib Awan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:41:52