0

我想在视频流运行时获取每个像素的 RGB 值。我目前正在使用 textureView 来显示相机流。每次我得到一个新帧,我都会扫描帧,看看我的 RG 和 B 值是否都等于 255。但问题是:扫描一帧需要 20 多秒,所以它会跳过 30-40 帧。我想读取像素而不跳过任何像素。因此,如果有人可以让我知道是否有有效的方法来做到这一点,我将非常感谢他们的帮助。

这就是我正在做的。

TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() {
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) {
        openCamera();
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) {

    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
    Bitmap bitmap = textureView.getBitmap();

        // Initializing all color values
        int redmax = 0, greenmax = 0, bluemax = 0, redx = 0, bluex = 0, greenx = 0, redy = 0, bluey = 0, greeny = 0;

        int Pixel = bitmap.getHeight() * bitmap.getWidth();

        ByteBuffer Pixels = ByteBuffer.allocateDirect(4 * Pixel);

        for (int y = 0; y < bitmap.getHeight(); y++) { // Height
            for (int x = 0; x < bitmap.getWidth(); x++) {    // Width
                GLES20.glReadPixels(x, y, bitmap.getWidth(), bitmap.getHeight(), GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, Pixels);

                byte rValue = Pixels.get(0);
                byte gValue = Pixels.get(1);
                byte bValue = Pixels.get(2);
                byte A = Pixels.get(3);


                if (rValue > redmax) { // This is just to get max red Value in the frame
                    redmax = rValue;
                    redx = x;
                    redy = y;

                }
                if (bValue > bluemax) { // This is just to get max blue Value in the frame
                    bluemax = bValue;
                    bluex = x;
                    bluey = y;
                }

                if (gValue > greenmax) { // This is just to get max green Value in the frame
                    greenmax = gValue;
                    greenx = x;
                    greeny = y;
                }
            }
        }

        Log.i("Picture max red value", "R: " + redmax + " x: " + redx + " y: " + redy);
        Log.i("Picture max green value", "G: " + greenmax + " x: " + greenx + " y: " + greeny);
        Log.i("Picture max blue value", "B: " + bluemax + " x: " + bluex + " y: " + bluey);
        Log.i("Picture x and y value", "XY: " + " x: " + bitmap.getWidth() + " y: " + bitmap.getHeight());

        if ((redmax + bluemax + greenmax) > 760) {
            BitmapUtil.saveBitmap(bitmap, String.format("/sdcard/read_1" + UUID.randomUUID().toString() + ".jpg"));
        } else {
            bitmap.recycle();
        }
4

1 回答 1

1

因此,如果我理解正确,您的代码可以正常工作,但您需要对其进行优化。您可能想要完整描述您要完成的工作以获得一些想法,但从您的代码中可能有一些指示。确保您为自己创建了一个具有可衡量结果的测试场景,以查看哪些程序优化了您的代码以及优化了多少。

调用glReadPixels可能非常耗时,因此请尽量减少它。您在 for 循环中使用它,如果我正确读取它,则读取整个缓冲区大小被偏移xy这意味着它在其他东西旁边溢出。我相信您尝试做的是:GLES20.glReadPixels(x, y, 1, 1在某个位置读取单个像素。这应该会给您带来相当大的性能提升,但正如@Rabbid76 的评论中所提到的,有人怀疑您将通过读取整个缓冲区然后遍历像素来获得性能。

一些程序可以直接卸载到 GPU,检查哪个是“最大”像素似乎直接像其中之一。您可以创建一个 FBO(帧缓冲区对象)并使用着色器将相同的纹理重新绘制到每个维度中一半大小的缓冲区,以保留较大的值。最后,这将为您提供一个包含您的值的单个像素的缓冲区,但问题是您丢失了该像素的位置(它原来的位置)。

为了解释一下重绘纹理的过程,假设您有一个 2x2 的纹理,它将被重绘到 1x1 缓冲区。片段着色器将获取 4 个最近的像素并输出最大的像素,如下所示:

gl_fragColor = vec4(max(t1.r, max(t2.r, max(t3.r, t4.r))), max(t1.g, max(t2.r, max(t3.g, ...

, , ,处tN的样本在哪里。(0.25, 0.25)(0.75, 0.25)(0.25, 0.75)(0.75, 0.75)

这将仅针对单个片段执行,但之前的调用将使用相同的着色器将 4x4 纹理重绘到具有 4 个片段的 2x2 缓冲区。相同的 16x16 重绘为 4x4...所以您真正需要做的就是将着色器中的这些常量转换为使用制服并计算它们。

您甚至应该能够将这项工作放在单独的线程上并跳过帧(如果允许并且需要),但成本太高。然后,视频应该仍然可以不间断地播放。

于 2018-05-03T07:09:16.650 回答