Android基于Shader的图像处理(5)-Canny边缘检测

完整代码位置:AndroidShaderDemo

下面两篇文章对Canny边界探测算法描述的很好,可以查看了解下原理。这里重点讲的是在Android里通过Shader实现。
Canny边界探测算法
边缘检测之Canny

android-gpuimage里没有实现Canny边缘检测,在网上查了很多,大都是OpenCV,C++等版本实现,没有看到在Android里通过Shader实现,这里研究了下ios版的gpuimage,写了个Android版的,为了清楚表示各个步骤,代码没有封装,最后效果和OpenCV的比有一定差距,有哪位提出改进更好。
主要实现在CannyRender.java

Canny边缘检测主要有以下几个步骤:
1、使用高斯滤波器,以平滑图像,滤除噪声,生成灰度图。
2、使用Sobel滤波器,计算每个像素点的梯度强度和方向。
3、用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测带来的杂散响应。
4、应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘。
5、通过抑制孤立的弱边缘最终完成边缘检测。

在CannyRender的onDrawFrame方法里,分别用gaussianProgram,sobelProgram,nonmaximumSuppressionProgram,weakPixelInclusionProgram这4个Program完成以上工作。因为需要4次渲染,前三次分别渲染到3个帧缓存,最后一次渲染到屏幕。onDrawFrame方法如下:

@Override
    public void onDrawFrame(GL10 gl) {
        glClear(GL_COLOR_BUFFER_BIT);

        //render to framebuffer0,gaussian and gray
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[0]);
        GLES20.glClearColor(0, 0, 0, 0);

        gaussianProgram.useProgram();
        gaussianProgram.setUniforms(textureIDs[0], 1.0f / width, 1.0f / height);

        vertexArray.setVertexAttribPointer(
                0,
                gaussianProgram.getPositionAttributeLocation(),
                POSITION_COMPONENT_COUNT,
                STRIDE);

        vertexArray.setVertexAttribPointer(
                POSITION_COMPONENT_COUNT,
                gaussianProgram.getTextureCoordinatesAttributeLocation(),
                TEXTURE_COORDINATES_COMPONENT_COUNT,
                STRIDE);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

        //render to framebuffer1, sobel
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[1]);
        GLES20.glClearColor(0, 0, 0, 0);

        sobelProgram.useProgram();
        sobelProgram.setUniforms(mFrameBufferTextures[0], 1.0f / width, 1.0f / height);

        vertexArray.setVertexAttribPointer(
                0,
                sobelProgram.getPositionAttributeLocation(),
                POSITION_COMPONENT_COUNT,
                STRIDE);

        vertexArray.setVertexAttribPointer(
                POSITION_COMPONENT_COUNT,
                sobelProgram.getTextureCoordinatesAttributeLocation(),
                TEXTURE_COORDINATES_COMPONENT_COUNT,
                STRIDE);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

        //render to framebuffer2, NonmaximumSuppression
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[2]);
        GLES20.glClearColor(0, 0, 0, 0);

        nonmaximumSuppressionProgram.useProgram();
        nonmaximumSuppressionProgram.setUniforms(mFrameBufferTextures[1], 1.0f / width, 1.0f / height);

        vertexArray.setVertexAttribPointer(
                0,
                nonmaximumSuppressionProgram.getPositionAttributeLocation(),
                POSITION_COMPONENT_COUNT,
                STRIDE);

        vertexArray.setVertexAttribPointer(
                POSITION_COMPONENT_COUNT,
                nonmaximumSuppressionProgram.getTextureCoordinatesAttributeLocation(),
                TEXTURE_COORDINATES_COMPONENT_COUNT,
                STRIDE);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

        //render to screen,weakPixelInclusion
        weakPixelInclusionProgram.useProgram();
        weakPixelInclusionProgram.setUniforms(mFrameBufferTextures[2], 1.0f / width, 1.0f / height);

        flipVertexArray.setVertexAttribPointer(
                0,
                weakPixelInclusionProgram.getPositionAttributeLocation(),
                POSITION_COMPONENT_COUNT,
                STRIDE);

        flipVertexArray.setVertexAttribPointer(
                POSITION_COMPONENT_COUNT,
                weakPixelInclusionProgram.getTextureCoordinatesAttributeLocation(),
                TEXTURE_COORDINATES_COMPONENT_COUNT,
                STRIDE);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
    }

最终渲染结果如下,图片压缩后效果不好:


Android基于Shader的图像处理(5)-Canny边缘检测_第1张图片
canny.png

你可能感兴趣的:(Android基于Shader的图像处理(5)-Canny边缘检测)