getting bitmap from cgimage - swift

I'm trying to get bitmap data from a CGImage and read it into an array of pixels. However, my imagecontext and imagecontext?.draw() always returns nil.
let capturedimage = UIImage(named: "random.jpg")
let image = capturedimage?.cgImage
let width = Int((image?.width)!)
let height = Int((image?.height)!)
let bytesPerPixel = 4;
let bytesPerRow = bytesPerPixel * width;
let bitsPerComponent = 8;
let pixelArr: [[UInt32]] = Array(repeating: Array(repeating: 0, count: width), count: height)
let pointer: UnsafeMutableRawPointer = UnsafeMutableRawPointer(mutating: pixelArr);
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo: UInt32 = CGBitmapInfo.byteOrder32Big.rawValue
let imageContext = CGContext(data: pointer, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo);
imageContext?.draw(image!, in: CGRect(x: 0, y: 0, width:width, height:height))
I've checked my code multiple times, but I don't know where I went wrong.

let image = UIImage(named: "2.png")
guard let cgImage = image?.cgImage else {
fatalError()
}
let width = cgImage.width
let height = cgImage.height
let bytesPerPixel = 4;
let bytesPerRow = bytesPerPixel * width;
let bitsPerComponent = 8;
let pixelsCount = width * height
let bitmapData: UnsafeMutablePointer<UInt32> = .allocate(capacity: pixelsCount)
defer {
bitmapData.deallocate()
}
bitmapData.initialize(repeating: 0, count: pixelsCount)
var pixelAtPosition: ((Int, Int) -> UInt32) = { x, y in
return bitmapData[y * width + x]
}
guard let context = CGContext(data: bitmapData, width: width, height: height,
bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow,
space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) else {
fatalError()
}
//draw image to context
var rect = CGRect(x: 0, y: 0, width: width, height: height)
context.draw(cgImage, in: rect)
let traget = pixelAtPosition(200, 200)
print(String(format: "pixel 0x%08X", traget))
print(String(format: "red 0x%2X", (traget & 0x000000FF)))
print(String(format: "green 0x%2X", (traget & 0x0000FF00) >> 8))
print(String(format: "blue 0x%2X", (traget & 0x00FF0000) >> 16))
print(String(format: "alpha 0x%2X", (traget & 0xFF000000) >> 24))

Related

Crash when creating gradient for CGImage: copy_read_only: vm_copy failed: status 1

Began crashing with EXC_BAD_ACCESS when building app after new Xcode 11.4 update.
Only crashes when building with release build configuration, not debugging configuration.
Breaks at: let img = ctx.makeImage()!
func image() -> CGImage {
if let image = generatedImage {
return image
}
let w = Int(size.width * scale)
let h = Int(size.height * scale)
let bitsPerComponent: Int = MemoryLayout<UInt8>.size * 8
let bytesPerPixel: Int = bitsPerComponent * 4 / 8
let bytesPerRow = w * bytesPerPixel
var data = [ARGB]()
for y in 0..<h {
for x in 0..<w {
let c = pixelDataForGradient(at: CGPoint(x: x, y: y),
size: CGSize(width: w, height: h),
colors: colors,
locations: locations,
startPoint: startPoint,
endPoint: endPoint)
data.append(c)
}
}
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue)
let ctx = CGContext(data: &data, width: w, height: h, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)!
ctx.interpolationQuality = .none
ctx.setShouldAntialias(false)
let img = ctx.makeImage()!
generatedImage = img
return img
}

how to properly extract the array of numbers from an image in swift?

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.
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())
}
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.

Get Alpha Percentage of image Swift

