在Android上实时从视频中提取帧

6
我正在开发一个记录视频的应用程序。我希望在录制视频时,每一帧也保存在一个RGB值的数组列表中,以便从中提取特定信息。我知道这两个过程(视频录制和帧提取)是异步的,但这不是问题:提取过程可以在视频录制后完成。
有谁能告诉我如何从视频中提取帧?
非常感谢!

1
目前有三个答案,其中两个用于在录制过程中检查相机预览,另一个用于检查录制后的输出。您应该澄清您的问题,以指定您要做什么。 - fadden
3个回答

1
使用相机对象,您可以覆盖一个函数setPreviewCallback。从那个函数中,您将获得一个字节数组。您可以将其转换为位图或将其保存在数组中。假设您扩展了SurfaceView并实现了SurfaceHolder.Callback
代码如下:
mCamera.setPreviewCallback(new PreviewCallback() {
    public void onPreviewFrame(byte[] data, Camera camera) {

        //do some operations using data

    }
});

如果您想获取RGB值,可以使用此函数:
static public void decodeYUV420SP(int[] rgb, byte[] yuv420sp, int width,
            int height) {
        final int frameSize = 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] = 0xff000000 | ((r << 6) & 0xff0000)
                        | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
            }
        }
    }

我认为这是您想要的答案。如果我错了,请让我知道您的实际需求。
要获取绿色值,您可以使用以下代码:
static public void calculateColor(int[] rgb, int[] color,
            int width, int height, int component) {
        for (int bin = 0; bin < 256; bin++) {
            color[bin] = 0;
        } // bin

        int start = 0;
        int end = width * height;

        if (component == 0) // red
        {
            for (int pix = start; pix < end; pix += 3) {
                int pixVal = (rgb[pix] >> 16) & 0xff;
                color[pixVal]++;
            } // pix
        } else if (component == 1) // green
        {
            for (int pix = start; pix < end; pix += 3) {
                int pixVal = (rgb[pix] >> 8) & 0xff;
                color[pixVal]++;
            } // pix
        } else // blue
        {
            for (int pix = start; pix < end; pix += 3) {
                int pixVal = rgb[pix] & 0xff;
                color[pixVal]++;
            } // pix
        }
    }

调用此函数将返回每个像素的绿色值。如果您想要特定像素的绿色值,您需要修改此代码。

非常感谢您提供的代码,它解决了我的问题。现在我还有最后一个问题,我需要仅保存RGB中的绿色通道。根据您的经验,是直接保存绿色通道比保存三个通道再从中提取绿色通道更好呢?请问如何实现呢?谢谢! :) - Grancein
嗨,我已经编辑了答案并包括获取绿色值的函数。请检查。 - Jin
非常感谢您,您非常友善!我稍微修改了您的代码,针对绿色通道进行了优化,现在它完美地解决了我的问题。 - Grancein

0

从 .mp4 文件中提取帧的示例代码可以在 bigflake site 上找到。它使用 API 16+ 上可用的 MediaCodec。如果您需要在旧设备上运行,则最好使用 ffmpeg

RGB 数据每像素需要 3 个字节。30 fps 的 720p 视频的 10 秒将需要 1280*720*3*30*10 = 791 兆字节。除非您的视频帧相当小,否则保留大量帧可能不切实际(或不可能)。


0

我觉得你想在视频录制过程中获取图像。

如果是这样,你需要查看构建相机应用。该页面描述了如何使用Android相机类记录视频。

然后你可以通过相机预览回调获取yuv图像。


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