Java 大缓冲区导致Android内存不足
我在这上面被困了三天。我得到了一个Dicom文件,我需要用一个缓冲读取器来解析它,该读取器返回来自文档头的一些信息和图像的原始数据。在那之后,我在raw上应用一个LUT,将字节转换成灰度,然后将其放入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预算。我知道
位图中。它非常适合小图像,但现在,我必须加载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;i lutBuffer[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
崩溃时的值是多少?@DavidintGrayScale
是一个常量,它的值是2^16=65536。rawDataLength
=resultBuffer.length
=7263120OK。您是否意识到分配int[7263120]需要28MB内存?大多数Android手机的最大进程堆大小为16MB、24MB或32MB。您可以使用ActivityManager.getMemoryClass()检查设备的最大内存
。在任何情况下,你都不能在内存中执行此操作。这是不可能的。你需要分配一个int数组吗?每个int需要4个字节。如果你输入的值只有0-255之间的值,那么你可以使用一个字节数组。这将使你的内存需求减少75%@david谢谢你的回答,我已经尝试创建了一个字节[]但是创建位图的方法需要一个int数组。