CVPixelBuffer to CGImage with Scale Down - ios

I have following code to convert CVPixelBuffer to CGImage and it is working properly:
let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
// Lock the base address of the pixel buffer
CVPixelBufferLockBaseAddress(imageBuffer!, CVPixelBufferLockFlags(rawValue: CVOptionFlags(0)))
// Get the number of bytes per row for the pixel buffer
let baseAddress = CVPixelBufferGetBaseAddress(imageBuffer!)
// Get the number of bytes per row for the pixel buffer
let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer!)
// Get the pixel buffer width and height
let width = CVPixelBufferGetWidth(imageBuffer!)
let height = CVPixelBufferGetHeight(imageBuffer!)
// Create a device-dependent RGB color space
let colorSpace = CGColorSpaceCreateDeviceRGB()
// Create a bitmap graphics context with the sample buffer data
let bitmap = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue|CGImageAlphaInfo.premultipliedFirst.rawValue)
let contextBM = CGContext(data: baseAddress, width: width, height: height, bitsPerComponent: 8,
bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmap.rawValue)
// Create a Quartz image from the pixel data in the bitmap graphics context
let quartzImage = contextBM!.makeImage()
//Unlock the pixel buffer
CVPixelBufferUnlockBaseAddress(imageBuffer!, CVPixelBufferLockFlags(rawValue: CVOptionFlags(0)))
self.myCGImage = quartzImage
Now, I want the CGImage that gets created to be smaller in dimensions. Maybe half the size? Is it possible to modify the above code without running anything extra?

Related

CGContext.init for ARGB image returns nil

I'm trying to create a CGContext and fill it with an array of pixels with an ARGB format. I've successfully created the pixel array, but when I try to create the CGContext with CGColorSpaceCreateDeviceRGB and CGImageAlphaInfo.first, it returns nil.
func generateBitmapImage8bit() -> CGImage {
let width = params[0]
let height = params[1]
let bitmapBytesPerRow = width * 4
let context = CGContext(data: nil,
width: width,
height: height,
bitsPerComponent: 8,
bytesPerRow: bitmapBytesPerRow,
space: CGColorSpaceCreateDeviceRGB(), //<-
bitmapInfo: CGImageAlphaInfo.first.rawValue)
context!.data!.storeBytes(of: rasterArray, as: [Int].self)
let image = context!.makeImage()
return image!
}
Please refer to the Supported Pixel Formats.
Seems like you use incorrect configuration (CGImageAlphaInfo.first). For 8 bitsPerComponent and RGB color space you have only the following valid alpha options:
kCGImageAlphaNoneSkipFirst
kCGImageAlphaNoneSkipLast
kCGImageAlphaPremultipliedFirst
kCGImageAlphaPremultipliedLast
You can also try setting CGBITMAP_CONTEXT_LOG_ERRORS environment variable in your scheme to get more information in runtime.

How to Create UIImage with kCVPixelFormatType_32BGRA formatted Buffer using Swift

We are creating UIImage from a buffer using following code
let context = CGContext(data: baseAddress, width: frameWidth, height: frameHeight, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)!
let cgImage0 = context.makeImage()
let uiImage0 = UIImage(cgImage: cgImage0!)
The problem is we can create color image buffer using AVCaptureVideoDataOutput() only with kCVPixelFormatType_32BGRA format which is the most similar format to kCVPixelFormatType_32RGBA
At some point we need UIImage and we needed to convert buffer to UIImage and because UIImage is RGBA formatted colors has become distorted.
How can we get UIImage from buffer in kCVPixelFormatType_32RGBA format.
By making
var bitmapInfo = CGBitmapInfo.byteOrder32Little.rawValue
bitmapInfo |= CGImageAlphaInfo.premultipliedFirst.rawValue & CGBitmapInfo.alphaInfoMask.rawValue
instead of
var bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
But getting this from Apple document is very hard.

Convert UIImage to UInt8 Array in Swift

