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

Отключить прием изображений в любой ориентации, отличной от Portrait AVFoundation

Я использую AVFoundation, чтобы показать камеру.

Я хочу, чтобы камера сама вращалась, чтобы зритель увидел камеру только в портретной ориентации, и изображения будут сниматься только в портретном режиме.

Я определил Supported Interface Orientation только для поддержки только портрета, и сам вид отображается только в портретном режиме, но не камера - поворачивается с ориентацией устройства

Как я могу заставить камеру AVFoundation отображаться и снимать изображения только на портрете, например, UIViewController?

Мой код для установки камеры:

AVCaptureVideoPreviewLayer* lay = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.sess];
UIView *view = [self videoPreviewView];
CALayer *viewLayer = [view layer];
[viewLayer setMasksToBounds:YES];
CGRect bounds = [view bounds];
[lay setFrame:bounds];
if ([lay respondsToSelector:@selector(connection)])
 {
   if ([lay.connection isVideoOrientationSupported])
   {
      [lay.connection setVideoOrientation:AVCaptureVideoOrientationPortrait];
    }
 }
 [lay setVideoGravity:AVLayerVideoGravityResizeAspectFill];
 [viewLayer insertSublayer:lay below:[[viewLayer sublayers] objectAtIndex:0]];
 self.previewLayer = lay;
4b9b3361

Ответ 1

Вот частичный ответ, основанный на моем понимании вашего вопроса (который отличается от других ответов, которые у вас были).

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

Это в идеале нужно делать непрерывно, так что, если у вас есть камера под углом 45 градусов, изображение будет вращаться на 45 градусов. В противном случае, в большинстве случаев изображение не будет правильно выровнено (альтернативой является то, что он всегда выходит за линию, пока не изменится ваш переключатель ориентации на 90 градусов, который повернет изображение на 90 градусов).

Для этого вам нужно использовать Core Motion и акселерометр. Вы хотите, чтобы угол Y по оси Y был верным и повернул изображение соответствующим образом. См. Здесь подробности геометрии:

Ориентация iPhone - как определить, какой путь вверх?

Используя Core Motion, вызовите этот метод из viewDidLoad

- (void)startAccelerometerUpdates {
    self.coreMotionManager = [[CMMotionManager alloc] init];
    if ([self.coreMotionManager isAccelerometerAvailable] == YES) {
        CGFloat updateInterval = 0.1;
            // Assign the update interval to the motion manager
        [self.coreMotionManager setAccelerometerUpdateInterval:updateInterval];
        [self.coreMotionManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] 
           withHandler: ^(CMAccelerometerData *accelerometerData, NSError *error) {
             CGFloat angle =  -atan2( accelerometerData.acceleration.x, 
                                      accelerometerData.acceleration.y) 
                               + M_PI ;
             CATransform3D rotate = CATransform3DMakeRotation(angle, 0, 0, 1);
             self.previewLayer.transform = rotate;

         }];
    }
}

aupright b45deg c90deg

телефон (a) портрет; (b) вращение ~ 30deg; (c) пейзаж
 ,

Вы можете обнаружить, что это немного нерешительно, и между движением устройства и представлением наблюдается некоторое отставание. Вы можете играть с обновлениемInterval и углубляться в другие трюки Core Motion, чтобы ослабить движение. ( Я не рассматривал случай, когда телефон был точно перевернут, и если вы держите камеру лицевой стороной вниз или лицом вверх, результат undefined исправлен с обновленным кодом/использованием atan2).

Теперь ориентация достаточно правильная, но ваше изображение не соответствует вашему представлению. Существует не так много, что вы можете сделать по этому поводу, так как формат подачи необработанной камеры фиксируется физическими размерами его сенсорной матрицы. Обходной путь - увеличить изображение, чтобы у вас было достаточно избыточных данных изображения под всеми углами, чтобы вы могли обрезать изображение в соответствии с желаемым форматом портрета.

