First a disclaimer: I am new to Cocoa and Objective-C. I am trying to learn to work with Cocoa and Opencv so I don't have to deal with Qt. So if there is an easier way to achieve what I'm trying to do, I'd welcome any tips on that. Also, if this question has already been answered, I'd appreciate it if someone could point me to the answer.
I am using the code from this post: NSImage to cv::Mat and vice versa to try and convert my NSImage to Mat and back. The problem is, I always end up getting the unrecognized selector sent to instance error. As far as I understand this error, it is thrown when a method that doesn't exist is called. But the method that I am calling does exist. I'm at a complete loss here and would really appreciate some help in dumbed down words. The code is exactly the same as the post linked to above. The error is as follows:
2013-08-15 17:24:52.554 Espejismo[2368:303] -[NSImage CVMat]: unrecognized selector sent to instance 0x1020198d0
2013-08-15 17:25:03.969 Espejismo[2368:303] An uncaught exception was raised
2013-08-15 17:25:03.969 Espejismo[2368:303] -[NSImage CVMat]: unrecognized selector sent to instance 0x1020198d0
2013-08-15 17:25:03.972 Espejismo[2368:303] (
0 CoreFoundation 0x00007fff93e76b06 __exceptionPreprocess + 198
1 libobjc.A.dylib 0x00007fff97e0c3f0 objc_exception_throw + 43
2 CoreFoundation 0x00007fff93f0d40a -[NSObject(NSObject) doesNotRecognizeSelector:] + 186
3 CoreFoundation 0x00007fff93e6502e ___forwarding___ + 414
4 CoreFoundation 0x00007fff93eaadad __forwarding_prep_1___ + 237
5 Espejismo 0x000000010000209e -[TestView initWithFrame:] + 334
6 AppKit 0x00007fff942c00a3 -[NSCustomView nibInstantiateWithObjectInstantiator:] + 657
7 AppKit 0x00007fff9429f625 -[NSIBObjectData instantiateObject:] + 266
8 AppKit 0x00007fff9429edf7 -[NSIBObjectData nibInstantiateWithOwner:topLevelObjects:] + 337
9 AppKit 0x00007fff9427e11d loadNib + 317
10 AppKit 0x00007fff9427d649 +[NSBundle(NSNibLoading) _loadNibFile:nameTable:withZone:ownerBundle:] + 219
11 AppKit 0x00007fff9427d47e -[NSBundle(NSNibLoading) loadNibNamed:owner:topLevelObjects:] + 200
12 AppKit 0x00007fff9427d25e +[NSBundle(NSNibLoading) loadNibNamed:owner:] + 360
13 AppKit 0x00007fff942799ff NSApplicationMain + 398
14 Espejismo 0x0000000100001112 main + 34
15 libdyld.dylib 0x00007fff922077e1 start + 0
)
2013-08-15 17:25:03.973 Espejismo[2368:303] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSImage CVMat]: unrecognized selector sent to instance 0x1020198d0'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff93e76b06 __exceptionPreprocess + 198
1 libobjc.A.dylib 0x00007fff97e0c3f0 objc_exception_throw + 43
2 CoreFoundation 0x00007fff93f0d40a -[NSObject(NSObject) doesNotRecognizeSelector:] + 186
3 CoreFoundation 0x00007fff93e6502e ___forwarding___ + 414
4 CoreFoundation 0x00007fff93eaadad __forwarding_prep_1___ + 237
5 Espejismo 0x000000010000209e -[TestView initWithFrame:] + 334
6 AppKit 0x00007fff942c00a3 -[NSCustomView nibInstantiateWithObjectInstantiator:] + 657
7 AppKit 0x00007fff9429f625 -[NSIBObjectData instantiateObject:] + 266
8 AppKit 0x00007fff9429edf7 -[NSIBObjectData nibInstantiateWithOwner:topLevelObjects:] + 337
9 AppKit 0x00007fff9427e11d loadNib + 317
10 AppKit 0x00007fff9427d649 +[NSBundle(NSNibLoading) _loadNibFile:nameTable:withZone:ownerBundle:] + 219
11 AppKit 0x00007fff9427d47e -[NSBundle(NSNibLoading) loadNibNamed:owner:topLevelObjects:] + 200
12 AppKit 0x00007fff9427d25e +[NSBundle(NSNibLoading) loadNibNamed:owner:] + 360
13 AppKit 0x00007fff942799ff NSApplicationMain + 398
14 Espejismo 0x0000000100001112 main + 34
15 libdyld.dylib 0x00007fff922077e1 start + 0
)
libc++abi.dylib: terminate called throwing an exception
This error is always thrown at the following line:
cvMat_test = [image CVMat];
Which I'm assuming means that Xcode can't see the CVMat method. However, Xcode lists that method as one of the methods for the project when I try to call it with an instance of NSImage.
Here's the code from the other post that I linked to in case people don't want to click over:
//
// NSImage+OpenCV.h
//
#import <AppKit/AppKit.h>
@interface NSImage (NSImage_OpenCV) {
}
+(NSImage*)imageWithCVMat:(const cv::Mat&)cvMat;
-(id)initWithCVMat:(const cv::Mat&)cvMat;
@property(nonatomic, readonly) cv::Mat CVMat;
@property(nonatomic, readonly) cv::Mat CVGrayscaleMat;
@end
The implementation file:
//
// NSImage+OpenCV.mm
//
#import "NSImage+OpenCV.h"
static void ProviderReleaseDataNOP(void *info, const void *data, size_t size)
{
return;
}
@implementation NSImage (NSImage_OpenCV)
-(CGImageRef)CGImage
{
CGContextRef bitmapCtx = CGBitmapContextCreate(NULL/*data - pass NULL to let CG allocate the memory*/,
[self size].width,
[self size].height,
8 /*bitsPerComponent*/,
0 /*bytesPerRow - CG will calculate it for you if it's allocating the data. This might get padded out a bit for better alignment*/,
[[NSColorSpace genericRGBColorSpace] CGColorSpace],
kCGBitmapByteOrder32Host|kCGImageAlphaPremultipliedFirst);
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:bitmapCtx flipped:NO]];
[self drawInRect:NSMakeRect(0,0, [self size].width, [self size].height) fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0];
[NSGraphicsContext restoreGraphicsState];
CGImageRef cgImage = CGBitmapContextCreateImage(bitmapCtx);
CGContextRelease(bitmapCtx);
return cgImage;
}
-(cv::Mat)CVMat
{
CGImageRef imageRef = [self CGImage];
CGColorSpaceRef colorSpace = CGImageGetColorSpace(imageRef);
CGFloat cols = self.size.width;
CGFloat rows = self.size.height;
cv::Mat cvMat(rows, cols, CV_8UC4); // 8 bits per component, 4 channels
CGContextRef contextRef = CGBitmapContextCreate(cvMat.data, // Pointer to backing data
cols, // Width of bitmap
rows, // Height of bitmap
8, // Bits per component
cvMat.step[0], // Bytes per row
colorSpace, // Colorspace
kCGImageAlphaNoneSkipLast |
kCGBitmapByteOrderDefault); // Bitmap info flags
CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), imageRef);
CGContextRelease(contextRef);
CGImageRelease(imageRef);
return cvMat;
}
-(cv::Mat)CVGrayscaleMat
{
CGImageRef imageRef = [self CGImage];
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
CGFloat cols = self.size.width;
CGFloat rows = self.size.height;
cv::Mat cvMat = cv::Mat(rows, cols, CV_8UC1); // 8 bits per component, 1 channel
CGContextRef contextRef = CGBitmapContextCreate(cvMat.data, // Pointer to backing data
cols, // Width of bitmap
rows, // Height of bitmap
8, // Bits per component
cvMat.step[0], // Bytes per row
colorSpace, // Colorspace
kCGImageAlphaNone |
kCGBitmapByteOrderDefault); // Bitmap info flags
CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), imageRef);
CGContextRelease(contextRef);
CGColorSpaceRelease(colorSpace);
CGImageRelease(imageRef);
return cvMat;
}
+ (NSImage *)imageWithCVMat:(const cv::Mat&)cvMat
{
return [[[NSImage alloc] initWithCVMat:cvMat] autorelease];
}
- (id)initWithCVMat:(const cv::Mat&)cvMat
{
NSData *data = [NSData dataWithBytes:cvMat.data length:cvMat.elemSize() * cvMat.total()];
CGColorSpaceRef colorSpace;
if (cvMat.elemSize() == 1)
{
colorSpace = CGColorSpaceCreateDeviceGray();
}
else
{
colorSpace = CGColorSpaceCreateDeviceRGB();
}
CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
CGImageRef imageRef = CGImageCreate(cvMat.cols, // Width
cvMat.rows, // Height
8, // Bits per component
8 * cvMat.elemSize(), // Bits per pixel
cvMat.step[0], // Bytes per row
colorSpace, // Colorspace
kCGImageAlphaNone | kCGBitmapByteOrderDefault, // Bitmap info flags
provider, // CGDataProviderRef
NULL, // Decode
false, // Should interpolate
kCGRenderingIntentDefault); // Intent
NSBitmapImageRep *bitmapRep = [[NSBitmapImageRep alloc] initWithCGImage:imageRef];
NSImage *image = [[NSImage alloc] init];
[image addRepresentation:bitmapRep];
CGImageRelease(imageRef);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorSpace);
return image;
}
@end
My use of the file:
#import "AppDelegate.h"
@implementation AppDelegate
- (void)dealloc
{
[super dealloc];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
cv::Mat cvMat_test;
NSImage *image = [NSImage imageNamed:@"image.jpg"];
cvMat_test = [image CVMat];
}
@end
PS: I have tried adding the "-all_load" to "Other Linker Flags" but that doesn't work either.