EBDOKUM
EBDOKUM

Reputation: 1846

UIImage to UIColor array of pixel colors

I'm sorry to ask this, but I can't figure out how to represent a UIImage as an array of UIColor for each pixel. I've tried my best with converting UIImagePNG/JPEGRepresentation but couldn't get the desired result.

Upvotes: 1

Views: 1642

Answers (3)

Mamazur
Mamazur

Reputation: 179

To get pixels try

let image = UIImage(named: "pic2.png")
if let cgImage = image?.cgImage, let data = cgImage.dataProvider?.data, let bytes = CFDataGetBytePtr(data){
assert(cgImage.colorSpace?.model == .rgb)
var stringArray = [String]()
let bytesPerPixel = cgImage.bitsPerPixel / cgImage.bitsPerComponent
for y in 0 ..< cgImage.height{
    for x in 0 ..< cgImage.width{
        let offset = (y * cgImage.bytesPerRow) + ( x * bytesPerPixel)
        let components = (r: bytes[offset], g: bytes[offset + 1], b: bytes[offset + 2])
        stringArray.append("[x: \(x), y:\(y)] \(components)")
    }
}
print(stringArray)

}

Upvotes: 1

Andrew Campoli
Andrew Campoli

Reputation: 222

Here's a Swiftier version (Swift 3):

extension UIImage {
    var colors: [UIColor]? {

        var colors = [UIColor]()

        let colorSpace = CGColorSpaceCreateDeviceRGB()

        guard let cgImage = cgImage else {
            return nil
        }

        let width = Int(size.width)
        let height = Int(size.height)

        var rawData = [UInt8](repeating: 0, count: width * height * 4)
        let bytesPerPixel = 4
        let bytesPerRow = bytesPerPixel * width
        let bytesPerComponent = 8

        let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Big.rawValue

        let context = CGContext(data: &rawData,
                                width: width,
                                height: height,
                                bitsPerComponent: bytesPerComponent,
                                bytesPerRow: bytesPerRow,
                                space: colorSpace,
                                bitmapInfo: bitmapInfo)

        let drawingRect = CGRect(origin: .zero, size: CGSize(width: width, height: height))
        context?.draw(cgImage, in: drawingRect)

        for x in 0..<width {
            for y in 0..<height {
                let byteIndex = (bytesPerRow * x) + y * bytesPerPixel

                let red = CGFloat(rawData[byteIndex]) / 255.0
                let green = CGFloat(rawData[byteIndex + 1]) / 255.0
                let blue = CGFloat(rawData[byteIndex + 2]) / 255.0
                let alpha = CGFloat(rawData[byteIndex + 3]) / 255.0

                let color = UIColor(red: red, green: green, blue: blue, alpha: alpha)
                colors.append(color)
            }
        }

        return colors
    }
}

Upvotes: 3

Code Different
Code Different

Reputation: 93191

This is simply a Swift's translation of Olie's answer to the same question in ObjC. Make sure you give him an upvote as well.

extension UIImage {
    func colorArray() -> [UIColor] {
        let result = NSMutableArray()

        let img = self.CGImage
        let width = CGImageGetWidth(img)
        let height = CGImageGetHeight(img)
        let colorSpace = CGColorSpaceCreateDeviceRGB()

        var rawData = [UInt8](count: width * height * 4, repeatedValue: 0)
        let bytesPerPixel = 4
        let bytesPerRow = bytesPerPixel * width
        let bytesPerComponent = 8

        let bitmapInfo = CGImageAlphaInfo.PremultipliedLast.rawValue | CGBitmapInfo.ByteOrder32Big.rawValue
        let context = CGBitmapContextCreate(&rawData, width, height, bytesPerComponent, bytesPerRow, colorSpace, bitmapInfo)

        CGContextDrawImage(context, CGRectMake(0, 0, CGFloat(width), CGFloat(height)), img);
        for x in 0..<width {
            for y in 0..<height {
                let byteIndex = (bytesPerRow * x) + y * bytesPerPixel

                let red   = CGFloat(rawData[byteIndex]    ) / 255.0
                let green = CGFloat(rawData[byteIndex + 1]) / 255.0
                let blue  = CGFloat(rawData[byteIndex + 2]) / 255.0
                let alpha = CGFloat(rawData[byteIndex + 3]) / 255.0

                let color = UIColor(red: red, green: green, blue: blue, alpha: alpha)
                result.addObject(color)
            }
        }

        return (result as NSArray) as! [UIColor]
    }
}

Note that this runs rather slowly. It takes 35 seconds for the simulator to decode a 15MP image, and that's on a quad-core i7.

Upvotes: 2

Related Questions