[CoreGraphics] Image manipulation - pixel by pixel

Apprentice
Posts: 8
Joined: 2010.07
Post: #1
Hi.
I'm writing an application that operates on image. I need to process this image pixel by pixel. So I made NSBitmapImageRep of NSImage (only to check how it works):

Code:
- (NSImage *)myMethod: (NSImage *)image
{
    int x = 0, y = 0;
    
    NSBitmapImageRep *bitmapImageRep = [[NSBitmapImageRep alloc] initWithData:[image TIFFRepresentation]];
    
    [myHelpText setIntValue:[bitmapImageRep pixelsWide]];
    [myHelpText2 setIntValue:[bitmapImageRep pixelsHigh]];
    
    NSColor *myColor = [NSColor blackColor];
    NSColor *myColor2 = [NSColor whiteColor];
    [myColor set];
    [myColor2 set];
    
    for (x=0; x<=[bitmapImageRep pixelsWide]; x++) {
        for (y=0; y<=[bitmapImageRep pixelsHigh]; y++) {
            [bitmapImageRep setColor:myColor atX:x y:y];
        }
    }
    
    [myColor release];
    [myColor2 release];
    NSImage *producedImage = [[NSImage alloc] init];
    [producedImage addRepresentation:bitmapImageRep];
    [bitmapImageRep release];
    
    return [producedImage autorelease];
}

It works BUT works slow. I can't find why it works slow. It only has to draw image to black and it takes 2 or 3 seconds to color whole image. And there are 2 more things about this. Method getPixel:atX:y:. In getPixel: should be passed NSInteger but I can't find anywhere nothing about this. As well as bitmapData. I can't find how to use this methods and when I try to use them I always get an error.

Then I tried to use CoreGraphics to redraw image from one NSImageView to second:

Code:
- (NSImage *)myMethod: (NSImage *)image
{
    NSBitmapImageRep *bitmapImageRep = [[NSBitmapImageRep alloc] initWithData:[image TIFFRepresentation]];
    int width = [bitmapImageRep pixelsWide];
    int height = [bitmapImageRep pixelsHigh];
    
    CGRect myBox;
    myBox = CGRectMake(0, 0, width, height);
    
    [myHelpText setIntValue:[bitmapImageRep pixelsWide]];
    [myHelpText2 setIntValue:[bitmapImageRep pixelsHigh]];
    
    CGContextRef myBitmapContext = MyCreateBitmapContext(width, height);
    
    [bitmapImageRep release];
    
    CGImageRef myImage = CGBitmapContextCreateImage(myBitmapContext);
    NSBitmapImageRep *bitmapRep = [[NSBitmapImageRep alloc] initWithCGImage:myImage];
    // Create an NSImage and add the bitmap rep to it...
    NSImage *producedImage = [[NSImage alloc] init];
    [producedImage addRepresentation:bitmapRep];
    
    [bitmapRep release];
    CGContextRelease(myBitmapContext);
    CGImageRelease(myImage);
    
    return [producedImage autorelease];
}

where MyCreateBitmapContext(width, height) is a routine from Apple's guide:

Code:
CGContextRef MyCreateBitmapContext (int pixelsWide, int pixelsHigh)
{
    CGContextRef myContext = NULL;
    CGColorSpaceRef myColorSpace;
    void *bitmapData;
    int bitmapByteCount;
    int bitmapBytesPerRow;
    
    bitmapBytesPerRow = (pixelsWide * 4);
    bitmapByteCount = (bitmapBytesPerRow * pixelsHigh);
    
    myColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
    
    bitmapData  = malloc(bitmapByteCount);
    
    if (bitmapData == NULL) {
        fprintf(stderr, "Memory not Allocated");
        return NULL;
    }
    
    myContext = CGBitmapContextCreate(bitmapData,
                                      pixelsWide,
                                      pixelsHigh,
                                      8,
                                      bitmapBytesPerRow,
                                      myColorSpace,
                                      kCGImageAlphaNone);
    if (myContext == NULL) {
        free(bitmapData);
        fprintf(stderr, "myContext not created");
        return NULL;
    }
    
    CGColorSpaceRelease(myColorSpace);
    
    return myContext;
}

And that code doesn't work as well. The only thing it does is color second NSImageView to black. And I don't know how to get to pixels of image.

I will be glad if someone will tell me what I'm doing wrong. I'm beginner in this so please don't write "google is your master". Smile Thanks in advise Smile
Quote this message in a reply
Sage
Posts: 1,482
Joined: 2002.09
Post: #2
There are at least 2 method calls per pixel there, and probably a lot more. That is almost certainly dwarfing the cost of setting an individual pixel by an order of magnitude. Your best bet is probably to copy the bitmap out of the bitmap rep, modify it using a pure C for loop, then copy it back into the bitmap rep.

Also, if you are just setting the whole thing to a solid color, why not just use a fill rect?

