問題描述
我需要做的很簡單,我想使用相機回調手動顯示來自相機的預覽,并且我想在真實設備上獲得至少 15fps.我什至不需要顏色,我只需要預覽灰度圖像.來自相機的圖像是 YUV 格式,您必須以某種方式對其進行處理,這是主要的性能問題.我正在使用 API 8.
What I need to do is quite simple, I want to manually display preview from camera using camera callback and I want to get at least 15fps on a real device. I don't even need the colors, I just need to preview grayscale image. Images from camera are in YUV format and you have to process it somehow, which is the main performance problem. I'm using API 8.
在所有情況下,我都使用 camera.setPreviewCallbackWithBuffer(),它比 camera.setPreviewCallback() 快.如果我不顯示預覽,我似乎無法在這里獲得大約 24 fps.所以沒有問題.
In all cases I'm using camera.setPreviewCallbackWithBuffer(), that is faster than camera.setPreviewCallback(). It seems that I cant get about 24 fps here, if I'm not displaying the preview. So there is not the problem.
我已經嘗試了這些解決方案:
I have tried these solutions:
1.在 SurfaceView 上將相機預覽顯示為位圖. 可以,但性能約為 6fps.
1. Display camera preview on a SurfaceView as a Bitmap. It works, but the performance is about 6fps.
baos = new ByteOutputStream();
yuvimage=new YuvImage(cameraFrame, ImageFormat.NV21, prevX, prevY, null);
yuvimage.compressToJpeg(new Rect(0, 0, prevX, prevY), 80, baos);
jdata = baos.toByteArray();
bmp = BitmapFactory.decodeByteArray(jdata, 0, jdata.length); // Convert to Bitmap, this is the main issue, it takes a lot of time
canvas.drawBitmap(bmp , 0, 0, paint);
<強>2.在 GLSurfaceView 上將相機預覽顯示為紋理. 這里我只顯示亮度數據(灰度圖像),這很簡單,每幀只需要一個 arraycopy().我可以獲得大約 12fps,但我需要對預覽應用一些過濾器,而且似乎在 OpenGL ES 1 中無法快速完成.所以我不能使用這個解決方案.另一個問題中的一些細節.
2. Display camera preview on a GLSurfaceView as a texture. Here I was displaying only luminance data (greyscale image), which is quite easy, it requires only one arraycopy() on each frame. I can get about 12fps, but I need to apply some filters to the preview and it seems, that it can't be done fast in OpenGL ES 1. So I can't use this solution. Some details of this in another question.
3.使用 NDK 在 (GL)SurfaceView 上顯示相機預覽以處理 YUV 數據.我在 here 找到了一個使用一些 C 函數和 NDK 的解決方案.但我沒有設法使用它,這里有更多細節.但無論如何,這個解決方案是返回 ByteBuffer 以在 OpenGL 中將其顯示為紋理,并且不會比之前的嘗試快.所以我必須修改它以返回 int[] 數組,可以用 canvas.drawBitmap() 繪制,但我對 C 的理解不足以做到這一點.
3. Display camera preview on a (GL)SurfaceView using NDK to process the YUV data. I find a solution here that uses some C function and NDK. But I didn't manage to use it, here some more details. But anyway, this solution is done to return ByteBuffer to display it as a texture in OpenGL and it won't be faster than the previous attempt. So I would have to modify it to return int[] array, that can be drawn with canvas.drawBitmap(), but I don't understand C enough to do this.
那么,我是否還有其他方法遺漏或改進了我嘗試的嘗試?
So, is there any other way that I'm missing or some improvement to the attempts I tried?
推薦答案
我正在研究完全相同的問題,但還沒有達到你的水平.
I'm working on exactly the same issue, but haven't got quite as far as you have.
您是否考慮過將像素直接繪制到畫布上而不先將它們編碼為 JPEG?OpenCV 工具包內部 http://sourceforge.net/projects/opencvlibrary/files/opencv-android/2.3.1/OpenCV-2.3.1-android-bin.tar.bz2/download (實際上并沒有使用 opencv;別擔心),有一個名為 tutorial-0-androidcamera 的項目,它演示了將 YUV 像素轉換為 RGB,然后將它們直接寫入位圖.
Have you considered drawing the pixels directly to the canvas without encoding them to JPEG first? Inside the OpenCV kit http://sourceforge.net/projects/opencvlibrary/files/opencv-android/2.3.1/OpenCV-2.3.1-android-bin.tar.bz2/download (which doesn't actually use opencv; don't worry), there's a project called tutorial-0-androidcamera that demonstrates converting the YUV pixels to RGB and then writing them directly to a bitmap.
相關代碼本質上是:
public void onPreviewFrame(byte[] data, Camera camera, int width, int height) {
int frameSize = width*height;
int[] rgba = new int[frameSize+1];
// Convert YUV to RGB
for (int i = 0; i < height; i++)
for (int j = 0; j < width; j++) {
int y = (0xff & ((int) data[i * width + j]));
int u = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 0]));
int v = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 1]));
y = y < 16 ? 16 : y;
int r = Math.round(1.164f * (y - 16) + 1.596f * (v - 128));
int g = Math.round(1.164f * (y - 16) - 0.813f * (v - 128) - 0.391f * (u - 128));
int b = Math.round(1.164f * (y - 16) + 2.018f * (u - 128));
r = r < 0 ? 0 : (r > 255 ? 255 : r);
g = g < 0 ? 0 : (g > 255 ? 255 : g);
b = b < 0 ? 0 : (b > 255 ? 255 : b);
rgba[i * width + j] = 0xff000000 + (b << 16) + (g << 8) + r;
}
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bmp.setPixels(rgba, 0/* offset */, width /* stride */, 0, 0, width, height);
Canvas canvas = mHolder.lockCanvas();
if (canvas != null) {
canvas.drawBitmap(bmp, (canvas.getWidth() - width) / 2, (canvas.getHeight() - height) / 2, null);
mHolder.unlockCanvasAndPost(canvas);
} else {
Log.w(TAG, "Canvas is null!");
}
bmp.recycle();
}
當然,您必須對其進行調整以滿足您的需求(例如,不為每一幀分配 rgba),但這可能是一個開始.我很想看看它是否適合你——我目前仍在與與你正交的問題作斗爭.
Of course you'd have to adapt it to meet your needs (ex. not allocating rgba each frame), but it might be a start. I'd love to see if it works for you or not -- i'm still fighting problems orthogonal to yours at the moment.
這篇關于Android:如何通過回調顯示相機預覽?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!