Jul 9, 2009
ImageMagick on iPhone – Xcode
UPDATE: iOS4.3 and Xcode4
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
[...] http://www.cloudgoessocial.net/2009/07/09/imagemagick-on-iphone-xcode/ [...]
hi,
Been playing around with this against OpenGL fragment shader and have a quick question i would be grateful for your assistance with as im still getting to grips with imagemagick
how can i execute the following using this library?
kk=w*0.5;
ll=h*0.5;
dx=(i-kk);
dy=(j-ll);
aa=atan2(dy,dx);
rr=hypot(dy,dx);
rs=sqrt(rr*200);
px=kk+rs*cos(aa);
py=ll+rs*sin(aa);
p{px,py}
Thanks,
Mike
HI Mike.
I’m sorry but I really cannot help you with this. Try and ask on the imagemagick forums. Regards,
~C
Thanks..
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???
Please look at the downloadable sample project.
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; }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!
hi cloud
thanks a lot for guidance
how can i store UIImage into custom location and process the file using ImageMagick.
Brilliant. It’s also short on any releasing probably :-)
@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! :)
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];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.
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!
@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!