很多時候,android攝像頭模塊不僅預覽,拍照這么簡單,而是需要在預覽視頻的時候,能夠做出一些檢測,比如最常見的人臉檢測。在未按下拍照按鈕前,就檢測出人臉然后矩形框標示出來,再按拍照。那么如何獲得預覽幀視頻么?
只需要在Activity里繼承PreviewCallback這個接口就行了。示例如下:
public class RectPhoto extends Activity implements SurfaceHolder.Callback, PreviewCallback{}。(注意這個SurfaceHolder.Callback是用來預覽攝像頭視頻,參見[我的前貼](http://blog.csdn.net/yanzi1225627/article/details/8577756))。
繼承這個方法后,會自動重載這個函數:public void onPreviewFrame(byte[] data, Camera camera) {}這個函數里的data就是實時預覽幀視頻。一旦程序調用PreviewCallback接口,就會自動調用onPreviewFrame這個函數。調用PreviewCallback的方法有三種,可以參考[這里](http://read.360buy.com/12142/576884.html),總共有三種方式調用這個回調。所謂回調就是當條件滿足時,自動觸發調用這個函數。分別是:.setPreviewCallback, setOneShotPreviewCallback, setPreviewCallbackWithBuffer, 我一般是使用第二種方式。
????? 這里解釋下,如果Activity繼承了PreviewCallback這個接口,只需?? ??? ??? ???? Camera.setOneShotPreviewCallback(this);就可以了。程序會自動調用主類Activity里的onPreviewFrame函數。如果Camera.setOneShotPreviewCallback()這個函數是在主類Activity里的內部類如class A里面,里面的參數應寫為Camera.setOneShotPreviewCallback(YourActivity.this)。當然這里,也可以定義一個變量,如Camera.PreviewCallback mPreviewCallback,在調用的時候用Camera.setOneShotPreviewCallback(mPreviewCallback)來完成。相信很多人都熟悉這點,就不羅嗦了。
? ? 按理說只要在onPreviewFrame()這個函數里寫你的處理程序就可以了。當通常不這么做,因為處理實時預覽幀視頻的算法可能比較復雜,這就需要借助AsyncTask開啟一個線程在后臺處理數據。這里假設我們定義一個FaceTask來進行人臉檢測,可以這樣寫:
?? /*自定義的FaceTask類,開啟一個線程分析數據*/
?private class FaceTask extends AsyncTask<Void, Void, Void>{
??? ?private byte[] mData;
??? ?//構造函數
??? ?PalmTask(byte[] data){
??? ??? ?this.mData = data;
??? ?}
??? ?
??? ?@Override
??? ?protected Void doInBackground(Void... params) {
??? ??? ?// TODO Auto-generated method stub
??? ??? ?Size size = myCamera.getParameters().getPreviewSize(); //獲取預覽大小
??? ??? ?final int w = size.width;? //寬度
??? ??? ?final int h = size.height;
??? ??? ?final YuvImage image = new YuvImage(mData, ImageFormat.NV21, w, h, null);
??? ??? ?ByteArrayOutputStream os = new ByteArrayOutputStream(mData.length);
??? ??? ?if(!image.compressToJpeg(new Rect(0, 0, w, h), 100, os)){
??? ??? ??? ?return null;
??? ??? ?}
??? ??? ?byte[] tmp = os.toByteArray();
??? ??? ?**Bitmap bmp = BitmapFactory.decodeByteArray(tmp, 0,tmp.length);?**
??? ??**? doSomethingNeeded(bmp);?? //自己定義的實時分析預覽幀視頻的算法**
???????? return null;
??? ?}
??? ?
?} ?
注意上面的bmp就是Bitmap格式的實時預覽幀數據。**doSomethingNeeded(bmp) 就是你要對預覽幀視頻進行的處理,可以是檢測人臉或其他,如分析有無火災。或者是進行傳輸。? 另外,這里是通過YuvImage和ImageFormat.NV21來解析數據的。在華為u9200上,android4.0.3的系統運行良好。不同手機上支持的格式可能有所不同。網上也有自己寫算法進行轉化的,需要的可以自己找,但這里如果支持這個格式就不用自己寫轉換算法了。?**
??? onPreviewFrame()里可以這樣寫:
??? /*獲取預覽幀視頻*/
?public void onPreviewFrame(byte[] data, Camera camera) {
??? ?// TODO Auto-generated method stub
??? ?if(null != mFaceTask){
??? ??? ?switch(mFaceTask.getStatus()){
??? ??? ?case RUNNING:
??? ??? ??? ?return;
??? ??? ?case PENDING:
??? ??? ??? ?mFaceTask.cancel(false);
??? ??? ??? ?break;
??? ??? ?}
??? ?}
??? ?mFaceTask = new PalmTask(data);
??? ?mFaceTask.execute((Void)null);
??? ?
?}
?? 上面的mFaceTask是一個全局變量。通過onPreviewFrame,AsyncTask的綜合應用,讓復雜的處理算法執行在后臺,也就是doInBackground這里,是不是比較綠色?
??? 接下來就是什么時候觸發onPreviewFrame()這個函數里,可以是按一個按鍵觸發一次,就在按鍵的監聽里寫上?? ??? myCamera.setOneShotPreviewCallback(RectPhoto.this);便會自動觸發一次。有人說想先聚焦,然后再分析預覽幀。就在onAutofocus里的回調寫。如下:
???? //自動聚焦變量回調
??? ?myAutoFocusCallback = new AutoFocusCallback() {
??? ??? ?public void onAutoFocus(boolean success, Camera camera) {
??? ??? ??? ?// TODO Auto-generated method stub
??? ??? ??? ?if(success)//success表示對焦成功
??? ??? ??? ?{
??? ??? ??? ??? ?Log.i(tag, "myAutoFocusCallback: success...");
??? ??? ??? ??? ?myCamera.setOneShotPreviewCallback(RectPhoto.this);
??? ??? ??? ????
??? ??? ??? ?}
??? ??? ??? ?else
??? ??? ??? ?{
??? ??? ??? ??? ?//未對焦成功
??? ??? ??? ???? Log.i(tag, "myAutoFocusCallback: 失敗了...");
**???????????????? //這里也可以加上myCamera.autoFocus(myAutoFocusCallback),如果聚焦失敗就再次啟動聚焦。**
??? ??? ??? ?}
??? ??? ?}
??? ?};
??? 大多數時候,希望程序自動每隔多長時間,自動進行一次檢測預覽幀。這也好辦,實施如下:
~~~
class ScanThread implements Runnable{
public void run() {
// TODO Auto-generated method stub
while(!Thread.currentThread().isInterrupted()){
try {
if(null != myCamera && isPreview)
{????
//myCamera.autoFocus(myAutoFocusCallback);
myCamera.setOneShotPreviewCallback(RectPhoto.this);
Log.i(tag, "setOneShotPreview...");
}
Thread.sleep(1500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
}
}
~~~
在onCreate里new Thread(new ScanThread()).start()開啟掃描線程。如果想手動觸發中止這種掃描活動,可以在ScanThread里的while循環里設置標志位,具體可看我[以前的博文](http://blog.csdn.net/yanzi1225627/article/details/8581200)。
?? 最后提醒的是,如果程序中加入了previewCallback,在surfaceDestroy釋放camera的時候,最好執行myCamera.setOneShotPreviewCallback(null); 或者myCamera.setPreviewCallback(null);中止這種回調,然后再釋放camera更安全。否則可能會報錯。
???? 歡迎android愛好者加群248217350,備注:yanzi
-----------------------------------------------------------------本文系原創,轉載請注明作者:yanzi1225627
- 前言
- Linux下使用QT調用opencv讀取攝像頭視頻 調試心得
- Android開發 攝像頭SurfaceView預覽 背景帶矩形框 實現(原理:雙surfaceview,頂層畫矩形框,底層預覽視頻)
- Android開發:安裝NDK,移植OpenCV2.3.1,JNI調用OpenCV全過程
- 2013新春奉送:Android攝像頭開發完美demo---(循環聚焦,縮放大小,旋轉picture,查詢支持的picturesize, ImageButton按鍵效果)
- 如何設置ImageButton按鍵按下去后的 特效----(如類似風車旋轉的動畫特效)
- Android攝像頭:只拍攝SurfaceView預覽界面特定區域內容(矩形框)---完整實現(原理:底層SurfaceView+上層繪制ImageView)
- Android開發:SurfaceView上新建線程繪制旋轉圖片 及 刷新特定區域(臟矩形)
- Android開發:ImageView上繪制旋轉圓環(透明度不同的旋轉圓環,利用canvas.drawArc實現)
- Android上掌紋識別第一步:基于OpenCV的6種膚色分割 源碼和效果圖
- Android開發:實時處理攝像頭預覽幀視頻------淺析PreviewCallback,onPreviewFrame,AsyncTask的綜合應用
- Android攝像頭開發:拍照后添加相框,融合相框和圖片為一副 圖片
- Android(OpenCV) NDK開發: 0xdeadbaad(code=1)錯誤 及 關閉armeabi和libnative_camera_r2.2.2.so的生成
- Android攝像頭開發:實時攝像頭視頻預覽幀的編碼問題(二)
- setContentView切換頁面(無需每次都findViewById)-----二
- Android開發:setContentView切換界面,自定義帶CheckBox的ListView顯示SQlite條目-----實現