Подтвердить что ты не робот

Преобразование UIImage в cv:: Mat

У меня есть UIImage, который является снимком, снятым с iPhone-камеры, теперь я хочу, чтобы 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 и Objective C, и поэтому я не могу понять, что не так.

Может кто-нибудь сказать мне, что не так с кодом, или я что-то упускаю.

4b9b3361

Ответ 1

Это будет потому, что UIImage на самом деле не портрет. Все снимки, сделанные с помощью камеры iPhone, - это пейзаж в исходном растровом состоянии, например, 3264 широкоформатный x 2488. Фотография "портрет" отображается как таковая с помощью флага EXIF, установленного на изображении, которое выполняется, например, приложением фотобиблиотеки, которое поворачивает изображения в соответствии с этим флагом и ориентацией просмотра камеры.

Флаг также влияет на то, как UIImage сообщает свои свойства ширины и высоты, перенося их из своих значений растрового изображения для изображений, помеченных как портрет.

cv::Mat не беспокоится об этом. Это означает, что (i) при переводе на cv::Mat портретное изображение будет иметь транспонированные значения size.width и size.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];

вы можете использовать этот метод, а reset ориентацию в соответствии с оригиналом.

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

Ответ 2

Вы должны использовать собственные функции OpenCV для преобразования вперед и назад:

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

Примечание. Если ваш UIImage поступает с камеры, вы должны "нормализовать" его (ориентацию изображения изображения iOS UIImagePickerController после загрузки) перед преобразованием в cv::Mat, так как OpenCV не учитывает данные Exif. Если вы этого не сделаете, результат должен быть разориентирован.