Pixel Array to UIImage in Swift
Note: This is a solution for iOS creating a UIImage
. For a solution for macOS and NSImage
, see this answer.
Your only problem is that the data types in your PixelData
structure need to be UInt8
. I created a test image in a Playground with the following:
public struct PixelData {
var a: UInt8
var r: UInt8
var g: UInt8
var b: UInt8
}
var pixels = [PixelData]()
let red = PixelData(a: 255, r: 255, g: 0, b: 0)
let green = PixelData(a: 255, r: 0, g: 255, b: 0)
let blue = PixelData(a: 255, r: 0, g: 0, b: 255)
for _ in 1...300 {
pixels.append(red)
}
for _ in 1...300 {
pixels.append(green)
}
for _ in 1...300 {
pixels.append(blue)
}
let image = imageFromARGB32Bitmap(pixels: pixels, width: 30, height: 30)
Update for Swift 4:
I updated imageFromARGB32Bitmap
to work with Swift 4. The function now returns a UIImage?
and guard
is used to return nil
if anything goes wrong.
func imageFromARGB32Bitmap(pixels: [PixelData], width: Int, height: Int) -> UIImage? {
guard width > 0 && height > 0 else { return nil }
guard pixels.count == width * height else { return nil }
let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue)
let bitsPerComponent = 8
let bitsPerPixel = 32
var data = pixels // Copy to mutable []
guard let providerRef = CGDataProvider(data: NSData(bytes: &data,
length: data.count * MemoryLayout<PixelData>.size)
)
else { return nil }
guard let cgim = CGImage(
width: width,
height: height,
bitsPerComponent: bitsPerComponent,
bitsPerPixel: bitsPerPixel,
bytesPerRow: width * MemoryLayout<PixelData>.size,
space: rgbColorSpace,
bitmapInfo: bitmapInfo,
provider: providerRef,
decode: nil,
shouldInterpolate: true,
intent: .defaultIntent
)
else { return nil }
return UIImage(cgImage: cgim)
}
Making it a convenience initializer for UIImage:
This function works well as a convenience
initializer for UIImage
. Here is the implementation:
extension UIImage {
convenience init?(pixels: [PixelData], width: Int, height: Int) {
guard width > 0 && height > 0, pixels.count == width * height else { return nil }
var data = pixels
guard let providerRef = CGDataProvider(data: Data(bytes: &data, count: data.count * MemoryLayout<PixelData>.size) as CFData)
else { return nil }
guard let cgim = CGImage(
width: width,
height: height,
bitsPerComponent: 8,
bitsPerPixel: 32,
bytesPerRow: width * MemoryLayout<PixelData>.size,
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue),
provider: providerRef,
decode: nil,
shouldInterpolate: true,
intent: .defaultIntent)
else { return nil }
self.init(cgImage: cgim)
}
}
Here is an example of its usage:
// Generate a 500x500 image of randomly colored pixels
let height = 500
let width = 500
var pixels: [PixelData] = .init(repeating: .init(a: 0, r: 0, g: 0, b: 0), count: width * height)
for index in pixels.indices {
pixels[index].a = 255
pixels[index].r = .random(in: 0...255)
pixels[index].g = .random(in: 0...255)
pixels[index].b = .random(in: 0...255)
}
let image = UIImage(pixels: pixels, width: width, height: height)
formula to pick every pixel in a bitmap without repeating
if you got enough memory space to store all the pixel positions you can shuffle them:
const int xs=640; // image resolution
const int ys=480;
color pixel[sz]; // image data
const int sz=xs*ys; // image size
int adr[sz],i,j;
for (i=0;i<sz;i++) adr[i]=i; // ordered positions
for (i=0;i<sz;i++) // shuffle them
{
j = random(sz); // pseudo-randomness with uniform distribution
swap(pixel[i],pixel[j]);
}
this way you got guaranteed that each pixel is used once and most likely all of them are shuffled ...
Image manipulation with Swift has random effects
You're forgetting to clear your image context when you draw your image into it. Try adding a call to CGContextClearRect
:
let rect = CGRect(origin: CGPointZero, size: image.size)
CGContextClearRect(imageContext, rect) // Avoid undefined pixels!
CGContextDrawImage(imageContext, rect, cgImage)
That will avoid undefined pixels peeking out from underneath your image's transparent areas.
How do you generate an image where each pixel is a random color in python
You can use numpy.random.randint
to assemble the required array efficiently, in a single line.
import numpy as np
from PIL import Image
# numpy.random.randint returns an array of random integers
# from low (inclusive) to high (exclusive). i.e. low <= value < high
arr = np.random.randint(
low=0,
high=256,
size=(300, 300, 3),
dtype=np.uint8
)
im = Image.fromarray(arr)
im.show()
Output:
Random UIImageView Alpha
If you need all your images to have an alpha of 1.0 and only to have an alpha of 0.7 I would suggest the following:
First get all your images in an array in your viewDidLoad()
, then randomly select one element and apply an alpha of 0.7 (default alpha for the others will be 1.0).
You can then add a tap recognizer to each of your imageViews and check in the called method to see if alpha is 0.7
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let getColor = UIColor.randomColor()
self.image1?.backgroundColor = getColor
self.image2?.backgroundColor = getColor
self.image3?.backgroundColor = getColor
self.image4?.backgroundColor = getColor
var imageViewsArray: [UIImageView] = [image1, image2, image3, image4]
var randomIndex = Int(arc4random()) % Int(imageViewsArray.count)
imageViewsArray[randomIndex].alpha = 0.7
for imageView in imageViewsArray {
imageView.userInteractionEnabled = true
let tapRecognizer = UITapGestureRecognizer(target: self, action:"imageTapped:")
imageView.addGestureRecognizer(tapRecognizer)
}
}
func imageTapped(gestureRecognizer: UITapGestureRecognizer) {
let tappedImageView = gestureRecognizer.view!
if tappedImageView.alpha == 0.7 {
// Success
}
}
How to create an empty, coloured CGImage canvas in Swift
You don't draw on a CGImage
, you draw on a CGContext
. What you want to do is create a bitmap context, draw into the bitmap context, then create an image from the context bitmap buffer. Here is a sample that I typed up in a Playground.
import UIKit
// CGContexts like rowbytes that are multiples of 16.
func goodBytesPerRow(_ width: Int) -> Int {
return (((width * 4) + 15) / 16) * 16
}
func drawMyImage() -> CGImage? {
let bounds = CGRect(x: 0, y:0, width: 200, height: 200)
let intWidth = Int(ceil(bounds.width))
let intHeight = Int(ceil(bounds.height))
let bitmapContext = CGContext(data: nil,
width: intWidth, height: intHeight,
bitsPerComponent: 8,
bytesPerRow: goodBytesPerRow(intWidth),
space: CGColorSpace(name: CGColorSpace.sRGB)!,
bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue)
if let cgContext = bitmapContext {
cgContext.saveGState()
cgContext.setFillColor(gray: 0, alpha: 1.0)
cgContext.fill(bounds)
/* ... do other drawing here ... */
cgContext.restoreGState()
return cgContext.makeImage()
}
return nil
}
let image = drawMyImage()
This is drawing using 32 bit ARGB values.
Core Graphics likes it best when its row bytes is a mutiple of 16 (or at least it did in 2006 when I wrote a Quartz 2D book). So goodBytesPerRow
calculates, for 32-bit ARGB pixels, a rowBytes that is a multiple of 16 for a given width.
The "bitmapInfo" is a combination of constants and determines more specifics of the pixel format CGImageAlphaInfo
and CGBitmapInfo
. In this case we're saying we want the alpha channel to be first (so ARGB instead of RGBA) and we want to use pixels where the color channels are premultiplied by the alpha values.
Once you have the bitmap context you can draw what you like. You said you wanted to draw some lines on a black background - so this just gives you the black background and leaves the lines as an exercise for the reader.
At the very end you get a CGImage
from the context using makeImage
. If you wanted a UIImage
, you could construct one with UIImage(cgImage:)
P.S. You would use CGImageProvider
if you wanted to construct an image from a raw chunk of memory, or by streaming data out of a file, or some other source. It basically tells the system how to get the image data.
In this case, when we create the bitmap context, we pass in "nil" as the data which asks the OS to allocate the frame buffer for the image for us.
Create an image from raw pixels, but color is not able to displayed according byRGBA hex value
I see red and magenta in your output. You say you used color values 0xffff00ff
and 0xff0000ff
. Therefore I deduce that the byte order in your bitmap is ABGR.
If you change your bitmapInfo to CGBitmapInfo.ByteOrder32Little.rawValue | CGImageAlphaInfo.PremultipliedLast.rawValue
, you'll get the byte order you expect.
Related Topics
Reading in from System.In - Java
How to Find How Much Disk Space Is Left Using Java
Detecting Device Type in a Web Application
How to Create My Own Appender in Log4J
How to Increase the Number of Displayed Lines of a Java Stack Trace Dump
"Invalid Privatekey" When Using Jsch
Java Filereader Encoding Issue
Why Is Double.Min_Value in Not Negative
Is Iterating Concurrenthashmap Values Thread Safe
Builder Pattern in Effective Java
The Specified Dsn Contains an Architecture Mismatch Between the Driver and Application. Java
How to Generate Multiple Lines in PDF Using Apache PDFbox
Why Should Wait() Always Be Called Inside a Loop
Running a .SQL Script Using MySQL with Jdbc