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

Почему AVCaptureSession выводит неверную ориентацию?

Итак, я последовал инструкциям Apple для захвата видеосеанса с помощью AVCaptureSession: http://developer.apple.com/iphone/library/qa/qa2010/qa1702.html. Одна из проблем, с которыми я сталкиваюсь, заключается в том, что даже если ориентация устройства камеры /iPhone вертикальна (и AVCaptureVideoPreviewLayer показывает вертикальный поток камеры), выходное изображение, похоже, находится в ландшафтном режиме. Я проверил ширину и высоту imageBuffer внутри imageFromSampleBuffer: образцового кода, и я получил 640px и 480px соответственно. Кто-нибудь знает, почему это так?

Спасибо!

4b9b3361

Ответ 1

Взгляните на заголовок AVCaptureSession.h. Существует определение для перечисления, называемого AVCaptureVideoOrientation, которое определяет различные ориентации видео. В объекте AVCaptureConnection имеется свойство videoOrientation, которое является AVCaptureVideoOrientation. Вы должны установить это, чтобы изменить ориентацию видео. Вероятно, вы хотите AVCaptureVideoOrientationLandscapeRight или AVCaptureVideoOrientationLandscapeLeft.

Вы можете найти AVCaptureConnections для сеанса, просмотрев результаты для сеанса. Выходы имеют свойство соединений, которое представляет собой массив соединений для этого вывода.

Ответ 2

Я сделал простую однострочную модификацию для imageFromSampleBuffer, чтобы исправить проблему ориентации (см. мой комментарий в коде под "I modified..." ). Надеюсь, это поможет кому-то, потому что я потратил слишком много времени на это.

// Create a UIImage from sample buffer data
- (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer  {
    // Get a CMSampleBuffer Core Video image buffer for the media data
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 
    // Lock the base address of the pixel buffer
    CVPixelBufferLockBaseAddress(imageBuffer, 0); 

    // Get the number of bytes per row for the pixel buffer
    void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer); 

    // Get the number of bytes per row for the pixel buffer
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); 
    // Get the pixel buffer width and height
    size_t width = CVPixelBufferGetWidth(imageBuffer); 
    size_t height = CVPixelBufferGetHeight(imageBuffer); 

    // Create a device-dependent RGB color space
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 

    // Create a bitmap graphics context with the sample buffer data
    CGContextRef context1 = CGBitmapContextCreate(baseAddress, width, height, 8, 
                                                 bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);

    // Create a Quartz image from the pixel data in the bitmap graphics context
    CGImageRef quartzImage = CGBitmapContextCreateImage(context1); 
    // Unlock the pixel buffer
    CVPixelBufferUnlockBaseAddress(imageBuffer,0);

    // Free up the context and color space
    CGContextRelease(context1); 
    CGColorSpaceRelease(colorSpace);

    // Create an image object from the Quartz image
    //I modified this line: [UIImage imageWithCGImage:quartzImage]; to the following to correct the orientation:
    UIImage *image =  [UIImage imageWithCGImage:quartzImage scale:1.0 orientation:UIImageOrientationRight]; 

    // Release the Quartz image
    CGImageRelease(quartzImage);

    return (image);
}

Ответ 3

Я делаю это трудным.

