Почему захват изображений с помощью AVFoundation дает мне изображения размером 480x640, если предустановка 640x480? - PullRequest
8 голосов
/ 08 августа 2011

У меня есть довольно простой код для захвата неподвижного изображения с помощью AVFoundation.

AVCaptureDeviceInput *newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self backFacingCamera] error:nil];

    AVCaptureStillImageOutput *newStillImageOutput = [[AVCaptureStillImageOutput alloc] init];

    NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:
                                    AVVideoCodecJPEG, AVVideoCodecKey,
                                    nil];


    [newStillImageOutput setOutputSettings:outputSettings];
    [outputSettings release];


    AVCaptureSession *newCaptureSession = [[AVCaptureSession alloc] init];
    [newCaptureSession beginConfiguration];
    newCaptureSession.sessionPreset = AVCaptureSessionPreset640x480;

    [newCaptureSession commitConfiguration];
    if ([newCaptureSession canAddInput:newVideoInput]) {
        [newCaptureSession addInput:newVideoInput];
    }
    if ([newCaptureSession canAddOutput:newStillImageOutput]) {
        [newCaptureSession addOutput:newStillImageOutput];
    }
    self.stillImageOutput = newStillImageOutput;
    self.videoInput = newVideoInput;
    self.captureSession = newCaptureSession;

    [newStillImageOutput release];
    [newVideoInput release];
    [newCaptureSession release];

Мой метод, который захватывает неподвижное изображение, также довольно прост и распечатывает ориентацию, которая является AVCaptureVideoOrientationPortrait:

- (void) captureStillImage
{
    AVCaptureConnection *stillImageConnection = [AVCamUtilities connectionWithMediaType:AVMediaTypeVideo fromConnections:[[self stillImageOutput] connections]];

    if ([stillImageConnection isVideoOrientationSupported]){
        NSLog(@"isVideoOrientationSupported - orientation = %d", orientation);
        [stillImageConnection setVideoOrientation:orientation];
    }

    [[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:stillImageConnection
                                                         completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {


                                                             ALAssetsLibraryWriteImageCompletionBlock completionBlock = ^(NSURL *assetURL, NSError *error) {
                                                                 if (error) { // HANDLE }
                                                             };

                                                                 if (imageDataSampleBuffer != NULL) {

                                                                 CFDictionaryRef exifAttachments = CMGetAttachment(imageDataSampleBuffer, kCGImagePropertyExifDictionary, NULL);
                                                                 if (exifAttachments) {
                                                                     NSLog(@"attachements: %@", exifAttachments);
                                                                 } else { 
                                                                     NSLog(@"no attachments");
                                                                 }
                                                                 self.stillImageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];                                                                  
                                                                 self.stillImage = [UIImage imageWithData:self.stillImageData];

                                                                 UIImageWriteToSavedPhotosAlbum(self.stillImage, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
                                                     }
                                                             else
                                                                 completionBlock(nil, error);

                                                         }];
}

Таким образом, устройство понимает, что находится в портретном режиме, как и должно быть, вложения exif показывают мне:

PixelXDimension = 640;
PixelYDimension = 480;

, поэтому кажется, что мы находимся в 640x480, и это означает, что WxH (очевидно ...)

Однако, когда я отправляю фото по электронной почтеЯ сам из приложения «Фотографии яблок», я получаю изображение размером 480x640, если проверяю свойства в Preview.Это не имело никакого смысла для меня, пока я не углубился в свойства изображения, чтобы выяснить, что ориентация изображения установлена ​​на «6 (повернуто на 90 градусов против часовой стрелки)». Я уверен, что против часовой стрелки против часовой стрелки

глядя на изображение в браузере: http://tonyamoyal.com/stuff/things_that_make_you_go_hmm/photo.JPG Мы видим, что изображение повернуто на 90 градусов против часовой стрелки, и оно составляет 640x480.

Я действительно запутался в этом поведении.Когда я делаю неподвижное изображение 640x480, используя AVFoundation, я ожидаю, что по умолчанию не будет повернутой ориентации.Я ожидаю, что изображение размером 640x480 будет ориентировано точно так, как мой глаз видит изображение в слое предварительного просмотра.Может кто-нибудь объяснить, почему это происходит и как настроить захват так, чтобы при сохранении моего изображения на сервере для последующего отображения в веб-представлении оно не поворачивалось на 90 градусов против часовой стрелки?

1 Ответ

33 голосов
/ 10 августа 2011

Это происходит потому, что ориентация, установленная в метаданных нового изображения, зависит от ориентации AV-системы, которая его создает. Расположение фактических данных изображения, конечно, отличается от ориентации, указанной в ваших метаданных. Некоторые программы просмотра изображений учитывают ориентацию метаданных, некоторые игнорируют ее.

Вы можете повлиять на ориентацию метаданных AV-системы, вызвав:

AVCaptureConnection *videoConnection = ...;
if ([videoConnection isVideoOrientationSupported])
    [videoConnection setVideoOrientation:AVCaptureVideoOrientationSomething];

Вы можете повлиять на ориентацию метаданных UIImage, вызвав:

UIImage *rotatedImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:1.0f orientation:UIImageOrientationSomething];

Но фактические данные из системы AVCapture всегда будут отображаться с более широким измерением как X и более узким измерением как Y и будут ориентированы в LandscapeLeft.

Если вы хотите, чтобы фактические данные соответствовали тому, что утверждают ваши метаданные, вам нужно изменить фактические данные. Вы можете сделать это, записав изображение в новое изображение, используя CGContexts и AffineTransforms. Или есть более легкий обходной путь. Используйте пакет UIImage + Resize, как обсуждено здесь . И измените размер изображения до его текущего размера, вызвав:

UIImage *rotatedImage = [image resizedImage:CGSizeMake(image.size.width, image.size.height) interpolationQuality:kCGInterpolationDefault];

Это исправит ориентацию данных как побочный эффект.

Если вы не хотите включать весь объект UIImage + Resize, вы можете проверить его код и вырезать части, где преобразуются данные.

...