android 环境数据采集,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();

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值