I have function to calculate alpha of image. But I got crash for iPhone 5, work well with iPhone 6 and later.
private func alphaOnlyPersentage(img: UIImage) -> Float {
let width = Int(img.size.width)
let height = Int(img.size.height)
let bitmapBytesPerRow = width
let bitmapByteCount = bitmapBytesPerRow * height
let pixelData = UnsafeMutablePointer<UInt8>.allocate(capacity: bitmapByteCount)
let colorSpace = CGColorSpaceCreateDeviceGray()
let context = CGContext(data: pixelData,
width: width,
height: height,
bitsPerComponent: 8,
bytesPerRow: bitmapBytesPerRow,
space: colorSpace,
bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.alphaOnly.rawValue).rawValue)!
let rect = CGRect(x: 0, y: 0, width: width, height: height)
context.clear(rect)
context.draw(img.cgImage!, in: rect)
var alphaOnlyPixels = 0
for x in 0...Int(width) {
for y in 0...Int(height) {
if pixelData[y * width + x] == 0 {
alphaOnlyPixels += 1
}
}
}
free(pixelData)
return Float(alphaOnlyPixels) / Float(bitmapByteCount)
}
Please help me to fix! Thank you. Sorry I am newbie with iOS coding.
Replace ... with ..< otherwise you are accessing one row and one column too many.
Note the crash is random depending on how the memory is allocated and whether you have access to the bytes at the given address, which is outside of the chunk allocated for you.
Or replace the iteration by simpler:
for i in 0 ..< bitmapByteCount {
if pixelData[i] == 0 {
alphaOnlyPixels += 1
}
}
You can also use Data to create your bytes, which will later simplify iteration:
var pixelData = Data(count: bitmapByteCount)
pixelData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) in
let context = CGContext(data: bytes,
width: width,
height: height,
bitsPerComponent: 8,
bytesPerRow: bitmapBytesPerRow,
space: colorSpace,
bitmapInfo: CGImageAlphaInfo.alphaOnly.rawValue)!
let rect = CGRect(x: 0, y: 0, width: width, height: height)
context.clear(rect)
context.draw(img.cgImage!, in: rect)
}
let alphaOnlyPixels = pixelData.filter { $0 == 0 }.count

Swift - Picking pixel colour of UIImage - memory crash

I want to pick colour of specific pixel of UIImage in Swift 3 and this method is called ~10k times.
func pixelColour(_ pixelPosition: CGPoint) {
if !CGRect(x: 0.0, y: 0.0, width: self.size.width, height: self.size.height).contains(pixelPosition) {
return false
}
let pointX = trunc(pixelPosition.x);
let pointY = trunc(pixelPosition.y);
let cgImage = self.cgImage;
let width = self.size.width;
let height = self.size.height;
let colorSpace = CGColorSpaceCreateDeviceRGB();
let bytesPerPixel = 4;
let bytesPerRow = bytesPerPixel * 1;
let bitsPerComponent = 8;
let pixelData = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: 4)
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
let context = CGContext(data: pixelData, width: 1, height: 1, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)
context?.setBlendMode(CGBlendMode.copy);
context?.translateBy(x: -pointX, y: pointY-CGFloat(height));
// This line takes too much memory, how to release memory here?
context?.draw(cgImage!, in: CGRect(x: 0.0, y: 0.0, width: CGFloat(width), height: CGFloat(height)));
print("\(pixelData[0]) \(pixelData[1]) \(pixelData[2]) ")
pixelData.deallocate(capacity: 4)
}
Unfortunately it seems that memory is not released, because it crashes after checking ~500 pixels. How I can solve this problem?
You have not shown how pixelColour is called, but I presume that it is in some sort of loop. If so, surround your repeated call to pixelColour with an autoreleasepool{...} call to release the accumulated memory each time through your loop:
let p = // next CGPoint
autoreleasepool {
pixelColour(p)
}
Taken from How do I get the color of a pixel in a UIImage with Swift?
and converted to Swift 3
extension UIImage{
func getPixelColor(pos: CGPoint) -> UIColor? {
guard let provider=cgImage?.dataProvider else{
return nil
}
let pixelData = provider.data
let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
let pixelInfo: Int = ((Int(self.size.width) * Int(pos.y)) + Int(pos.x)) * 4
let r = CGFloat(data[pixelInfo]) / CGFloat(255.0)
let g = CGFloat(data[pixelInfo+1]) / CGFloat(255.0)
let b = CGFloat(data[pixelInfo+2]) / CGFloat(255.0)
let a = CGFloat(data[pixelInfo+3]) / CGFloat(255.0)
return UIColor(red: r, green: g, blue: b, alpha: a)
}
}
This should not take to much memory, since it uses pointers so it doesn't load the entire image.
hope it helps

How to reconstruct grayscale image from intensity values?

