Преобразование 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, и поэтому я не могу понять, что не так.
Может кто-нибудь сказать мне, что не так с кодом, или я что-то упускаю.
Ответы
Ответ 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. Если вы этого не сделаете, результат должен быть разориентирован.