Либо в Interface Builder:

  • установите для параметра previewLayer значение квадрата с центром над ним, с шириной и высотой, равной диагонали области видимого изображения (sqrt (ширина 2 + height 2))

Или в коде:

- (void)resizeCameraView
{
    CGSize size = self. videoPreviewView.bounds.size;
    CGFloat diagonal = sqrt(pow(size.width,2)+pow(size.height,2));
    diagonal = 2*ceil(diagonal/2);  //rounding
    self.videoPreviewView.bounds = (CGRect){0,0,diagonal,diagonal};
}

Если вы сделаете это в коде, resizeCameraView должен работать, если вы вызываете его из viewDidLoad. Убедитесь, что self.videoPreviewView соответствует вашей ссылке IBOutlet на правильный вид.

Теперь, когда вы делаете снимок, вы будете захватывать все "сырые" данные изображения из массива камеры, которые будут в альбомном формате. Он будет сохранен с флагом ориентации для поворота изображения. Но вы можете захотеть сохранить фотографию, как показано на экране. Это означает, что вам нужно будет повернуть и обрезать фотографию в соответствии с вашим экранным представлением перед ее сохранением и удалить ее метаданные ориентации. Это для вас, чтобы выработать (другая часть "частичного ответа" ): я подозреваю, что вы можете решить, что весь этот подход не дает вам то, что вы хотите (я думаю, что вам действительно понравится датчик камеры, что аппаратное обеспечение -ротаты против вращения устройства, чтобы поддерживать стабильность горизонта).

Обновление
изменил startAccelerometerUpdates, чтобы получить угол от atan2 вместо acos, более плавный и учитывающий все направления без возиться

обновить 2
Из ваших комментариев кажется, что ваш вращающийся слой предварительного просмотра застревает? Я не могу воспроизвести вашу ошибку, это должно быть какое-то другое место в вашем коде или настройках.

Чтобы вы могли проверить с помощью чистого кода, я добавил свое решение в проект Apple AVCam, поэтому вы можете проверить его на это. Вот что делать:

  • добавьте структуру Core Motion в AVCam.

В AVCamViewController.m

  • #import <CoreMotion/CoreMotion.h>
  • добавить мой метод startAccelerometerUpdates
  • добавьте мой метод resizeCameraView (придерживайтесь обоих этих методов в верхней части файла класса или вы можете запутаться, в этом файле более одного @implementation)
  • добавьте строку: [self resizeCameraView]; в viewDidLoad (это может быть первая строка метода)
  • добавить свойство
    @property (strong, nonatomic) CMMotionManager* coreMotionManager
    на @interface (это не должно быть свойство, но мой метод предполагает, что он существует, поэтому, если вы его не добавите, вам придется изменить мой метод).

В startAccelerometerUpdates измените эту строку:

    self.previewLayer.transform = rotate;  

to:

    self.captureVideoPreviewLayer.transform = rotate;

также в списке объектов в AVCamViewController.xib переместите представление VideoPreview над панелью инструментов (в противном случае при его увеличении вы будете закрывать элементы управления)

Обязательно отключите вращения - для iOS и 6.0, это уже верно, но для 6.0+ вам нужно выбрать только портрет в поддерживаемых ориентациях в сводке цели.

Я думаю, что это полный список изменений, которые я внес в AVCam, и поворот/ориентация - все работает очень хорошо. Я предлагаю вам попробовать сделать то же самое. Если вы можете заставить это работать плавно, вы знаете, что где-то есть какой-то другой глюк. Если вы все еще найдете свои повороты, мне было бы интересно узнать больше о вашей аппаратной и программной среде, например о том, какие устройства вы тестируете.

Я компилирую на XCode 4.6/OSX10.8.2 и тестирую на:

- iPhone4S  /  iOS5.1  
- iPhone3G  /  iOS6.1  
- iPad mini /  iOS6.1 

Все результаты являются гладкими и точными.

Ответ 2

Я думаю, вам нужно использовать этот метод, чтобы ограничить вращение камеры.

