程序员人生 网站导航

android环境下摄像头数据采集及显示

栏目:综合技术时间:2015-07-01 08:05:30

       之前项目触及些摄像头预览及数据处理操作,当时的需求是除做摄像头预览外,还要显示文字、个性图象等,当初在查找资料实现相干模块时,发现很多资料讲的比较繁琐,不够简洁,这里将自己的实现方式分享出来,希望能够为正在做相干工作的同学提供些思路。不过这里先顺便提1下,如果单纯的做摄像头预览,不在预览数据时做添加文字、图象等额外操作,可以用surfaceview方式,性能上会更好些。

       这里将摄像头收集及视频图象绘制放在1个模块中,比较便于管理及保护,同时在使用时,由于该类继承自view类,所以可以向操作很多view类1样,将其添加到任何布局中,在与收集的数据宽高比例保持1致的条件下,在页面显示上可以非常灵活的控制视图尺寸大小。不过使用这类方式实现摄像头预览,最大的瓶颈是在旋转yuv数据及将其转为rgb数据时,计算比较耗时,1般情况下收集640*480数据还好,但对960*720数据来讲,手机性能1般的话,就会显得比较卡了。解决方式在做数据旋转时,可以尝试采取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




------分隔线----------------------------
------分隔线----------------------------

最新技术推荐