TypechoJoeTheme

js-john's Blog

js_john

Stay hungry, Stay Naive.
网站页面

_

2021-08-23
/
0 评论
/
1,041 阅读
/
正在检测是否收录...
08/23

概览

完成了上一节的工作,我们可以获取到摄像头的每一帧图像。为了使我们的程序对图片有更好的兼容性,不对摄像头的参数设置作过多要求。所有的图像质量检测与缩放,增强操作均通过 OpenCV 完成。

总体思路与当前进度

要开发的是一个 1:N 的人脸识别应用,实现的人脸识别的总体思路是
0、完成基础性工作,引入主要的依赖包
1、通过 iOS 内置的摄像头捕获画面
2、将每一帧的图像传递给 OpenCV 做缩放和增强处理
3、处理后的图像交给 Vision 检测人脸
4、使用 Dlib 提取人脸的特征值,获得特征向量
5、建立用于保存人脸信息的数据库
6、从摄像头获取图像提取特征向量,逐一与数据库中的特征向量计算距离
7、若距离足够接近,输出本次识别结果,否则输出未识别

本节进度是:2、将每一帧的图像传递给 OpenCV 做缩放和增强处理

通过 OpenCV 进行图像处理

所有的需要检测的图像首先会完成一次基础的质量检测,检测维度包括图像尺寸与清晰度两项。只有通过质量检测的图像才会被交由 Dlib 完成人脸检测与特征提取。

质量检测

图像质量检测主要包括以下两个维度:

  • 图像尺寸指标

    • 图像尺寸应该至少不低于 480 * 480 分辨率
  • 图像清晰度指标

    • 图像应该正确对焦
    • 图像的噪点数量应该在可接受范围内
    • 图像的亮度应该在可接受范围内

图像质量检测器开发

在一切开始前,我们需要知道一些基础概念。iOS 使用的图像类型通常是 UIImageOpenCV 处理的图像是 cv::Mat 类型。为了能让 OpenCV 可以处理图像,首先我们必须将 UIImage 转换成 cv::Mat

转换 UIImage 为 cv::Mat

OpenCV 提供了转换函数供我们使用,只需 import imgcodecs 模块的 ios.h 头文件即可方便的调用 UIImagecv::Mat 的转换函数。值得一提的是,OpenCV 中处理的彩色图像通常是 BGR 通道,故我们还需将 UIImage 的 RGB 通道转换成 BRG 格式。

FaceRecognizer.mm 文件

#import <opencv2/imgcodecs/ios.h>

图像质量检测函数

UIImage 的定义在 iOS 的 UI 框架 UIKit中,首先在 FaceRecognizer.h 文件中引入 iOS 的 UIKit 框架的头文件,然后添加一个检测图像的实例方法,返回 ImageQualityResult 表示图片质量检测的结果。移除之前用于检查 OpenCV 和 Dlib 是否正常引入的 test 函数。

FaceRecognizer.h 文件

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN
struct ImageQualityResult {
    bool passed;
    double brightness;
    double blur;
    int minSize;
};
@interface FaceRecognizer : NSObject
+ (FaceRecognizer *) shared;
- (struct ImageQualityResult) checkImageQuality: (UIImage *) img;
@end

NS_ASSUME_NONNULL_END

接下来编写对应的实现文件里的 checkImageQuality 函数,还有对应的检测指标函数 checkImageQualityBlurcheckImageQualityBriteness,均返回对应的检测值。检测指标函数不对外公开,所以直接在实现文件中编写即可。Objc 代码可以与 C++ 代码混合编译,为了方便 OpenCV 处理图像,这些检测指标函数均为 C++ 代码。

FaceRecognizer.mm 文件

#import <opencv2/opencv.hpp>
#import <opencv2/imgcodecs/ios.h>
#import <dlib/image_processing.h>
#import <dlib/image_processing/frontal_face_detector.h>
#import <dlib/image_processing/render_face_detections.h>
#import <dlib/opencv.h>
#import "FaceRecognizer.h"
#import <stdio.h>

#define MIN_IMG_SIZE 480.0
#define MIN_SOBEL_VALUE 2.5
#define MIN_BRIGHTNESS_VALUE 80
#define MAX_BRIGHTNESS_VALUE 200


@implementation FaceRecognizer
FaceRecognizer *mFaceRecognizer;

+ (FaceRecognizer *) shared {
    if(!mFaceRecognizer) {
        mFaceRecognizer = [FaceRecognizer new];
    }
    return mFaceRecognizer;
}

