2

I'm trying to extract the array of numbers from a UIImage in swift but at the end I got only a bunch of zeros no useful information at all.

that's the code I wrote to try accomplishing this.

var photo = UIImage(named: "myphoto.jpg")!

var withAlpha = true
var bytesPerPixels: Int = withAlpha ? 4 : 3

var width: Int = Int(photo.size.width)
var height: Int = Int(photo.size.height)

var bitsPerComponent: Int = 8
var bytesPerRow = bytesPerPixels * width
var totalPixels = (bytesPerPixels * width) * height

var alignment = MemoryLayout<UInt32>.alignment

var data = UnsafeMutableRawPointer.allocate(byteCount: totalPixels, alignment: alignment )

var bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue | CGBitmapInfo.byteOrder32Little.rawValue).rawValue


var colorSpace = CGColorSpaceCreateDeviceRGB()

let ctx = CGContext(data: data, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo)


let bindedPointer: UnsafeMutablePointer<UInt32> = data.bindMemory(to: UInt32.self, capacity: totalPixels)

var pixels = UnsafeMutableBufferPointer.init(start: bindedPointer, count: totalPixels)


for p in pixels{
    print(p, Date())
}

At the end I tried to bind the unsafeMutableRawPointer to extract the values but got no success, what could I be missing here?

Thank you all in advance.

Lucas
  • 746
  • 8
  • 23

2 Answers2

2

A few observations:

  • You need to draw the image to the context.
  • I’d also suggest that rather than creating a buffer that you have to manage manually, that you pass nil and let the OS create (and manage) that buffer for you.
  • Note that totalPixels should be just width * height.
  • Your code assumes the scale of the image is 1. That’s not always a valid assumption. I’d grab the cgImage and use its width and height.
  • Even if you have only three components, you still need to use 4 bytes per pixel.

Thus:

guard 
    let photo = UIImage(named: "myphoto.jpg”),
    let cgImage = photo.cgImage
else { return }

let bytesPerPixels = 4

let width = cgImage.width
let height = cgImage.height

let bitsPerComponent: Int = 8
let bytesPerRow = bytesPerPixels * width
let totalPixels = width * height

let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue | CGBitmapInfo.byteOrder32Little.rawValue).rawValue

let colorSpace = CGColorSpaceCreateDeviceRGB()

guard
    let ctx = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo),
    let data = ctx.data
else { return }

ctx.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height))

let pointer = data.bindMemory(to: UInt32.self, capacity: totalPixels)

let pixels = UnsafeMutableBufferPointer(start: pointer, count: totalPixels)

for p in pixels {
    print(String(p, radix: 16), Date())
}
Rob
  • 415,655
  • 72
  • 787
  • 1,044
1

You need to draw the image into the context.

ctx?.draw(photo.cgImage!, in: CGRect(origin: .zero, size: photo.size))

Add that just after creating the CGContext.

rmaddy
  • 314,917
  • 42
  • 532
  • 579