all!
I've been doing a lot of research into this and I've integrated several different solutions into my project, but none of them seem to work. My current solution has been borrowed from this thread.
When I run my code, however, two things happen:
The pixel array remains initialized but unpopulated (Full of 0s)
I get two errors:
CGBitmapContextCreate: unsupported parameter combination: set CGBITMAP_CONTEXT_LOG_ERRORS environmental variable to see the details
and
CGContextDrawImage: invalid context 0x0. If you want to see the backtrace, please set
Any ideas? Here is my current built function that I'm calling for my Image class:
init?(fromImage image: UIImage!) {
let imageRef = image!.CGImage
self.width = CGImageGetWidth(imageRef)
self.height = CGImageGetHeight(imageRef)
let colorspace = CGColorSpaceCreateDeviceRGB()
let bytesPerRow = (4 * width);
let bitsPerComponent :UInt = 8
let pixels = UnsafeMutablePointer<UInt8>(malloc(width*height*4))
var context = CGBitmapContextCreate(pixels, width, height, Int(bitsPerComponent), bytesPerRow, colorspace, 0);
CGContextDrawImage(context, CGRectMake(0, 0, CGFloat(width), CGFloat(height)), imageRef)
Any pointers would help a lot, as I'm new to understanding how all of this CGBitmap stuff works.
Thanks a ton!
You should not pass an 0 as the bitmapInfo param for CGBitmapContextCreate. For RGBA you shall pass CGImageAlphaInfo.PremultipliedLast.rawValue.
Supported combinations of bitsPerComponent, bytesPerRow, colorspace and bitmapInfo can be found here:
https://developer.apple.com/library/mac/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_context/dq_context.html#//apple_ref/doc/uid/TP30001066-CH203-BCIBHHBB
Note that 32 bits per pixel (bpp) is 4 bytes per pixel and you use it to calculate bytesPerRow
You need to convert the image to NSData and then convert NSData
to UInt8 array.
let data: NSData = UIImagePNGRepresentation(image)
// or let data: NSData = UIImageJPGRepresentation(image)
let count = data.length / sizeof(UInt8)
// create an array of Uint8
var array = [UInt8](count: count, repeatedValue: 0)
// copy bytes into array
data.getBytes(&array, length:count * sizeof(UInt8))

Find right CGBitmapInfo for a picture

I'm trying to do a steganography app, and since now I've managed to get pixels bytes from actual image and modify them. But I have't managed to rebuild image in a proper way (I get bytes from image, rebuild image, get bytes again and they are not the same). After some debugging I've get to a conclusion. I'm not build image the right way. Since all other vars are pretty standard(see the code below), the only var that I'm not sure what it does and I think is wrong how I use it is bitMapInfo. Can someone help me out to figure what do I need?
let bitsPerComponent = 8
let bytesPerPixel = 4
let bytesPerRow = Int(image.size.width) * bytesPerPixel
let colorSpace = CGColorSpaceCreateDeviceRGB()
var bitmapInfo: UInt32 = CGBitmapInfo.ByteOrder32Big.rawValue
bitmapInfo |= CGImageAlphaInfo.PremultipliedLast.rawValue & CGBitmapInfo.AlphaInfoMask.rawValue
let imageContext = CGBitmapContextCreateWithData(imageData,
Int(image.size.width),
Int(image.size.height),
bitsPerComponent,
bytesPerRow,
colorSpace,
bitmapInfo,
nil,
nil)
let cgImage = CGBitmapContextCreateImage(imageContext)
let newImage = UIImage(CGImage: cgImage!)

How do I load and edit a bitmap file at the pixel level in Swift for iOS?

I want to manipulate an image at the pixel level in Swift. This question was answered for objective c: How do I access and manipulate JPEG image pixels?, but I would like to see the equivalent source for Swift.
This is how I am getting a color from an image at a touch location.
I translated this answer: https://stackoverflow.com/a/12579413/359578
(This sample does no error checking for nil)
func createARGBBitmapContext(inImage: CGImage) -> CGContext {
var bitmapByteCount = 0
var bitmapBytesPerRow = 0
//Get image width, height
let pixelsWide = CGImageGetWidth(inImage)
let pixelsHigh = CGImageGetHeight(inImage)
// Declare the number of bytes per row. Each pixel in the bitmap in this
// example is represented by 4 bytes; 8 bits each of red, green, blue, and
// alpha.
bitmapBytesPerRow = Int(pixelsWide) * 4
bitmapByteCount = bitmapBytesPerRow * Int(pixelsHigh)
// Use the generic RGB color space.
let colorSpace = CGColorSpaceCreateDeviceRGB()
// Allocate memory for image data. This is the destination in memory
// where any drawing to the bitmap context will be rendered.
let bitmapData = malloc(bitmapByteCount)
// Create the bitmap context. We want pre-multiplied ARGB, 8-bits
// per component. Regardless of what the source image format is
// (CMYK, Grayscale, and so on) it will be converted over to the format
// specified here by CGBitmapContextCreate.
let context = CGBitmapContextCreate(bitmapData, pixelsWide, pixelsHigh, 8, bitmapBytesPerRow, colorSpace, CGImageAlphaInfo.PremultipliedFirst.rawValue)
// Make sure and release colorspace before returning
return context!
}
func getPixelColorAtLocation(point:CGPoint, inImage:CGImageRef) -> NSColor {
// Create off screen bitmap context to draw the image into. Format ARGB is 4 bytes for each pixel: Alpa, Red, Green, Blue
let context = self.createARGBBitmapContext(inImage)
let pixelsWide = CGImageGetWidth(inImage)
let pixelsHigh = CGImageGetHeight(inImage)
let rect = CGRect(x:0, y:0, width:Int(pixelsWide), height:Int(pixelsHigh))
//Clear the context
CGContextClearRect(context, rect)
// Draw the image to the bitmap context. Once we draw, the memory
// allocated for the context for rendering will then contain the
// raw image data in the specified color space.
CGContextDrawImage(context, rect, inImage)
// Now we can get a pointer to the image data associated with the bitmap
// context.
let data = CGBitmapContextGetData(context)
let dataType = UnsafePointer<UInt8>(data)
let offset = 4*((Int(pixelsWide) * Int(point.y)) + Int(point.x))
let alpha = dataType[offset]
let red = dataType[offset+1]
let green = dataType[offset+2]
let blue = dataType[offset+3]
let color = NSColor(red: CGFloat(red)/255.0, green: CGFloat(green)/255.0, blue: CGFloat(blue)/255.0, alpha: CGFloat(alpha)/255.0)
// Free image data memory for the context
free(data)
return color;
}

Resources