android环境下摄像头数据采集及显示
来源:程序员人生 发布时间:2015-07-01 08:05:30 阅读次数:5356次
之前项目触及些摄像头预览及数据处理操作,当时的需求是除做摄像头预览外,还要显示文字、个性图象等,当初在查找资料实现相干模块时,发现很多资料讲的比较繁琐,不够简洁,这里将自己的实现方式分享出来,希望能够为正在做相干工作的同学提供些思路。不过这里先顺便提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
生活不易,码农辛苦
如果您觉得本网站对您的学习有所帮助,可以手机扫描二维码进行捐赠