- (struct ImageQualityResult) checkImageQuality: (UIImage *) img; {
    cv::Mat src;
    UIImageToMat(img, src);
    cv::cvtColor(src, src, cv::COLOR_RGB2BGR);
    struct ImageQualityResult result;
    result.minSize = MIN(src.cols, src.rows);
    if (result.minSize < MIN_IMG_SIZE) {
        result.passed = false;
        return result;
    }
    //若尺寸是符合规定的,再将图片根据长高的比例,保持原图比例缩小较短的一边的长度为 480 像素。
    resizeImg(src);
    cv::Mat gray;
    //转为灰度
    cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY);
    //检测清晰度
    result.blur = checkImageQualityBlur(gray);
    //如果小于设定的平均值,则判定为不够清晰。经过实践,该分辨率下,取2.5是比较合理的值。
    if (result.blur < MIN_SOBEL_VALUE) {
        result.passed = false;
        return result;
    }
    result.brightness = checkImageBriteness(src);
    //如果平均亮度在规定范围以外,择判定为过暗或过量。经过实践,范围定在80-200是比较理想的亮度值。
    if (result.brightness < MIN_BRIGHTNESS_VALUE || result.brightness > MAX_BRIGHTNESS_VALUE) {
        result.passed = false;
        return result;
    }
    result.passed = true;
    return result;
}

void resizeImg(cv::Mat &img) {
    int newWidth, newHeight;
    double ratio = img.cols * 1.0 / img.rows * 1.0; //图片长高比
    if (ratio > 1) {
        newHeight = MIN_IMG_SIZE;
        newWidth = MIN_IMG_SIZE * ratio;
    } else {
        newWidth = MIN_IMG_SIZE;
        newHeight = MIN_IMG_SIZE / ratio;
    }
    cv::resize(img, img, cv::Size(newWidth, newHeight));
    cv::cvtColor(img, img, cv::COLOR_RGBA2BGR);
}


double checkImageQualityBlur(cv::Mat &img) {
    cv::Mat sobel;
    //Tenengrad梯度方法利用Sobel算子分别计算水平和垂直方向的梯度,梯度值越高,图像越清晰。
    cv::Sobel(img, sobel, CV_16U, 1, 1);
    //图像的平均梯度值
    double meanValue = cv::mean(sobel)[0];
    return meanValue;
}

double checkImageBriteness(cv::Mat &img) {
    cv::Mat hsvImg;
    //将图像转换为HSV色彩空间,提取亮度信息。
    cv::cvtColor(img, hsvImg, cv::COLOR_BGR2HSV);
    //计算hsv中的亮度平均值
    double meanValue = cv::mean(hsvImg)[2];
    return meanValue;
}
@end

将检测结果显示在屏幕上

为了让我们在脱离控制台输出时也可以判断检测器是否正常工作,我们需要设计一些机制让检测的结果回传到 UI 里。得益于强大的 SwiftUI,我们可以很轻松的做到这一点。
CameraController 类中添加一个 imageQualityResult 的变量用于保存图像质量检测结果。
CameraController.swift 文件

...
class CameraController: ObservableObject {
...
@Published var imageQualityResult = ImageQualityResult.init()
...
}

FaceRecognizeView 中编写对应的视图元素代码,实现在屏幕的左上方显示质量指标,当图像达标时使用绿色字体,不达标时显示红色字体。
FaceRecognizeView.swift 文件

...
ZStack {
            CameraPreviewView(cameraController: cameraController)
            VStack {
                HStack {
                     Text("清晰度:\(cameraController.imageQualityResult.blur) 亮度:\(cameraController.imageQualityResult.brightness)")
                        .fontWeight(.bold)
                        .foregroundColor(cameraController.imageQualityResult.passed ? .green : .red)
                        .padding()
                        .background(Color.white)

                    Spacer()
                    
                }
                Spacer()
            }
        }
        .ignoresSafeArea()
...

定义一个委托,实现检测结果传递到 UI。
FaceRecognizeController.swift 文件

@objc protocol FaceRecognizerDelegate: NSObjectProtocol {
    @objc optional func updateImageQualityResult(result: ImageQualityResult)
}

CameraController.swift 文件

...
class CameraController: NSObject, ObservableObject, FaceRecognizerDelegate {
...
var faceRecognizeController: FaceRecognizeController!
...
func setup() {
        do {
            ...
            faceRecognizeController = FaceRecognizeController.init();
            faceRecognizeController.delegate = self
            ...
        } catch {
            print(error.localizedDescription)
        }
    }
...
 func updateImageQualityResult(result: ImageQualityResult) {
        DispatchQueue.main.async {
            self.imageQualityResult = result
        }
    }

FaceRecognizeController 中添加一个 delegete 变量,获得摄像头图像输出时执行图片质量检测。
FaceRecognizeController.swift 文件

class FaceRecognizeController: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate {
weak var delegate: FaceRecognizerDelegate?
...
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
       ...
        let image = UIImage(cgImage: cgImage)
        let result = faceRecognizer.checkImageQuality(image)
        self.delegate?.updateImageQualityResult?(result: result)
        if (!result.passed) {
            lock = false;
            return;
        }
        lock = false
    }
...
}

连接真机调试,运行 App。

场景1 画面清晰光线充足

场景2 未对焦

场景3 摄像头晃动导致的模糊

场景4 已对焦,环境昏暗

至此,程序已经有一定检测图像质量的能力,可以进入下一步的开发。


文章二维码
赞(1)
评论 (0)