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.