В DidOutputSampleBuffer просто измените ориентацию, прежде чем захватить изображение. Это моно, но вы

    public class OutputRecorder : AVCaptureVideoDataOutputSampleBufferDelegate {    
        public override void DidOutputSampleBuffer (AVCaptureOutput captureOutput, CMSampleBuffer sampleBuffer, AVCaptureConnection connection)
        {
            try {
                connection.videoOrientation = AVCaptureVideoOrientation.LandscapeLeft;

в objC этот метод

- ( void ) captureOutput: ( AVCaptureOutput * ) captureOutput
   didOutputSampleBuffer: ( CMSampleBufferRef ) sampleBuffer
      fromConnection: ( AVCaptureConnection * ) connection

Ответ 4

Вот правая последовательность:

AVCaptureVideoDataOutput *videoCaptureOutput = [[AVCaptureVideoDataOutput alloc] init];

if([self.captureSession canAddOutput:self.videoCaptureOutput]){
    [self.captureSession addOutput:self.videoCaptureOutput];
}else{
    NSLog(@"cantAddOutput");
}

// set portrait orientation
AVCaptureConnection *conn = [self.videoCaptureOutput connectionWithMediaType:AVMediaTypeVideo];
[conn setVideoOrientation:AVCaptureVideoOrientationPortrait];

Ответ 6

Если ориентация AVCaptureVideoPreviewLayer правильная, вы можете просто установить ориентацию перед захватом изображения.

AVCaptureStillImageOutput *stillImageOutput;
AVCaptureVideoPreviewLayer *previewLayer;
NSData *capturedImageData;

AVCaptureConnection *videoConnection = [stillImageOutput connectionWithMediaType:AVMediaTypeVideo];
if ([videoConnection isVideoOrientationSupported]) {
    [videoConnection setVideoOrientation:previewLayer.connection.videoOrientation];
}
[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
    CFDictionaryRef exifAttachments =
            CMGetAttachment(imageSampleBuffer, kCGImagePropertyExifDictionary, NULL);
    if (exifAttachments) {
        // Do something with the attachments.
    }
    // TODO need to manually add GPS data to the image captured
    capturedImageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
    UIImage *image = [UIImage imageWithData:capturedImageData];
}];

Также важно отметить, что UIImageOrientation и AVCaptureVideoOrientation различны. UIImageOrientationUp относится к ландшафтному режиму с регуляторами громкости вниз к земле (не), если вы думаете об использовании регуляторов громкости в качестве кнопки спуска затвора).

Таким образом, портретная ориентация с кнопкой питания, указывающей на небо (AVCaptureVideoOrientationPortrait), на самом деле UIImageOrientationLeft.

Ответ 7

Для тех людей, которые должны работать с CIImage и ориентацией из буфера, я ошибаюсь, я использовал эту коррекцию.

Так просто. BTW цифры 3,1,6,8 отсюда https://developer.apple.com/reference/imageio/kcgimagepropertyorientation

И не спрашивайте меня, почему 3,1,6,8 - правильная комбинация. Я использовал метод грубой силы, чтобы найти его. Если вы знаете, почему дайте объяснение в комментарии, пожалуйста...

- (void)captureOutput:(AVCaptureOutput *)captureOutput
    didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
           fromConnection:(AVCaptureConnection *)connection
{

    // common way to get CIImage

    CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);

    CFDictionaryRef attachments = CMCopyDictionaryOfAttachments(kCFAllocatorDefault, sampleBuffer, kCMAttachmentMode_ShouldPropagate);

    CIImage *ciImage = [[CIImage alloc] initWithCVPixelBuffer:pixelBuffer
                                                      options:(__bridge NSDictionary *)attachments];

    if (attachments) {
       CFRelease(attachments);
    }

    // fixing the orientation of the CIImage

    UIInterfaceOrientation curOrientation = [[UIApplication sharedApplication] statusBarOrientation];

    if (curOrientation == UIInterfaceOrientationLandscapeLeft){
        ciImage = [ciImage imageByApplyingOrientation:3];
    } else if (curOrientation == UIInterfaceOrientationLandscapeRight){
        ciImage = [ciImage imageByApplyingOrientation:1];
    } else if (curOrientation == UIInterfaceOrientationPortrait){
        ciImage = [ciImage imageByApplyingOrientation:6];
    } else if (curOrientation == UIInterfaceOrientationPortraitUpsideDown){
        ciImage = [ciImage imageByApplyingOrientation:8];
    }



    // ....

}

Ответ 8

Проблема с ориентацией с передней камерой, поэтому проверьте тип устройства и создайте новое изображение, оно определенно решит проблему ориентации:

-(void)capture:(void(^)(UIImage *))handler{

AVCaptureConnection *videoConnection = nil;
for (AVCaptureConnection *connection in self.stillImageOutput.connections)
{
    for (AVCaptureInputPort *port in [connection inputPorts])
    {
        if ([[port mediaType] isEqual:AVMediaTypeVideo] )
        {
            videoConnection = connection;
            break;
        }
    }
    if (videoConnection) { break; }
}

[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) {

    if (imageSampleBuffer != NULL) {
        NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
        **UIImage *capturedImage = [UIImage imageWithData:imageData];
        if (self.captureDevice == [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo][1]) {
            capturedImage = [[UIImage alloc] initWithCGImage:capturedImage.CGImage scale:1.0f orientation:UIImageOrientationLeftMirrored];
        }**

        handler(capturedImage);
    }
}];
}

Ответ 9

Просто попробуйте это;)

private func startLiveVideo() {

    let captureSession = AVCaptureSession()
    captureSession.sessionPreset = .photo
    let captureDevice = AVCaptureDevice.default(for: .video)

    let input = try! AVCaptureDeviceInput(device: captureDevice!)
    let output = AVCaptureVideoDataOutput()
    captureSession.addInput(input)
    captureSession.addOutput(output)

    output.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoQueue"))
    output.connection(with: .video)?.videoOrientation = .portrait

    let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
    previewLayer.frame = view.bounds
    view.layer.addSublayer(previewLayer)

    captureSession.startRunning()
}