UIImagePickerController 中的前置摄像头

问题描述 投票:0回答:10

我正在使用

UIImagePickerController
开发 iPad2 中的前置摄像头应用程序。

当我捕获图像时,它显示为从左到右翻转。

我该如何纠正这个问题?

if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) 
    {
        UIImagePickerController *imgPkr = [[UIImagePickerController alloc] init];
        imgPkr.delegate = self;
        imgPkr.sourceType = UIImagePickerControllerSourceTypeCamera;
        imgPkr.cameraDevice=UIImagePickerControllerCameraDeviceFront;


        UIImageView *anImageView=[[UIImageView alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:@"select%d.png",val]]];
        anImageView.frame = CGRectMake(0, 0, anImageView.image.size.width, anImageView.image.size.height);
        imgPkr.cameraOverlayView = anImageView;
        [theApp.TabViewControllerObject presentModalViewController:imgPkr animated:YES];
        [imgPkr release];
    }
ios objective-c iphone swift uiimagepickercontroller
10个回答
76
投票

您可以使用此功能从源图像翻转图像

UIImage *flippedImage = [UIImage imageWithCGImage:picture.CGImage scale:picture.scale orientation:UIImageOrientationLeftMirrored];

编辑:添加了快速代码

let flippedImage = UIImage(CGImage: picture.CGImage, scale: picture.scale, orientation:.LeftMirrored)

19
投票

我遇到了同样的问题 - 上面的解决方案只给了我一半的答案,因为用户必须在进入我的应用程序的下一页之前批准镜像图像 - 我在翻转后使用捕获的图像。

为了解决这个问题,每当我切换到前置摄像头时,我都必须翻转摄像头视图:

- (IBAction)flipCamera:(id)sender {
if(cameraUI.cameraDevice == UIImagePickerControllerCameraDeviceFront)
{
    cameraUI.cameraDevice = UIImagePickerControllerCameraDeviceRear;
}
else {
    cameraUI.cameraDevice = UIImagePickerControllerCameraDeviceFront;
}
cameraUI.cameraViewTransform = CGAffineTransformScale(cameraUI.cameraViewTransform, -1,     1);     
}

只是为了扩展这个伟大的答案,一些典型的完整代码,2013 年 12 月,iOS7 / Xcode5。什么都做。您只需要一个图标(示例中的cameraToggle.PNG)。

-(void)showTheDeviceCamera
    {
    if ( ! [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera] )
        return;

    // self.cameraController is a UIImagePickerController
    self.cameraController = [[UIImagePickerController alloc] init];
    self.cameraController.delegate = (id)self;
    self.cameraController.mediaTypes = @[(NSString *)kUTTypeImage];
    self.cameraController.allowsEditing = YES;
    self.cameraController.sourceType = UIImagePickerControllerSourceTypeCamera;
    [self presentViewController:self.cameraController animated:YES completion:NULL];


        // Add front-rear toggle button MANUALLY, IF NECESSARY
        // (You seem to usually get it for free, on iPhone, but
        // need to add manually on an iPad.)

        UIView *buttonView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"cameraToggle"]];
        [buttonView sizeToFit];

        buttonView.userInteractionEnabled = YES;
        [self.cameraController.view addSubview:buttonView];

        UITapGestureRecognizer *tap =
            [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_frontRearButtonClicked) ];
        tap.numberOfTapsRequired = 1;
        [buttonView addGestureRecognizer:tap];

        // we'll add it at the top right .. could be anywhere you want
        buttonView.center = CGPointMake(
                self.cameraController.view.frame.size.width-buttonView.frame.size.width,
                3.0 * buttonView.frame.size.height
                );

    }

-(void)_frontRearButtonClicked
    {
    [UIView transitionWithView:self.cameraController.view
        duration:1.0
        options:UIViewAnimationOptionAllowAnimatedContent | UIViewAnimationOptionTransitionFlipFromLeft
        animations:^{
            if ( self.cameraController.cameraDevice == UIImagePickerControllerCameraDeviceRear )
                self.cameraController.cameraDevice = UIImagePickerControllerCameraDeviceFront;
            else
                self.cameraController.cameraDevice = UIImagePickerControllerCameraDeviceRear;
        } completion:NULL];
    }

10
投票

正如其他答案,我也遇到了同样的问题。正如 Yonatan Betzer 提到的,仅翻转最终图像只是答案的一半,因为当您使用前置摄像头拍照时 UIPickerController 显示的预览图像仍然是反转的(镜像)。

Yonatan Betzer 的 anwser 效果很好,但他没有提到如何或在哪里进行更改相机设备的操作。

根据互联网上的一些代码,我创建了一个 Pod 来获得这个想要的行为:

https://github.com/lucasecf/LEMirroredImagePicker

