当我使用setBitmap给Frame.builder()一个位图时,一切都按预期工作。人脸被检测到,微笑概率也起作用。但是,由于某些原因,带有NV21格式YUV图像的ByteBuffer的.setImageData不起作用。它不会引发任何错误,但是YuvImage数据未检测到面部。

i420Frame从这里:https://media.twiliocdn.com/sdk/android/conversations/releases/0.8.1/docs/com/twilio/conversations/I420Frame.html

(如果不是完全相同,我假设它与Webrtc的i420Frame非常相似)

这是主要代码:

    @Override
    public void renderFrame(final I420Frame i420Frame) {
        YuvImage yuvImage = i420ToYuvImage(i420Frame.yuvPlanes, i420Frame.yuvStrides, i420Frame.width, i420Frame.height);

       // Set image data (YUV N21 format) -- NOT working. The commented bitmap line works.
        Frame frame = new Frame.Builder().setImageData(ByteBuffer.wrap(yuvImage.getYuvData()), yuvImage.getWidth(), yuvImage.getHeight(), yuvImage.getYuvFormat()).build();
       //Frame frame = new Frame.Builder().setBitmap(yuvImage).build();

       // Detect faces
       SparseArray<Face> faces = detector.detect(frame);

        if (!detector.isOperational()) {
            Log.e(TAG, "Detector is not operational!");
        }

        if (faces.size() > 0) {
            Log.i("yuv", "Smiling %: " + faces.valueAt(0).getIsSmilingProbability());
        }

       i420Frame.release();
       Log.i("yuv", "Faces detected: " + faces.size());
    }


这是用于i420ToYuvImage的功能(摘自Twilio的快速入门指南)。 i420到YuvImage的代码不是我写的,但我相信它能正常工作,因为我将输出的yuvimage转换为jpeg,将其转换为位图,并且Google移动视觉库能够检测到位图中的人脸。但是进行所有这些转换会产生大量的开销。因此,我正在尝试使用YuvImage直接将其馈入上述移动视觉库中。

private YuvImage i420ToYuvImage(ByteBuffer[] yuvPlanes, int[] yuvStrides, int width, int height) {
                if (yuvStrides[0] != width) {
                    return fastI420ToYuvImage(yuvPlanes, yuvStrides, width, height);
                }
                if (yuvStrides[1] != width / 2) {
                    return fastI420ToYuvImage(yuvPlanes, yuvStrides, width, height);
                }
                if (yuvStrides[2] != width / 2) {
                    return fastI420ToYuvImage(yuvPlanes, yuvStrides, width, height);
                }

                byte[] bytes = new byte[yuvStrides[0] * height +
                        yuvStrides[1] * height / 2 +
                        yuvStrides[2] * height / 2];
                ByteBuffer tmp = ByteBuffer.wrap(bytes, 0, width * height);
                copyPlane(yuvPlanes[0], tmp);

                byte[] tmpBytes = new byte[width / 2 * height / 2];
                tmp = ByteBuffer.wrap(tmpBytes, 0, width / 2 * height / 2);

                copyPlane(yuvPlanes[2], tmp);
                for (int row = 0 ; row < height / 2 ; row++) {
                    for (int col = 0 ; col < width / 2 ; col++) {
                        bytes[width * height + row * width + col * 2]
                                = tmpBytes[row * width / 2 + col];
                    }
                }
                copyPlane(yuvPlanes[1], tmp);
                for (int row = 0 ; row < height / 2 ; row++) {
                    for (int col = 0 ; col < width / 2 ; col++) {
                        bytes[width * height + row * width + col * 2 + 1] =
                                tmpBytes[row * width / 2 + col];
                    }
                }
                return new YuvImage(bytes, NV21, width, height, null);
            }

            private YuvImage fastI420ToYuvImage(ByteBuffer[] yuvPlanes,
            int[] yuvStrides,
            int width,
            int height) {
                byte[] bytes = new byte[width * height * 3 / 2];
                int i = 0;
                for (int row = 0 ; row < height ; row++) {
                    for (int col = 0 ; col < width ; col++) {
                        bytes[i++] = yuvPlanes[0].get(col + row * yuvStrides[0]);
                    }
                }
                for (int row = 0 ; row < height / 2 ; row++) {
                    for (int col = 0 ; col < width / 2; col++) {
                        bytes[i++] = yuvPlanes[2].get(col + row * yuvStrides[2]);
                        bytes[i++] = yuvPlanes[1].get(col + row * yuvStrides[1]);
                    }
                }
                return new YuvImage(bytes, NV21, width, height, null);
            }

            private void copyPlane(ByteBuffer src, ByteBuffer dst) {
                src.position(0).limit(src.capacity());
                dst.put(src);
                dst.position(0).limit(dst.capacity());
            }

最佳答案

因此,事实证明Twilio发送到renderFrame()的i420frame实际上旋转了270度。因此,调用.setRotation可解决此问题。我实际上曾经尝试过,但是我曾经打电话给.setRotation(270),这对我来说很直观,但是在检查了文档之后,您必须执行.setRotation(Frame.ROTATION_270)或类似的操作。现在一切正常。这是完整的工作线:

Frame frame = new Frame.Builder().setImageData(ByteBuffer.wrap(yuvImage.getYuvData()), yuvImage.getWidth(), yuvImage.getHeight(), yuvImage.getYuvFormat()).setRotation(Frame.ROTATION_270).build();

09-06 17:00