[分享] 取得攝影機預覽資料並回饋到螢幕上

看板AndroidDev作者 (mamaya)時間13年前 (2011/04/26 11:02), 編輯推噓17(1706)
留言23則, 15人參與, 最新討論串1/2 (看更多)
原本不想寫的..因為架構上需要用到三個class 另外有些細節部分我也不很熟 怕寫出來有誤導大眾之嫌 不過最近還是會有人問類似的問題 所以我乾脆抽點時間 開個新專案把主要機制 重新整理出來 大家也比較方便了解 在一切起頭前 這邊首先介紹會用到的class 一共有三個 ViewToDraw class 從View繼承而來 功能就是提供畫布功能讓你畫東西 Camera preview class 很一般的攝影機預覽class 唯一不同的地方是它會有個變數來存ViewToDraw 這樣每次預覽更新 就可以丟圖像資料直接到畫布上 順便提醒它更新畫面 main class 功能不多 主要就是鎖住螢幕的旋轉方向在lanscape(因為2.2以前不支援potrait) 還有取得全畫面(如果你想要的話) 並建立起主要的layout 好了 這邊了解後接下來就開始動工了 首先你會需要修改AndroidManifest.xml 這應該寫過攝影機功能的人都知道 要加入下面兩行 <uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" /> 這邊就不多談了 再來就是改layout 不過由於你的layout會用到custom view而你還沒實作 所以會有錯誤 訊息...先不管它吧 anyway, 整個xml如下 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <FrameLayout android:id="@+id/FrameLayout01" android:layout_height="fill_parent" android:layout_width="fill_parent"> <FrameLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/preview"> </FrameLayout> <com.maya.mcp.ViewToDraw android:id="@+id/vtd" android:layout_height="fill_parent" android:layout_width="fill_parent"> </com.maya.mcp.ViewToDraw> </FrameLayout> </LinearLayout> ID為preview的FrameLayout之後會加上攝影機預覽的surfaceView ID為vtd的cutsom View就是等會要拿來畫畫的畫布 搞定了layout後接下來開始實作class 首先從畫布開始... 在這邊 你會需要幾個變數 首先是輸入圖像的長跟寬 由於它不一定跟螢幕解析度相同 所以你等會需要它做長寬比縮放 另外 你當然也需要一個byte的陣列去存preview callback時丟過來的圖像資料 還有 你也要一個變數確定Camera已經架好運作 資料已經丟來的旗標 不然甚麼圖像都沒 讀到時就讓程式傻傻跑onDraw 結果一定是會crash的 ok..變數搞定了 接下來是method... 變數設定存取的那些就不管了 相信你都會寫 這邊你還會需要一個把YUV420SP資料轉存的method 由於在我的範例中我只來簡單判斷 亮度 所以程式相當簡單如下 private boolean getBoolean(int x, int y){ int l; l = (0xff & ((int) image[x+y*imgWidth])) - 16; if (l > 128){ return true; }else{ return false; } } 如果你要轉成RGB 我也有附在檔案裡 不過是從網路上挖來的..請自行研究 至於onDraw()要做甚麼呢? 以我的範例是要把讀到的圖二元化後塗回畫布去 所以是用二層迴圈把圖像資料解讀出來 然後再丟白或黑點上去 @Override protected void onDraw(Canvas canvas){ int i, j; //將預覽圖資料與畫布做長寬縮放比運算 float hscale = (float)canvas.getHeight()/imgHeight; float wscale = (float)canvas.getWidth()/imgWidth; if(isCameraSet){ for(i=0;i<imgWidth;i+=8){ for(j=0;j<imgHeight;j+=8){ if(getBoolean(i,j)){ canvas.drawPoint(i*wscale, j*hscale, whitePaint); }else{ canvas.drawPoint(i*wscale, j*hscale, blackPaint); } } } } } 接下來是cameraView class 大部分都跟一般開相機功能的範例一樣 主要不同點在於 使用了setPreViewCallbackWithBuffer 設定callback來處理相機預覽畫面的資料更新 這是一個2.2新增的method 在2.1以前請用setPreViewCallback 兩者差別只在於前者 要先產生多個buffer給callback預備使用 讓傳回效率可以進一步提升 程式如下 //產生 buffer PixelFormat p = new PixelFormat(); PixelFormat.getPixelFormatInfo(parameters.getPreviewFormat(),p); int bufSize = (pickedW*pickedH*p.bitsPerPixel)/8; //把buffer給preview callback備用 byte[] buffer = new byte[bufSize]; mycamera.addCallbackBuffer(buffer); buffer = new byte[bufSize]; mycamera.addCallbackBuffer(buffer); buffer = new byte[bufSize]; mycamera.addCallbackBuffer(buffer); //設定預覽畫面更新時的callback mycamera.setPreviewCallbackWithBuffer(new PreviewCallback() { public void onPreviewFrame(byte[] data, Camera camera) { vtd.putImage(data); vtd.CameraSet(); //更新畫布 (call onDraw()) vtd.invalidate(); //把buffer丟回給callback重新利用 mycamera.addCallbackBuffer(data); } }); 最後是處理main class 這部分就很簡單 主要有三 1.把畫面鎖在landscape 2.取得ViewToDraw 3.產生攝影機預覽的surfaceView 並把ViewToDraw pass過去 4.把上面的surfaceView加到FrameLayout 程式如下 // 取得全螢幕 this.requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay(); setContentView(R.layout.main); //鎖住螢幕方向 setRequestedOrientation(0); //取得畫圖的View ViewToDraw dtw = (ViewToDraw) findViewById(R.id.vtd); //產生攝影機預覽surfaceView CameraView cameraView = new CameraView(this, dtw, this.getApplicationContext()); //把預覽的surfaceView加到名為preview的FrameLayout ((FrameLayout) findViewById(R.id.preview)).addView(cameraView); 整個範例程式碼可以到下面下載 不保證跑在每台手機上都能很順就是了XD http://www.megaupload.com/?d=S0U489LO -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 71.109.152.84 ※ 編輯: mamaya3 來自: 71.109.152.84 (04/26 11:23)

04/26 12:04, , 1F
這個讚!
04/26 12:04, 1F

04/26 12:17, , 2F
先推再看.
04/26 12:17, 2F

04/26 14:29, , 3F
先推
04/26 14:29, 3F

04/26 17:18, , 4F
已收錄
04/26 17:18, 4F

04/26 17:31, , 5F
非常感謝大大分享!!! 我需要的就是這些
04/26 17:31, 5F

04/26 17:41, , 6F
給個推
04/26 17:41, 6F

04/26 19:21, , 7F
先推 先讚, 謝謝分享
04/26 19:21, 7F

04/26 20:24, , 8F
推!! 不錯說!
04/26 20:24, 8F

04/26 21:38, , 9F
未看先推
04/26 21:38, 9F

04/26 22:11, , 10F
推推推~感謝無私分享
04/26 22:11, 10F

04/26 23:16, , 11F
推推推!!!
04/26 23:16, 11F

04/27 15:50, , 12F
很實用耶!!
04/27 15:50, 12F

04/28 15:31, , 13F
感謝分享!
04/28 15:31, 13F

04/29 17:01, , 14F
太棒了!! 謝謝分享!!
04/29 17:01, 14F

04/30 13:47, , 15F
請問 YCrCb 就是 YUV421嗎?? ...因為看到getBoolean這個
04/30 13:47, 15F

04/30 13:48, , 16F
地方的註解是YUV421 ... 可是我看DEV網站是說YCrCb
04/30 13:48, 16F

04/30 13:49, , 17F
預設是 NV21 (YCrCb)
04/30 13:49, 17F

04/30 13:53, , 18F
YUV即YCrCb,亮度與彩度~~~
04/30 13:53, 18F

04/30 14:03, , 19F
了解 謝謝
04/30 14:03, 19F

04/30 17:55, , 20F
想請問 為什麼我抓到的PreviewFormat是YUV422SP格式的?
04/30 17:55, 20F

04/30 17:56, , 21F
使用setPreviewFormat也無法設定成其他格式
04/30 17:56, 21F

04/30 17:56, , 22F
網路上都是YUV420SP轉RGB ... 不知道有沒有YUV422SP的~"~
04/30 17:56, 22F

07/26 17:37, , 23F
推...好棒
07/26 17:37, 23F
文章代碼(AID): #1DjZNPA1 (AndroidDev)
文章代碼(AID): #1DjZNPA1 (AndroidDev)