如何从大量值中创建缩放图像

时间:2017-07-17 14:39:56

标签: java android arrays canvas bitmap

我正在使用媒体播放器录制时从麦克风中获取的音量值列表中创建波形图像。

这适用于长达几分钟的录制内容,但是当录制时间约为一小时时,我会收到内存不足的错误。

我知道这个错误来自我的代码,我试图创建一个位图图像,宽度(以像素为单位)是数组的长度,高度总是200像素。

有人可以帮我创建一个方法,可以接收n长度的大数组int值(从0到100),并创建一个缩放的位图图像吗?

据我所知,我会更好地通过较小的阵列运行并从中创建缩放图像,然后可能会以某种方式将其附加到主缩放图像上,但我正在努力。

这是我的代码:

    public void generateWaveFormImage() {
    try {
        mBitmap = Bitmap.createBitmap(waveValues.size(), 200, Bitmap.Config.RGB_565);
        canvas = new Canvas(mBitmap);

        for(int i=0; i<waveValues.size(); i++) {
            canvas.drawLine(i, 100, i, 100-(int)waveValues.get(i), paint);
            canvas.drawLine(i, 100, i, 100 + (int) waveValues.get(i), paint);
        }

        Bitmap resized = Bitmap.createScaledBitmap(mBitmap, 760, 200, true);
        FileManager.saveImage(mShortNameWaveImage, resized);

    } catch(Exception e) {}
    waveValues = new ArrayList();
}

这适用于较小的录音,即当waveValues数组中存储了2000个值时。

这是抛出内存不足错误的行

    mBitmap = Bitmap.createBitmap(waveValues.size(), 200, Bitmap.Config.RGB_565);

我想补充一下到目前为止我尝试过的内容。

假设waveValues数组长3500。

我获取前1000个值,创建一个1000乘200的图像并将值写入该图像,然后缩小到760 X 200并保存。

然后我接下来的1000个值并做同样的事情,然后将它们附加在一起,然后将图像重新缩放到760 x 200。

然后我接下来的1000个值,做同样的事情,并附加到主图像上,重新缩放到760,然后对最后500个值进行相同的操作。

这导致创建错误缩放的图像,前2个图像缩放太多,第三个图像正确,最后一个图像缩放为更大的图像,这使得波形非常奇怪和美妙。

2 个答案:

答案 0 :(得分:0)

存储器内位图无压缩存储,在RGB_565中每点2个字节,如果图像宽度为2000 * 200高度= 400k点* 2 = 800千字节数据 - 在更大的图像上应该有OOM错误。 无论如何,你必须在绘制之前重新缩放数据,但不是之后 - 绘制全尺寸点然后使用特定于图像的重新缩放方法进行中继是什么用途?

double scaleFactor = (double) 760/waveValues.size(); //This will fit all your points inside 760px resolution
for(int i=0; i<waveValues.size(); i++) {
        canvas.drawLine((int) (i*scaleFactor), 100, (int) (i*scaleFactor), 100 - (int)waveValues.get(i), paint);
        canvas.drawLine((int) (i*scaleFactor), 100, (int) (i*scaleFactor), 100 + (int)waveValues.get(i), paint);
    }

当然,您可以通过一次绘制操作快速完成两次:

double scaleFactor = (double) 760/waveValues.size(); //This will fit all your points inside 760px resolution
for(int i=0; i<waveValues.size(); i++) {
        canvas.drawLine((int) (i*scaleFactor), 100 - (int)waveValues.get(i), (int) (i*scaleFactor), 100 + (int)waveValues.get(i), paint);           
    }

现在,如果您有76个点,则每行的宽度为10px。

如果您有7600个点,则每行的宽度为0.1px,因此1px中的10行

你的形象将永远留在760x200内

顺便说一下你应该确定你的值总是在0..100之内,否则你会有剪辑(线条将落在图片之外)

答案 1 :(得分:0)

大型位图不仅会导致内存问题,而且渲染每个样本也会做很多不必要的工作,因为你只是一遍又一遍地覆盖相同像素的顶部。

处理此问题的最佳方法是首先为位图设置大小限制。在下面的示例中,即2000像素,当样本计数小于此值时,位图将按样本计数大小。如果样本计数大于位图,则将限制为2000像素。

对于长像素,其中每个像素列将覆盖许多样本,您只需绘制该列覆盖的样本的最大值。因此,保留一个变量x,即当前样本转换为像素位置。

当值x与之前的迭代x相同时,只需通过max = Math.max(waveValues.get(i));获取最大值

当x值更改为下一列时,您将呈现最大值。该max是前一列的最大样本值。然后,将最大值重置为当前样本。

在循环结束时,您需要渲染最后一列。

由于这只会节省您每列样本数大于约1.5(估计值)的时间,因此您应该只在该点使用该最大值方法。在示例中,我已将其设置为每列2个或更多样本。

public void generateWaveFormImage() {
    try {
        int samples = waveValues.size();
        int width = Math.min(2000, samples);
        mBitmap = Bitmap.createBitmap(width, 200, Bitmap.Config.RGB_565);
        canvas = new Canvas(mBitmap);
        double samplesPerColumn = samples / width;
        int x;
        // if not downsampling much then render as normal
        if(samplesPerColumn < 2.0){
            for(int i=0; i < waveValues.size(); i++) {
                x = (int)(i / samplesPerColumn);
                canvas.drawLine(x, 100- (int) waveValues.get(i), x, 100 + (int) waveValues.get(i), paint);
            }
        }else{  // if down sampling then render the max value for every samplesPerColumn
            double max = 0;
            int lastX = -1;
            for(int i=0; i<waveValues.size(); i++) {
                x = (int)(i / samplesPerColumn);
                if(x != lastX){
                    if(i != 0){
                       canvas.drawLine(x - 1, 100 - (int) max, x - 1, 100 + (int) max, paint);
                    }
                    max = waveValues.get(i);
                }else{
                    max = Math.max(max,waveValues.get(i)); 
                }
                lastX = x;
             }
             canvas.drawLine(x - 1, 100 - (int) max, x - 1, 100 + (int) max, paint);
        }



        Bitmap resized = Bitmap.createScaledBitmap(mBitmap, 760, 200, true);
        FileManager.saveImage(mShortNameWaveImage, resized);

    } catch(Exception e) {}

}
相关问题