computer, travel, movies, music, cuisine and more
ImageMagick on iPhone – Xcode
>> PLEASE READ THE UPDATE <<
UPDATE! (30.08.09)
Thanks to Jon Kean (see comments) the downloadable project now shows how to use images with unusual number of bits per component. As usual look at the defines at the top to check the functionality you want.
UPDATE! (14.07.09)
Thanks to Karl (see comments) the downloadable project now shows different ways of integrating ImageMagick with Objective-C UIImages offering both compressed methods, using JPEG compression as an example, and raw-data methods. Look at the define at the top to change which method the program will be using.
Many of you have asked me if I could post an Xcode project example to use the libraries and ImageMagick, and so, here it is.
The code is self-describing so I didn’t comment it much, it’s just a few functions call. All it does in this case is loading an image from an UIImage (Objective-C) into a format that ImageMagick (C) understands and can manipulate. Then it applies an ImageMagick filter (OrderedPosterize), which uses a configuration file (thresholds.xml).
Everything is working fine, and you have the complete set of libraries, headers and configuration files, and a configured XCode project right at hand.
If you still have any problem, don’t hesitate to contact me!
ciop ciop
| Print article | This entry was posted by Cloud on July 9, 2009 at 9:07 pm, and is filed under Code, tips & howtos. Follow any responses to this post through RSS 2.0. You can leave a response or trackback from your own site. |



about 2 weeks ago
it’s really nice tutorial but i want to run imagemagick command through the code on the image how can it be possible……
any idea???
about 1 week ago
Please look at the downloadable sample project.
about 1 year ago
the code for pulling in UIImages directly (without compression) doesn’t work for images with an unusual number of bits per component (such as 5-bit components).
Since ImageMagick doesn’t support components with arbitrary bit depth (only 8/16/32/etc), the simplest solution is to convert the UIImage into a consistent format before processing with ImageMagick.
Here is something to do just that (also fixed a leaking data provider):
CGImageRef createStandardImage(CGImageRef image) { const size_t width = CGImageGetWidth(image); const size_t height = CGImageGetHeight(image); CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); CGContextRef ctx = CGBitmapContextCreate(NULL, width, height, 8, 4*width, space, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedFirst); CGColorSpaceRelease(space); CGContextDrawImage(ctx, CGRectMake(0, 0, width, height), image); CGImageRef dstImage = CGBitmapContextCreateImage(ctx); CGContextRelease(ctx); return dstImage; } - (UIImage*) createPosterizeImage:(CGImageRef)srcCGImage { const unsigned long width = CGImageGetWidth(srcCGImage); const unsigned long height = CGImageGetHeight(srcCGImage); // could use the image directly if it has 8/16 bits per component, // otherwise the image must be converted into something more common (such as images with 5-bits per component) // here we'll be simple and always convert const char *map = "ARGB"; // hard coded const StorageType inputStorage = CharPixel; CGImageRef standardized = createStandardImage(srcCGImage); NSData *srcData = (NSData *) CGDataProviderCopyData(CGImageGetDataProvider(standardized)); CGImageRelease(standardized); const void *bytes = [srcData bytes]; MagickWandGenesis(); MagickWand *magick_wand = NewMagickWand(); MagickBooleanType status = MagickConstituteImage(magick_wand, width, height, map, inputStorage, bytes); if (status == MagickFalse) { ThrowWandException(magick_wand); } status = MagickOrderedPosterizeImage(magick_wand, "h8x8o"); if (status == MagickFalse) { ThrowWandException(magick_wand); } const int bitmapBytesPerRow = (width * strlen(map)); const int bitmapByteCount = (bitmapBytesPerRow * height); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); char *trgt_image = malloc(bitmapByteCount); status = MagickExportImagePixels(magick_wand, 0, 0, width, height, map, CharPixel, trgt_image); if (status == MagickFalse) { ThrowWandException(magick_wand); } magick_wand = DestroyMagickWand(magick_wand); MagickWandTerminus(); CGContextRef context = CGBitmapContextCreate (trgt_image, width, height, 8, // bits per component bitmapBytesPerRow, colorSpace, kCGImageAlphaPremultipliedFirst); CGColorSpaceRelease(colorSpace); CGImageRef cgimage = CGBitmapContextCreateImage(context); UIImage *image = [[UIImage alloc] initWithCGImage:cgimage]; CGImageRelease(cgimage); CGContextRelease(context); [srcData release]; free(trgt_image); return image; }about 1 year ago
Thank you for this great tip and for the supplied code. I’ve added it to the project and referenced you as usual. Thanks again!
about 1 year ago
hi cloud
thanks a lot for guidance
how can i store UIImage into custom location and process the file using ImageMagick.
about 1 year ago
Brilliant. It’s also short on any releasing probably :-)
about 1 year ago
@Karl: amazing job. I will put it into the XCode project for everyone to look at it in a ‘better formatted’ way. I will also look into some benchmarking! :)
about 1 year ago
Here we go, the final piece of the puzzle… getting the pixels back out and into a UIImage. It appears that this way we avoid any compression, so it must be quicker and non-lossy.
This code assumes that it follows the code above, and basically tries to reconstruct an image in the same format as the original. Apologies for the desperately messy code… it’s the thought that counts ;-)
CGColorSpaceRef colorSpace; int bitmapByteCount; int bitmapBytesPerRow; CGContextRef context = NULL; bitmapBytesPerRow = (width * 4); bitmapByteCount = (bitmapBytesPerRow * height); colorSpace = CGColorSpaceCreateDeviceRGB(); char *my_image = malloc(bitmapBytesPerRow * height); status = MagickExportImagePixels(magick_wand,0,0,width,height,map,CharPixel,my_image); if (status == MagickFalse) { ThrowWandException(magick_wand); } context = CGBitmapContextCreate (my_image, width, height, 8, // bits per component bitmapBytesPerRow, colorSpace, bitmapInfo); CGColorSpaceRelease( colorSpace ); CGImageRef cgimage = CGBitmapContextCreateImage(context); UIImage *image = [[UIImage alloc] initWithCGImage:cgimage];about 1 year ago
Ok, I’ve worked out how to get data into MagickWand without going through a compressed format. The “map” endianness is a hack to make it work with my current image and environments – more work required here!
NSData *srcData = (NSData*) CGDataProviderCopyData(CGImageGetDataProvider([src CGImage])); unsigned long width = src.size.width; unsigned long height = src.size.height; const void *bytes = [srcData bytes]; const char *map; CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(srcImage); if ((bitmapInfo & kCGBitmapByteOrder32Little) != 0) map = "BGRA"; // DEVICE else map = "ARGB"; // SIMULATOR status = MagickConstituteImage(magick_wand, width, height, map, CharPixel, bytes); if (status == MagickFalse) { ThrowWandException(magick_wand); }And now you have the image in the wand. To get it back out I am currently setting the format to JPG and getting it out as a JPG blob as before. Without setting the format it appears to be a raw image format that ImageMagick uses internally? Not much use.
Haven’t performance tested yet. Hope it’s an improvement – at least it’s one less compression.
about 1 year ago
Thank you very much for your efforts getting ImageMagick working on the iPhone, and for posting it here.
Do you think there is a way to get image data from a UIImage / CGImage into and out of ImageMagick without going through a JPEG or PNG encode? It’s an unfortunate and time consuming extra step in the process!
about 1 year ago
@Karl: I don’t know, but remember that ImageMagick expects the image to be a .gif, a .jpeg or a .png encoded image. Therefore I think that step is necessary. If anyone has a better solution I’ll gladly try it and see the results!