2013年4月25日 星期四

OpenCV on iOS with Xcode - Convert between UIImage, CvMat and IplImage

通常IplImage和CvMat 在 3 channels下的data,都是以BGR的方式排列的

例:

IplImage *img = cvCreateImage(cvSize(imageWidth, imageHeight),IPL_DEPTH_8U,3);
cv::Mat imgMat(imagewidth, imageHeight, CV_8UC3);

for (int j=0; j<imageHeight; j++){
    for (int i=0; i<imageWidth; i++){

        imgMat.data[j*imgMat.widthStep+i*imgMat.nChannels] = img->imageData[j*img->widthStep+i*nChannels];        // B
        imgMat.data[j*imgMat.widthStep+i*imgMat.nChannels+1] = img->imageData[j*img->widthStep+i*nChannels+1];    // G
        imgMat.data[j*imgMat.widthStep+i*imgMat.nChannels+2] = img->imageData[j*img->widthStep+i*nChannels+2];    // R    
    }
}

正常來說應該是這樣子把IplImage的資料複製到cvMat底下,但我經過了以上的code後,在把cvMat透過以下的方式轉到UIImage,卻發現顏色跑掉。

+(UIImage *)UIImageFromCVMat:(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);
    bool alpha = cvMat.channels() == 4;
    CGBitmapInfo bitMapInfo = (alpha ? kCGImageAlphaLast : kCGImageAlphaNone) | kCGBitmapByteOrderDefault;
    // Creating CGImage from cv::Mat
    CGImageRef imageRef = CGImageCreate(cvMat.cols,                                 //width
                                        cvMat.rows,                                 //height
                                        8,                                          //bits per component
                                        8 * cvMat.elemSize(),                       //bits per pixel
                                        cvMat.step[0],                            //bytesPerRow
                                        colorSpace,                                 //colorspace
                                        bitMapInfo,                                 // bitmap info
                                        provider,                                   //CGDataProviderRef
                                        NULL,                                       //decode
                                        false,                                      //should interpolate
                                        kCGRenderingIntentDefault                   //intent
                                        );
    
    
    // Getting UIImage from CGImage
    UIImage *finalImage = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);
    CGDataProviderRelease(provider);
    CGColorSpaceRelease(colorSpace);
    
    return finalImage; 
}


所以我試著把BGR的位置對調,如下:

IplImage *img = cvCreateImage(cvSize(imageWidth, imageHeight),IPL_DEPTH_8U,3);
cv::Mat imgMat(imagewidth, imageHeight, CV_8UC3);

for (int j=0; j<imageHeight; j++){
    for (int i=0; i<imageWidth; i++){

        imgMat.data[j*imgMat.widthStep+i*imgMat.nChannels+2] = img->imageData[j*img->widthStep+i*nChannels];      // B 
        imgMat.data[j*imgMat.widthStep+i*imgMat.nChannels+1] = img->imageData[j*img->widthStep+i*nChannels+1];    // G
        imgMat.data[j*imgMat.widthStep+i*imgMat.nChannels] = img->imageData[j*img->widthStep+i*nChannels+2];      // R    
    }
}

是的! 可能是CvMat 轉UIImage時,把他看成RGB的排列方式轉了吧!?(不確定)

但使用上列方式,顏色就正常了。

最後在丟一些convert的code:

UIImage to CvMat:

+ (cv::Mat)cvMatFromUIImage:(UIImage *)image
{
    CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
    CGFloat cols = image.size.width;
    CGFloat rows = image.size.height;
    
    cv::Mat cvMat(rows, cols, CV_8UC4); // 8 bits per component, 4 channels
    
    CGContextRef contextRef = CGBitmapContextCreate(cvMat.data,                 // Pointer to  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), image.CGImage);
    CGContextRelease(contextRef);
   // CGColorSpaceRelease(colorSpace);
    
    return cvMat;
}



+ (cv::Mat)cvMatGrayFromUIImage:(UIImage *)image
{
 cv::Mat cvMat = [UIImageCVMatConverter cvMatFromUIImage:image];
 cv::Mat grayMat;
 
    if (cvMat.channels() == 1) {
        grayMat = cvMat;
 } else{
        grayMat = cv :: Mat(cvMat.rows,cvMat.cols, CV_8UC1);
        cv::cvtColor(cvMat, grayMat, CV_BGR2GRAY);
    }
 return grayMat;
}

UIImage to IplImage:
// NOTE you SHOULD cvReleaseImage() for the return value when end of the code.
- (IplImage *)CreateIplImageFromUIImage:(UIImage *)image {
  // Getting CGImage from UIImage
  CGImageRef imageRef = image.CGImage;

  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  // Creating temporal IplImage for drawing
  IplImage *iplimage = cvCreateImage(
    cvSize(image.size.width,image.size.height), IPL_DEPTH_8U, 4
  );
  // Creating CGContext for temporal IplImage
  CGContextRef contextRef = CGBitmapContextCreate(
    iplimage->imageData, iplimage->width, iplimage->height,
    iplimage->depth, iplimage->widthStep,
    colorSpace, kCGImageAlphaPremultipliedLast|kCGBitmapByteOrderDefault
  );
  // Drawing CGImage to CGContext
  CGContextDrawImage(
    contextRef,
    CGRectMake(0, 0, image.size.width, image.size.height),
    imageRef
  );
  CGContextRelease(contextRef);
  CGColorSpaceRelease(colorSpace);

  // Creating result IplImage
  IplImage *ret = cvCreateImage(cvGetSize(iplimage), IPL_DEPTH_8U, 3);
  cvCvtColor(iplimage, ret, CV_RGBA2BGR);
  cvReleaseImage(&iplimage);

  return ret;
}

IplImage to UIImage:
// NOTE You should convert color mode as RGB before passing to this function
- (UIImage *)UIImageFromIplImage:(IplImage *)image {
  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  // Allocating the buffer for CGImage
  NSData *data =
    [NSData dataWithBytes:image->imageData length:image->imageSize];
  CGDataProviderRef provider =
    CGDataProviderCreateWithCFData((CFDataRef)data);
  // Creating CGImage from chunk of IplImage
  CGImageRef imageRef = CGImageCreate(
    image->width, image->height,
    image->depth, image->depth * image->nChannels, image->widthStep,
    colorSpace, kCGImageAlphaNone|kCGBitmapByteOrderDefault,
    provider, NULL, false, kCGRenderingIntentDefault
  );
  // Getting UIImage from CGImage
  UIImage *ret = [UIImage imageWithCGImage:imageRef];
  CGImageRelease(imageRef);
  CGDataProviderRelease(provider);
  CGColorSpaceRelease(colorSpace);
  return ret;
}

ref:
https://sites.google.com/site/iprinceps/Home/iphone-1/converting-images-between-uiimage-and-iplimage
http://code.opencv.org/svn/gsoc2012/ios/trunk/ImageFilter/ImageFilter/UIImageCVMatConverter.mm

沒有留言:

張貼留言