android環境下攝像頭數據采集及顯示
來源:程序員人生 發布時間:2015-07-01 08:05:30 閱讀次數:5507次
之前項目觸及些攝像頭預覽及數據處理操作,當時的需求是除做攝像頭預覽外,還要顯示文字、個性圖象等,當初在查找資料實現相干模塊時,發現很多資料講的比較繁瑣,不夠簡潔,這里將自己的實現方式分享出來,希望能夠為正在做相干工作的同學提供些思路。不過這里先順便提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
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