android环境下摄像头数据采集及显示
以前项目涉及些摄像头预览及数据处理操作,当时的需求是除了做摄像头预览外,还要显示文字、个性图像等,当初在查找资料实现相关模块时,发现很多资料讲的比较繁琐,不够简洁,这里将自己的实现方式分享出来,希望能够为正在做相关工作的同学提供些思路。不过这里先顺便提一下,如果单纯的做摄像头预览,不在预览数据时做添加文字、图像等额外操作,可以用surfaceview方式,性能上会更好些。
这里将摄像头采集及视频图像绘制放在一个模块中,比较便于管理及维护,同时在使用时,因为该类继承自view类,所以可以向操作很多view类一样,将其添加到任何布局中,在与采集的数据宽高比例保持一致的前提下,在页面显示上可以非常灵活的控制视图尺寸大小。不过使用这种方式实现摄像头预览,最大的瓶颈是在旋转yuv数据及将其转为rgb数据时,计算比较耗时,一般情况下采集640*480数据还好,但对于960*720数据来说,手机性能一般的话,就会显得比较卡了。解决方式在做数据旋转时,可以尝试采用ndk
c的方式,以提高运行效率,在做yuv转rgb时,也可以尝试用ndk c的方式,但是最好的方式是采用gpu shader方式,直接渲染yuv数据,即将采集的yuv数据以纹理的方式上传至gpu,然后由gpu完成yuv转rgb并显示。下面是相关代码:
public class CameraView extends View implements PreviewCallback { // 源视频帧宽/高 private int srcFrameWidth = 640; private int srcFrameHeight = 480; private int frameSize = srcFrameWidth * srcFrameHeight; private int qtrFrameSize = srcFrameWidth * srcFrameHeight >> 2; // 帧预览贴图 private Bitmap previewBmp = null; private Rect previewRect = null; private Camera camera = null; // 图层 private BaseLayer[] layers = null; // 数据采集 private int[] rgb_data = null; private byte[] yuvdata = null; // 摄像头前置/后置 public static final int CAMERA_BACK = 0; public static final int CAMERA_FRONT = 1; private int curCameraIndex = CAMERA_BACK; public CameraView(Context _context) { super(_context); } public CameraView(Context _context, AttributeSet _attrs) { super(_context, _attrs); } public CameraView(Context context, int previewWidth, int previewHeight, int cameraIndex) { super(context); curCameraIndex = cameraIndex; rgb_data = new int[frameSize]; yuvdata = new byte[frameSize * 3 / 2]; previewBmp = Bitmap.createBitmap(srcFrameHeight, srcFrameWidth, Config.ARGB_8888); previewRect = new Rect(0, 0, previewWidth, previewHeight); // 定义图层 layers = new BaseLayer[2]; layers[0] = new TextLayer(context, 0, false); layers[1] = new ImageLayer(context, 1, false); // 文字 ((TextLayer)layers[0]).setFontParams(32, Color.CYAN); ((TextLayer)layers[0]).setTextPos(100, 300); ((TextLayer)layers[0]).setContent("天气还不错...."); layers[0].setVisible(true); // 图像 ((ImageLayer)layers[1]).setImagePos(100, 150); layers[1].setVisible(true); // 初始化并打开摄像头 startCamera(cameraIndex); this.setBackgroundColor(Color.parseColor("#82858b")); } // 根据索引初始化摄像头 public void startCamera(int cameraIndex) { // 先停止摄像头 stopCamera(); // 再初始化并打开摄像头 if (camera == null) { camera = Camera.open(cameraIndex); Camera.Parameters params = camera.getParameters(); params.setPreviewSize(srcFrameWidth, srcFrameHeight); params.setPreviewFormat(ImageFormat.NV21); camera.setParameters(params); camera.setPreviewCallback(this); camera.startPreview(); } } // 停止并释放摄像头 public void stopCamera() { if (camera != null) { camera.setPreviewCallback(null); camera.stopPreview(); camera.release(); camera = null; } } // 绘制 @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 填充数据(因为数据已经旋转过,此时宽与高需要互换) previewBmp.setPixels(rgb_data, 0, srcFrameHeight, 0, 0, srcFrameHeight, srcFrameWidth); // 绘制图层 for (BaseLayer layer : layers) { if (layer.isVisible()) { layer.drawLayer(previewBmp); } } // 贴图 canvas.drawBitmap(previewBmp, null, previewRect, null); } // 获取摄像头视频数据 @Override public void onPreviewFrame(byte[] data, Camera camera) { int i = 0, j = 0, k = 0; int uvHeight = srcFrameHeight >> 1; // 旋转yuv数据 if (curCameraIndex == CAMERA_BACK) { // 旋转y for (i = 0; i < srcFrameWidth; i++) { for (j = srcFrameHeight - 1; j >= 0; j--) { yuvdata[k] = data[srcFrameWidth * j + i]; k++; } } // 旋转uv for (i = 0; i < srcFrameWidth; i += 2) { for (j = uvHeight - 1; j >= 0; j--) { yuvdata[k] = data[frameSize + srcFrameWidth * j + i + 1];// cb/u yuvdata[k + qtrFrameSize] = data[frameSize + srcFrameWidth * j + i];// cr/v k++; } } } else { // 旋转y for (i = srcFrameWidth - 1; i >= 0; i--) { for (j = srcFrameHeight - 1; j >= 0; j--) { yuvdata[k] = data[srcFrameWidth * j + i]; k++; } } // 旋转uv for (i = srcFrameWidth - 2; i >= 0; i -= 2) { for (j = uvHeight - 1; j >= 0; j--) { yuvdata[k] = data[frameSize + srcFrameWidth * j + i + 1];// cb/u yuvdata[k + qtrFrameSize] = data[frameSize + srcFrameWidth * j + i];// cr/v k++; } } } // yuv转rgb(因为数据已经旋转过,此时宽与高需要互换) int yp = 0; for (i = 0, yp = 0; i < srcFrameWidth; i++) { int uvp = frameSize + (i >> 1) * uvHeight, u = 0, v = 0; for (j = 0; j < srcFrameHeight; j++, yp++) { int y = (0xff & yuvdata[yp]) - 16; if ((j & 1) == 0) { u = (0xff & yuvdata[uvp + (j>>1)]) - 128; v = (0xff & yuvdata[uvp + qtrFrameSize + (j>>1)]) - 128; } int y1192 = 1192 * y; int r = (y1192 + 1634 * v); int g = (y1192 - 833 * v - 400 * u); int b = (y1192 + 2066 * u); if (r < 0) r = 0; else if (r > 262143) r = 262143; if (g < 0) g = 0; else if (g > 262143) g = 262143; if (b < 0) b = 0; else if (b > 262143) b = 262143; rgb_data[i*srcFrameHeight + j] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff); }// for }// for invalidate(); } }工程下载链接:http://download.csdn.net/detail/u013085897/8652979
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。