• 一点背景知识 OpenCV 是一个开源的计算机视觉和机器学习库。它包含成千上万优化过的算法,为各种计算机视觉应用提供了一个通用工具包。根据这个项目的关于页面,OpenCV 已被广泛运用在各种项目上,从谷歌街景的...

    一点背景知识

    OpenCV 是一个开源的计算机视觉和机器学习库。它包含成千上万优化过的算法,为各种计算机视觉应用提供了一个通用工具包。根据这个项目的关于页面,OpenCV 已被广泛运用在各种项目上,从谷歌街景的图片拼接,到交互艺术展览的技术实现中,都有 OpenCV 的身影。

    OpenCV 起始于 1999 年 Intel 的一个内部研究项目。从那时起,它的开发就一直很活跃。进化到现在,它已支持如 OpenCL 和 OpenGL 等现代技术,也支持如 iOS 和 Android 等平台。

    1999 年,半条命发布后大红大热。Intel 奔腾 3 处理器是当时最高级的 CPU,400-500 MHZ 的时钟频率已被认为是相当快。2006 年 OpenCV 1.0 版本发布的时候,当时主流 CPU 的性能也只和 iPhone 5 的 A6 处理器相当。尽管计算机视觉从传统上被认为是计算密集型应用,但我们的移动设备性能已明显地超出能够执行有用的计算机视觉任务的阈值,带着摄像头的移动设备可以在计算机视觉平台上大有所为。

    在本文中,我会从一个 iOS 开发者的视角概述一下 OpenCV,并介绍一点基础的类和概念。随后,会讲到如何集成 OpenCV 到你的 iOS 项目中以及一些 Objective-C++ 基础知识。最后,我们会看一个 demo 项目,看看如何在 iOS 设备上使用 OpenCV 实现人脸检测与人脸识别。

    OpenCV 概述

    概念

    OpenCV 的 API 是 C++ 的。它由不同的模块组成,这些模块中包含范围极为广泛的各种方法,从底层的图像颜色空间转换到高层的机器学习工具。

    使用 C++ API 并不是绝大多数 iOS 开发者每天都做的事,你需要使用 Objective-C++ 文件来调用 OpenCV 的函数。 也就是说,你不能在 Swift 或者 Objective-C 语言内调用 OpenCV 的函数。 这篇 OpenCV 的 iOS 教程告诉你只要把所有用到 OpenCV 的类的文件后缀名改为 .mm 就行了,包括视图控制器类也是如此。这么干或许能行得通,却不是什么好主意。正确的方式是给所有你要在 app 中使用到的 OpenCV 功能写一层 Objective-C++ 封装。这些 Objective-C++ 封装把 OpenCV 的 C++ API 转化为安全的 Objective-C API,以方便地在所有 Objective-C 类中使用。走封装的路子,你的工程中就可以只在这些封装中调用 C++ 代码,从而避免掉很多让人头痛的问题,比如直接改文件后缀名会因为在错误的文件中引用了一个 C++ 头文件而产生难以追踪的编译错误。

    OpenCV 声明了命名空间 cv,因此 OpenCV 的类的前面会有个 cv:: 前缀,就像 cv::Mat、 cv::Algorithm 等等。你也可以在 .mm 文件中使用 using namespace cv 来避免在一堆类名前使用 cv::前缀。但是,在某些类名前你必须使用命名空间前缀,比如 cv::Rect 和 cv::Point,因为它们会跟定义在 MacTypes.h 中的 Rect 和 Point 相冲突。尽管这只是个人偏好问题,我还是偏向在任何地方都使用 cv::以保持一致性。

    模块

    下面是在官方文档中列出的最重要的模块。

    • core:简洁的核心模块,定义了基本的数据结构,包括稠密多维数组 Mat 和其他模块需要的基本函数。
    • imgproc:图像处理模块,包括线性和非线性图像滤波、几何图像转换 (缩放、仿射与透视变换、一般性基于表的重映射)、颜色空间转换、直方图等等。
    • video:视频分析模块,包括运动估计、背景消除、物体跟踪算法。
    • calib3d:包括基本的多视角几何算法、单体和立体相机的标定、对象姿态估计、双目立体匹配算法和元素的三维重建。
    • features2d:包含了显著特征检测算法、描述算子和算子匹配算法。
    • objdetect:物体检测和一些预定义的物体的检测 (如人脸、眼睛、杯子、人、汽车等)。
    • ml:多种机器学习算法,如 K 均值、支持向量机和神经网络。
    • highgui:一个简单易用的接口,提供视频捕捉、图像和视频编码等功能,还有简单的 UI 接口 (iOS 上可用的仅是其一个子集)。
    • gpu:OpenCV 中不同模块的 GPU 加速算法 (iOS 上不可用)。
    • ocl:使用 OpenCL 实现的通用算法 (iOS 上不可用)。
    • 一些其它辅助模块,如 Python 绑定和用户贡献的算法。

    基础类和操作

    OpenCV 包含几百个类。为简便起见,我们只看几个基础的类和操作,进一步阅读请参考全部文档。过一遍这几个核心类应该足以对这个库的机理产生一些感觉认识。

    cv::Mat

    cv::Mat 是 OpenCV 的核心数据结构,用来表示任意 N 维矩阵。因为图像只是 2 维矩阵的一个特殊场景,所以也是使用 cv::Mat 来表示的。也就是说,cv::Mat 将是你在 OpenCV 中用到最多的类。

    一个 cv::Mat 实例的作用就像是图像数据的头,其中包含着描述图像格式的信息。图像数据只是被引用,并能为多个 cv::Mat 实例共享。OpenCV 使用类似于 ARC 的引用计数方法,以保证当最后一个来自 cv::Mat 的引用也消失的时候,图像数据会被释放。图像数据本身是图像连续的行的数组 (对 N 维矩阵来说,这个数据是由连续的 N-1 维数据组成的数组)。使用 step[] 数组中包含的值,图像的任一像素地址都可通过下面的指针运算得到:

    uchar *pixelPtr = cvMat.data + rowIndex * cvMat.step[0] + colIndex * cvMat.step[1]
    

    每个像素的数据格式可以通过 type() 方法获得。除了常用的每通道 8 位无符号整数的灰度图 (1 通道,CV_8UC1) 和彩色图 (3 通道,CV_8UC3),OpenCV 还支持很多不常用的格式,例如 CV_16SC3 (每像素 3 通道,每通道使用 16 位有符号整数),甚至 CV_64FC4 (每像素 4 通道,每通道使用 64 位浮点数)。

    cv::Algorithm

    Algorithm 是 OpenCV 中实现的很多算法的抽象基类,包括将在我们的 demo 工程中用到的 FaceRecognizer。它提供的 API 与苹果的 Core Image 框架中的 CIFilter 有些相似之处。创建一个 Algorithm 的时候使用算法的名字来调用 Algorithm::create(),并且可以通过 get() 和 set()方法来获取和设置各个参数,这有点像是键值编码。另外,Algorithm 从底层就支持从/向 XML 或 YAML 文件加载/保存参数的功能。

    在 iOS 上使用 OpenCV

    添加 OpenCV 到你的工程中

    集成 OpenCV 到你的工程中有三种方法:

    • 使用 CocoaPods 就好: pod "OpenCV"
    • 下载官方 iOS 框架发行包,并把它添加到工程里。
    • 从 GitHub 拉下代码,并根据教程自己编译 OpenCV 库。

    Objective-C++

    如前面所说,OpenCV 是一个 C++ 的 API,因此不能直接在 Swift 和 Objective-C 代码中使用,但能在 Objective-C++ 文件中使用。

    Objective-C++ 是 Objective-C 和 C++ 的混合物,让你可以在 Objective-C 类中使用 C++ 对象。clang 编译器会把所有后缀名为 .mm 的文件都当做是 Objective-C++。一般来说,它会如你所期望的那样运行,但还是有一些使用 Objective-C++ 的注意事项。内存管理是你最应该格外注意的点,因为 ARC 只对 Objective-C 对象有效。当你使用一个 C++ 对象作为类属性的时候,其唯一有效的属性就是 assign。因此,你的 dealloc 函数应确保 C++ 对象被正确地释放了。

    第二重要的点就是,如果你在 Objective-C++ 头文件中引入了 C++ 头文件,当你在工程中使用该 Objective-C++ 文件的时候就泄露了 C++ 的依赖。任何引入你的 Objective-C++ 类的 Objective-C 类也会引入该 C++ 类,因此该 Objective-C 文件也要被声明为 Objective-C++ 的文件。这会像森林大火一样在工程中迅速蔓延。所以,应该把你引入 C++ 文件的地方都用 #ifdef __cplusplus 包起来,并且只要可能,就尽量只在 .mm实现文件中引入 C++ 头文件。

    要获得更多如何混用 C++ 和 Objective-C 的细节,请查看 Matt Galloway 写的这篇教程

    Demo:人脸检测与识别

    现在,我们对 OpenCV 及如何把它集成到我们的应用中有了大概认识,那让我们来做一个小 demo 应用:从 iPhone 的摄像头获取视频流,对它持续进行人脸检测,并在屏幕上标出来。当用户点击一个脸孔时,应用会尝试识别这个人。如果识别结果正确,用户必须点击 “Correct”。如果识别错误,用户必须选择正确的人名来纠正错误。我们的人脸识别器就会从错误中学习,变得越来越好。

    demo 应用中人脸检测与识别系统线框图

    本 demo 应用的源码可从 GitHub 获得。

    视频拍摄

    OpenCV 的 highgui 模块中有个类,CvVideoCamera,它把 iPhone 的摄像机抽象出来,让我们的 app 通过一个代理函数 - (void)processImage:(cv::Mat&)image 来获得视频流。CvVideoCamera 实例可像下面这样进行设置:

    CvVideoCamera *videoCamera = [[CvVideoCamera alloc] initWithParentView:view];
    videoCamera.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront;
    videoCamera.defaultAVCaptureSessionPreset = AVCaptureSessionPreset640x480;
    videoCamera.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationPortrait;
    videoCamera.defaultFPS = 30;
    videoCamera.grayscaleMode = NO;
    videoCamera.delegate = self;
    

    摄像头的帧率被设置为 30 帧每秒, 我们实现的 processImage 函数将每秒被调用 30 次。因为我们的 app 要持续不断地检测人脸,所以我们应该在这个函数里实现人脸的检测。要注意的是,如果对某一帧进行人脸检测的时间超过 1/30 秒,就会产生掉帧现象。

    人脸检测

    其实你并不需要使用 OpenCV 来做人脸检测,因为 Core Image 已经提供了 CIDetector 类。用它来做人脸检测已经相当好了,并且它已经被优化过,使用起来也很容易:

    CIDetector *faceDetector = [CIDetector detectorOfType:CIDetectorTypeFace context:context options:@{CIDetectorAccuracy: CIDetectorAccuracyHigh}];
    
    NSArray *faces = [faceDetector featuresInImage:image];
    

    从该图片中检测到的每一张面孔都在数组 faces 中保存着一个 CIFaceFeature 实例。这个实例中保存着这张面孔的所处的位置和宽高,除此之外,眼睛和嘴的位置也是可选的。

    另一方面,OpenCV 也提供了一套物体检测功能,经过训练后能够检测出任何你需要的物体。该库为多个场景自带了可以直接拿来用的检测参数,如人脸、眼睛、嘴、身体、上半身、下半身和笑脸。检测引擎由一些非常简单的检测器的级联组成。这些检测器被称为 Haar 特征检测器,它们各自具有不同的尺度和权重。在训练阶段,决策树会通过已知的正确和错误的图片进行优化。关于训练与检测过程的详情可参考此原始论文。当正确的特征级联及其尺度与权重通过训练确立以后,这些参数就可被加载并初始化级联分类器了:

    // 正面人脸检测器训练参数的文件路径
    NSString *faceCascadePath = [[NSBundle mainBundle] pathForResource:@"haarcascade_frontalface_alt2"
                                                       ofType:@"xml"];
    
    const CFIndex CASCADE_NAME_LEN = 2048;
    char *CASCADE_NAME = (char *) malloc(CASCADE_NAME_LEN);
    CFStringGetFileSystemRepresentation( (CFStringRef)faceCascadePath, CASCADE_NAME, CASCADE_NAME_LEN);
    
    CascadeClassifier faceDetector;
    faceDetector.load(CASCADE_NAME);
    

    这些参数文件可在 OpenCV 发行包里的 data/haarcascades 文件夹中找到。

    在使用所需要的参数对人脸检测器进行初始化后,就可以用它进行人脸检测了:

    cv::Mat img;
    vector<cv::Rect> faceRects;
    double scalingFactor = 1.1;
    int minNeighbors = 2;
    int flags = 0;
    cv::Size minimumSize(30,30);
    faceDetector.detectMultiScale(img, faceRects,
                                  scalingFactor, minNeighbors, flags
                                  cv::Size(30, 30) );
    

    检测过程中,已训练好的分类器会用不同的尺度遍历输入图像的每一个像素,以检测不同大小的人脸。参数 scalingFactor 决定每次遍历分类器后尺度会变大多少倍。参数 minNeighbors 指定一个符合条件的人脸区域应该有多少个符合条件的邻居像素才被认为是一个可能的人脸区域;如果一个符合条件的人脸区域只移动了一个像素就不再触发分类器,那么这个区域非常可能并不是我们想要的结果。拥有少于 minNeighbors 个符合条件的邻居像素的人脸区域会被拒绝掉。如果 minNeighbors 被设置为 0,所有可能的人脸区域都会被返回回来。参数 flags 是 OpenCV 1.x 版本 API 的遗留物,应该始终把它设置为 0。最后,参数 minimumSize 指定我们所寻找的人脸区域大小的最小值。faceRects 向量中将会包含对 img 进行人脸识别获得的所有人脸区域。识别的人脸图像可以通过 cv::Mat 的 () 运算符提取出来,调用方式很简单:cv::Mat faceImg = img(aFaceRect)

    不管是使用 CIDetector 还是 OpenCV 的 CascadeClassifier,只要我们获得了至少一个人脸区域,我们就可以对图像中的人进行识别了。

    人脸识别

    OpenCV 自带了三个人脸识别算法:Eigenfaces,Fisherfaces 和局部二值模式直方图 (LBPH)。如果你想知道它们的工作原理及相互之间的区别,请阅读 OpenCV 的详细文档

    针对于我们的 demo app,我们将采用 LBPH 算法。因为它会根据用户的输入自动更新,而不需要在每添加一个人或纠正一次出错的判断的时候都要重新进行一次彻底的训练。

    要使用 LBPH 识别器,我们也用 Objective-C++ 把它封装起来。这个封装中暴露以下函数:

    + (FJFaceRecognizer *)faceRecognizerWithFile:(NSString *)path;
    - (NSString *)predict:(UIImage*)img confidence:(double *)confidence;
    - (void)updateWithFace:(UIImage *)img name:(NSString *)name;
    

    像下面这样用工厂方法来创建一个 LBPH 实例:

    + (FJFaceRecognizer *)faceRecognizerWithFile:(NSString *)path {
        FJFaceRecognizer *fr = [FJFaceRecognizer new];
        fr->_faceClassifier = createLBPHFaceRecognizer();
        fr->_faceClassifier->load(path.UTF8String);
        return fr;
    }
    

    预测函数可以像下面这样实现:

    - (NSString *)predict:(UIImage*)img confidence:(double *)confidence {
        cv::Mat src = [img cvMatRepresentationGray];
        int label;
        self->_faceClassifier->predict(src, label, *confidence);
        return _labelsArray[label];
    }
    

    请注意,我们要使用一个类别方法把 UIImage 转化为 cv::Mat。此转换本身倒是相当简单直接:使用 CGBitmapContextCreate 创建一个指向 cv::Image 中的 data 指针所指向的数据的 CGContextRef。当我们在此图形上下文中绘制此 UIImage 的时候,cv::Image 的 data 指针所指就是所需要的数据。更有趣的是,我们能对一个 Objective-C 类创建一个 Objective-C++ 的类别,并且确实管用。

    另外,OpenCV 的人脸识别器仅支持整数标签,但是我们想使用人的名字作标签,所以我们得通过一个 NSArray 属性来对二者实现简单的转换。

    一旦识别器给了我们一个识别出来的标签,我们把此标签给用户看,这时候就需要用户给识别器一个反馈。用户可以选择,“是的,识别正确”,也可以选择,“不,这是 Y,不是 X”。在这两种情况下,我们都可以通过人脸图像和正确的标签来更新 LBPH 模型,以提高未来识别的性能。使用用户的反馈来更新人脸识别器的方式如下:

    - (void)updateWithFace:(UIImage *)img name:(NSString *)name {
        cv::Mat src = [img cvMatRepresentationGray];
        NSInteger label = [_labelsArray indexOfObject:name];
        if (label == NSNotFound) {
            [_labelsArray addObject:name];
            label = [_labelsArray indexOfObject:name];
        }
        vector<cv::Mat> images = vector<cv::Mat>();
        images.push_back(src);
        vector<int> labels = vector<int>();
        labels.push_back((int)label);
        self->_faceClassifier->update(images, labels);
    }
    

    这里,我们又做了一次了从 UIImage 到 cv::Matint 到 NSString 标签的转换。我们还得如 OpenCV 的 FaceRecognizer::update API所期望的那样,把我们的参数放到 std::vector 实例中去。

    如此“预测,获得反馈,更新循环”,就是文献上所说的监督式学习

    结论

    OpenCV 是一个强大而用途广泛的库,覆盖了很多现如今仍在活跃的研究领域。想在一篇文章中给出详细的使用说明只会是让人徒劳的事情。因此,本文仅意在从较高层次对 OpenCV 库做一个概述。同时,还试图就如何集成 OpenCV 库到你的 iOS 工程中给出一些实用建议,并通过一个人脸识别的例子来向你展示如何在一个真正的项目中使用 OpenCV。如果你觉得 OpenCV 对你的项目有用, OpenCV 的官方文档写得非常好非常详细,请继续前行,创造出下一个伟大的 app!


    原文 Face Recognition with OpenCV

    展开全文
  • 使用Opencv在IOS平台实现人脸识别

    一点背景知识

    OpenCV 是一个开源的计算机视觉和机器学习库。它包含成千上万优化过的算法,为各种计算机视觉应用提供了一个通用工具包。根据这个项目的关于页面,OpenCV 已被广泛运用在各种项目上,从谷歌街景的图片拼接,到交互艺术展览的技术实现中,都有 OpenCV 的身影。

    OpenCV 起始于 1999 年 Intel 的一个内部研究项目。从那时起,它的开发就一直很活跃。进化到现在,它已支持如 OpenCL 和 OpenGL 等现代技术,也支持如 iOS 和 Android 等平台。

    1999 年,半条命发布后大红大热。Intel 奔腾 3 处理器是当时最高级的 CPU,400-500 MHZ 的时钟频率已被认为是相当快。2006 年 OpenCV 1.0 版本发布的时候,当时主流 CPU 的性能也只和 iPhone 5 的 A6 处理器相当。尽管计算机视觉从传统上被认为是计算密集型应用,但我们的移动设备性能已明显地超出能够执行有用的计算机视觉任务的阈值,带着摄像头的移动设备可以在计算机视觉平台上大有所为。

    在本文中,我会从一个 iOS 开发者的视角概述一下 OpenCV,并介绍一点基础的类和概念。随后,会讲到如何集成 OpenCV 到你的 iOS 项目中以及一些 Objective-C++ 基础知识。最后,我们会看一个 demo 项目,看看如何在 iOS 设备上使用 OpenCV 实现人脸检测与人脸识别。

    OpenCV 概述

    概念
    OpenCV 的 API 是 C++ 的。它由不同的模块组成,这些模块中包含范围极为广泛的各种方法,从底层的图像颜色空间转换到高层的机器学习工具。

    使用 C++ API 并不是绝大多数 iOS 开发者每天都做的事,你需要使用 Objective-C++ 文件来调用 OpenCV 的函数。 也就是说,你不能在 Swift 或者 Objective-C 语言内调用 OpenCV 的函数。 这篇 OpenCV 的iOS 教程告诉你只要把所有用到 OpenCV 的类的文件后缀名改为 .mm 就行了,包括视图控制器类也是如此。这么干或许能行得通,却不是什么好主意。正确的方式是给所有你要在 app 中使用到的 OpenCV 功能写一层 Objective-C++ 封装。这些 Objective-C++ 封装把 OpenCV 的 C++ API 转化为安全的 Objective-C API,以方便地在所有 Objective-C 类中使用。走封装的路子,你的工程中就可以只在这些封装中调用 C++ 代码,从而避免掉很多让人头痛的问题,比如直接改文件后缀名会因为在错误的文件中引用了一个 C++ 头文件而产生难以追踪的编译错误。

    OpenCV 声明了命名空间 cv,因此 OpenCV 的类的前面会有个 cv:: 前缀,就像 cv::Mat、 cv::Algorithm 等等。你也可以在 .mm 文件中使用 using namespace cv 来避免在一堆类名前使用 cv:: 前缀。但是,在某些类名前你必须使用命名空间前缀,比如cv::Rect 和cv::Point,因为它们会跟定义在 MacTypes.h 中的Rect 和 Point 相冲突。尽管这只是个人偏好问题,我还是偏向在任何地方都使用 cv:: 以保持一致性。

    模块

    下面是在官方文档中列出的最重要的模块。

    • core:简洁的核心模块,定义了基本的数据结构,包括稠密多维数组 Mat 和其他模块需要的基本函数。
    • imgproc:图像处理模块,包括线性和非线性图像滤波、几何图像转换
      (缩放、仿射与透视变换、一般性基于表的重映射)、颜色空间转换、直方图等等。
    • video:视频分析模块,包括运动估计、背景消除、物体跟踪算法。
    • calib3d:包括基本的多视角几何算法、单体和立体相机的标定、对象姿态估计、双目立体匹配算法和元素的三维重建。
    • features2d:包含了显著特征检测算法、描述算子和算子匹配算法。
    • objdetect:物体检测和一些预定义的物体的检测 (如人脸、眼睛、杯子、人、汽车等)。
    • ml:多种机器学习算法,如 K 均值、支持向量机和神经网络。
    • highgui:一个简单易用的接口,提供视频捕捉、图像和视频编码等功能,还有简单的
    • UI 接口 (iOS 上可用的仅是其一个子集)。
    • gpu:OpenCV 中不同模块的 GPU 加速算法 (iOS 上不可用)。
    • ocl:使用 OpenCL 实现的通用算法 (iOS 上不可用)。

    一些其它辅助模块,如 Python 绑定和用户贡献的算法。
    基础类和操作
    OpenCV 包含几百个类。为简便起见,我们只看几个基础的类和操作,进一步阅读请参考全部文档。过一遍这几个核心类应该足以对这个库的机理产生一些感觉认识。

    cv::Mat
    cv::Mat 是 OpenCV 的核心数据结构,用来表示任意 N 维矩阵。因为图像只是 2 维矩阵的一个特殊场景,所以也是使用 cv::Mat 来表示的。也就是说,cv::Mat 将是你在 OpenCV 中用到最多的类。

    一个 cv::Mat 实例的作用就像是图像数据的头,其中包含着描述图像格式的信息。图像数据只是被引用,并能为多个 cv::Mat 实例共享。OpenCV 使用类似于 ARC 的引用计数方法,以保证当最后一个来自cv::Mat 的引用也消失的时候,图像数据会被释放。图像数据本身是图像连续的行的数组 (对 N 维矩阵来说,这个数据是由连续的 N-1 维数据组成的数组)。使用step[] 数组中包含的值,图像的任一像素地址都可通过下面的指针运算得到:

    uchar *pixelPtr = cvMat.data + rowIndex * cvMat.step[0] + colIndex * cvMat.step[1]  
    

    每个像素的数据格式可以通过 type() 方法获得。除了常用的每通道 8 位无符号整数的灰度图 (1 通道,CV_8UC1) 和彩色图 (3 通道,CV_8UC3),OpenCV 还支持很多不常用的格式,例如CV_16SC3 (每像素 3 通道,每通道使用 16 位有符号整数),甚至CV_64FC4 (每像素 4 通道,每通道使用 64 位浮点数)。

    cv::Algorithm
    Algorithm 是 OpenCV 中实现的很多算法的抽象基类,包括将在我们的 demo 工程中用到的 FaceRecognizer。它提供的 API 与苹果的 Core Image 框架中的CIFilter 有些相似之处。创建一个Algorithm 的时候使用算法的名字来调用 Algorithm::create(),并且可以通过get() 和set()方法来获取和设置各个参数,这有点像是键值编码。另外,Algorithm 从底层就支持从/向 XML 或 YAML 文件加载/保存参数的功能。

    在 iOS 上使用 OpenCV

    添加 OpenCV 到你的工程中
    集成 OpenCV 到你的工程中有三种方法:

    Objective-C++
    如前面所说,OpenCV 是一个 C++ 的 API,因此不能直接在 Swift 和 Objective-C 代码中使用,但能在 Objective-C++ 文件中使用。

    Objective-C++ 是 Objective-C 和 C++ 的混合物,让你可以在 Objective-C 类中使用 C++ 对象。clang 编译器会把所有后缀名为.mm 的文件都当做是 Objective-C++。一般来说,它会如你所期望的那样运行,但还是有一些使用 Objective-C++ 的注意事项。内存管理是你最应该格外注意的点,因为 ARC 只对 Objective-C 对象有效。当你使用一个 C++ 对象作为类属性的时候,其唯一有效的属性就是assign。因此,你的dealloc 函数应确保 C++ 对象被正确地释放了。

    第二重要的点就是,如果你在 Objective-C++ 头文件中引入了 C++ 头文件,当你在工程中使用该 Objective-C++ 文件的时候就泄露了 C++ 的依赖。任何引入你的 Objective-C++ 类的 Objective-C 类也会引入该 C++ 类,因此该 Objective-C 文件也要被声明为 Objective-C++ 的文件。这会像森林大火一样在工程中迅速蔓延。所以,应该把你引入 C++ 文件的地方都用#ifdef __cplusplus 包起来,并且只要可能,就尽量只在.mm 实现文件中引入 C++ 头文件。

    要获得更多如何混用 C++ 和 Objective-C 的细节,请查看 Matt Galloway 写的这篇教程。http://www.raywenderlich.com/62989/introduction-c-ios-developers-part-1

    Demo:人脸检测与识别

    现在,我们对 OpenCV 及如何把它集成到我们的应用中有了大概认识,那让我们来做一个小 demo 应用:从 iPhone 的摄像头获取视频流,对它持续进行人脸检测,并在屏幕上标出来。当用户点击一个脸孔时,应用会尝试识别这个人。如果识别结果正确,用户必须点击 “Correct”。如果识别错误,用户必须选择正确的人名来纠正错误。我们的人脸识别器就会从错误中学习,变得越来越好。

    demo 应用的源码

    视频拍摄
    OpenCV 的 highgui 模块中有个类,CvVideoCamera,它把 iPhone 的摄像机抽象出来,让我们的 app 通过一个代理函数- (void)processImage:(cv::Mat&)image 来获得视频流。CvVideoCamera 实例可像下面这样进行设置:

    CvVideoCamera *videoCamera = [[CvVideoCamera alloc] initWithParentView:view];  
    videoCamera.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront;  
    videoCamera.defaultAVCaptureSessionPreset = AVCaptureSessionPreset640x480;  
    videoCamera.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationPortrait;  
    videoCamera.defaultFPS = 30;  
    videoCamera.grayscaleMode = NO;  
    videoCamera.delegate = self;  
    

    摄像头的帧率被设置为 30 帧每秒, 我们实现的 processImage 函数将每秒被调用 30 次。因为我们的 app 要持续不断地检测人脸,所以我们应该在这个函数里实现人脸的检测。要注意的是,如果对某一帧进行人脸检测的时间超过 1/30 秒,就会产生掉帧现象。

    ##人脸检测##

    其实你并不需要使用 OpenCV 来做人脸检测,因为 Core Image 已经提供了 CIDetector 类。用它来做人脸检测已经相当好了,并且它已经被优化过,使用起来也很容易:

    CIDetector *faceDetector = [CIDetector detectorOfType:CIDetectorTypeFace context:context options:@{CIDetectorAccuracy: CIDetectorAccuracyHigh}];
    NSArray *faces = [faceDetector featuresInImage:image];  
    

    从该图片中检测到的每一张面孔都在数组 faces 中保存着一个 CIFaceFeature 实例。这个实例中保存着这张面孔的所处的位置和宽高,除此之外,眼睛和嘴的位置也是可选的。

    另一方面,OpenCV 也提供了一套物体检测功能,经过训练后能够检测出任何你需要的物体。该库为多个场景自带了可以直接拿来用的检测参数,如人脸、眼睛、嘴、身体、上半身、下半身和笑脸。检测引擎由一些非常简单的检测器的级联组成。这些检测器被称为 Haar 特征检测器,它们各自具有不同的尺度和权重。在训练阶段,决策树会通过已知的正确和错误的图片进行优化。关于训练与检测过程的详情可参考此原始论文http://www.multimedia-computing.de/mediawiki//images/5/52/MRL-TR-May02-revised-Dec02.pdf

    // 正面人脸检测器训练参数的文件路径
    NSString *faceCascadePath = [[NSBundle mainBundle] pathForResource:@"haarcascade_frontalface_alt2"
    ofType:@"xml"];
    
    const CFIndex CASCADE_NAME_LEN = 2048;
    char *CASCADE_NAME = (char *) malloc(CASCADE_NAME_LEN);
    CFStringGetFileSystemRepresentation( (CFStringRef)faceCascadePath, CASCADE_NAME, CASCADE_NAME_LEN);
    
    CascadeClassifier faceDetector; //这些参数可以在OpenCV包的data/haarcascades文件夹中找到
    faceDetector.load(CASCADE_NAME);
    
    //开始人脸检测
    cv::Mat img;
    vector<cv::Rect> faceRects;
    double scalingFactor = 1.1; //用不同的尺度遍历检测不同大小的人脸,scalingFactor决定每次遍历尺度会变大多少倍。
    int minNeighbors = 2; //拥有少于minNeighbors个符合条件的邻居像素人脸区域会被拒绝掉。
    int flags = 0; //1.x的遗留物,始终设置为0
    cv::Size minimumSize(30,30); //寻找的人脸区域大小的最小值
    //faceRects向量会包含对识别获得的所有人脸区域。识别的人脸图像可以通过cv::Mat的()运算符提取出,方式为:cv::Mat faceImg = img(aFaceRect)
    faceDetector.detectMultiScale(img, faceRects,
         scalingFactor, minNeighbors, flags
         cv::Size(30, 30) );
    

    检测过程中,已训练好的分类器会用不同的尺度遍历输入图像的每一个像素,以检测不同大小的人脸。参数 scalingFactor 决定每次遍历分类器后尺度会变大多少倍。参数minNeighbors 指定一个符合条件的人脸区域应该有多少个符合条件的邻居像素才被认为是一个可能的人脸区域;如果一个符合条件的人脸区域只移动了一个像素就不再触发分类器,那么这个区域非常可能并不是我们想要的结果。拥有少于minNeighbors 个符合条件的邻居像素的人脸区域会被拒绝掉。如果minNeighbors 被设置为 0,所有可能的人脸区域都会被返回回来。参数flags 是 OpenCV 1.x 版本 API 的遗留物,应该始终把它设置为 0。最后,参数minimumSize 指定我们所寻找的人脸区域大小的最小值。faceRects 向量中将会包含对img 进行人脸识别获得的所有人脸区域。识别的人脸图像可以通过cv::Mat 的 () 运算符提取出来,调用方式很简单:cv::Mat faceImg = img(aFaceRect)。

    不管是使用 CIDetector 还是 OpenCV 的 CascadeClassifier,只要我们获得了至少一个人脸区域,我们就可以对图像中的人进行识别了。

    ##人脸识别##
    OpenCV 自带了三个人脸识别算法:Eigenfaces,Fisherfaces 和局部二值模式直方图 (LBPH)。如果你想知道它们的工作原理及相互之间的区别,请阅读 OpenCV 的详细文档。

    针对于我们的 demo app,我们将采用 LBPH 算法。因为它会根据用户的输入自动更新,而不需要在每添加一个人或纠正一次出错的判断的时候都要重新进行一次彻底的训练。

    要使用 LBPH 识别器,我们也用 Objective-C++ 把它封装起来。这个封装中暴露以下函数:

    + (FJFaceRecognizer *)faceRecognizerWithFile:(NSString *)path;
    - (NSString *)predict:(UIImage*)img confidence:(double *)confidence;
    - (void)updateWithFace:(UIImage *)img name:(NSString *)name;
    

    像下面这样用工厂方法来创建一个 LBPH 实例:

    + (FJFaceRecognizer *)faceRecognizerWithFile:(NSString *)path {
        FJFaceRecognizer *fr = [FJFaceRecognizer new];
        fr->_faceClassifier = createLBPHFaceRecognizer();
        fr->_faceClassifier->load(path.UTF8String);
        return fr;
    }
    

    预测函数可以像下面这样实现:

    - (NSString *)predict:(UIImage*)img confidence:(double *)confidence {
        cv::Mat src = [img cvMatRepresentationGray];
        int label;
        self->_faceClassifier->predict(src, label, *confidence);
        return _labelsArray[label];
    }
    

    请注意,我们要使用一个类别方法把 UIImage 转化为 cv::Mat。此转换本身倒是相当简单直接:使用CGBitmapContextCreate 创建一个指向cv::Image 中的 data 指针所指向的数据的CGContextRef。当我们在此图形上下文中绘制此UIImage 的时候,cv::Image 的data 指针所指就是所需要的数据。更有趣的是,我们能对一个 Objective-C 类创建一个 Objective-C++ 的类别,并且确实管用。

    另外,OpenCV 的人脸识别器仅支持整数标签,但是我们想使用人的名字作标签,所以我们得通过一个 NSArray 属性来对二者实现简单的转换。

    一旦识别器给了我们一个识别出来的标签,我们把此标签给用户看,这时候就需要用户给识别器一个反馈。用户可以选择,“是的,识别正确”,也可以选择,“不,这是 Y,不是 X”。在这两种情况下,我们都可以通过人脸图像和正确的标签来更新 LBPH 模型,以提高未来识别的性能。使用用户的反馈来更新人脸识别器的方式如下:

    - (void)updateWithFace:(UIImage *)img name:(NSString *)name {
        cv::Mat src = [img cvMatRepresentationGray];
        NSInteger label = [_labelsArray indexOfObject:name];
        if (label == NSNotFound) {
            [_labelsArray addObject:name];
            label = [_labelsArray indexOfObject:name];
        }
        vector<cv::Mat> images = vector<cv::Mat>();
        images.push_back(src);
        vector<int> labels = vector<int>();
        labels.push_back((int)label);
        self->_faceClassifier->update(images, labels);
    }
    

    这里,我们又做了一次了从 UIImage 到 cv::Mat、int 到 NSString 标签的转换。我们还得如 OpenCV 的 FaceRecognizer::update API所期望的那样,把我们的参数放到std::vector 实例中去。

    如此“预测,获得反馈,更新循环”,就是文献上所说的监督式学习:zh.wikipedia.org/wiki/%E7%9B%A3%E7%9D%A3%E5%BC%8F%E5%AD%B8%E7%BF%92

    展开全文
  • Cocos2d-x调用IOS设备摄像头可以混编来实现 1.新建一个OC类Camera Camera.h #import #import #include "cocos2d.h" @interface Camera :UIViewController { } +(Camera*)initCamera; //打开摄像头 -(void)Open...

    Cocos2d-x开启Ios摄像头时,可以混编来实现。在这里可以写一个Oc类专门负责Ios拍照,当需要拍照功能时,只需添加该类到工程即可,这样就简单实现了Ios拍照模块化。

    #import <Foundation/Foundation.h>
    #include "cocos2d.h"
    @interface Camera :UIViewController<UIImagePickerControllerDelegate,UINavigationControllerDelegate> {
        
    }
    +(Camera*)TakePhoto;
    //打开摄像头
    -(void)OpenCamera;
    @end
    

    #import "TakePhoto.h"
    #include "cocos2d.h"
    @implementation Camera
    
    +(Camera*)TakePhoto
    {
        UIWindow *window = [[UIApplication sharedApplication] keyWindow];
        Camera *viewController = [[Camera alloc] init];
        [window addSubview:viewController.view];
        [viewController OpenCamera];
        
        return viewController;
    }
    -(void)OpenCamera
    {
        
        NSLog(@"启动相机");
        UIImagePickerController *picker= [[UIImagePickerController alloc] init];
        picker.delegate = self;
        
        if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
        {
            picker.sourceType = UIImagePickerControllerSourceTypeCamera;
            
        }
        else
        {
            NSLog(@"模拟器无法打开相机");
        }
        [self presentViewController:picker animated:YES completion:nil];
        
    }
    
    //拍照
    - (void)imagePickerController:(UIImagePickerController *)picker
    didFinishPickingMediaWithInfo:(NSDictionary *)info
    {
        NSLog(@"拍照");
        UIImage *originImage = [info objectForKey:UIImagePickerControllerOriginalImage];
        //保存原图到相册
        UIImageWriteToSavedPhotosAlbum(originImage, nil, nil, nil);
        
        [picker dismissViewControllerAnimated:false completion:nil];
        [self.view removeFromSuperview];
        
        
    }
    //取消
    - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
        NSLog(@"取消");
        
        [picker dismissViewControllerAnimated:false completion:nil];
        [self.view removeFromSuperview];
        
        
    }
    
    -(void) dealloc
    {
        [self release];
        [super dealloc];
    }
    @end


    在需要的地方,比如点击一个按钮后打开摄像头,

    那么如下调用即可:

        [Camera TakePhoto];


    展开全文
  • -目录 - 国内3D动态人脸识别现状概况 -新形势下人脸识别技术发展潜力 -基于深度学习的3D动态人脸...众所周知,在3D人脸识别整体技术方案方面,目前全球范围内掌握3D人脸识别核心技术的公司并不多,特别是在核心...

    - 目录

    - 国内3D动态人脸识别现状概况

    - 新形势下人脸识别技术发展潜力

    基于深度学习的3D动态人脸识别技术分析

    1. 非线性数据建模方法

    2. 基于3D变形模型的人脸建模

    案例结合——世纪晟人脸识别实现三维人脸建模

     

     

    · 3D动态人脸识别现状概况

     

    众所周知,在3D人脸识别整体技术方案方面,目前全球范围内掌握3D人脸识别核心技术的公司并不多,特别是在核心算法、芯片层面。然而,从2D到3D,技术更迭升级势在必行。

     

    3D人脸识别主要采用的是主动光技术,通过红外发光器发射出一束光,形成光斑,再通过IR 摄像头读取该图案,并对点状图在物体上发生的扭曲、以及点与点之间的距离进行计算,再加上RGB图像,结合起来就构成了一个3D模型,传闻中的苹果iPhone X 就采用的就是类似这样的3D摄像头模组。

     

    尽管3D成像非常有市场前景,但在3D人脸识别领域,世纪晟科技认为除手机移动端领域苹果IOS系统外,其它行业要消化3D人脸识别的软件原理、硬件设计等,还需要较长的一段时间才能完成。

     

    · 新形势下人脸识别技术发展潜力

     

    据前瞻产业研究院发布的《人脸识别行业市场前瞻与投资战略规划分析报告》数据显示,2009年,全球生物识别市场规模为34.22亿美元,其中,人脸识别占比11.4%,市场规模约3.90亿美元;到2016年,全球生物识别市场规模在127.13亿美元左右,其中人脸识别规模约26.53亿美元,占比在20%左右。

     

    从技术发展趋势来看,目前,越来越多研究机构开始对人脸识别技术进行更深入的研究,以寻求更好、更新的人脸识别技术。

    从市场趋势来看,随着高科技信息技术的快速发展,未来人脸识别技术将逐渐向市场化、产品化的方向发展,目前随着世纪晟人脸识别技术应用范围的扩大,针对人脸识别产品、行业应用领域将越来越多。

     

    人脸识别作为人类视觉上最杰出的能力之一,优势明显,相比指纹识别、虹膜识别等传统的生物识别方式,具有非接触性、非侵扰性、硬件基础完善和采集快捷便利、可拓展性好的优势,未来将成为识别主导技术。

     

    · 基于深度学习的3D动态人脸识别技术实现

     

    1. 非线性数据建模方法

     

    > 独立成分分析

     

    > 主曲线和曲面

     

     

    2. 基于3D变形模型的人脸建模

     

    · 案例结合——世纪晟人脸识别技术实现三维人脸建模

     

    人脸建模,即是根据输入的人脸图像,如眼睛、鼻尖、嘴角点、眉毛以及人脸各部件轮廓点等,自动定位出面部关键特征点。世纪晟人脸识别结合计算机视觉、深度学习技术的研究和应用,以3D动态人脸识别技术为算法核心,不断深耕生物识别技术,提升人脸识别领域行业管理方案解决能力。

     

    1、世纪晟人脸识别技术分析--人脸检测

     

    人脸检测的目的就是在一张图中找到所有的人脸,MTCNN(多任务级联卷积神经网络)人脸检测算法更有效地解决了传统算法对环境要求高、人脸要求高、检测耗时高的弊端。

    2、世纪晟人脸识别技术分析活体检测

     

    在生物识别系统中,为防止恶意者伪造和窃取他人的生物特征用于身份认证,生物识别系统需具有活体检测功能,即判断提交的生物特征是否来自有生命的个体。

    3、世纪晟人脸识别技术分析3D人脸建模

    人脸建模任务即根据输入的人脸图像,自动定位出面部关键特征点,如眼睛、鼻尖、嘴角点、眉毛以及人脸各部件轮廓点等。

    4、世纪晟人脸识别技术分析特征点提取

    世纪晟科技采用DeepID网特征提取+softmax分类的方式,提取人脸中136个关键点,达到高精度,高识别率的效果。

    · 算法示意图

     

    · DeepNets的训练网络示意图

     

    5、世纪晟人脸识别技术分析特征点比对

    通过之前的处理方式,将整体特征点结合局部特征点,进行三维人脸识别,输出比对结果。

     

    转载于:https://www.cnblogs.com/sjskj/p/9511528.html

    展开全文
  • 2018-09-04更新: 很久没有更新文章了,工作之余花时间看了之前写的这篇文章并运行了之前写的配套Demo,通过...和 Vision (iOS 11新API)两种库实现人脸面部识别,敬请期待~~ OC版下载地址, swift版下载地址 Cor...
    2018-09-04更新: 很久没有更新文章了,工作之余花时间看了之前写的这篇文章并运行了之前写的配套Demo,通过打印人脸特征CIFaceFeature的属性,发现识别的效果并不是很好,具体说明见文章最底部的更新标题,后续我将分别用OpenCV(跨平台计算机视觉库) 和 Vision (iOS 11新API)两种库实现人脸面部识别,敬请期待~~
    OC版下载地址, swift版下载地址
    
    

    CoreImage是Cocoa Touch中一个强大的API,也是iOS SDK中的关键部分,不过它经常被忽视。在本篇教程中,我会带大家一起验证CoreImage的人脸识别特性。在开始之前,我们先要简单了解下CoreImage framework 组成
    CoreImage framework组成
    Apple 已经帮我们把image的处理分类好,来看看它的结构:
    在这里插入图片描述
    主要分为三个部分:

    1.定义部分:CoreImage 和CoreImageDefines。见名思义,代表了CoreImage 这个框架和它的定义。
    2.操作部分:

    滤镜(CIFliter):CIFilter 产生一个CIImage。典型的,接受一到多的图片作为输入,经过一些过滤操作,产生指定输出的图片。
    检测(CIDetector):CIDetector 检测处理图片的特性,如使用来检测图片中人脸的眼睛、嘴巴、等等。
    特征(CIFeature):CIFeature 代表由 detector处理后产生的特征。

    3.图像部分:

    画布(CIContext):画布类可被用与处理Quartz 2D 或者 OpenGL。可以用它来关联CoreImage类。如滤镜、颜色等渲染处理。
    颜色(CIColor): 图片的关联与画布、图片像素颜色的处理。
    向量(CIVector): 图片的坐标向量等几何方法处理。
    图片(CIImage): 代表一个图像,可代表关联后输出的图像。

    在了解上述基本知识后,我们开始通过创建一个工程来带大家一步步验证Core Image的人脸识别特性。
    将要构建的应用

    iOS的人脸识别从iOS 5(2011)就有了,不过一直没怎么被关注过。人脸识别API允许开发者不仅可以检测人脸,也可以检测到面部的一些特殊属性,比如说微笑或眨眼。
    首先,为了了解Core Image的人脸识别技术我们会创建一个app来识别照片中的人脸并用一个方框来标记它。在第二个demo中,让用户拍摄一张照片,检测其中的人脸并检索人脸位置。这样一来,就充分掌握了iOS中的人脸识别,并且学会如何利用这个强大却总被忽略的API。
    话不多说,开搞!
    建立工程(我用的是Xcode8.0)

    这里提供了初始工程,当然你也可以自己创建(主要是为了方便大家)点我下载 用Xcode打开下载后的工程,可以看到里面只有一个关联了IBOutlet和imageView的StoryBoard。

    在这里插入图片描述
    使用CoreImage识别人脸

    在开始工程中,故事板中的imageView组件与代码中的IBOutlet已关联,接下来要编写实现人脸识别的代码部分。在ViewController.swift文件中写下如下代码:

    import UIKit
    import CoreImage // 引入CoreImage
    class ViewController: UIViewController {
        @IBOutlet weak var personPic: UIImageView!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            personPic.image = UIImage(named: "face-1")
            // 调用detect
            detect()
    
        }
        //MARK: - 识别面部
        func detect() {
            // 创建personciImage变量保存从故事板中的UIImageView提取图像并将其转换为CIImage,使用Core Image时需要用CIImage
            guard let personciImage = CIImage(image: personPic.image!) else {
                return
            }
            // 创建accuracy变量并设为CIDetectorAccuracyHigh,可以在CIDetectorAccuracyHigh(较强的处理能力)与CIDetectorAccuracyLow(较弱的处理能力)中选择,因为想让准确度高一些在这里选择CIDetectorAccuracyHigh
            let accuracy = [CIDetectorAccuracy: CIDetectorAccuracyHigh]
            // 这里定义了一个属于CIDetector类的faceDetector变量,并输入之前创建的accuracy变量
            let faceDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: accuracy)
            // 调用faceDetector的featuresInImage方法,识别器会找到所给图像中的人脸,最后返回一个人脸数组
            let faces = faceDetector?.features(in: personciImage)
            // 循环faces数组里的所有face,并将识别到的人脸强转为CIFaceFeature类型
            for face in faces as! [CIFaceFeature] {
                
                print("Found bounds are \(face.bounds)")
                // 创建名为faceBox的UIView,frame设为返回的faces.first的frame,绘制一个矩形框来标识识别到的人脸
                let faceBox = UIView(frame: face.bounds)
                // 设置faceBox的边框宽度为3
                faceBox.layer.borderWidth = 3
                // 设置边框颜色为红色
                faceBox.layer.borderColor = UIColor.red.cgColor
                // 将背景色设为clear,意味着这个视图没有可见的背景
                faceBox.backgroundColor = UIColor.clear
                // 最后,把这个视图添加到personPic imageView上
                personPic.addSubview(faceBox)
                // API不仅可以帮助你识别人脸,也可识别脸上的左右眼,我们不在图像中标识出眼睛,只是给你展示一下CIFaceFeature的相关属性
                if face.hasLeftEyePosition {
                    print("Left eye bounds are \(face.leftEyePosition)")
                }
                
                if face.hasRightEyePosition {
                    print("Right eye bounds are \(face.rightEyePosition)")
                }
            }
        }
    }
    

    编译并运行app,结果应如下图所示:
    在这里插入图片描述
    2.png
    根据控制台的输出来看,貌似识别器识别到了人脸:
    Found bounds are (314.0, 243.0, 196.0, 196.0)
    当前的实现中没有解决的问题:

    人脸识别是在原始图像上进行的,由于原始图像的分辨率比image view要高,因此需要设置image view的content mode为aspect fit(保持纵横比的情况下缩放图片)。为了合适的绘制矩形框,需要计算image view中人脸的实际位置与尺寸
    还要注意的是,CoreImage与UIView使用两种不同的坐标系统(看下图),因此要实现一个CoreImage坐标到UIView坐标的转换。

    UIView坐标系:

    在这里插入图片描述
    CoreImage坐标系:
    在这里插入图片描述
    现在使用下面的代码替换detect()方法:

    func detect1() {
    
        guard let personciImage = CIImage(image: personPic.image!) else { return }
        let accuracy = [CIDetectorAccuracy: CIDetectorAccuracyHigh]
        let faceDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: accuracy)
        let faces = faceDetector?.features(in: personciImage)
            
        // 转换坐标系
        let ciImageSize = personciImage.extent.size
        var transform = CGAffineTransform(scaleX: 1, y: -1)
        transform = transform.translatedBy(x: 0, y: -ciImageSize.height)
            
        for face in faces as! [CIFaceFeature] {
            print("Found bounds are \(face.bounds)")     
            // 应用变换转换坐标
            var faceViewBounds = face.bounds.applying(transform)
            // 在图像视图中计算矩形的实际位置和大小
            let viewSize = personPic.bounds.size
            let scale = min(viewSize.width / ciImageSize.width, viewSize.height / ciImageSize.height)
            let offsetX = (viewSize.width - ciImageSize.width * scale) / 2
            let offsetY = (viewSize.height - ciImageSize.height * scale) / 2
                
            faceViewBounds = faceViewBounds.applying(CGAffineTransform(scaleX: scale, y: scale))
            faceViewBounds.origin.x += offsetX
            faceViewBounds.origin.y += offsetY
                
            let faceBox = UIView(frame: faceViewBounds)
            faceBox.layer.borderWidth = 3
            faceBox.layer.borderColor = UIColor.red.cgColor
            faceBox.backgroundColor = UIColor.clear
            personPic.addSubview(faceBox)
                
            if face.hasLeftEyePosition {
                print("Left eye bounds are \(face.leftEyePosition)")
            }
                
            if face.hasRightEyePosition {
                print("Right eye bounds are \(face.rightEyePosition)")
            }
        }
    }
    

    上述代码中,首先使用仿射变换(AffineTransform)将Core Image坐标转换为UIKit坐标,然后编写了计算实际位置与矩形视图尺寸的代码。

    再次运行app,应该会看到人的面部周围会有一个框。OK,你已经成功使用Core Image识别出了人脸。
    在这里插入图片描述
    但是有的童鞋在使用了上面的代码运行后可能会出现方框不存在(即没有识别人脸)这种情况,这是由于忘记关闭Auto Layout以及Size Classes了。 选中storyBoard中的ViewController,选中view下的imageView。然后在右边的面板中的第一个选项卡中找到use Auto Layout ,将前面的✔️去掉就可以了

    在这里插入图片描述
    经过上面的设置后我们再次运行App,就会看到图三出现的效果了。

    构建一个人脸识别的相机应用

    想象一下你有一个用来照相的相机app,照完相后你想运行一下人脸识别来检测一下是否存在人脸。若存在一些人脸,你也许想用一些标签来对这些照片进行分类。我们不会构建一个保存照片后再处理的app,而是一个实时的相机app,因此需要整合一下UIImagePicker类,在照完相时立刻进行人脸识别。
    在开始工程中已经创建好了CameraViewController类,使用如下代码实现相机的功能:

    class CameraViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
        @IBOutlet var imageView: UIImageView!
        let imagePicker = UIImagePickerController()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            imagePicker.delegate = self
        }
        
        @IBAction func takePhoto(_ sender: AnyObject) {
            
            if !UIImagePickerController.isSourceTypeAvailable(.camera) {
                return
            }
            
            imagePicker.allowsEditing = false
            imagePicker.sourceType = .camera
            
            present(imagePicker, animated: true, completion: nil)
        }
    
        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
    
            if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
                imageView.contentMode = .scaleAspectFit
                imageView.image = pickedImage
            }
            
            dismiss(animated: true, completion: nil)
            self.detect()
        }
        
        func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
            dismiss(animated: true, completion: nil)
        }
    }
    

    前面几行设置UIImagePicker委托为当前视图类,在didFinishPickingMediaWithInfo方法(UIImagePicker的委托方法)中设置imageView为在方法中所选择的图像,接着返回上一视图调用detect函数。
    还没有实现detect函数,插入下面代码并分析一下

    func detect() {
        let imageOptions =  NSDictionary(object: NSNumber(value: 5) as NSNumber, forKey: CIDetectorImageOrientation as NSString)
        let personciImage = CIImage(cgImage: imageView.image!.cgImage!)
        let accuracy = [CIDetectorAccuracy: CIDetectorAccuracyHigh]
        let faceDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: accuracy)
        let faces = faceDetector?.features(in: personciImage, options: imageOptions as? [String : AnyObject])
            
        if let face = faces?.first as? CIFaceFeature {
            print("found bounds are \(face.bounds)")
                
            let alert = UIAlertController(title: "提示", message: "检测到了人脸", preferredStyle: UIAlertControllerStyle.alert)
            alert.addAction(UIAlertAction(title: "确定", style: UIAlertActionStyle.default, handler: nil))
            self.present(alert, animated: true, completion: nil)
                
            if face.hasSmile {
                print("face is smiling");
            }
                
            if face.hasLeftEyePosition {
                print("左眼的位置: \(face.leftEyePosition)")
            }
                
            if face.hasRightEyePosition {
                print("右眼的位置: \(face.rightEyePosition)")
            }
        } else {
            let alert = UIAlertController(title: "提示", message: "未检测到人脸", preferredStyle: UIAlertControllerStyle.alert)
            alert.addAction(UIAlertAction(title: "确定", style: UIAlertActionStyle.default, handler: nil))
            self.present(alert, animated: true, completion: nil)
        }
    }
    

    这个detect()函数与之前实现的detect函数非常像,不过这次只用它来获取图像不做变换。当识别到人脸后显示一个警告信息“检测到了人脸!”,否则显示“未检测到人脸”。运行app测试一下:

    在这里插入图片描述
    在这里插入图片描述
    我们已经使用到了一些CIFaceFeature的属性与方法,比如,若想检测人物是否微笑,可以调用.hasSmile,它会返回一个布尔值。可以分别使用.hasLeftEyePosition与.hasRightEyePosition检测是否存在左右眼。

    同样,可以调用hasMouthPosition来检测是否存在嘴,若存在则可以使用mouthPosition属性,如下所示:

    if (face.hasMouthPosition) {
        print("mouth detected")
    }
    

    如你所见,使用Core Image来检测面部特征是非常简单的。除了检测嘴、笑容、眼睛外,也可以调用leftEyeClosed与rightEyeClosed检测左右眼是否睁开,这里就不在贴出代码了。
    总结

    在这篇教程中尝试了CoreImage的人脸识别API与如何在一个相机app中应用它,构建了一个简单的UIImagePicker来选取照片并检测图像中是否存在人物。
    如你所见,Core Image的人脸识别是个强大的API!希望这篇教程能给你提供一些关于这个鲜为人知的iOS API有用的信息。
    点击swift版地址,OC版地址下载最终工程, 如果觉得对您有帮助的话,请帮我点个星星哦,您的星星是对我最大的支持。(__) 嘻嘻……**
    更新:
    很久没有更新文章了,工作之余花时间回顾了之前写的这篇文章并运行了之前写的配套Demo,通过打印人脸特征CIFaceFeature的属性(如下),发现识别的效果并不是很好,如下图:

    在这里插入图片描述
    人脸特征CIFaceFeature的属性

    /** CIDetector发现的脸部特征。
      所有的位置都是相对于原始图像. */
    NS_CLASS_AVAILABLE(10_7, 5_0)
    @interface CIFaceFeature : CIFeature
    {
        CGRect bounds;
        BOOL hasLeftEyePosition;
        CGPoint leftEyePosition;
        BOOL hasRightEyePosition;
        CGPoint rightEyePosition;
        BOOL hasMouthPosition;
        CGPoint mouthPosition;
        
        BOOL hasTrackingID;
        int trackingID;
        BOOL hasTrackingFrameCount;
        int trackingFrameCount;
        
        BOOL hasFaceAngle;
        float faceAngle;
        
        BOOL hasSmile;
        BOOL leftEyeClosed;
        BOOL rightEyeClosed;
    }
    
    /** coordinates of various cardinal points within a face.
     脸部各个基点的坐标。
    
     Note that the left eye is the eye on the left side of the face
     from the observer's perspective. It is not the left eye from
     the subject's perspective.
    请注意,左眼是脸左侧的眼睛从观察者的角度来看。 这不是左眼主体的视角.
     */
    @property (readonly, assign) CGRect bounds;              // 指示图像坐标中的人脸位置和尺寸的矩形。
    @property (readonly, assign) BOOL hasLeftEyePosition;    // 指示检测器是否找到了人脸的左眼。
    @property (readonly, assign) CGPoint leftEyePosition;    // 左眼的坐标
    @property (readonly, assign) BOOL hasRightEyePosition;   // 指示检测器是否找到了人脸的右眼。
    @property (readonly, assign) CGPoint rightEyePosition;   // 右眼的坐标
    @property (readonly, assign) BOOL hasMouthPosition;      // 指示检测器是否找到了人脸的嘴部
    @property (readonly, assign) CGPoint mouthPosition;      // 嘴部的坐标
    
    @property (readonly, assign) BOOL hasTrackingID;         // 指示面部对象是否具有跟踪ID。
    /**
     * 关于trackingID:
     * coreImage提供了在视频流中检测到的脸部的跟踪标识符,您可以使用该标识符来识别在一个视频帧中检测到的CIFaceFeature对象是在先前视频帧中检测到的同一个脸部。
     * 只有在框架中存在人脸并且不与特定人脸相关联时,该标识符才会一直存在。如果脸部移出视频帧并在稍后返回到帧中,则分配另一个ID。 (核心图像检测面部,但不识别特定的面部。)
     * 这个有点抽象
     */
    @property (readonly, assign) int trackingID;
    @property (readonly, assign) BOOL hasTrackingFrameCount; // 指示面部对象的布尔值具有跟踪帧计数。
    @property (readonly, assign) int trackingFrameCount;     // 跟踪帧计数
    
    @property (readonly, assign) BOOL hasFaceAngle;          // 指示是否有关于脸部旋转的信息可用。
    @property (readonly, assign) float faceAngle;            // 旋转是以度数逆时针测量的,其中零指示在眼睛之间画出的线相对于图像方向是水平的。
    
    @property (readonly, assign) BOOL hasSmile;              // 是否有笑脸
    @property (readonly, assign) BOOL leftEyeClosed;         // 左眼是否闭上
    @property (readonly, assign) BOOL rightEyeClosed;        // 右眼是否闭上
    

    问题:那么如何让人脸识别的效果更好呢? 如何让面部识别点更加精确呢?有没有别的方法呢? 答案是肯定的。
    现在市面上有很多成熟的面部识别产品:

    Face++, 收费

    Video++,收费

    ArcFace 虹软人脸认知引擎, 收费

    百度云人脸识别, 收费

    阿里云识别, 收费

    等等, 我们看到都是收费的。 当然这些sdk是可以试用的。如果你有折腾精神,想自己尝试人脸识别的实现,我们可以一起交流。 毕竟市面上的这些sdk也不是一开始就有的, 也是通过人们不断研究开发出来的。 而且自己折腾过程中,通过不断地遇坑爬坑,对知识的理解更加深透,自己的技术也会有增进,不是吗? 不好意思,有点扯远了。
    Core Image只是简单的图像识别, 并不能对流中的人脸进行识别。 它只适合对图片的处理。比较有名的OpenCV(跨平台计算机视觉库)就可以用来进行面部识别,识别精度自然很高。还有就是iOS 11.0+ 推出的Vision框架(让我们轻松访问苹果的模型,用于面部检测、面部特征点、文字、矩形、条形码和物体)也可以进行面部识别。后面我将会用这两个框架讲解如何进行面部识别。敬请期待!!!

    展开全文
  •  运用以下代码将原图中的人脸头像识别、提取、调整大小(这里是150*200),并分别保存。  运行环境:win7 64+VS2013+openCV3.1。  (PS:抱怨下vs和opencv的版本密切相关,注意安装配置)     #...
  • Unity 百度平台实现 简单的人脸识别系统 目录Unity 百度平台实现 简单的人脸识别系统人脸识别系统实时画面获取 代码很简单没有难度,都有注解,随便 看一看 就会了。 要使用百度 AI 平台 需要注册 创建应用 和下载 ...
  • 今天探索了一下人脸识别API的使用,这里分享一下。 首先通过微软的门户开通了认知服务账户,生成了密钥。在上面有API SDK的链接,打开后是github的1个项目,里面包含有android、ios、windows、Python的版本,下载...
  • unity人脸识别源码

    2020-07-05 23:30:58
    这是一款用于在Unity中基于OpenCV的识别脸部特征的插件,支持包含iOS、Android及WindowsPhone 8.1在内的移动平台以及PC端与WebGL平台。Dlib FaceLandmark Detector能够检测2D纹理、WebCamTexture以及图片中正面出现...
  • 一点背景知识 OpenCV 是一个开源的计算机视觉和机器学习库。它包含成千上万优化过的算法,为各种计算机视觉应用提供了一个通用工具包。根据这个项目的关于页面,OpenCV 已被广泛运用在各种项目上,从谷歌街景的图片...
  • 苹果首次将深度学习应用于人脸识别是在 iOS 10 上。通过 Vision 框架,开发者现在可以在 App 中将该技术与其他很多计算机视觉算法进行整合。为了保护用户隐私,保证有效运行,苹果在开发这个框架的过程中克服了大量...
  • 2018-09-04更新: 很久没有更新文章了,工作之余花时间看了之前写的这篇文章并运行了之前写的配套Demo,通过...和 Vision (iOS 11新API)两种库实现人脸面部识别,敬请期待~~OC版下载地址, swift版下载地址 ```Co...
  • 近日IFAA联盟发布“本地人脸识别安全解决方案”,用来实现金融级别现金支付的技术,“像iPhone X那样去人脸支付吧!安卓终于再一次追平了苹果”,并总结出“攻克了几乎是行业性的四大难题”:1,摄像头信息传输安全...
  • iOS黑科技之(CoreImage)静态人脸识别(一) 人脸识别原理简介:每一张图片都是由每一个像素点组成,而每一个像素点中又有对应的颜色值(如RGB),人的面部特征中,不同的五官,颜色值肯定存在差异,而人脸识别技术就是...
  • 在混合App开发中,如何在Web中调用本地摄像头并实现人脸识别功能?本文给出了一个解决方案,并给出了该方案的核心代码,其中前端使用vue,该方案适配了Android手机。
  • 人脸识别和关键点检测(二) 步骤 这篇博客虽然涉及的是看起来很前沿的人工智能,但对于我们初学者来说,入门并没有想象中的那么困难。这篇文章就是给有一定python基础(或者有其他编程语言基础)的同学,介绍一个...
  • 为什么要做这个呢? ... 前端实现思路是什么呢? ...创建一个sql 里面存一些有权限的人员信息包含照片等 用上传的照片和sql里面的数据进行对比人脸识别 进而知道进入当前系统的人是否具有某些权限 ...
  • 基于深度学习的人脸识别系统,一共用到了5个开源库:OpenCV(计算机视觉库)、Caffe(深度学习库)、Dlib(机器学习库)、libfacedetection(人脸检测库)、cudnn(gpu加速库)。 用到了一个开源的深度学习模型:...
  • 人脸识别贴纸 整个处理过程大致分为3个步骤:1、使用AVFoundation调用摄像头采集视频流获得图像信息2、使用CoreImage库判断采集到的图像信息中是否包含有人脸3、将结果使用OpenGL渲染显示到屏幕上 一、调用摄像头...
  • opencv -dnn人脸识别

    2016-10-27 15:50:15
    下面是使用opencv的dnn模块来进行人脸识别: 1:编译opencv3.1 首先下载opencv源码https://github.com/opencv/opencv 下载Cmake https://cmake.org/download/ 下载opencv的 具体的camk
1 2 3 4 5 ... 20
收藏数 1,562
精华内容 624
热门标签