Ориентация детектора лица iOS и настройка ориентации CIImage
EDIT нашел этот код, который помог с изображениями с фронтальной камеры http://blog.logichigh.com/2008/06/05/uiimage-fix/
Надеюсь, что у других была похожая проблема, и они могут мне помочь. Пока не нашли решение. (Это может показаться немного длинным, но просто куча вспомогательного кода)
Я использую детектор лица ios на изображениях, полученных с камеры (спереди и сзади), а также на изображениях из галереи (я использую UIImagePicker
- как для захвата изображения с помощью камеры, так и для выбора изображения из галереи - без использования функции avfoundation для съемки, как в демонстрации squarecam)
Я получаю действительно испорченные координаты для обнаружения (если таковые имеются), поэтому я написал короткий метод отладки, чтобы получить границы граней, а также утилиту, которая рисует квадрат над ними, и я хотел проверить, для какой ориентации детектор работал:
#define RECTBOX(R) [NSValue valueWithCGRect:R]
- (NSArray *)detectFaces:(UIImage *)inputimage
{
_detector = \[CIDetector detectorOfType:CIDetectorTypeFace context:nil options:\[NSDictionary dictionaryWithObject:CIDetectorAccuracyLow forKey:CIDetectorAccuracy\]\];
NSNumber *orientation = \[NSNumber numberWithInt:\[inputimage imageOrientation\]\]; // i also saw code where they add +1 to the orientation
NSDictionary *imageOptions = \[NSDictionary dictionaryWithObject:orientation forKey:CIDetectorImageOrientation\];
CIImage* ciimage = \[CIImage imageWithCGImage:inputimage.CGImage options:imageOptions\];
// try like this first
// NSArray* features = \[self.detector featuresInImage:ciimage options:imageOptions\];
// if not working go on to this (trying all orientations)
NSArray* features;
int exif;
// ios face detector. trying all of the orientations
for (exif = 1; exif <= 8 ; exif++)
{
NSNumber *orientation = \[NSNumber numberWithInt:exif\];
NSDictionary *imageOptions = \[NSDictionary dictionaryWithObject:orientation forKey:CIDetectorImageOrientation\];
NSTimeInterval start = \[NSDate timeIntervalSinceReferenceDate\];
features = \[self.detector featuresInImage:ciimage options:imageOptions\];
if (features.count > 0)
{
NSString *str = \[NSString stringWithFormat:@"found faces using exif %d",exif\];
\[faceDetection log:str\];
break;
}
NSTimeInterval duration = \[NSDate timeIntervalSinceReferenceDate\] - start;
NSLog(@"faceDetection: facedetection total runtime is %f s",duration);
}
if (features.count > 0)
{
[faceDetection log:@"-I- Found faces with ios face detector"];
for(CIFaceFeature *feature in features)
{
CGRect rect = feature.bounds;
CGRect r = CGRectMake(rect.origin.x,inputimage.size.height - rect.origin.y - rect.size.height,rect.size.width,rect.size.height);
[returnArray addObject:RECTBOX(r)];
}
return returnArray;
} else {
// no faces from iOS face detector. try OpenCV detector
}
[1]
Попробовав множество разных снимков, я заметил, что ориентация детектора лиц не соответствует свойству изображения с камеры. Я сделал несколько фотографий с фронтальной камеры, где ориентация uiimage была равна 3 (запрашивала imageOrienation), но детектор лиц не находил лица для этой настройки. При прохождении всех возможностей exif, детектор лица, наконец, подбирал лица, но для другой ориентации все вместе.
Как я могу решить это? Есть ли ошибка в моем коде?
Другая проблема, с которой я столкнулся (но тесно связанная с детектором лица), когда детектор лица распознает лица, но из-за "неправильной" ориентации (это происходит в основном на фронтальной камере) UIImage
Первоначально использовались дисплеи правильно в uiiimageview, но когда я рисую квадратное наложение (я использую opencv в своем приложении, поэтому я решил преобразовать UIImage
в cvmat, чтобы нарисовать оверлей с помощью opencv) все изображение поворачивается на 90 градусов (только изображение cvmat, а не UIImage
я изначально отображается)
Я могу подумать здесь о том, что детектор лица работает с каким-то буфером (контекстом?), Который использует преобразование UIimage в opencv mat. Как я могу разделить эти буферы?
Код для преобразования uiimage в cvmat есть (из "известного" UIImage
категорию кто то сделал)
-(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;
}
- (id)initWithCVMat:(const 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);
CGImageRef imageRef = CGImageCreate(cvMat.cols, // Width
cvMat.rows, // Height
8, // Bits per component
8 * cvMat.elemSize(), // Bits per pixel
cvMat.step[0], // Bytes per row
colorSpace, // Colorspace
kCGImageAlphaNone | kCGBitmapByteOrderDefault, // Bitmap info flags
provider, // CGDataProviderRef
NULL, // Decode
false, // Should interpolate
kCGRenderingIntentDefault); // Intent
self = [self initWithCGImage:imageRef];
CGImageRelease(imageRef);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorSpace);
return self;
}
-(cv::Mat)CVRgbMat
{
cv::Mat tmpimage = self.CVMat;
cv::Mat image;
cvtColor(tmpimage, image, cv::COLOR_BGRA2BGR);
return image;
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)img editingInfo:(NSDictionary *)editInfo {
self.prevImage = img;
// self.previewView.image = img;
NSArray *arr = [[faceDetection sharedFaceDetector] detectFaces:img];
for (id r in arr)
{
CGRect rect = RECTUNBOX(r);
//self.previewView.image = img;
self.previewView.image = [utils drawSquareOnImage:img square:rect];
}
[self.imgPicker dismissModalViewControllerAnimated:YES];
return;
}
2 ответа
Я не думаю, что это хорошая идея, чтобы вращать целую кучу пикселей изображения и соответствовать CIFaceFeature. Вы можете себе представить, что перерисовка при повороте ориентации очень тяжелая. У меня была та же проблема, и я решил ее, преобразовав систему координат CIFaceFeature относительно UIImageOrientation. Я расширил класс CIFaceFeature с помощью некоторых методов преобразования, чтобы получить правильные местоположения и границы точек относительно UIImage и его UIImageView (или CALayer UIView). Полная реализация размещена здесь: https://gist.github.com/laoyang/5747004. Вы можете использовать напрямую.
Вот самое основное преобразование для точки из CIFaceFeature, возвращенный CGPoint конвертируется на основе ориентации изображения:
- (CGPoint) pointForImage:(UIImage*) image fromPoint:(CGPoint) originalPoint {
CGFloat imageWidth = image.size.width;
CGFloat imageHeight = image.size.height;
CGPoint convertedPoint;
switch (image.imageOrientation) {
case UIImageOrientationUp:
convertedPoint.x = originalPoint.x;
convertedPoint.y = imageHeight - originalPoint.y;
break;
case UIImageOrientationDown:
convertedPoint.x = imageWidth - originalPoint.x;
convertedPoint.y = originalPoint.y;
break;
case UIImageOrientationLeft:
convertedPoint.x = imageWidth - originalPoint.y;
convertedPoint.y = imageHeight - originalPoint.x;
break;
case UIImageOrientationRight:
convertedPoint.x = originalPoint.y;
convertedPoint.y = originalPoint.x;
break;
case UIImageOrientationUpMirrored:
convertedPoint.x = imageWidth - originalPoint.x;
convertedPoint.y = imageHeight - originalPoint.y;
break;
case UIImageOrientationDownMirrored:
convertedPoint.x = originalPoint.x;
convertedPoint.y = originalPoint.y;
break;
case UIImageOrientationLeftMirrored:
convertedPoint.x = imageWidth - originalPoint.y;
convertedPoint.y = originalPoint.x;
break;
case UIImageOrientationRightMirrored:
convertedPoint.x = originalPoint.y;
convertedPoint.y = imageHeight - originalPoint.x;
break;
default:
break;
}
return convertedPoint;
}
И вот методы категории, основанные на вышеупомянутом преобразовании:
// Get converted features with respect to the imageOrientation property
- (CGPoint) leftEyePositionForImage:(UIImage *)image;
- (CGPoint) rightEyePositionForImage:(UIImage *)image;
- (CGPoint) mouthPositionForImage:(UIImage *)image;
- (CGRect) boundsForImage:(UIImage *)image;
// Get normalized features (0-1) with respect to the imageOrientation property
- (CGPoint) normalizedLeftEyePositionForImage:(UIImage *)image;
- (CGPoint) normalizedRightEyePositionForImage:(UIImage *)image;
- (CGPoint) normalizedMouthPositionForImage:(UIImage *)image;
- (CGRect) normalizedBoundsForImage:(UIImage *)image;
// Get feature location inside of a given UIView size with respect to the imageOrientation property
- (CGPoint) leftEyePositionForImage:(UIImage *)image inView:(CGSize)viewSize;
- (CGPoint) rightEyePositionForImage:(UIImage *)image inView:(CGSize)viewSize;
- (CGPoint) mouthPositionForImage:(UIImage *)image inView:(CGSize)viewSize;
- (CGRect) boundsForImage:(UIImage *)image inView:(CGSize)viewSize;
(Еще одна вещь, на которую следует обратить внимание, это указание правильной ориентации EXIF при извлечении элементов лица из ориентации UIImage. Довольно запутанно... вот что я сделал:
int exifOrientation;
switch (self.image.imageOrientation) {
case UIImageOrientationUp:
exifOrientation = 1;
break;
case UIImageOrientationDown:
exifOrientation = 3;
break;
case UIImageOrientationLeft:
exifOrientation = 8;
break;
case UIImageOrientationRight:
exifOrientation = 6;
break;
case UIImageOrientationUpMirrored:
exifOrientation = 2;
break;
case UIImageOrientationDownMirrored:
exifOrientation = 4;
break;
case UIImageOrientationLeftMirrored:
exifOrientation = 5;
break;
case UIImageOrientationRightMirrored:
exifOrientation = 7;
break;
default:
break;
}
NSDictionary *detectorOptions = @{ CIDetectorAccuracy : CIDetectorAccuracyHigh };
CIDetector *faceDetector = [CIDetector detectorOfType:CIDetectorTypeFace context:nil options:detectorOptions];
NSArray *features = [faceDetector featuresInImage:[CIImage imageWithCGImage:self.image.CGImage]
options:@{CIDetectorImageOrientation:[NSNumber numberWithInt:exifOrientation]}];
)
iOS 10 и Swift 3
Вы можете проверить пример Apple, вы можете обнаружить лицо или значение штрих-кода и Qrcode
https://developer.apple.com/library/content/samplecode/AVCamBarcode/Introduction/Intro.html