如何在 Swift 中使用道尔顿化算法更改 UIImage 的像素值

How do I change the pixel values of UIImage with daltonization algorithm in Swift

我正在尝试更改 UIImage 的像素数据并使用新像素数据创建新图像。我一直在尝试实现下面link到Swift.

中的功能

https://galactic.ink/labs/Color-Vision/Javascript/Color.Vision.Daltonize.js

我一直遇到转换错误。

错误:无法将类型 'UInt32' 的值分配给类型 'RGBA32'

的下标

错误:无法将类型 'RGBA32' 的值转换为预期的参数类型 'UInt32'

此外,我在处理算法中使用的数据类型是 UInt32,它不包含负数,所以我想知道是否有可能在 Swift 中编写此方法。

var image = UIImage(named: "ME2.png")!



func processPixels(in image: UIImage) -> UIImage? {
    guard let inputCGImage = image.cgImage else { print("unable to get cgImage")
        return nil}
    
    let colorSpace = CGColorSpaceCreateDeviceRGB()
    let width = inputCGImage.width
    let height = inputCGImage.height
    let bytesPerPixel = 4
    let bitsPerComponent = 8
    let bytesPerRow = bytesPerPixel * width
    let bitmapInfo = RGBA32.bitmapInfo
    
    guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) else {
        print("unable to create context")
        return nil
    }
    
    context.draw(inputCGImage, in: CGRect(x: 0, y: 0, width: width, height: height))
    
    guard let buffer = context.data else {
        print("unable to get context data")
        return nil
    }
    
    let pixelBuffer = buffer.bindMemory(to: RGBA32.self, capacity: width * height)
    
    
    let pro :[Float] = [0.0, 2.02344, -2.52581, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]
    let a = pro[0];
    let b = pro[1];
    let c = pro[2];
    let d = pro[3];
    let e = pro[4];
    let f = pro[5];
    let g = pro[6];
    let h = pro[7];
    let i = pro[8];

    var L : UInt32
    var M : UInt32
    var S : UInt32

    var l : UInt32
    var m : UInt32
    var s : UInt32

    var R : UInt32
    var G : UInt32
    var B : UInt32

    var RR : UInt32
    var GG : UInt32
    var BB : UInt32


    for y in 0 ..< inputCGImage.height {
        for x in 0 ..< inputCGImage.width {
            let offset = (y * inputCGImage.bytesPerRow) + (x * bytesPerPixel)
            let r = pixelBuffer[offset]
            let g = pixelBuffer[offset + 1]
            let b = pixelBuffer[offset + 2]
            let components = (r: pixelBuffer[offset], g: pixelBuffer[offset + 1], b: pixelBuffer[offset + 2])
            print("[x:\(x), y:\(y)] \(components)")
            
            //RGB To LMS matrix conversion


error: ColorPractice.playground:78:15: error: the compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions
            L = (UInt32(17.8824) * r) + (UInt32(43.5161) * g) + (UInt32(4.11935) * b);
            

error: ColorPractice.playground:79:15: error: the compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions
            M = (UInt32(3.45565) * r) + (UInt32(27.1554) * g) + (UInt32(3.86714) * b);
            

error: ColorPractice.playground:80:15: error: the compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions
            S = (UInt32(0.0299566) * r) + (UInt32(0.184309) * g) + (UInt32(1.46709) * b);
~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~



            L = (UInt32(17.8824) * r) + (UInt32(43.5161) * g) + (UInt32(4.11935) * b);
            M = (UInt32(3.45565) * r) + (UInt32(27.1554) * g) + (UInt32(3.86714) * b);
            S = (UInt32(0.0299566) * r) + (UInt32(0.184309) * g) + (UInt32(1.46709) * b);
            
            
            
            //Simulate Color Blindness



error: ColorPractice.playground:86:36: error: no exact matches in call to initializer 
            l = (UInt32(a) * L) + (UInt32(b) * M) + (UInt32(c) * S);



error: ColorPractice.playground:88:18: error: no exact matches in call to initializer 
            s = (UInt32(g) * L) + (UInt32(h) * M) + (UInt32(i) * S);

~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            
            l = (UInt32(a) * L) + (UInt32(b) * M) + (UInt32(c) * S);
            m = (UInt32(d) * L) + (UInt32(e) * M) + (UInt32(f) * S);
            s = (UInt32(g) * L) + (UInt32(h) * M) + (UInt32(i) * S);
            
            //LMS to RGB matrix conversion 
             
            R = (UInt32(0.0809444479) * l) + (UInt32(0.130504409) * m) + (UInt32(0.116721066) * s);
            G = (UInt32(0.0102485335) * l) + (UInt32(0.0540193266) * m) + (UInt32(0.113614708) * s);
            B = (UInt32(0.000365296938) * l) + (UInt32(0.00412161469) * m) + (UInt32(0.693511405) * s);
            
            
            //Isolate invisible colors to color vision deficiency (calculate error matrix)


error: ColorPractice.playground:98:17: error: cannot convert value of type 'RGBA32' to expected argument type 'UInt32'
            R = r - R;
                ^

error: ColorPractice.playground:99:17: error: cannot convert value of type 'RGBA32' to expected argument type 'UInt32'
            G = g - G;
                ^

error: ColorPractice.playground:100:17: error: cannot convert value of type 'RGBA32' to expected argument type 'UInt32'
            B = b - B;
                ^
~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


            R = r - R;
            G = g - G;
            B = b - B;
            
            //Shift colors towards visible spectrum (apply error modifications)
            RR = (UInt32(0.0) * R) + (UInt32(0.0) * G) + (UInt32(0.0) * B);
            GG = (UInt32(0.7) * R) + (UInt32(1.0) * G) + (UInt32(0.0) * B);
            BB = (UInt32(0.7) * R) + (UInt32(0.0) * G) + (UInt32(1.0) * B);
            
            //Add compensation to original values


error: ColorPractice.playground:108:22: error: cannot convert value of type 'RGBA32' to expected argument type 'UInt32'
            R = RR + r;
                     ^

error: ColorPractice.playground:109:22: error: cannot convert value of type 'RGBA32' to expected argument type 'UInt32'
            G = GG + g;
                     ^


error: ColorPractice.playground:110:22: error: cannot convert value of type 'RGBA32' to expected argument type 'UInt32'
            B = BB + b;
                     ^
~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~



            R = RR + r;
            G = GG + g;
            B = BB + b;
            
           //Keep values in range
            
            if(R < 0) {R = 0}
            if(R > 255) {R = 255}
            if(G < 0 ) {G = 0}
            if(G > 255) {G = 255}
            if (B < 0) {B = 0}
            if (B > 255) {B = 255}
            
            //Resassign Pixels in Array



error: ColorPractice.playground:123:37: error: cannot assign value of type 'UInt32' to subscript of type 'RGBA32'
            pixelBuffer[offset] = R >> 0;
                                  ~~^~~~


error: ColorPractice.playground:124:41: error: cannot assign value of type 'UInt32' to subscript of type 'RGBA32'
            pixelBuffer[offset + 1] = G >> 0;
                                      ~~^~~~


error: ColorPractice.playground:125:41: error: cannot assign value of type 'UInt32' to subscript of type 'RGBA32'
            pixelBuffer[offset + 2] = B >> 0;
                                      ~~^~~~
~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


            pixelBuffer[offset] = R >> 0;
            pixelBuffer[offset + 1] = G >> 0;
            pixelBuffer[offset + 2] = B >> 0;
            
        }
        
    }
    
    let outputCGImage = context.makeImage()!
    let outputImage = UIImage(cgImage: outputCGImage, scale: image.scale, orientation: image.imageOrientation)
    
    return outputImage

}



