Android OpenGL ES 绘图 -- 热力图

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

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

  • 宽: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);

效果图:
这里写图片描述

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值