AVCaptureConnection *videoConnection = [CameraVC connectionWithMediaType:AVMediaTypeVideo fromConnections:[imageCaptureOutput connections]];
if ([videoConnection isVideoOrientationSupported])
{
    [videoConnection setVideoOrientation:[UIDevice currentDevice].orientation];
}

Предполагая, что ваш слой предварительного просмотра определяется как свойство, можно использовать

[self.previewLayer setOrientation:[[UIDevice currentDevice] orientation]];

В вашем случае вы можете заменить [[UIDevice currentDevice] ориентацию] на UIInterfaceOrientationPortrait

отредактировано

Попробуйте добавить слой предварительного просмотра, когда он вам действительно нужен. Пример

preview = [[self videoPreviewWithFrame:CGRectMake(0, 0, 320, 480)] retain];
[self.view addSubview:preview];

Функция videoPreviewWithFrame.

- (UIView *) videoPreviewWithFrame:(CGRect) frame 
{
  AVCaptureVideoPreviewLayer *tempPreviewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:[self captureSession]];
  [tempPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
  tempPreviewLayer.frame = frame;

  UIView* tempView = [[UIView alloc] init];
  [tempView.layer addSublayer:tempPreviewLayer];
  tempView.frame = frame;

  [tempPreviewLayer autorelease];
  [tempView autorelease];
  return tempView;
}

Ответ 3

Предполагая, что ваш предварительный просмотр добавлен в представление viewcontroller. Сделайте это в viewDidLoad:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];

и определите селектор как:

- (void)orientationChanged:(NSNotification*)notification {
    UIInterfaceOrientation interfaceOrientation = [UIApplication sharedApplication].statusBarOrientation;
    if ([self.previewlayer respondsToSelector:@selector(orientation)]) {
        //for iOS5
        if (interfaceOrientation != UIInterfaceOrientationPortrait) {
            self.previewlayer.orientation = (AVCaptureVideoOrientation)UIInterfaceOrientationPortrait;
        }
    } else {
        //for iOS6
        if (interfaceOrientation != UIInterfaceOrientationPortrait) {
            self.previewlayer.connection.videoOrientation = (AVCaptureVideoOrientation)UIInterfaceOrientationPortrait;
        }
    }
}

Примечание: введите tempPreviewLayer в свойство self.previewlayer.

Это приведет к тому, что слой предварительного просмотра будет сохранен в портретном положении при изменении ориентации устройства.

ИЗМЕНИТЬ

вы также можете добавить это в метод ur' shouldAutoRotate` viewController

    - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
    {
        // Return YES for supported orientations
        if ([self.previewlayer respondsToSelector:@selector(orientation)]) {
            if (interfaceOrientation != UIInterfaceOrientationPortrait) {
                self.previewlayer.orientation = (AVCaptureVideoOrientation)UIInterfaceOrientationPortrait;
            }
        } else {
            if (interfaceOrientation != UIInterfaceOrientationPortrait) {
                self.previewlayer.connection.videoOrientation = (AVCaptureVideoOrientation)UIInterfaceOrientationPortrait;
            }
        }

        return (interfaceOrientation == UIInterfaceOrientationPortrait);
    }

для ios6 над этими двумя и проверьте.

-(NSUInteger)supportedInterfaceOrientations {
    //UIInterfaceOrientation interfaceOrientation = [UIApplication sharedApplication].statusBarOrientation;
    //return (
    //interfaceOrientation == UIInterfaceOrientationLandscapeLeft |
    //interfaceOrientation == UIInterfaceOrientationLandscapeRight);
    return UIInterfaceOrientationMaskLandscape;//(UIInterfaceOrientationLandscapeLeft | UIInterfaceOrientationLandscapeRight);
}

- (BOOL)shouldAutorotate {
    return YES;
}

до return в этих двух методах аппроксимирует код... и в уведомлении, которое я дал, см., если его вызывал, когда вы проходили по устройству.