Android OpenGL ES 绘图 -- 热力图

来源:互联网 发布:u盘必装软件 编辑:程序博客网 时间:2024/05/16 12:59

使用高斯核密度估计算法绘制热力图

绘图数据是一个一维的图形数组:

  • 宽:50
  • 高:60

一、OpenGL 绘图准备配置

在Render中
onSurfaceCreated方法:

        // 黑色背景        gl.glClearColor(0, 0, 0, 0);        // 设置深度缓存        gl.glClearDepthf(1.0f);        // 启用深度测试        gl.glEnable(GL10.GL_DEPTH_TEST);        // 所作深度测试的类型        gl.glDepthFunc(GL10.GL_LEQUAL);        // 设置点大小        gl.glPointSize(1.5f);        // 设置所画的点为圆点        gl.glEnable(GL10.GL_POINT_SMOOTH);

onSurfaceChanged方法:

        //设置可显示区域        gl.glViewport(0, 0, width, height);        //设备正投影        gl.glMatrixMode(GL10.GL_PROJECTION);        gl.glMatrixMode(GL10.GL_MODELVIEW);        //初始化数据宽高与View的宽高        this.mWidth = 50 * 5;        this.mHeight = 60 * 5;        this.mViewWidth = width;        this.mViewHeight = height;        //重设投影模型        gl.glLoadIdentity();        // 校准显示内容区域        gl.glTranslatef(-0.15f, 0.15f, 0f);        gl.glRotatef(-90f, 0, 0, 1);        gl.glScalef(0.8f, 0.8f, 1f);        //初始化数据缓冲区        ByteBuffer colorTmp = ByteBuffer.allocateDirect(mHeight * mWidth * 4 * 4);        colorTmp.order(ByteOrder.nativeOrder());        colorBuff = colorTmp.asFloatBuffer();        ByteBuffer quateTmp = ByteBuffer.allocateDirect(mHeight * mWidth * 2 * 4);        quateTmp.order(ByteOrder.nativeOrder());        vertexBuff = quateTmp.asFloatBuffer();

二、 图形数组处理

扩大原数组并转化为一个二维数组

        double[][] intensity = new double[mWidth + 2 * mRadius][mHeight + 2 * mRadius];        int n; //数组索引位置        double value; //数据的值        mMaxIntensity = 0;        for (int i = 0; i < 50; i++) {            for (int j = 0; j < 60; j++) {                n = i * 60 + j;                value = data[n];                if (value > 0) {                    //根据原有的数组,填充到一个更大的数组                    int bucketX = (int) (clamp((float) i / 50 , 0.0f, mWidth));                    int bucketY = (int) (clamp((float) j / 60, 0.0f, mHeight));                    if (bucketX < mWidth && bucketX >= 0                            && bucketY < mHeight && bucketY >= 0) {                        intensity[bucketX][bucketY] = value;                    }                    //获取数据中的最大值                        if (value > mMaxIntensity) {                            mMaxIntensity = value;                        }                }            }        }

计算核的大小

    private double[] mKernel = generateKernel(mRadius, mRadius / 3.0);    static private double[] generateKernel(int radius, double sd) {        double[] kernel = new double[radius * 2 + 1];        for (int i = -radius; i <= radius; i++) {            kernel[i + radius] = (Math.exp(-i * i / (2 * sd * sd)));        }        return kernel;    }

使用高斯核密度算法处理数组

 double[][] convolved = convolve(intensity, mKernel); static private double[][] convolve(double[][] grid, double[] kernel) {        int radius = mRadius;        int dimOldW = grid.length;        int dimOldH = grid[0].length;        int dimW = dimOldW - 2 * radius;        int dimH = dimOldH - 2 * radius;        int lowerLimit = radius;        int upperLimitW = radius + dimW - 1;        int upperLimitH = radius + dimH - 1;        double[][] intermediate = new double[dimOldW][dimOldH];        int x, y, x2, xUpperLimit, initial;        double val;        for (x = 0; x < dimOldW; x++) {            for (y = 0; y < dimOldH; y++) {                val = grid[x][y];                if (val != 0) {                    xUpperLimit = ((upperLimitW < x + radius) ? upperLimitW : x + radius) + 1;                    initial = (lowerLimit > x - radius) ? lowerLimit : x - radius;                    for (x2 = initial; x2 < xUpperLimit; x2++) {                        intermediate[x2][y] += val * kernel[x2 - (x - radius)];                    }                }            }        }        double[][] outputGrid = new double[dimW][dimH];        int y2, yUpperLimit;        for (x = lowerLimit; x < upperLimitW + 1; x++) {            for (y = 0; y < dimOldH; y++) {                val = intermediate[x][y];                if (val != 0) {                    yUpperLimit = ((upperLimitH < y + radius) ? upperLimitH : y + radius) + 1;                    initial = (lowerLimit > y - radius) ? lowerLimit : y - radius;                    for (y2 = initial; y2 < yUpperLimit; y2++) {                        outputGrid[x - radius][y2 - radius] += val * kernel[y2 - (y - radius)];                    }                }            }        }        return outputGrid;    }

