扫描每个像素以获得 RGB 值

Scan every pixels to get RGB values

我想在我的视频流 运行 时获取每个像素的 RGB 值。我目前正在使用 textureView 来显示相机流。每次得到一个新帧,我都会扫描帧,看看是否所有的 R G 和 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();
        }

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

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

一些程序可以直接卸载到 GPU,检查哪个是 "largest" 像素似乎直接就是其中之一。您可以创建一个 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...所以您真正需要做的就是将着色器中的这些常量转换为使用制服并计算它们。

您甚至应该能够将这项工作放在一个单独的线程上,并且在成本太高的情况下在允许和需要的情况下跳帧。视频应该仍然可以不间断地播放。