2017-07-17 40 views
1

我使用媒體播放器錄製時從麥克風取得的音量值列表中創建波形圖像。如何從大量值中創建縮放圖像

這對長達幾分鐘的錄音非常適用,但是當錄音時間大約一個小時時,會出現內存不足錯誤。

據我所知,這個錯誤來自我的代碼,我試圖創建一個位圖圖像,寬度(以像素爲單位)是數組的長度,高度始終爲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張圖像縮放得太多,第三張圖像正確,最後一張圖像縮放爲更大的圖像,這會形成非常奇怪和美妙的波形。

回答

0

存儲器內的位圖存儲不壓縮,在RGB_565中,如果圖像寬度爲2000 * 200高度= 400k點* 2 = 800千字節數據,則每個點爲2個字節 - 您應該有更大的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的寬度,10號線在1px的

你的圖像將百達留在裏面760x200

BTW你應該確保你的價值觀是永諾內0..100,否則你會削波(線將下降外圖片)

+0

非常感謝你,你是一個救星!我簡直不敢相信那麼簡單!我想它需要一些新鮮的眼睛,所以非常感謝你,我非常感謝。你對RGB_565是正確的,最初我把這個設置爲ARGB_8888,這導致了各種各樣的內存問題,所以我將它改爲RGB_565,但是有些人仍然有問題,特別是如果waveValues數組中有500,000個值。再次感謝,親切的問候,Neil – NDroid

+0

在win32上,jvm32可以訪問大約1.4-1.8 Gb,在linux32上大概是2Gb,在win64上,jvm32可以訪問大約4Gb但有一些限制。如果你需要在一個線程/一個對象中使用超過1.4Gb的RAM,你應該考慮使用jvm64。雖然200px高度的500k點在565格式中僅爲100mp = 200Mb,但不同的重新調整功能可以創建大型緩衝區,消耗內存,還要確保處理不需要的位圖/畫布 - 即使單個內存泄漏位圖意味着您的內存將被填充在瞬間。 –

0

不僅大的位圖會導致你的內存問題,而且渲染每個樣本也會做很多不必要的工作,因爲你只是反覆繪製相同像素的頂部。

處理此問題的最佳方法是首先爲位圖設置大小限制。在下面的例子中是2000像素,當採樣計數小於這個時,位圖將變成採樣計數。如果樣本數量大於位圖,將被限制爲2000像素。

對於每個像素列將覆蓋多個樣本的長樣本,您只需繪製該列所覆蓋的樣本的最大值。所以保留一個變量x即當前樣本轉換爲像素位置。

當值x是一樣的以前的迭代X,則只需通過max = Math.max(waveValues.get(i));

得到最大當x值變化到下一個欄目你再渲染最大。該最大值是前一列的最大樣本值。然後您將最大值重置爲當前樣本。

在循環結束時,您需要渲染最後一列。

由於當每列樣本數大於約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) {} 

}