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

Time:02-16

I'm trying to change the pixel data of a UIImage and create a new image with the new pixel data. I've been trying to implement the function in the following link to Swift.

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

I keep hitting conversion errors.

Error: cannot assign value of type 'UInt32' to subscript of type 'RGBA32'

error: cannot convert value of type 'RGBA32' to expected argument type 'UInt32'

Also the data types that i'm using in the processing algorithm are UInt32 which doesn't include negative numbers, so i'm wondering if it's even possible at all to write this method in 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
    }
}

CodePudding user response:

You defined

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

But then later, throughout the rest of your code, you seem to think that every offset within pixelBuffer is a UInt8. It isn't. It's an RGBA32!

For example, you say:

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

That seems to imply you think r, g, and b are numbers (e.g. UInt8 values). They are not. You bound pixelBuffer to RGBA32, so every offset within pixelBuffer is an RGBA32. That is what memory binding means.

All your errors, it seems to me, stem from that one wrong idea.

If your goal is to pick up the red, green, blue, and alpha components of the nth pixel in the pixelBuffer, those would be respectively pixelBuffer[n].redComponent, pixelBuffer[n].greenComponent, pixelBuffer[n].blueComponent, and pixelBuffer[n].alphaComponent.

Even then you won't be able to multiply a component directly by a UInt32, because you cannot multiply a UInt32 by a UInt8; but you will be a lot closer to accomplishing something useful.

  • Related