convolved为处理好的数组

三、根据数组,添加点坐标与颜色

        int dimw = d.length;        int dimh = d[0].length;        //根据OpenGL坐标系统,坐标由-1到+1,范围为2,实际坐标需影射到OpenGL坐标系(0:-1, Max:1)        //每个点坐标包含两个浮点数:X、Y        float[] ver = new float[dimw * dimh * 2];        //每个颜色数据包令4个浮点数:R、G、B        float[] colorDBuff = new float[dimw * dimh * 3];        //点颜色的RGB数组        float[] colorArr;        //每点的宽高        float z = (float) mViewWidth / (float) dimw;        float Ysplit = (2f / (float) mViewHeight) * z;        float Xsplit = (2f / (float) mViewWidth) * z;        int k = 0;//点坐标的序号        int c = 0;//点颜色的序号        int i, j;        double val;        indexNum = 0;        for (i = 0; i < dimw; i++) {            for (j = 0; j < dimh; j++) {                val = d[i][j];                if (val != 0) {                    colorArr = getColor((float) (val / mMaxIntensity));                    //point and color                    ver[k++] = -1f + i * Ysplit;                    ver[k++] = -1f + j * Xsplit;                    colorDBuff[c++] = colorArr[0];                    colorDBuff[c++] = colorArr[1];                    colorDBuff[c++] = colorArr[2];                    indexNum++;                }            }        }        colorBuff.put(colorDBuff);        colorBuff.position(0);        vertexBuff.put(ver);        vertexBuff.position(0);    // 需重置数组位置,否则不能顶点数据访问为空,无图形输出

能量色条值获取方法

    /**     * 能量色条值获取     * @param value 实际数据值     * @return 对应能量色条颜色数组[r, g, b]     */    protected float[] getColor(float value) {        int sr1 = 0x00, sg1 = 0x00, sb1 = 0x00;        int sr2 = 0x46, sg2 = 0xb7, sb2 = 0xec;        int sr3 = 0x22, sg3 = 0xfe, sb3 = 0x2c;        int sr4 = 0xff, sg4 = 0xea, sb4 = 0x00;        int sr5 = 0xff, sg5 = 0x4e, sb5 = 0x00;        double r = 0, g = 0, b = 0;        if( value < 0.15 ) {            r = sr1 + (sr2 - sr1) * (value / 0.15);            g = sg1 + (sg2 - sg1) * (value / 0.15);            b = sb1 + (sb2 - sb1) * (value / 0.15);        } else if(value < 0.5){            r = sr2 + (sr3 - sr2) * ((value - 0.15) / 0.35);            g = sg2 + (sg3 - sg2) * ((value - 0.15) / 0.35);            b = sb2 + (sb3 - sb2) * ((value - 0.15) / 0.35);        } else if(value < 0.75){            r = sr3 + (sr4 - sr3) * ((value - 0.5) / 0.25);            g = sg3 + (sg4 - sg3) * ((value - 0.5) / 0.25);            b = sb3 + (sb4 - sb3) * ((value - 0.5) / 0.25);        } else {            r = sr4 + (sr5 - sr4) * ((value - 0.75) / 0.25);            g = sg4 + (sg5 - sg4) * ((value - 0.75) / 0.25);            b = sb4 + (sb5 - sb4) * ((value - 0.75) / 0.25);        }        return new float[]{(float) (r / 0xFF), (float) (g / 0xFF), (float) (b / 0xFF)};    }

四、绘制

onDrawFrame方法:

            //启动相关管道            gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);    // 开启顶点数组画图模式,否则不能使用顶点数组画图            gl.glEnableClientState(GL10.GL_COLOR_ARRAY);    // 打开着色数组,启用顶点着色功能            //绘图            gl.glVertexPointer(2, GL10.GL_FLOAT, 0, vertexBuff);            gl.glColorPointer(3, GL10.GL_FLOAT, 0, colorBuff);            gl.glDrawArrays(GL10.GL_POINTS, 0, indexNum);            //关闭相关管道            gl.glDisableClientState(GL10.GL_COLOR_ARRAY);            gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);

效果图:
这里写图片描述

原创粉丝点击