代码之家  ›  专栏  ›  技术社区  ›  Zhinkk

Google mobile vision无法处理Yuv数据?

  •  1
  • Zhinkk  · 技术社区  · 7 年前

    当我给框架。builder()使用setBitmap创建位图,一切正常。人脸被检测到,微笑概率也起作用。然而,由于某些原因,使用NV21格式YUV图像的ByteBuffer设置ImageData不起作用。它不会抛出任何错误,但YuvImage数据不会检测到人脸。

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

    以下是主要代码:

        @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,转换成位图,谷歌移动视觉库能够检测位图中的人脸。但是在进行所有这些转换时会有大量开销。所以我试着用YuviImage直接输入到上面的移动视觉库中。

    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());
                }
    
    1 回复  |  直到 7 年前
        1
  •  1
  •   Zhinkk    7 年前

    因此,Twilio发送到renderFrame()的i420frame实际上旋转了270度。因此,调用.setRotation解决了这个问题。实际上我之前已经试过了,但我调用了.setRotation(270),这对我来说很直观,但在检查文档后,你必须执行.setrogation(Frame.ROTATION\u 270)或类似操作。现在一切都好了。以下是完整的工作流程:

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