Java 大缓冲区导致Android内存不足

Java 大缓冲区导致Android内存不足,java,android,optimization,memory-leaks,bitmap,Java,Android,Optimization,Memory Leaks,Bitmap,我在这上面被困了三天。我得到了一个Dicom文件,我需要用一个缓冲读取器来解析它,该读取器返回来自文档头的一些信息和图像的原始数据。在那之后,我在raw上应用一个LUT,将字节转换成灰度,然后将其放入位图中。它非常适合小图像,但现在,我必须加载13Mo图像,不仅打开它需要很长时间(大约20秒),而且在应用LUT int位图方法时,Android抛出一个关于位图的错误29052480字节外部分配太大,无法执行此过程。java.lang.OutOfMemoryError:位图大小超过VM预算。我知道

我在这上面被困了三天。我得到了一个Dicom文件,我需要用一个缓冲读取器来解析它,该读取器返回来自文档头的一些信息和图像的原始数据。在那之后,我在raw上应用一个LUT,将字节转换成灰度,然后将其放入
位图中。它非常适合小图像,但现在,我必须加载13Mo图像,不仅打开它需要很长时间(大约20秒),而且在应用LUT int位图方法时,Android抛出一个关于位图的错误
29052480字节外部分配太大,无法执行此过程。
java.lang.OutOfMemoryError:位图大小超过VM预算
。我知道有很多关于这个错误的线程,但在我的例子中,它有点原始,因为我只想打开一个图像(所以它不是关于堆叠很多位图)。我可以给你看一些代码:

刷新BMP:

private void refreshBmp(int windowWidth, int windowCentre) {
    int[] colorArray = process.transformBuffer(myDicomObject.getRawData(),
            myDicomObject.isInverted(), windowWidth, windowCentre,
            myDicomObject.getnBits());

    Bitmap bmp = Bitmap.createBitmap(colorArray,
            myDicomObject.getColumns(), myDicomObject.getRows(),
            Bitmap.Config.ARGB_8888);
    dicomImageView.setImageBitmap(bmp);
}
这就叫我的LUT:

public int[] transformBuffer(int[] rawData, boolean inverted,
        int windowWidth, int windowCenter, int nBits) {
    System.gc();

    int min = windowCenter - (windowWidth/2);       
    int max = windowCenter + (windowWidth/2);       
    int intGrayscale = (int) Math.pow(2, nBits);    
    int intDivisionFactor = nBits-8;                

    double dmin = (double) min;                     
    double dmax = (double) max;                     
    double doubleGrayScale = (double) intGrayscale; 

    int rawDataLength = rawData.length;             

    int[] resultBuffer = new int[rawDataLength];    
    lutBuffer = new int[intGrayscale];              

    if(inverted){

        for(int i = 0 ; i < min ; i++){
            lutBuffer[i] = 255;
        }

        for(int i = min ; i < max ; i++){
            double value = doubleGrayScale * ((i - dmin + 1) / (dmax - dmin + 1));
            lutBuffer[i] = (int) (doubleGrayScale - value) >> intDivisionFactor;
        }

        for(int i = max ; i < intGrayscale ; i++){
            lutBuffer[i] = 0;
        }

    }else{
        for(int i = 0 ; i < min ; i++){
            lutBuffer[i] = 0;
        }

        for(int i = min ; i < max ; i++){
            double value = ((i - dmin + 1) / (dmax - dmin + 1));
            lutBuffer[i] = (int) (value) << intDivisionFactor;
        }

        for(int i = max ; i < intGrayscale ; i++){
            lutBuffer[i] = 255;
        }
    }

    for(int i = 0 ; i < rawDataLength ; i++){
        int colorValue = lutBuffer[rawData[i]];
        resultBuffer[i] = Color.argb(255, colorValue, colorValue, colorValue);
    }

    System.out.println(resultBuffer.length);
    return resultBuffer;
}
public int[]transformBuffer(int[]rawData,布尔值倒置,
int windowWidth、int windowCenter、int nBits){
gc();
int min=窗口中心-(窗口宽度/2);
int max=窗口中心+(窗口宽度/2);
intGrayscale=(int)Math.pow(2,nBits);
int系数=nBits-8;
双dmin=(双)min;
双dmax=(双)最大值;
双双灰度=(双)灰度;
int rawDataLength=rawData.length;
int[]resultBuffer=新int[rawDataLength];
lutBuffer=newint[intGrayscale];
如果(倒置){
对于(int i=0;i>intfactor;
}
对于(inti=max;ilutBuffer[i]=(int)(value)如果您不再需要它,那么应该尝试直接修改
rawData
。类似于这样:

public void transformBuffer(int[] rawData, boolean inverted,
        int windowWidth, int windowCenter, int nBits) {
/*
change rowData instead resultBuffer
*/
}

public void methodThatCallsTransformBuffer(...) {
    transformBuffer(data, inverted, ...);
    //now data is transformed
}
另外,您可以使用
lutBuffer
,而不是为每个像素计算
colorValue
。这会稍微慢一点,但会节省一些内存:

int colorValue = 0;
if (inverted) {
   if (rawData[i] < min) {
      colorValue = 255;
   } else {
      if (rawData[i] < max) {
         double value = doubleGrayScale * ((i - dmin + 1) / (dmax - dmin + 1));
         colorValue  = (int) (doubleGrayScale - value) >> intDivisionFactor;         
      }
   }
} else {
   if (rawData[i] >= min && rowData[i] < max) {
      double value = doubleGrayScale * ((i - dmin + 1) / (dmax - dmin + 1));
      colorValue  = (int) (doubleGrayScale - value) >> intDivisionFactor;      
   } else if (rowData[i] > max) {
      colorValue = 255;
   }
}
int colorValue=0;
如果(倒置){
if(原始数据[i]>intfactor;
}
}
}否则{
if(rawData[i]>=min&&rowData[i]>intfactor;
}else if(rowData[i]>max){
颜色值=255;
}
}

我决定回答自己,因为我最终找到了一个解决方案。避免这些大位图数据出现这种
OutOfMemory
错误的唯一方法是post binning(我不确定它是否是实际名称)。它包括每一个备用像素。您可以通过读取一个像素、跳过一个像素、读取另一个像素、跳过一个像素并继续执行此操作,直到到达行的末尾,然后跳过一行并继续此过程到数据缓冲区的末尾


我希望它能帮助其他人。

谢谢你的回答!但我以后确实需要它,所以它在我的情况下不起作用。
rawDataLength
intGrayScale
崩溃时的值是多少?@David
intGrayScale
是一个常量,它的值是2^16=65536。
rawDataLength
=
resultBuffer.length
=7263120OK。您是否意识到分配int[7263120]需要28MB内存?大多数Android手机的最大进程堆大小为16MB、24MB或32MB。您可以使用
ActivityManager.getMemoryClass()检查设备的最大内存
。在任何情况下,你都不能在内存中执行此操作。这是不可能的。你需要分配一个int数组吗?每个int需要4个字节。如果你输入的值只有0-255之间的值,那么你可以使用一个字节数组。这将使你的内存需求减少75%@david谢谢你的回答,我已经尝试创建了一个字节[]但是创建位图的方法需要一个int数组。