Create Random Pixel Images in Swift

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:

Sample Image

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



Leave a reply



Submit