web-dev-qa-db-ja.com

UIImageをcv :: Matに変換する

IPhoneカメラからキャプチャした写真であるUIImageがあります。UIImageをcv :: Mat(OpenCV)に変換したいと思います。これを実現するために、次のコード行を使用しています。

-(cv::Mat)CVMat
{

CGColorSpaceRef colorSpace = CGImageGetColorSpace(self.CGImage);
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), self.CGImage);
CGContextRelease(contextRef);

return cvMat;
}

このコードは、横向きモードのUIImageで正常に機能しますが、縦向きモードで取得した画像で同じコードを使用すると、画像が右に90度回転します。

私はiOSとObjectiveCの初心者なので、何が悪いのか理解できません。

誰かがコードの何が問題なのか教えてもらえますか、それとも私は何かを見逃していますか?.

31
user1606191

これは、UIImageが実際には縦向きではないためです。 iPhoneカメラで撮影されたすべての写真は、生のビットマップ状態の風景です。たとえば、幅3264x高さ2488です。 「ポートレート」写真は、画像に設定された向きEXIFフラグによってそのように表示されます。これは、たとえば、このフラグとカメラの表示方向に従って画像を回転させるフォトライブラリアプリによって尊重されます。

このフラグは、UIImageが幅と高さのプロパティを報告する方法にも影響し、縦向きとしてフラグが付けられた画像のビットマップ値からそれらを置き換えます。

cv::Matはそれを気にしません。つまり、(i)cv::Matに変換すると、ポートレート画像のsize.widthsize.heightの値が転置され、(ii)cv::Matから変換し直すと次のようになります。オリエンテーションフラグを失いました。

UIImageからcv::Matに移動するときにこれを処理する最も簡単な方法は、画像に縦向きのフラグが付けられている場合に幅と高さの値を交換することです。

if  (self.imageOrientation == UIImageOrientationLeft
      || self.imageOrientation == UIImageOrientationRight) {
         cols = self.size.height;
         rows = self.size.width;
    }

cv::MatからUIImageに変換し直すときは、方向フラグを元に戻す必要があります。 cv::Mat -> UIImageコードに次のものが含まれていると仮定します。

self = [self initWithCGImage:imageRef];

代わりにこの方法を使用して、元の方向をリセットすることができます。

self = [self initWithCGImage:imageRef scale:1 orientation:orientation];
43
foundry

ネイティブのOpenCV関数を使用して前後に変換することを検討する必要があります。

#import <opencv2/imgcodecs/ios.h>
...
UIImage* MatToUIImage(const cv::Mat& image);
void UIImageToMat(const UIImage* image,
                         cv::Mat& m, bool alphaExist = false);

注:UIImageがカメラからのものである場合、OpenCVはExifデータを考慮しないため、cv::Matに変換する前に、それを「正規化」する必要があります(アップロード後のiOS UIImagePickerController結果画像の向き)。そうしないと、結果の方向が間違ってしまうはずです。

10
cdescours