Scott Lembcke - Howling Moon Software
Author of Chipmunk Physics - A fast and simple rigid body physics library in C.
Quote this message in a reply
Apprentice
Posts: 8
Joined: 2010.07
Post: #3
(Jul 28, 2010 06:09 AM)Skorche Wrote:  There are at least 2 method calls per pixel there, and probably a lot more.

getPixel:atX:y: and bitmapData. But in first I don't know what to pass as getPixel: (NSInteger)p. And second is not clear enough as well. On some pages people write something like this:

unsigned char *data = [bitmapImageRep bitmapData];

what is strange for me...

(Jul 28, 2010 06:09 AM)Skorche Wrote:  That is almost certainly dwarfing the cost of setting an individual pixel by an order of magnitude. Your best bet is probably to copy the bitmap out of the bitmap rep, modify it using a pure C for loop, then copy it back into the bitmap rep.

Hmmm that is why I wrote my post here - I don't really know how to do that. I mean - when creating a bitmap context there is a buffer. But whole issue is to get into this buffer. As I wrote in my last post - I am new to image processing and maybe some problems are easy to solve, but for me everything is strange and unfriendly...

(Jul 28, 2010 06:09 AM)Skorche Wrote:  Also, if you are just setting the whole thing to a solid color, why not just use a fill rect?

It was only to check the code. Finally it should check color of pixel and decide - change color or go to next pixel.
Quote this message in a reply
Moderator
Posts: 1,560
Joined: 2003.10
Post: #4
(Jul 28, 2010 07:28 AM)g00se Wrote:  
(Jul 28, 2010 06:09 AM)Skorche Wrote:  That is almost certainly dwarfing the cost of setting an individual pixel by an order of magnitude. Your best bet is probably to copy the bitmap out of the bitmap rep, modify it using a pure C for loop, then copy it back into the bitmap rep.

Hmmm that is why I wrote my post here - I don't really know how to do that. I mean - when creating a bitmap context there is a buffer. But whole issue is to get into this buffer. As I wrote in my last post - I am new to image processing and maybe some problems are easy to solve, but for me everything is strange and unfriendly...

NSImage definitely doesn't make it obvious how this can be done. Here's my method:

Code:
unsigned char * NSImageRepToRGBAPixelArray(NSImageRep * bitmap, int targetWidth, int targetHeight) {
    unsigned char * pixels;
    NSBitmapImageRep * bitmap2;
    NSGraphicsContext * context;
    
    pixels = malloc(4 * targetWidth * targetHeight);
    
    bitmap2 = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: &pixels
                                                      pixelsWide: targetWidth
                                                      pixelsHigh: targetHeight
                                                   bitsPerSample: 8
                                                 samplesPerPixel: 4
                                                        hasAlpha: YES
                                                        isPlanar: NO
                                                  colorSpaceName: NSDeviceRGBColorSpace
                                                    bitmapFormat: 0
                                                     bytesPerRow: (targetWidth * 4)
                                                    bitsPerPixel: 32];
    
    context = [NSGraphicsContext graphicsContextWithBitmapImageRep: bitmap2];
    [NSGraphicsContext saveGraphicsState];
    [NSGraphicsContext setCurrentContext: context];
    [bitmap drawInRect: NSMakeRect(0, 0, targetWidth, targetHeight)];
    [NSGraphicsContext restoreGraphicsState];
    
    [bitmap2 release];
    
    return pixels;
}

If you want to turn it back into an NSImage afterward, you could keep bitmap2 around and use it after modifying pixels directly.
Quote this message in a reply
Sage
Posts: 1,482
Joined: 2002.09
Post: #5
I'd argue that it doesn't really make it obvious when you have to use several classes to get the bitmap data into your own buffer. Rasp

Scott Lembcke - Howling Moon Software
Author of Chipmunk Physics - A fast and simple rigid body physics library in C.
Quote this message in a reply
Moderator
Posts: 1,560
Joined: 2003.10
Post: #6
(Jul 28, 2010 08:22 AM)Skorche Wrote:  I'd argue that it doesn't really make it obvious when you have to use several classes to get the bitmap data into your own buffer. Rasp

Whoa, left out a crucial word. Fixed.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  OpenGL Pixel Buffer Object setup issue dotbianry 2 3,225 Jan 6, 2013 11:03 AM
Last Post: dotbianry
  Changing Pixel Values using CG LIPH700 1 4,575 Nov 25, 2010 03:17 PM
Last Post: SethWillits
  ? Find color value of 'pixel' in color buffer? Elphaba 1 4,494 Jul 22, 2009 01:23 PM
Last Post: Bachus
  per pixel image manipulation (for iphone) oldmanwinter 1 3,646 Jul 17, 2009 12:15 PM
Last Post: unknown
  2D Pixel Collision Detection using OCCLUSION Elphaba 0 3,355 Jun 8, 2009 06:30 AM
Last Post: Elphaba