Janusz Chudzynski
Janusz Chudzynski

Reputation: 2710

Core Graphics messing changes UIimage format

I wrote a simple algorithm that detects the edges in the UIImages. It works perfectly fine with the images taken from bundle (look at first image). After I am doing some image manipulations (apply filters, masks, crop and etc) and I pass the image to the same function it comes up messed up (image 2). I assume that that the CoreGrahics is changing something internally in the image. The question is what?

That's how I start processing the image:

public struct PixelData {
    var a:UInt8 = 255
    var r:UInt8
    var g:UInt8
    var b:UInt8
}

   func findEdges(cgImage:CGImageRef)->UIImage{
    var pixelData = CGDataProviderCopyData(CGImageGetDataProvider(cgImage))

    //var data = CFDataGetMutableBytePtr
    var mdata: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
    var data = UnsafeMutablePointer<UInt8>(mdata)
    let height = CGImageGetHeight(cgImage)
    let width = CGImageGetWidth(cgImage)
    var start = CACurrentMediaTime()

    //create an empty buffer
    let emptyPixel = PixelData(a: 0, r: 0, g: 0, b: 0)
    let blackPixel = PixelData(a: 255, r: 255, g: 255, b: 255)

    var buffer = [PixelData](count: Int(width  * height), repeatedValue: emptyPixel)
    var booleanArray = [Bool](count: Int(width  * height), repeatedValue: false)


    for var y = 0; y < height-1; y++ {
        for var x = 0; x < width; x++ {
            //Current one
            var currentPixelInfo: Int = ((Int(width) * Int(y)) + Int(x)) * 4
            var currentAlpha = CGFloat(data[currentPixelInfo+3]) / CGFloat(255.0)
            var downPixelInfo: Int = ((Int(width) * Int(y+1)) + Int(x)) * 4
            var downAlpha = CGFloat(data[downPixelInfo+3]) / CGFloat(255.0)

            if y == 0 && currentAlpha != 0{ // Top Edge
                booleanArray[currentPixelInfo/4] = true
                buffer[currentPixelInfo/4] = blackPixel
            }

            if y > 0 && y < height - 2{
                //one up
                var topPixelInfo: Int = ((Int(width) * Int(y - 1)) + Int(x )) * 4
                var topAlpha = CGFloat(data[topPixelInfo+3]) / CGFloat(255.0)

                if downAlpha == 0 && currentAlpha != 0 {//edge
                    booleanArray[currentPixelInfo/4] = true
                    buffer[currentPixelInfo/4] = blackPixel
                }

                if topAlpha == 0 && currentAlpha != 0 {//edge
                    booleanArray[currentPixelInfo/4] = true
                    buffer[currentPixelInfo/4] = blackPixel
                }

            }

            if y == height - 2 && downAlpha != 0 {
                booleanArray[downPixelInfo/4] = true
                buffer[downPixelInfo/4] = blackPixel
            }

        }
    }


    for var y = 0; y < height-1; y++ {
        for var x = 0; x < width-1; x++ {

            //Current one
            var currentPixelInfo: Int = ((Int(width) * Int(y)) + Int(x)) * 4
            var currentAlpha = CGFloat(data[currentPixelInfo+3]) / CGFloat(255.0)
            //Next
            var nextPixelInfo: Int = ((Int(width) * Int(y)) + Int(x + 1)) * 4
            var nextAlpha = CGFloat(data[nextPixelInfo+3]) / CGFloat(255.0)


            //check horizontally
            if x == 0 && currentAlpha != 0{ // Edge case
                booleanArray[currentPixelInfo/4] = true
                buffer[currentPixelInfo/4] = blackPixel
            }
            if x > 0 && x < width - 2{
                //One before
                var previousPixelInfo: Int = ((Int(width) * Int(y)) + Int(x - 1)) * 4
                var previousAlpha = CGFloat(data[previousPixelInfo+3]) / CGFloat(255.0)

                if nextAlpha == 0 && currentAlpha != 0 {//Living on the edge
                    booleanArray[currentPixelInfo/4] = true
                    buffer[currentPixelInfo/4] = blackPixel
                }
                if previousAlpha == 0 && currentAlpha != 0 {//Living on the edge
                    booleanArray[currentPixelInfo/4] = true
                    buffer[currentPixelInfo/4] = blackPixel
                }
            }

            if x == width - 2 && nextAlpha != 0 {
                booleanArray[nextPixelInfo/4] = true
                buffer[nextPixelInfo/4] = blackPixel
            }
        }
    }


    var stop = CACurrentMediaTime()


    let image = imageFromARGB32Bitmap(buffer, width: width, height: height)

    println(stop - start)
    return image!;
    //self.imageView.image = image

}

func imageFromARGB32Bitmap(pixels:[PixelData], width:Int, height:Int)->UIImage? {

        let bitsPerComponent:Int = 8
        let bitsPerPixel:Int = 32

        assert(pixels.count == Int(width * height))

        var data = pixels // Copy to mutable []
        let providerRef = CGDataProviderCreateWithCFData(
            NSData(bytes: &data, length: data.count * sizeof(PixelData))
        )

        // let redPixel = PixelData(a: 255, r: 192, g: 0, b: 0)

        let cgim = CGImageCreate(
            width,
            height,
            bitsPerComponent,
            bitsPerPixel,
            width * Int(sizeof(PixelData)),
            rgbColorSpace,
            bitmapInfo,
            providerRef,
            nil,
            true,
            kCGRenderingIntentDefault
        )
        return UIImage(CGImage: cgim)
    }

enter image description here [Result][3]

Upvotes: 0

Views: 38

Answers (1)

Janusz Chudzynski
Janusz Chudzynski

Reputation: 2710

Although I didn't figure out what exactly UIImage is doing to raw pixel data, the function that I wrote fixes the problem. Key point here is using CGImageAlphaInfo.PremultipliedFirst value of bitmap info since my data structure is expecting ARGB format.

     func imageFromBitmapContext(image:CGImageRef, width:Int, height:Int)->UIImage?
            {

            let colorSpace:CGColorSpace = CGColorSpaceCreateDeviceRGB()
            let bitmapInfo = CGBitmapInfo(CGImageAlphaInfo.PremultipliedFirst.rawValue)
            let bytesPerRow = 4 * width
            let context = CGBitmapContextCreate(nil, Int(width), Int(height), 8, Int(bytesPerRow), colorSpace, bitmapInfo)

            CGContextDrawImage(context, CGRectMake(0, 0, CGFloat(width), CGFloat(height)), image)


            let image = CGBitmapContextCreateImage(context)

            return UIImage(CGImage: image)
        }

Upvotes: 0

Related Questions