美文网首页
iOS下使用OpenCV来控制相机

iOS下使用OpenCV来控制相机

作者: unravelW | 来源:发表于2018-07-05 14:45 被阅读0次

    本文翻译自iOS  Application  Development  with  OpenCV 3

    iOS SDK和OpenCV提供了几个用于摄像头控制的编程接口。 在iOS SDK中,AVFoundation是用于视听(AV)内容的所有录制和回放的通用框架。 AVFoundation提供对iOS相机参数的完全访问,包括图像格式,焦距,曝光,闪光,帧速率和数字变焦(裁剪因子)。 但是,AVFoundation无法解决任何GUI问题。 应用程序开发人员可以创建自定义相机GUI,使用提供GUI的更高级别框架,或自动化相机,使其在没有GUI输入的情况下运行。 AVFoundation足够灵活,可以支持任何这些设计,但由于AVFoundation很复杂,这种灵活性是有代价的。

    iOS SDK在UIImagePickerController类中实现了标准的相机GUI,该类在AVFoundation上构建。 该GUI使用户能够配置相机并捕获照片或视频。 应用程序开发人员可以在捕获后处理照片或视频,但可以选择自定义控件和视频预览有限。

    OpenCV提供了一个CvVideoCamera类,它实现了高级摄像机控制功能和预览GUI,但支持高度自定义。 CvVideoCamera在AVFoundation之上构建,并提供对某些底层类的访问。 因此,应用程序开发人员可以选择使用高级CvVideoCamera功能和较低级别AVFoundation功能的组合。 应用程序开发人员实现了大部分GUI,可能会禁用视频预览或指定其中CvVideoCamera将呈现它的父视图。 此外,应用程序可以在捕获时处理每个视频帧,并且如果应用程序就地编辑捕获的帧,则CvVideoCamera将在预览中显示结果。

    子类化CvVideoCamera

    CvVideoCamera是一个Objective-C类,Objective-C允许我们覆盖子类中的任何实例方法或属性。 而且,由于OpenCV是开源的,我们可以研究CvVideoCamera的整个实现。 因此,我们有能力和知识来创建一个子类,通过修改重新实现CvVideoCamera的各个部分。 这是一种调整或修补开源类实现的便捷方式,无需修改和重建库的源代码。

    我们将创建一个名为VideoCamera的子类。 添加一个名为VideoCamera.h的新头文件。 在这里,我们将声明子类的公共接口,包括一个新的属性和方法,如下面的代码所示:

    #import <opencv2/videoio/cap_ios.h>

      @interface VideoCamera : CvVideoCamera

      @property BOOL letterboxPreview;

      - (void)setPointOfInterestInParentViewSpace:(CGPoint)point;

      @end

    setPointOfInterestInParentViewSpace:方法将为相机的自动对焦和自动曝光算法设置一个感兴趣的点。

    现在,让我们创建类的实现文件VideoCamera.m。 我们将添加一个带有属性customPreviewLayer的私有接口,如以下代码所示:

    #import "VideoCamera.h"

      @interface VideoCamera ()

      @property (nonatomic, retain) CALayer *customPreviewLayer;

    @end

    我们将实现customPreviewLayer,以便它访问一个变量_customPreviewLayer,它在父类的私有接口中定义。此变量是视频预览图层,我们将在VideoCamera中自定义其位置和大小。 以下是开始实现VideoCamera并设置属性和变量之间关系的代码:

    @implementation VideoCamera

      @synthesize customPreviewLayer = _customPreviewLayer;

    接下来,让我们考虑设置PointOfInterestInParentViewSpace:方法.这个方法实现将涉及几个案例,但概念相当简单。作为参数,我们接受预览的父视图的坐标系中的一个点。如果我们假设调用者是视图控制器,那么这是一个方便的坐标系。 AVFoundation允许我们指定焦点和曝光的兴趣点,但它使用比例横向坐标系。这意味着左上角为(0.0,0.0),右下角为(1.0,1.0),轴方向基于横向右方向,与设备的实际方向无关。横向右方向意味着设备的主页按钮位于用户的右侧。因此,正X指向主页按钮,正Y指向远离音量按钮。这是我们方法的实现,它检查相机的自动曝光和自动对焦功能,执行坐标转换,验证坐标,并通过AVFoundation功能设置感兴趣的点:

    - (void)setPointOfInterestInParentViewSpace:(CGPoint)parentViewPoint {

        if (!self.running) {

            return;

        }

        NSArray *captureDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];

        AVCaptureDevice *captureDevice;

        for (captureDevice in captureDevices) {

            if (captureDevice.position == self.defaultAVCaptureDevicePosition) {

                break;

            }

        }

        BOOL canSetFocus = [captureDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus] && captureDevice.isFocusPointOfInterestSupported;

        BOOL canSetExposure = [captureDevice isExposureModeSupported:AVCaptureExposureModeAutoExpose] && captureDevice.isExposurePointOfInterestSupported;

        if (!canSetFocus && !canSetExposure) {

            return;

        }

        if (![captureDevice lockForConfiguration:nil]) {

            return;

        }

        CGFloat offsetX = 0.5 * (self.parentView.bounds.size.width - self.customPreviewLayer.bounds.size.width);

        CGFloat offsetY = 0.5 * (self.parentView.bounds.size.height - self.customPreviewLayer.bounds.size.height);

        CGFloat focusX =  (parentViewPoint.x - offsetX) / self.customPreviewLayer.bounds.size.width;

        CGFloat focusY =  (parentViewPoint.y - offsetY) / self.customPreviewLayer.bounds.size.height;

        if (focusX < 0.0 || focusX > 1.0 || focusY < 0.0 || focusY > 1.0) {

            return;

        }

        switch (self.defaultAVCaptureVideoOrientation) {

            case AVCaptureVideoOrientationPortraitUpsideDown:{

                CGFloat oldFocusX = focusX;

                focusX = 1.0 - focusY;

                focusY = oldFocusX;

                break;

            }

            case AVCaptureVideoOrientationLandscapeLeft:{

                focusX = 1.0 - focusX;

                focusY = 1.0 - focusY;

                break;

            }

            case AVCaptureVideoOrientationLandscapeRight:{

                break;

            }

            default:{

                CGFloat oldFocuX = focusX;

                focusX = focusY;

                focusY = 1.0 - oldFocuX;

                break;

            }

        }

        if (self.defaultAVCaptureDevicePosition == AVCaptureDevicePositionFront) {

            focusX = 1.0 - focusX;

        }

        CGPoint focusPoint = CGPointMake(focusX, focusY);

        if (canSetFocus) {

            captureDevice.focusMode = AVCaptureFocusModeAutoFocus;

            captureDevice.focusPointOfInterest = focusPoint;

        }

        if (canSetExposure) {

            captureDevice.exposureMode = AVCaptureExposureModeAutoExpose;

            captureDevice.exposurePointOfInterest = focusPoint;

        }

        [captureDevice unlockForConfiguration];

    }

    此时,我们已经实现了一个类,该类能够配置摄像机并捕获帧。但是,我们仍然需要实现另一个类来选择一个配置并接收帧。

    在视图控制器中使用CvVideoCamera子类

    以下是viewDidLoad的实现:

    - (void)viewDidLoad {

        [super viewDidLoad];

        UIImage *originalStillImage = [UIImage imageNamed:@"Fleur.jpg"];

        UIImageToMat(originalStillImage, originalStillMat);

        self.videoCamera = [[VideoCamera alloc] initWithParentView:self.imageView];

        self.videoCamera.delegate = self;

        self.videoCamera.defaultAVCaptureSessionPreset = AVCaptureSessionPresetHigh;

        self.videoCamera.defaultFPS = 30;

        self.videoCamera.letterboxPreview = YES;

    }

    我们还将覆盖另一个名为viewDidLayoutSubviews的UIViewController方法。

    - (void)viewDidLayoutSubviews {

        [super viewDidLayoutSubviews];

        switch ([UIDevice currentDevice].orientation) {

          case UIDeviceOrientationPortraitUpsideDown:

            self.videoCamera.defaultAVCaptureVideoOrientation =

              AVCaptureVideoOrientationPortraitUpsideDown;

            break;

          case UIDeviceOrientationLandscapeLeft:

            self.videoCamera.defaultAVCaptureVideoOrientation =

              AVCaptureVideoOrientationLandscapeLeft;

            break;

          case UIDeviceOrientationLandscapeRight:

            self.videoCamera.defaultAVCaptureVideoOrientation =

              AVCaptureVideoOrientationLandscapeRight;

            break;

          default:

            self.videoCamera.defaultAVCaptureVideoOrientation =

              AVCaptureVideoOrientationPortrait;

    break;

    }

        [self refresh];

    }

    请注意,我们在重新配置相机后调用辅助方法refresh。 它将确保重新启动摄像机或重新处理静态图像以反映最新配置。

    当用户点击预览的父视图时,我们将找到坐标点击并将它们传递给我们之前在VideoCamera中实现的setPointOfInterestInParentViewSpace:方法。 以下是点击事件的相关回调:

    - (void)onTapToSetPointOfInterest:(UITapGestureRecognizer *)tapGesture {

        if (tapGesture.state == UIGestureRecognizerStateEnded) {

            if (self.videoCamera.running) {

                CGPoint tapPoint = [tapGesture locationInView:self.imageView];

                [self.videoCamera setPointOfInterestInParentViewSpace:tapPoint];

            }

        }

    }

    切换灰度图和彩色图

    - (IBAction)onColorModeSelected:

          (UISegmentedControl *)segmentedControl {

        switch (segmentedControl.selectedSegmentIndex) {

          case 0:

            self.videoCamera.grayscaleMode = NO;

            break;

          default:

            self.videoCamera.grayscaleMode = YES;

    break;

    }

        [self refresh];

      }

    切换摄像头

    - (void)onSwitchCameraButtonPressed {

        if (self.videoCamera.running) {

            switch (self.videoCamera.defaultAVCaptureDevicePosition) {

                case AVCaptureDevicePositionFront:

                    self.videoCamera.defaultAVCaptureDevicePosition = AVCaptureDevicePositionBack;

                    [self refresh];

                    break;

                default:

                    [self.videoCamera stop];

                    [self refresh];

                    break;

            }

        }else {

            self.imageView.image = nil;

            self.videoCamera.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront;

            [self.videoCamera start];

        }

    }

    - (void)refresh {

        if (self.videoCamera.running) {

            self.imageView.image = nil;

            [self.videoCamera stop];

            [self.videoCamera start];

        } else {

            UIImage *image;

            if (self.videoCamera.grayscaleMode) {

                cv::cvtColor(originalStillMat, updatedStillMatGray, cv::COLOR_RGB2GRAY);

                [self processImage:updatedStillMatGray];

                image = MatToUIImage(updatedStillMatGray);

            } else {

                cv::cvtColor(originalStillMat, updatedStillMatRGBA, cv::COLOR_RGBA2BGRA);

                [self processImage:updatedStillMatRGBA];

                cv::cvtColor(updatedStillMatRGBA, updatedStillMatRGBA, cv::COLOR_BGRA2RGBA);

                image = MatToUIImage(updatedStillMatRGBA);

            }

            self.imageView.image = image;

        }

    }

    - (void)processImage:(cv::Mat &)mat {

        if (self.videoCamera.running) {

            switch (self.videoCamera.defaultAVCaptureVideoOrientation) {

                case AVCaptureVideoOrientationLandscapeLeft:

                case AVCaptureVideoOrientationLandscapeRight:

                    cv::flip(mat, mat, -1);

                    break;

                default:

                    break;

            }

        }

        [self processImageHelper:mat];

        if (self.saveNextFrame) {

            UIImage *image;

            if (self.videoCamera.grayscaleMode) {

                mat.copyTo(updatedVideoMatGray);

                image = MatToUIImage(updatedVideoMatGray);

            } else {

                cv::cvtColor(mat, updatedVideoMatRGBA, cv::COLOR_BGRA2RGBA);

                image = MatToUIImage(updatedVideoMatRGBA);

            }

            [self saveImage:image];

            self.saveNextFrame = NO;

        }

    }

    相关文章

      网友评论

          本文标题:iOS下使用OpenCV来控制相机

          本文链接:https://www.haomeiwen.com/subject/udhpuftx.html