It is commonly required to get the pixel data from an image or reconstruct that image from pixel data. How can I take an image, convert it to an array of pixel values and then reconstruct it using the pixel array in Swift using CoreGraphics?
The quality of the answers to this question have been all over the place so I'd like a canonical answer.
Get pixel values as an array
This function can easily be extended to a color image. For simplicity I'm using grayscale, but I have commented the changes to get RGB.
func pixelValuesFromImage(imageRef: CGImage?) -> (pixelValues: [UInt8]?, width: Int, height: Int)
{
var width = 0
var height = 0
var pixelValues: [UInt8]?
if let imageRef = imageRef {
let totalBytes = imageRef.width * imageRef.height
let colorSpace = CGColorSpaceCreateDeviceGray()
pixelValues = [UInt8](repeating: 0, count: totalBytes)
pixelValues?.withUnsafeMutableBytes({
width = imageRef.width
height = imageRef.height
let contextRef = CGContext(data: $0.baseAddress, width: width, height: height, bitsPerComponent: 8, bytesPerRow: width, space: colorSpace, bitmapInfo: 0)
let drawRect = CGRect(x: 0.0, y:0.0, width: CGFloat(width), height: CGFloat(height))
contextRef?.draw(imageRef, in: drawRect)
})
}
return (pixelValues, width, height)
}
Get image from pixel values
I reconstruct an image, in this case grayscale 8-bits per pixel, back into a CGImage.
func imageFromPixelValues(pixelValues: [UInt8]?, width: Int, height: Int) -> CGImage?
{
var imageRef: CGImage?
if let pixelValues = pixelValues {
let bitsPerComponent = 8
let bytesPerPixel = 1
let bitsPerPixel = bytesPerPixel * bitsPerComponent
let bytesPerRow = bytesPerPixel * width
let totalBytes = width * height
let unusedCallback: CGDataProviderReleaseDataCallback = { optionalPointer, pointer, valueInt in }
let providerRef = CGDataProvider(dataInfo: nil, data: pixelValues, size: totalBytes, releaseData: unusedCallback)
let bitmapInfo: CGBitmapInfo = [CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue), CGBitmapInfo(rawValue: CGImageByteOrderInfo.orderDefault.rawValue)]
imageRef = CGImage(width: width,
height: height,
bitsPerComponent: bitsPerComponent,
bitsPerPixel: bitsPerPixel,
bytesPerRow: bytesPerRow,
space: CGColorSpaceCreateDeviceGray(),
bitmapInfo: bitmapInfo,
provider: providerRef!,
decode: nil,
shouldInterpolate: false,
intent: .defaultIntent)
}
return imageRef
}
Demoing the code in a Playground
You'll need an image copied into the Playground's Resources folder and then change the filename and extension below to match. The result on the last line is a UIImage constructed from the CGImage.
import Foundation
import CoreGraphics
import UIKit
import PlaygroundSupport
let URL = playgroundSharedDataDirectory.appendingPathComponent("zebra.jpg")
print("URL \(URL)")
var image: UIImage? = nil
if FileManager().fileExists(atPath: URL.path) {
do {
try NSData(contentsOf: URL, options: .mappedIfSafe)
} catch let error as NSError {
print ("Error: \(error.localizedDescription)")
}
image = UIImage(contentsOfFile: URL.path)
} else {
print("File not found")
}
let (intensityValues, width, height) = pixelValuesFromImage(imageRef: image?.cgImage)
let roundTrippedImage = imageFromPixelValues(pixelValues: intensityValues, width: width, height: height)
let zebra = UIImage(cgImage: roundTrippedImage!)
I was having trouble getting Cameron's code above to work, so I wanted to test another method. I found Vacawama's code, which relies on ARGB pixels. You can use that solution and convert each grayscale value to an ARGB value by simply mapping on each value:
/// Assuming grayscale pixels contains floats in the range 0...1
let grayscalePixels: [Float] = ...
let pixels = grayscalePixels.map {
let intensity = UInt8(round($0 / Float(UInt8.max)))
return PixelData(a: UInt8.max, r: intensity, g: intensity, b: intensity)
}
let image = UIImage(pixels: pixels, width: width, height: height)