扫描每个像素以获得RGB值

时间:2018-05-03 04:23:08

标签: android opengl-es camera android-camera gpu

我希望在视频流运行时获取每个像素的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();
        }

1 个答案:

答案 0 :(得分: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 ......所以你真正需要做的就是在着色器中转换这些常量以使用制服并计算它们。

你应该能够将这项工作放在一个单独的线程上,如果允许的话就跳过框架,当它太贵时需要跳过框架。然后视频应该不间断地播放。