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