安装完成后,你只需要将这两行代码与你的

UIImagePickerController
一起调用即可:

self.mirrorFrontPicker = [[LEMirroredImagePicker alloc] initWithImagePicker:pickerController];
[self.mirrorFrontPicker mirrorFrontCamera];

就是这样,就这么简单。您可以在 github 链接的 README 中查看更多信息。


6
投票

只是补充一下我是如何在不继承 UIImagePickerController 且不向相机视图添加额外按钮的情况下实现这一目标的。

只需监听此通知,每当相机更改时,该通知就会触发多次:

[[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(cameraChanged:)
                                                 name:@"AVCaptureDeviceDidStartRunningNotification"
                                               object:nil];

然后用这个方法翻转相机视图:

- (void)cameraChanged:(NSNotification *)notification
{
    if(imagePicker.cameraDevice == UIImagePickerControllerCameraDeviceFront)
    {
        imagePicker.cameraViewTransform = CGAffineTransformIdentity;
        imagePicker.cameraViewTransform = CGAffineTransformScale(imagePicker.cameraViewTransform, -1,     1);
    } else {
        imagePicker.cameraViewTransform = CGAffineTransformIdentity;
    }
}

6
投票

我知道这个问题确实很老,但似乎这仍然是一个常见问题。只需在

CGAffineTransform
对象的
cameraViewTransform
属性上设置
UIImagePickerController
即可。

let picker = UIImagePickerController()
picker.cameraViewTransform = CGAffineTransformScale(picker.cameraViewTransform, -1, 1)

5
投票

更新了 swift 4 的“bandog”答案

let picker = UIImagePickerController()
picker.cameraViewTransform = picker.cameraViewTransform.scaledBy(x: -1, y: 1)

2
投票

我花了几个小时,但我想我已经到了那里。这是 Swift 5.2 的工作解决方案,如何获取正确的图像(在 ImagePicker 预览和输出中)


     //Registering to get notification when users takes a picture

    override func viewDidLoad() {
        NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "_UIImagePickerControllerUserDidCaptureItem"), object: nil, queue: nil) { (notification) in
            self.changePhotoOrientation()
    }

    //Changing image orientation for ImagePicker preview

    func changePhotoOrientation() {
        var subviews: [UIView] = [imagePicker.view]
        while (!subviews.isEmpty) {
            let subview = subviews.removeFirst()
            subviews += subview.subviews
            if (subview.isKind(of: UIImageView.self)) {
                subview.transform = self.imagePicker.cameraViewTransform.scaledBy(x: -1, y: 1)
            }
        }
    }

    //Changing image orientation for the output image

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        if let userPickedImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
            image = UIImage(cgImage: userPickedImage.cgImage!, scale: userPickedImage.scale, orientation: .leftMirrored)
        }
    }

}


2
投票

看起来

AVCaptureDeviceDidStartRunningNotification
不再可用作检测相机设备变化的方法。此外,
cameraDevice
上的
UIImagePickerController
属性不适用于 KVO。但是,仍然可以检测相机设备的更改,如下所示(尽管不能保证对该解决方案的长期支持,因为我们在未明确标记为符合 KVO 的属性上使用 KVO)。

import AVFoundation

var context = 0

