play-services-vision:如何将人脸检测速度与相机预览速度同步?

Posted

技术标签:

【中文标题】play-services-vision:如何将人脸检测速度与相机预览速度同步?【英文标题】:play-services-vision: How do I sync the Face Detection speed with the Camera Preview speed? 【发布时间】:2016-09-06 06:58:10 【问题描述】:

我有一些代码可以让我在实时摄像头预览中检测人脸,并使用 Google 提供的 play-services-vision 库在他们的地标上绘制一些 GIF。

当人脸静止时效果很好,但当人脸以中等速度移动时,人脸检测器需要比相机的帧速率更长的时间来检测人脸新位置的地标。我知道这可能与位图绘制速度有关,但我采取了一些措施来尽量减少其中的延迟。

(基本上我会抱怨 GIF 的重新定位不够“流畅”)

编辑:我确实尝试过获取坐标检测代码...

    List<Landmark> landmarksList = face.getLandmarks();
    for(int i = 0; i < landmarksList.size(); i++)
    
        Landmark current = landmarksList.get(i);
        //canvas.drawCircle(translateX(current.getPosition().x), translateY(current.getPosition().y), FACE_POSITION_RADIUS, mFacePositionPaint);
        //canvas.drawCircle(current.getPosition().x, current.getPosition().y, FACE_POSITION_RADIUS, mFacePositionPaint);
        if(current.getType() == Landmark.LEFT_EYE)
        
            //Log.i("current_landmark", "l_eye");
            leftEyeX = translateX(current.getPosition().x);
            leftEyeY = translateY(current.getPosition().y);
        
        if(current.getType() == Landmark.RIGHT_EYE)
        
            //Log.i("current_landmark", "r_eye");
            rightEyeX = translateX(current.getPosition().x);
            rightEyeY = translateY(current.getPosition().y);
        
        if(current.getType() == Landmark.NOSE_BASE)
        
            //Log.i("current_landmark", "n_base");
            noseBaseY = translateY(current.getPosition().y);
            noseBaseX = translateX(current.getPosition().x);
        
        if(current.getType() == Landmark.BOTTOM_MOUTH) 
            botMouthY = translateY(current.getPosition().y);
            botMouthX = translateX(current.getPosition().x);
            //Log.i("current_landmark", "b_mouth "+translateX(current.getPosition().x)+" "+translateY(current.getPosition().y));
        
        if(current.getType() == Landmark.LEFT_MOUTH) 
            leftMouthY = translateY(current.getPosition().y);
            leftMouthX = translateX(current.getPosition().x);
            //Log.i("current_landmark", "l_mouth "+translateX(current.getPosition().x)+" "+translateY(current.getPosition().y));
        
        if(current.getType() == Landmark.RIGHT_MOUTH) 
            rightMouthY = translateY(current.getPosition().y);
            rightMouthX = translateX(current.getPosition().x);
            //Log.i("current_landmark", "l_mouth "+translateX(current.getPosition().x)+" "+translateY(current.getPosition().y));
        
    
    eyeDistance = (float)Math.sqrt(Math.pow((double) Math.abs(rightEyeX - leftEyeX), 2) + Math.pow(Math.abs(rightEyeY - leftEyeY), 2));
    eyeCenterX = (rightEyeX + leftEyeX) / 2;
    eyeCenterY = (rightEyeY + leftEyeY) / 2;
    noseToMouthDist = (float)Math.sqrt(Math.pow((double)Math.abs(leftMouthX - noseBaseX), 2) + Math.pow(Math.abs(leftMouthY - noseBaseY), 2));

...在 View draw 方法中的一个单独线程中,但它只是给我一个 SIGSEGV 错误。

我的问题:

    在这种情况下,将人脸检测器的处理速度与相机预览帧速率同步是正确的做法,还是相反,还是其他方式? 当人脸检测器在相机预览帧中找到人脸时,我是否应该在 FD 完成之前丢弃预览提供的帧?如果是这样,我该怎么做? 我是否应该在相机预览中仅使用 setClassificationMode(NO_CLASSIFICATIONS)setTrackingEnabled(false) 以加快检测速度? play-services-vision 库是否使用 OpenCV,实际上哪个更好?

编辑 2:

我读过一篇研究论文,使用 OpenCV,OpenCV 中可用的人脸检测和其他功能在 android 中更快,因为它们具有更高的处理能力。我想知道我是否可以利用它来加速人脸检测。

【问题讨论】:

