【问题标题】:How to process each frame of a video and shift pixels between frames using android camera2如何使用android camera2处理视频的每一帧并在帧之间移动像素
【发布时间】:2019-11-11 15:47:09
【问题描述】:

我是 android 新手,在我的应用程序中,我需要从相机接收视频并混合来自不同帧的像素。我尝试使用 mediaMetaDataRetriever 从捕获的视频中获取帧,但耗时太长。现在我使用 ImageReader 来获取每个实时帧,创建一个位图并将其放入一个数组中。这占用了非常多的 RAM 空间。并且这个数组的后续处理三个周期也是相当长的。 那么如何使用 Camera2 解决这个问题呢?

Bitmap的混合数组:

 val outputFrames = arrayOfNulls<Bitmap>(1080)
    var videoDuration = frames.size
    if(videoDuration%2 != 0) videoDuration--
    var index = 0
    while (index < videoDuration) {
        var indexWidth = 1079
        while (indexWidth >= 0) {
            var indexHeight = 1919
            while (indexHeight >= 0) {
                if (outputFrames[indexWidth] == null) {
                    outputFrames[indexWidth] =
                        Bitmap.createBitmap(videoDuration, 1920, Bitmap.Config.ARGB_8888)
                    outputFrames[indexWidth]!![index, indexHeight] =
                        frames[0][indexWidth, indexHeight]
                } else outputFrames[indexWidth]!![index, indexHeight] =
                    frames[0][indexWidth, indexHeight]
                //Log.e("frame:", "$indexWidth $indexHeight /${outputFrames[indexWidth]}/")
                indexHeight--
            }
            indexWidth--
        }
        frames.removeAt(0)
        index++
    }

我的图片阅读器:

mImageReader = ImageReader.newInstance(mWidth,mHeight, PixelFormat.RGBA_8888,1)
    mImageReader.setOnImageAvailableListener(
        { reader ->
            val image = reader.acquireLatestImage()
            val planes = image.planes
            val buffer = planes[0].buffer
            val offset = 0
            val pixelStride = planes[0].pixelStride
            val rowStride = planes[0].rowStride
            val rowPadding = rowStride - pixelStride * mWidth
            // create bitmap
            val bitmap = Bitmap.createBitmap(
                mWidth + rowPadding / pixelStride,
                mHeight,
                Bitmap.Config.ARGB_8888
            )
            bitmap.copyPixelsFromBuffer(buffer)
            image.close()

        },
        mBackgroundHandler)

【问题讨论】:

  • 你想做什么混合?您将在 ImageReader 侦听器中创建的位图存储在哪里,同时保存了多少?
  • @Eddy 如果您将视频想象为像素的三维数组,那么我想制作一个好像从侧面播放的视频,因为新视频的每一帧都应该包含来自所有捕获帧的像素。不幸的是,现在我还没有想出比同时将所有位图存储在数组中更好的方法,它们的数量取决于视频的持续时间。

标签: android-camera2


【解决方案1】:

创建位图非常昂贵,它会像地狱一样拖慢您的应用程序。尝试将您的图像设置为:

mImageReader = ImageReader.newInstance(DISPLAY_WIDTH, DISPLAY_HEIGHT, ImageFormat.YUV_420_888, Runtime.getRuntime().availableProcessors());

之后,您可以使用 ScriptIntrinsicYuvToRGB 来创建您的位图,例如:

Image.Plane Y = mImage.getPlanes()[0];
        Image.Plane U = mImage.getPlanes()[1];
        Image.Plane V = mImage.getPlanes()[2];

        int Yb = Y.getBuffer().remaining();
        int Ub = U.getBuffer().remaining();
        int Vb = V.getBuffer().remaining();

        byte[] data = new byte[Yb + Ub + Vb];

        Y.getBuffer().get(data, 0, Yb);
        V.getBuffer().get(data, Yb, Vb);
        U.getBuffer().get(data, Yb + Vb, Ub);

        RenderScript rs = RenderScript.create(Main2Activity.this);
        ScriptIntrinsicYuvToRGB yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));

        Type.Builder yuvType = new Type.Builder(rs, Element.U8(rs)).setX(data.length);
        Allocation in = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT);

        Type.Builder rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs)).setX(DISPLAY_WIDTH).setY(DISPLAY_HEIGHT);
        Allocation out = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT);

        final Bitmap bmpout = Bitmap.createBitmap(DISPLAY_WIDTH, DISPLAY_HEIGHT, Bitmap.Config.ARGB_8888);

        in.copyFromUnchecked(data);

        yuvToRgbIntrinsic.setInput(in);
        yuvToRgbIntrinsic.forEach(out);
        out.copyTo(bmpout);

如果您在后台线程中使用此代码,它将大大加快一切速度。

【讨论】:

    猜你喜欢
    • 2015-09-22
    • 2012-12-31
    • 1970-01-01
    • 1970-01-01
    • 2011-03-29
    • 2020-06-04
    • 1970-01-01
    • 2012-11-09
    相关资源
    最近更新 更多