-1

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
    }
}
  • What's the actual question? – matt Feb 15 '22 at 00:04
  • Why am I getting type conversion errors between my RGBA Struct and the variables in the conversion algorithm? – Marcus Westbrooks Feb 15 '22 at 01:23
  • Where's the part of your question where you tell me which line(s) elicit the errors? Did I miss it? – matt Feb 15 '22 at 02:20
  • Added errors statements to code. – Marcus Westbrooks Feb 15 '22 at 05:20
  • Cool. Okay, so what is hard about this? It seems perfectly clear. Take `UInt32(17.8824) * r`. Well, `r` is an RGBA32 which is a big complicated struct. UInt32 is a number. What on earth would it mean to multiply them? Swift doesn't know, and neither do I. Do you? What is that expression intended to do? – matt Feb 15 '22 at 11:16
  • I see what happened here. I tried a different solution and forgot to change the code so i'm accessing the color values instead of the pixels. – Marcus Westbrooks Feb 15 '22 at 20:06

1 Answers1

0

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.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • There's some good example code here: https://stackoverflow.com/questions/33214508/how-do-i-get-the-rgb-value-of-a-pixel-using-cgcontext But note that some of it is very old and will have to be translated up to modern Swift. – matt Feb 15 '22 at 12:47
  • Yeah, I see where I can take this now. Thank you. – Marcus Westbrooks Feb 15 '22 at 20:07