update: ended up not using the old version so i didn’t look at it much. today i needed it again and realized it was crap so i rewrote it and here’s a new, (hopefully) less buggy version that also handles single-channel images.
Through work i found the need to display an IplImage in cocoa.. after looking at CVOCV, James Hurleys example and a couple of other snippets i remembered that opencv can be built for iOS and started browsing through the source. Inside OpenCV-2.4.1/modules/highgui/src/window_cocoa.mm i found what i was looking for (CVView). i rewrote it a bit and came up with the following NSView subclass:
MyCVView.h
#import <Foundation/Foundation.h> #import <opencv/cv.h> @interface MyCVView : NSView { @private NSBitmapImageRep *bm; @public } @property (nonatomic, retain) NSImage *image; - (void)setImagePrefs:(int)width // image width height:(int)height // " height channels:(int)nChannels // usually 4/3/1 (rgba/rgb/monochrome) widthStep:(int)widthStep // usually width*nChannels depth:(int)depth; - (void)setImageData:(IplImage *)img releaseWhenCopied:(BOOL)shouldRelease; - (void)drawRect:(NSRect)rect; @end
MyCVView.m:
#import "MyCVView.h" @implementation MyCVView @synthesize image; - (id)init { if((self = [super init])) [self setCanDrawConcurrently:true]; return self; } - (void)setImagePrefs:(int)width height:(int)height channels:(int)nChannels widthStep:(int)widthStep depth:(int)depth { if(bm) [bm release]; bm = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL pixelsWide: width pixelsHigh: height bitsPerSample: 8 // i only use IPL_DEPTH_8U, if you use others set it as an arg.. samplesPerPixel: nChannels hasAlpha: NO isPlanar: NO colorSpaceName: (nChannels == 1 ? NSDeviceWhiteColorSpace : NSDeviceRGBColorSpace) bytesPerRow: widthStep bitsPerPixel: nChannels*depth]; if(image) [image release]; image = [[NSImage alloc] init]; [image addRepresentation:bm]; } - (void)setImageData:(IplImage *)img releaseWhenCopied:(BOOL)shouldRelease { if(!bm) [self setImagePrefs:img->width height:img->height channels:img->nChannels widthStep:img->widthStep depth:img->depth]; unsigned char *src = (unsigned char *)img->imageData; unsigned char *dst = [bm bitmapData]; if(img->nChannels == 1) memcpy(dst, src, (img->width*img->height)); else { // red-blue swap and flip incorporated for(int i = img->width * img->height - 1; i >= 0; i--) { dst[i * 4 + 0] = src[2]; dst[i * 4 + 1] = src[1]; dst[i * 4 + 2] = src[0]; src += ((CvMat *)img)->step - ((CvMat *)img)->cols; } } [self setNeedsDisplay:YES]; if(shouldRelease) cvReleaseImage(&img); } - (void)drawRect:(NSRect)rect { [super drawRect:rect]; // autoscaling - if you don't want it replace second part with comment below NSRect ir = {{0,0}, {[self bounds].size.width, [self bounds].size.height}}; // {[image size].width, [image size].height}}; if(image != nil) { [image drawInRect: ir fromRect: NSZeroRect operation: NSCompositeSourceOver fraction: 1.0]; } } - (void)dealloc { if(bm) [bm release]; if(image) [image release]; [super dealloc]; } @end
This was built without warnings in xcode 4 on snow leopard and saves some cpu-cycles compared to the original version since the rb-swap and flip is stuffed into a the data-copy-loop. Note that you should not call it CVView (will cause a crash) since opencv already contains a class with that name.
// sluggo