2011-12-19 163 views
10

同时使用OpenCV我需要将NSImage转换为OpenCV多通道2D矩阵(cvMat),反之亦然。NSImage cv :: Mat,反之亦然

这样做的最佳方法是什么?

映入眼帘,
大教堂

+0

这真的很有帮助,但这不是实现它的方法。你需要调整你的材料到Q&A格式,所以先问问题然后回答。 – karlphillip 2011-12-19 18:52:52

+0

啊,好的。编辑我的第一篇文章,并用我的解决方案回答:) – dom 2011-12-19 18:56:32

回答

23

这里是我的结果,这工作得很好。

NSImage中+ OpenCV.h:

// 
// 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 

NSImage中+ OpenCV.mm:

// 
// 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 

用法示例:

只需导入这样的:

#import "NSImage+OpenCV.h" 

而且使用这样的:

cv::Mat cvMat_test; 
NSImage *image = [NSImage imageNamed:@"test.jpg"]; 
cvMat_test = [image CVMat]; 
[myImageView setImage:[NSImage imageWithCVMat:cvMat_test]]; 
+0

+1 Super。谢谢(你的)信息。 – karlphillip 2011-12-19 19:03:57

+1

有一些内存泄漏。需要释放[self CGImage],将其设置为一个变量,然后释放CVmat和CVGrayscaleMat中的CGContextDrawImage。 CGImageRef imageRef = [self CGImage]; ... CGImageRelease(imageRef); – 2012-01-11 17:41:39

+0

@PaulSolt感谢您的信息!修正了:) – dom 2012-01-12 13:26:59

1

在 - (ID)initWithCVMat:(常量CV ::垫&)CvMat中,你不应该被加入表示自我,而不是新NSImage中?

-(id)initWithCVMat:(const cv::Mat *)iMat 
{ 
    if(self = [super init]) { 
     NSData *tData = [NSData dataWithBytes:iMat->data length:iMat->elemSize() * iMat->total()]; 

     CGColorSpaceRef tColorSpace; 

     if(iMat->elemSize() == 1) { 
      tColorSpace = CGColorSpaceCreateDeviceGray(); 
     } else { 
      tColorSpace = CGColorSpaceCreateDeviceRGB(); 
     } 

     CGDataProviderRef tProvider = CGDataProviderCreateWithCFData((CFDataRef) tData); 

     CGImageRef tImage = CGImageCreate(
      iMat->cols, 
      iMat->rows, 
      8, 
      8 * iMat->elemSize(), 
      iMat->step[0], 
      tColorSpace, 
      kCGImageAlphaNone | kCGBitmapByteOrderDefault, 
      tProvider, 
      NULL, 
      false, 
      kCGRenderingIntentDefault); 

     NSBitmapImageRep *tBitmap = [[NSBitmapImageRep alloc] initWithCGImage:tImage]; 
     [self addRepresentation:tBitmap]; 
     [tBitmap release]; 

     CGImageRelease(tImage); 
     CGDataProviderRelease(tProvider); 
     CGColorSpaceRelease(tColorSpace); 
    } 
    return self; 
}