且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

禁用以纵向AVFoundation以外的任何方向拍摄图像

更新时间:2023-02-02 17:46:12

这是基于我对你的了解的部分答案问题(与你所拥有的其他答案不同)。



您已将应用锁定为纵向。因此,无论手机的方向如何,状态栏始终位于手机的纵向顶部。这会成功锁定您的界面,包括AVCapture界面。但是您还希望锁定来自摄像机的原始图像馈送,以使图像范围始终与状态栏平行。



理想情况下,这需要连续 - 这样,如果摄像机处于45度角,图像将被反转45度。否则,大多数情况下,图像将无法正确对齐(替代方案是,在您的90度方向开关更新之前,它总是不在线,这会将图像旋转90度)。



为此,您需要使用Core Motion和加速度计。您希望将手机的Y轴角度设置为真垂直并相应地旋转图像。请参阅此处了解几何详细信息:



,因此您可以对此进行检查。以下是该做什么:




  • 将Core Motion框架添加到AVCam。



在AVCamViewController.m中




  • #import< CoreMotion / CoreMotion.h>

  • 添加我的 startAccelerometerUpdates 方法

  • 添加我的 resizeCameraView 方法(将这两种方法都放在类文件的顶部附近,否则你可能会感到困惑,还有更多

  • 添加以下行: [self resizeCameraView]; viewDidLoad (它可以是方法的第一行)

  • 添加属性

    @property(强,非原子)CMMotionManager * coreMotionManager

    到@interface(它不需要是属性,但是我的方法假设它存在,所以如果你不添加它,你将不得不修改我的方法。)



In startAccelerometerUpdates 更改此行:

  self.previewLayer.transform =旋转; 

to:

  self.captureVideoPreviewLayer.transform = rotate; 

此外,在中的对象列表中AVCamViewController.xib ,将videoPreview View移动到ToolBar上方(否则当你放大它时会覆盖控件)



确保禁用旋转 - 对于iOS< 6.0,已经是是的,但对于6.0+,您需要在目标摘要中选择仅支持方向的肖像。



我认为这是完整的列表我对AVCam所做的更改,旋转/方向都运行良好。我建议你尝试做同样的事情。如果你能让它顺利运行,你知道你的代码中还有其他一些小问题。如果您仍然发现您的轮换,我很想知道您的硬件和软件环境,例如您正在测试的设备。



我正在编译XCode 4.6 / OSX10.8.2,并测试:

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

所有结果都是流畅准确的。


I am using AVFoundation to show the camera.

I would like to prevent the camera itself to rotate so the viewer will see the camera only in portrait and the images will be taken only in portrait mode.

I defined Supported Interface Orientation to support portrait only and the view itself is being displayed only in portrait mode, but not the camera - is being rotated with the device orientation

How can I force the AVFoundation camera to be displayed and capture images only in portrait like the UIViewController?

My code to set the camera:

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;

Here is a partial answer based on my understanding of your question (which differs from the other answers you have had).

You have the app locked to portrait orientation. So the status bar is always at the portrait top of the phone regardless of the phone's orientation. This successfully locks your interface, including your AVCapture interface. But you want to also lock the raw image feed from the camera so that the image horizon is always parallel with the status bar.

This will ideally need to be done continuously - so that if you have the camera at a 45degree angle the image will be counter-rotated 45 degrees. Otherwise, most of the time, the image will not be aligned correctly (the alternative is that it is always out of line until your 90degree orientation switch updates, which would swivel the image 90 degrees).

To do this you need to use Core Motion and the accelerometer. You want to get angle of the phone's Y-axis to true vertical and rotate the image accordingly. See here for geometry details:

iPhone orientation -- how do I figure out which way is up?

Using Core Motion, trigger this method from 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;

         }];
    }
}

abc

phone held (a) portrait; (b) rotated ~30deg; (c) landscape
.

You may find this is a little jumpy, and there is a bit of a lag between the device movement and the view. You can play with the updateInterval, and get in deeper with other Core Motion trickery to dampen the movement. (I have not treated the case of the phone being exactly upside down, and if you hold the camera face down or face up, the result is undefined fixed with updated code/ use of atan2).

Now orientation is reasonably correct, but your image does not fit your view. There is not a lot you can do about this as the format of the raw camera feed is fixed by the physical dimensions of it's sensor array. The workaround is to zoom the image so that you have enough excess image data at all angles to enable you to crop the image to fit the portrait format you want.

Either in Interface Builder:

  • set your previewLayer's view to square centered on it's superview, with width and height equal to the diagonal of the visible image area (sqrt (width2+height2)

Or in code:

- (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};
}

If you do this in code, resizeCameraView should work if you call it from your viewDidLoad. Make sure that self.videoPreviewView is your IBOutlet reference to the correct view.

Now when you take a photo, you will capture the whole of the 'raw' image data from the camera's array, which will be in landscape format. It will be saved with an orientation flag for display rotation. But what you may want is to save the photo as seen onscreen. This means that you will have to rotate and crop the photo to match your onscreen view before saving it, and remove it's orientation metadata. That's for you to work out (the other part of the 'partial answer'): I suspect you might decide that this whole approach doesn't get you what you want (I think what you'd really like is a camera sensor that hardware-rotates against the rotation of the device to keep the horizon stable).

update
changed startAccelerometerUpdates to get angle from atan2 instead of acos, smoother and takes account of all directions without fiddling

update 2
From your comments, it seems your rotated preview layer is getting stuck? I cannot replicate your error, it must be some other place in your code or settings.

So that you can check with clean code, I have added my solution into Apple's AVCam project, so you can check it against that. Here is what to do:

  • add the Core Motion framework to AVCam.

In AVCamViewController.m

  • #import <CoreMotion/CoreMotion.h>
  • add my startAccelerometerUpdates method
  • add my resizeCameraView method (stick both of these methods near the top of the class file or you may get confused, there are more than one @implementations in that file)
  • add the line: [self resizeCameraView]; to viewDidLoad (it can be the first line of the method)
  • add the property
    @property (strong, nonatomic) CMMotionManager* coreMotionManager
    to the @interface (it doesn't need to be a property, but my method assumes it exists, so if you don't add it you will have to modify my method instead).

In startAccelerometerUpdates change this line:

    self.previewLayer.transform = rotate;  

to:

    self.captureVideoPreviewLayer.transform = rotate;

also, in the Objects list in AVCamViewController.xib, move the videoPreview View above the ToolBar (otherwise when you enlarge it you cover the controls)

Be sure to disable rotations - for iOS<6.0, that is already true, but for 6.0+ you need to select just portrait in supported orientations in the target summary.

I think that is a complete list of changes I made to AVCam, and the rotation/orientation is all working very well. I suggest you try doing the same. If you can get this to work smoothly, you know there is some other glitch in your code somewhere. If you still find your rotations stick, I would be curious to know more about your hardware and software environment such as which devices are you testing on.

I am compiling on XCode 4.6/OSX10.8.2, and testing on:

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

All results are smooth and accurate.