由於在每個週期內分配和取消分配內存,應用程序會變慢。有三種方法可以避免這種情況。
第一個版本沒有OpenCV,但仍在每個週期分配一些內存。但數量要小得多,因此速度至少要快兩倍。怎麼樣?通過重用現有和已分配的緩衝區(byte [])。我使用了一個長度爲1.000.000的預先分配的SteamInfo緩衝區(大約比我預期的大一倍)。
順便說一句 - 讀取塊中的輸入流並使用BitmapFactory.decodeByteArray比將URL的輸入流直接放入BitmapFactory.decodeStream快得多。
public static class StreamInfo {
public byte[] buffer;
public int length;
public StreamInfo(int length) {
buffer = new byte[length];
}
}
public static StreamInfo imageByte(StreamInfo buffer, String url) {
try {
URL newUrl = new URL(url);
InputStream is = (InputStream) newUrl.getContent();
byte[] tempBuffer = new byte[8192];
int bytesRead;
int position = 0;
if (buffer != null) {
// re-using existing buffer
while ((bytesRead = is.read(tempBuffer)) != -1) {
System.arraycopy(tempBuffer, 0, buffer.buffer, position,
bytesRead);
position += bytesRead;
}
buffer.length = position;
return buffer;
} else {
// allocating new buffer
ByteArrayOutputStream output = new ByteArrayOutputStream();
while ((bytesRead = is.read(tempBuffer)) != -1) {
output.write(tempBuffer, 0, bytesRead);
position += bytesRead;
}
byte[] result = output.toByteArray();
buffer = new StreamInfo(result.length * 2, false);
buffer.length = position;
System.arraycopy(result, 0, buffer.buffer, 0, result.length);
return buffer;
}
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
第二個版本使用了OpenCV墊和預分配的位圖。接收流是按照版本1完成的。所以它不再需要進一步的內存分配(詳情請查看this link)。此版本工作正常,但速度稍慢,因爲它包含OpenCV Mat和Bitmap之間的轉換。
private NetworkCameraFrame frame;
private HttpUtils.StreamInfo buffer = new HttpUtils.StreamInfo(1000000);
private MatOfByte matForConversion;
private NetworkCameraFrame receive() {
buffer = HttpUtils.imageByte(buffer, uri);
if (buffer == null || buffer.length == 0)
return null;
Log.d(TAG, "Received image with byte-array of length: "
+ buffer.length/1024 + "kb");
if (frame == null) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap bmp = BitmapFactory.decodeByteArray(buffer.buffer, 0,
buffer.length);
frame = new NetworkCameraFrame(bmp.getWidth(), bmp.getHeight());
Log.d(TAG, "NetworkCameraFrame created");
bmp.recycle();
}
if (matForConversion == null)
matForConversion = new MatOfByte(buffer.buffer);
else
matForConversion.fromArray(buffer.buffer);
Mat newImage = Highgui.imdecode(matForConversion,
Highgui.IMREAD_UNCHANGED);
frame.put(newImage);
return frame;
}
private class NetworkCameraFrame implements CameraFrame {
Mat mat;
private int mWidth;
private int mHeight;
private Bitmap mCachedBitmap;
private boolean mBitmapConverted;
public NetworkCameraFrame(int width, int height) {
this.mWidth = width;
this.mHeight = height;
this.mat = new Mat(new Size(width, height), CvType.CV_8U);
this.mCachedBitmap = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
}
@Override
public Mat gray() {
return mat.submat(0, mHeight, 0, mWidth);
}
@Override
public Mat rgba() {
return mat;
}
// @Override
// public Mat yuv() {
// return mYuvFrameData;
// }
@Override
public synchronized Bitmap toBitmap() {
if (mBitmapConverted)
return mCachedBitmap;
Mat rgba = this.rgba();
Utils.matToBitmap(rgba, mCachedBitmap);
mBitmapConverted = true;
return mCachedBitmap;
}
public synchronized void put(Mat frame) {
mat = frame;
invalidate();
}
public void release() {
mat.release();
mCachedBitmap.recycle();
}
public void invalidate() {
mBitmapConverted = false;
}
};
第三版本使用the instructions上BitmapFactory.Options「BitmapFactory的用法」和一個可變的位圖,其然後在解碼重新使用。它甚至可以在Android JellyBean上爲我編寫。確保在創建第一個Bitmap時使用了正確的BitmapFactory.Options。
BitmapFactory.Options options = new BitmapFactory.Options();
options.inBitmap = bmp; // the old Bitmap that should be reused
options.inMutable = true;
options.inSampleSize = 1;
Bitmap bmp = BitmapFactory.decodeByteArray(buffer, 0, buffer.length, options);
options.inBitmap = bmp;
這實際上是最快的流媒體。
請考慮使用http://square.github.io/picasso/ –
我確實相信在操作系統改進的某些時候,以前位圖空間的內部存儲空間會一遍又一遍地被重複使用,而不是被重新分配。但我不記得細節,如果我能找到它們,我會指出。然而,這可能與此不同。 –