如何在 iOS 工程中使用 OpenCV?

Forelax發表於2015-08-17

前言

OpenCV ,是一個開源的跨平臺計算機視覺和機器學習庫,通俗點的說,就是他給計算機提供了一雙眼睛,一雙可以從圖片中獲取資訊的眼鏡,從而完成人臉識別、去紅眼、追蹤移動物體等等的影像相關的功能。更多具體的說明可參見 OpenCV 官網

匯入工程

匯入 OpenCV 到 Xcode 的工程中還是比較簡單的,從官網下載對應的 framework,直接丟到 Xcode 的工程中,然後在你想用 OpenCV 的地方引入 OpenCV 的標頭檔案:

#import <opencv2/opencv.hpp>

或者直接在 PCH 檔案中新增:

#ifdef __cplusplus
#import <opencv2/opencv.hpp>
#endif

然後把使用到 OpenCV 中 C++方法的實現檔案字尾名改成.mm,就可以開始使用 OpenCV 的方法了。看起來很簡單,對吧?確實是很簡單,不過這裡有一些要注意的地方,我們一起來看看。

實際問題

首先說明,為何在 PCH 檔案中引入 OpenCV 的標頭檔案我們需要多加#ifdef __cpluseplus這一部分呢?這是因為 PCH 檔案是一個會被所有的檔案引入的標頭檔案,而我們又希望 #import <opencv2/opencv.hpp>這部分只會被一些 C++實現檔案編譯,所以我們加上#ifdef __cpluseplus來表示這是 C++ 檔案才會編譯的,除了#ifdef __cpluseplus,還有#ifdef __OBJC__這樣的巨集來說明編譯規則(按照 OC 檔案編譯),這樣的巨集多出現於一些會被多種型別的實現檔案引用的標頭檔案中。

另外注意另一個問題:如果一個標頭檔案是C++型別的標頭檔案,那麼一定要保證所有直接或者間接引用這個標頭檔案的實現檔案都要為.mm或者.cpp,否則 Xcode 就不會把這個標頭檔案當做 C++標頭檔案來編譯,就會出現最基本的#include <iostream>這種引用都會報出file not found 這樣的編譯錯誤的問題。我在編譯的過程中,某個C++標頭檔案 A.h 被 B.h 引用,然後 B.h 又被 C.m 引用,雖然 B 的實現檔案是 B.mm ,但是仍然報出了之前說的那個錯誤, 感謝 StackOberflow 讓我找到了問題發生的原因。所以對於 C++ 標頭檔案的引用一定要注意,但凡是引用了 A.h 的實現部分,都必須是.mm或者.cpp字尾名。(同時我們也可以知道,Xcode 是根據標頭檔案被引用的情況來判定標頭檔案的編譯 型別的)。

轉換 UIImage 和 cv::Mat

在 OpenCV 中同常用 cv::Mat 表示圖片,而 iOS 中則是 UIImage 來表示圖片,因此我們就需要一些轉換的方法,OpenCV 的官方教程中給吃了轉換的方法,這裡摘錄如下:

UIImage To cv::Mat:

- (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 (color channels + alpha)

  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);

  return cvMat;
}

cv::Mat To 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);

  // 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
                                     kCGImageAlphaNone|kCGBitmapByteOrderDefault,// 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;
 }

相關文章