你们能帮忙解决这个问题吗? ***.com/questions/45141098/… 【参考方案1】:

总会有一些延迟,因为任何面部检测器都需要一些时间来运行。当您绘制结果时,您通常会将其绘制在未来的帧上,在该帧中脸部可能已经移动了一点。

以下是一些减少延迟的建议:

Google 的视觉库提供的 CameraSource 实现会在需要时自动处理丢失的预览帧,以便尽可能保持最佳状态。如果您想在您的应用中加入类似的方法,请参阅此代码的开源版本:https://github.com/googlesamples/android-vision/blob/master/visionSamples/barcode-reader/app/src/main/java/com/google/android/gms/samples/vision/barcodereader/ui/camera/CameraSource.java#L1144

使用较低的相机预览分辨率(例如 320x240)将加快人脸检测速度。

如果您只跟踪一张人脸,使用 setProminentFaceOnly() 选项将加快人脸检测速度。使用这个和 LargestFaceFocusingProcessor 也会使这更快。

要使用 LargestFaceFocusingProcessor,请将其设置为人脸检测器的处理器。例如:

Tracker<Face> tracker = *your face tracker implementation*
detector.setProcessor(
    new LargestFaceFocusingProcessor.Builder(detector, tracker).build());

您的跟踪器实施将仅接收它最初找到的最大人脸的人脸更新。此外,它会向检测器发回信号,表明它只需要在该人脸可见的情况下对其进行跟踪。

如果您不需要检测较小的人脸,使用较大的 setMinFaceSize() 将使人脸检测更快。只检测较大的人脸会更快,因为它不需要花时间寻找较小的人脸。

如果您不需要睁眼或微笑指示,您可以关闭分类。但是,这只会给您带来很小的速度优势。

使用跟踪选项也会使此过程更快,但会以一定的准确性为代价。这对一些中间帧使用了预测算法,以避免在每一帧上运行全脸检测的开销。

【讨论】:

我该如何构建 LargestFaceFocusingProcessor? 我在上面的 LargestFaceFocusingProcessor 上添加了更多内容。【参考方案2】:

您无法保证人脸检测速度足够快,即使在头部运动适中时也不会显示明显的延迟。即使你成功地在你的开发设备上优化了它,你肯定会在成千上万的模型中找到另一个模型,那太慢了。

您的代码应该能够适应这种情况。您可以提前一秒预测面部位置,假设它移动平稳。如果用户决定抽动他们的头部或设备,那么任何算法都无济于事。

如果您使用已弃用 Camera API,您应该预先分配一个缓冲区并使用setPreviewCallbackWithBuffer()。这样,您可以保证帧一次到达您的图像处理器。你也不应该忘记open the Camera on a background thread,这样你的繁重图像处理所在的 [onPreviewFrame()](http://developer.android.com/reference/android/hardware/Camera.PreviewCallback.html#onPreviewFrame(byte[], android.hardware.Camera)) 回调就不会阻塞 UI 线程。

是的,OpenCV 人脸检测在某些情况下可能会更快,但更重要的是它比 Google 人脸检测器更强大。 是的,如果您不在乎微笑和睁开眼睛,最好关闭分类器。性能提升可能会有所不同。 我相信关闭跟踪只会减慢 Google 人脸检测器的速度,但您应该自行测量,并选择最佳策略。 打开setProminentFaceOnly() 可以获得最显着的收益,但我无法预测此设置对您的设备的实际效果。

【讨论】:

我正在研究您在第二段中提到的位置预测。它是如何工作的?是不是像获取当前人脸位置和上一次迭代的人脸位置在 X 和 Y 坐标上的差值,将它们乘以 2,然后将它们加到当前人脸的 X 和 Y 坐标上一样简单? 我可能会尝试抛物线外推法:给定最后 3 个位置,将它们拟合在曲线上,然后以 30 FPS 的速率计算未来某些时刻的 x,y 我可以使用 Apache commons 数学库吗? 我不太了解这个库。重要的一点是以比您计算的实际位置更高的速度生成预期位置。

以上是关于play-services-vision:如何将人脸检测速度与相机预览速度同步?的主要内容,如果未能解决你的问题,请参考以下文章

play-services-vision 库中没有 Frame.java

java基础之抽象类

Google Play Vision:定义 OCR 字符范围?

人脉网络模型

无法在颤振中同步 build.gradle 中的依赖项

Kinect+Unity实现虚拟人物动作同步