Android JavaCV + Camera2

3

我正在尝试使用JavaCV从摄像头录制视频。

  // recoder settings:
  private int imageWidth  = 320;  
  private int imageHeight = 240;
  private int frameRate   = 30;

  recorder = new FFmpegFrameRecorder(ffmpeg_link, imageWidth, imageHeight, 1);
  recorder.setFormat("mp4");
  recorder.setFrameRate(frameRate);

  // frame settings:
  IplImage yuvIplimage = null;
  yuvIplimage = IplImage.create(320, 320, IPL_DEPTH_16U, 1); //32 not supported

  //image reader:
  private ImageReader mImageReader;
  mImageReader = ImageReader.newInstance(320, 320, ImageFormat.YUV_420_888, 10);  
  mImageReader.setOnImageAvailableListener(
                    mOnImageAvailableListener, mBackgroundHandler);

private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
        = new ImageReader.OnImageAvailableListener() {

    @Override  
    public void onImageAvailable(ImageReader reader) {
        Image image = reader.acquireNextImage();// acquireLatestImage(); - also tried
        if (image == null)
            return; 

        final ByteBuffer buffer = image.getPlanes()[0].getBuffer();
        byte[] bytes = new byte[buffer.remaining()];
        buffer.get(bytes, 0, bytes.length); 


        if (yuvIplimage != null ) {
            // OPTION 1
            yuvIplimage.getByteBuffer().put(convertYUV420ToNV21(image)); 
            // OPTION 2
            //yuvIplimage.getByteBuffer().put(decodeYUV420SP(bytes,320,320));
            try {
                if (started)  {
                recorder.record(yuvIplimage);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }  

        image.close();
    }
}; 

选项1是使用以下代码将图像解码为NV21:

 private byte[] convertYUV420ToNV21(Image imgYUV420) {
    byte[] rez;

    ByteBuffer buffer0 = imgYUV420.getPlanes()[0].getBuffer();
    ByteBuffer buffer2 = imgYUV420.getPlanes()[2].getBuffer();
    int buffer0_size = buffer0.remaining();
    int buffer2_size = buffer2.remaining();
    rez = new byte[buffer0_size + buffer2_size]; 

    buffer0.get(rez, 0, buffer0_size);  
    buffer2.get(rez, buffer0_size, buffer2_size);

    return rez;
}

在此输入图片描述

选项2是将颜色转换为RGB格式,如果我理解正确的话:

public byte[] decodeYUV420SP( byte[] yuv420sp, int width, int height) {
    final int frameSize = width * height;
    byte rgb[]=new byte[width*height];
    for (int j = 0, yp = 0; j < height; j++) {
        int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
        for (int i = 0; i < width; i++, yp++) {
            int y = (0xff & ((int) yuv420sp[yp])) - 16;
            if (y < 0) y = 0;
            if ((i & 1) == 0) {
                v = (0xff & yuv420sp[uvp++]) - 128;
                u = (0xff & yuv420sp[uvp++]) - 128;
            }
            int y1192 = 1192 * y;
            int r = (y1192 + 1634 * v);
            int g = (y1192 - 833 * v - 400 * u);
            int b = (y1192 + 2066 * u);
            if (r < 0) r = 0; else if (r > 262143) r = 262143;
            if (g < 0) g = 0; else if (g > 262143) g = 262143;
            if (b < 0) b = 0; else if (b > 262143) b = 262143;
            rgb[yp] = (byte) (0xff000000 | ((r << 6) & 0xff0000)
                    | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff));
        }
    }
    return rgb;   }

enter image description here

看起来不正确。 将camera2图像转换为IplImage的正确方式是什么? 并且是否可以实时进行?


1
尝试使用图像格式JPEG。在Camera2Basic Google示例中所述的onImageAvailable中获取图像的字节数组。修改JAVACV recordactivity示例并使用Frame和FrameRecorder通过将图像字节数组传递给frame类实例来记录图像。 - Rajen Raiyarela
2个回答

2
如果你愿意将处理转移到C++,你可以这样进行转换: cv::cvtColor((cv::_InputArray)mNV, (cv::_OutputArray)rgba, CV_YUV2RGBA_NV12, 0); 这将把图像存储为RGBA mat,然后你可以像平常一样保存它。

2
如果录制器需要NV21格式,那么将图像转换为该格式而不是RGB可能是最快的选项。
但是为什么不直接使用android.media.MediaRecorder呢?它更加高效,并且可以使用硬件编码器。
但是,如果你必须坚持使用ffmpeg,那么你的第一个选项对许多设备来说是不正确的。此外,请确保在早期删除buffer.get调用——它会使从plane 0读取的其余内容无法正常工作,这可能是您当前遇到的问题。一旦你读取了plane 0,.remaining()将返回0。
YUV图像有3个plane,除非你已经检查了底层格式实际上是NV21,否则你不应该盲目地假设。或者假设行跨距等于宽度。 为了安全起见,在将三个平面复制到半平面字节数组中时,您需要查看行和像素跨距。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接