struct RGBA32: Equatable {
    private var color: UInt32

    var redComponent: UInt8 {
        return UInt8((color >> 24) & 255)
    }

    var greenComponent: UInt8 {
        return UInt8((color >> 16) & 255)
    }

    var blueComponent: UInt8 {
        return UInt8((color >> 8) & 255)
    }

    var alphaComponent: UInt8 {
        return UInt8((color >> 0) & 255)
    }

    init(red: UInt8, green: UInt8, blue: UInt8, alpha: UInt8) {
        let red   = UInt32(red)
        let green = UInt32(green)
        let blue  = UInt32(blue)
        let alpha = UInt32(alpha)
        color = (red << 24) | (green << 16) | (blue << 8) | (alpha << 0)
    }

    static let red     = RGBA32(red: 255, green: 0,   blue: 0,   alpha: 255)
    static let green   = RGBA32(red: 0,   green: 255, blue: 0,   alpha: 255)
    static let blue    = RGBA32(red: 0,   green: 0,   blue: 255, alpha: 255)
    static let white   = RGBA32(red: 255, green: 255, blue: 255, alpha: 255)
    static let black   = RGBA32(red: 0,   green: 0,   blue: 0,   alpha: 255)
    static let magenta = RGBA32(red: 255, green: 0,   blue: 255, alpha: 255)
    static let yellow  = RGBA32(red: 255, green: 255, blue: 0,   alpha: 255)
    static let cyan    = RGBA32(red: 0,   green: 255, blue: 255, alpha: 255)

    static let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Little.rawValue

    static func ==(lhs: RGBA32, rhs: RGBA32) -> Bool {
        return lhs.color == rhs.color
    }
}

你定义了

pixelBuffer = buffer.bindMemory(to: RGBA32.self...

但是后来,在您的其余代码中,您似乎认为 pixelBuffer 中的每个 offset 都是 UInt8。它不是。这是一个 RGBA32!

比如你说:

let r = pixelBuffer[offset]
let g = pixelBuffer[offset + 1]
let b = pixelBuffer[offset + 2]

这似乎暗示您认为 rgb 数字 (例如 UInt8 值)。他们不是。您将 pixelBuffer 绑定到 RGBA32,因此 pixelBuffer 中的每个 offset 都是一个 RGBA32。这就是内存绑定的意思

在我看来,你所有的错误都源于那个错误的想法。

如果您的目标是获取 pixelBuffer 中第 nth 像素的红色、绿色、蓝色和 alpha 分量,这些分别是 pixelBuffer[n].redComponentpixelBuffer[n].greenComponentpixelBuffer[n].blueComponentpixelBuffer[n].alphaComponent

即便如此,您也无法将组件直接乘以 UInt32,因为您无法将 UInt32 乘以 UInt8;但你会更接近完成一些有用的事情。