override func viewDidLoad() {
    super.viewDidLoad()
    // Register for notifications
    let notificationCenter = NSNotificationCenter.defaultCenter()
    notificationCenter.addObserver(self, selector: #selector(handleCaptureSessionDidStartRunning(_:)), name: AVCaptureSessionDidStartRunningNotification, object: nil)
    notificationCenter.addObserver(self, selector: #selector(handleCaptureSessionDidStopRunning(_:)), name: AVCaptureSessionDidStopRunningNotification, object: nil)
}

deinit {
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

func handleCaptureSessionDidStartRunning(notification: NSNotification) {
    guard let session = notification.object as? AVCaptureSession else { return }
    session.addObserver(self, forKeyPath: "inputs", options: [ .Old, .New ], context: &context)
}

func handleCaptureSessionDidStopRunning(notification: NSNotification) {
    guard let session = notification.object as? AVCaptureSession else { return }
    session.removeObserver(self, forKeyPath: "inputs")
}

override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
    if context == &self.context {
        if let inputs = change?[NSKeyValueChangeNewKey] as? [AnyObject], captureDevice = (inputs.first as? AVCaptureDeviceInput)?.device {
            switch captureDevice.position {
            case .Back: print("Switched to back camera")
            case .Front: print("Switched to front camera")
            case .Unspecified: break
            }
        }
    } else {
        super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
    }
}

Swift 4+ 版本:

import AVFoundation

var context = 0

override func viewDidLoad() {
    super.viewDidLoad()
    // Register for notifications
    let notificationCenter = NSNotificationCenter.defaultCenter()
    notificationCenter.addObserver(forName: NSNotification.Name(rawValue: "AVCaptureSessionDidStartRunningNotification"), object: nil, queue: nil) { [weak self] notification in
        self?.handleCaptureSessionDidStartRunning(notification: notification)
    }
    notificationCenter.addObserver(forName: NSNotification.Name(rawValue: "AVCaptureSessionDidStopRunningNotification"), object: nil, queue: nil) { [weak self] notification in
        self?.handleCaptureSessionDidStopRunning(notification: notification)
    }
}

deinit {
    NotificationCenter.default.removeObserver(self)
}

func handleCaptureSessionDidStartRunning(notification: Notification){
    guard let session = notification.object as? AVCaptureSession else { return }
    session.addObserver(self, forKeyPath: "inputs", options: [ .old, .new ], context: &context)
}

func handleCaptureSessionDidStopRunning(notification: Notification){
    guard let session = notification.object as? AVCaptureSession else { return }
    session.removeObserver(self, forKeyPath: "inputs")
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if context == &self.context {
        if let inputs = change?[NSKeyValueChangeKey.newKey] as? [AnyObject], let captureDevice = (inputs.first as? AVCaptureDeviceInput)?.device {
            switch captureDevice.position {
            case .back: print("Switched to back camera")
            case .front: print("Switched to front camera")
            case .unspecified: break
            }
        }
    } else {
        super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
    }
}

0
投票

Swift 中的完整工作示例,它回答了本文的最初问题(在使用 iOS 8.2 的 iPhone 5c 上测试):

import UIKit class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UIActionSheetDelegate { @IBOutlet var myUIImageView: UIImageView! var myUIImagePickerController: UIImagePickerController! override func viewDidLoad() { super.viewDidLoad() } override func viewWillAppear(animated: Bool) { println("viewWillAppear(animated: Bool) method called.") super.viewWillAppear(animated) NSNotificationCenter.defaultCenter().removeObserver(self) } override func viewWillDisappear(animated: Bool) { println("viewWillDisappear(animated: Bool) method called.") super.viewWillDisappear(animated) NSNotificationCenter.defaultCenter().addObserver(self, selector: "cameraChanged:", name: "AVCaptureDeviceDidStartRunningNotification", object: nil) } /* UIImagePickerControllerDelegate Section */ func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) { if(self.myUIImagePickerController.sourceType == UIImagePickerControllerSourceType.Camera) { self.myUIImageView.image = info[UIImagePickerControllerEditedImage] as? UIImage } else { self.myUIImageView.image = info[UIImagePickerControllerOriginalImage] as? UIImage } self.dismissViewControllerAnimated(true, completion: nil) } func imagePickerControllerDidCancel(picker: UIImagePickerController) { self.dismissViewControllerAnimated(true, completion: nil) } /* You can choose to use one of the UIResponder methods: touchesBegan, touchesMoved, touchesEnded etc, in order to detect the touch on the UIImageView. */ override func touchesEnded(touches: NSSet, withEvent event: UIEvent) { let touch: UITouch? = touches.anyObject() as? UITouch if (touch?.view == myUIImageView) { println("myUIImageView has been tapped by the user.") self.takingAPictureUsingTheCamera() } } func takingAPictureUsingTheCamera() { self.myUIImagePickerController = UIImagePickerController() self.myUIImagePickerController.delegate = self // Set the delegate self.myUIImagePickerController.sourceType = UIImagePickerControllerSourceType.Camera self.myUIImagePickerController.cameraDevice = UIImagePickerControllerCameraDevice.Front // self.myUIImagePickerController.editing = true self.myUIImagePickerController.allowsEditing = true self.presentViewController(self.myUIImagePickerController, animated: true, completion: nil) } func cameraChanged(notification: NSNotification) { println("cameraChanged(notification: NSNotification) method called.") self.myUIImagePickerController.cameraViewTransform = CGAffineTransformIdentity if(self.myUIImagePickerController.cameraDevice == UIImagePickerControllerCameraDevice.Front){ self.myUIImagePickerController.cameraViewTransform = CGAffineTransformScale(self.myUIImagePickerController.cameraViewTransform, -1, 1) } } }// End class
    

0
投票
用类似子句回答

cameraViewTransform = cameraViewTransform.scaledBy ..
完全错误。

您只需将

cameraViewTransform

 
设置为一个值

事实上,是仿射变换。

因此,

var mirrored = false @objc func userTappedToToggleDisplayMirroring() { mirrored = !mirrored cameraViewTransform = CGAffineTransform(scaleX: mirrored ? -1 : 1, y: 1) }
    
© www.soinside.com 2019 - 2024. All rights reserved.