1. 程式人生 > >影象處理(三) 檢測障礙物

影象處理(三) 檢測障礙物

前幾天寫了一個小的專案關於:當手機處於靜止狀態時,識別是否動或者前方有不同物體

MainActivity

public class MainActivity extends Activity implements SurfaceHolder.Callback,
        PreviewCallback {

    // 定義物件
    private SurfaceView mSurfaceview = null; // SurfaceView物件:(檢視元件)視訊顯示
    private SurfaceHolder mSurfaceHolder = null; // SurfaceHolder物件:(抽象介面)SurfaceView支援類
private Camera mCamera; private Camera.Parameters parameters = null; private Bitmap bitmap;// 原圖 private Bitmap tempBitmap;// temp bitmap private Bitmap bitmap_;//處理後的bitmap private Bitmap bgBitmap;//背景圖 private Bitmap bgBitmap_;//處理後的背景圖片 private ImageView img; private SurfaceView view; private
long times = 0l;//連續多久不動時間 private long indexFrame = 0l; private static final String TAG ="MainActivity"; private long times_ = 0l;//temp time private int police_time = 25;//持續時間 private int police_time_ = 10;//持續時間 private long times_2 = 0l;//報警確認次數 @Override protected void onCreate
(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.camera_layout); init(); } // OpenCV庫載入並初始化成功後的回撥函式 private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) { @Override public void onManagerConnected(int status) { // TODO Auto-generated method stub switch (status) { case BaseLoaderCallback.SUCCESS: Log.i(TAG, "成功載入"); break; default: super.onManagerConnected(status); Log.i(TAG, "載入失敗"); break; } } }; @Override protected void onResume() { // TODO Auto-generated method stub super.onResume(); // load OpenCV engine and init OpenCV library OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_4, getApplicationContext(), mLoaderCallback); Log.i(TAG, "onResume sucess load OpenCV..."); } // 初始化控制元件 private void init() { // SurfaceView view = (SurfaceView) findViewById(R.id.surface_view); // 照相機預覽的空間 view.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); view.getHolder().setFixedSize(1920, 1080); // 設定Surface解析度 view.getHolder().setKeepScreenOn(true);// 螢幕常亮 view.getHolder().addCallback(this);// 為SurfaceView的控制代碼新增一個回撥函式 // ImageView img = (ImageView) findViewById(R.id.img_res); } // SurfaceHoder.Callback // 當SurfaceView/預覽介面的格式和大小發生改變時,該方法被呼叫 @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // TODO Auto-generated method stub parameters = mCamera.getParameters(); // 獲取各項引數 mCamera.setParameters(parameters); parameters.setPictureFormat(PixelFormat.JPEG); // 設定圖片格式 parameters.setPreviewSize(width, height); // 設定預覽大小 parameters.setPreviewFrameRate(5); // 設定每秒顯示4幀 parameters.setPictureSize(width, height); // 設定儲存的圖片尺寸 parameters.setJpegQuality(80); // 設定照片質量 mCamera.setPreviewCallback(this); startFocus(); } // SurfaceView啟動時/初次例項化,預覽介面被建立時,該方法被呼叫。 @Override public void surfaceCreated(SurfaceHolder mSurfaceHolder) { // TODO Auto-generated method stub try { mCamera = Camera.open(1); // 開啟攝像頭 mCamera.setPreviewDisplay(mSurfaceHolder); // 設定用於顯示拍照影像的SurfaceHolder物件 mCamera.setDisplayOrientation(getPreviewDegree(MainActivity.this)); mCamera.startPreview(); // 開始預覽 } catch (Exception e) { e.printStackTrace(); } } // 提供一個靜態方法,用於根據手機方向獲得相機預覽畫面旋轉的角度 public static int getPreviewDegree(Activity activity) { // 獲得手機的方向 int rotation = activity.getWindowManager().getDefaultDisplay() .getRotation(); int degree = 0; // 根據手機的方向計算相機預覽畫面應該選擇的角度 switch (rotation) { case Surface.ROTATION_0: degree = 90; break; case Surface.ROTATION_90: degree = 0; break; case Surface.ROTATION_180: degree = 270; break; case Surface.ROTATION_270: degree = 180; break; } return degree; } // SurfaceView銷燬時,該方法被呼叫 @Override public void surfaceDestroyed(SurfaceHolder arg0) { // TODO Auto-generated method stub if (mCamera != null) { mCamera.setPreviewCallback(null); mCamera.release(); // 釋放照相機 mCamera = null; } } // PreviewCallback @Override public void onPreviewFrame(byte[] data, Camera camera) { // TODO Auto-generated method stub if (indexFrame++ % 6 != 0) return; Size size = camera.getParameters().getPreviewSize(); try { YuvImage image = new YuvImage(data, ImageFormat.NV21, size.width, size.height, null); if (image != null) { ByteArrayOutputStream stream = new ByteArrayOutputStream(); image.compressToJpeg(new Rect(0, 0, size.width, size.height), 80, stream); Bitmap bmp = BitmapFactory.decodeByteArray( stream.toByteArray(), 0, stream.size()); // ********************** // 因為圖片會放生旋轉,因此要對圖片進行旋轉到和手機在一個方向上 bmp = rotaingImageView(-90, bmp); // 圖片太大處理較慢,就把圖片縮放裁剪 Matrix matrix = new Matrix(); matrix.postScale(0.125f, 0.125f); // 長和寬縮小的比例 bitmap = bmp.createBitmap(bmp, 0, 0, size.height, size.width, matrix, true); bitmap_ = procSrc2Gray(bitmap);//灰度 bitmap_ = changeBitmap(bitmap);//二值 //報警 getPolice(); // ********************************** stream.close(); } } catch (Exception ex) { Log.e("Sys", "Error:" + ex.getMessage()); } } // 自動對焦 Timer timer = new Timer(true); TimerTask task = new TimerTask() { public void run() { // 每次需要執行的程式碼放到這裡面 // 實現自動對焦 mCamera.autoFocus(null); } }; public void startFocus() { timer.schedule(task, 0, 3000); } // 旋轉 public Bitmap rotaingImageView(int angle, Bitmap bitmap) { // 旋轉圖片 動作 Matrix matrix = new Matrix(); matrix.postRotate(angle); // 建立新的圖片 Bitmap bm = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); return bm; } // 比較圖片 public boolean isEquals(Bitmap b1, Bitmap b2) { int xCount = b1.getWidth(); int yCount = b1.getHeight(); int number = 0; for (int x = 0; x < xCount; x++) { for (int y = 0; y < yCount; y++) { // 比較每個畫素點顏色 if (b1.getPixel(x, y) != b2.getPixel(x, y)) { number++; } } } if(number < 500) return true; return false; } //灰度化 public Bitmap procSrc2Gray(Bitmap bm){ Mat rgbMat = new Mat(); Mat grayMat = new Mat(); Bitmap graybm = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Config.ARGB_8888); Utils.bitmapToMat(bm, rgbMat);//convert original bitmap to Mat, R G B. Imgproc.cvtColor(rgbMat, grayMat, Imgproc.COLOR_RGB2GRAY);//rgbMat to gray grayMat Utils.matToBitmap(grayMat, graybm); return graybm; } //二值化 public Bitmap changeBitmap(Bitmap bm) { Mat rgbMat = new Mat(); Mat grayMat = new Mat(); Bitmap graybm = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Config.ARGB_8888); Utils.bitmapToMat(bm,rgbMat); Imgproc.threshold(rgbMat,grayMat,100,255,Imgproc.THRESH_BINARY); Utils.matToBitmap(grayMat,graybm); return graybm; } // 影象計算 public Bitmap getPicture(Bitmap bmp1, Bitmap bmp2) { /* * pixels 接收點陣圖顏色值的陣列 offset 寫入到pixels[]中的第一個畫素索引值 stride * pixels[]中的行間距個數值(必須大於等於點陣圖寬度)。可以為負數 x   從點陣圖中讀取的第一個畫素的x座標值。 y * 從點陣圖中讀取的第一個畫素的y座標值 width   從每一行中讀取的畫素寬度 height    讀取的行數 */ int m_ImageWidth, m_ImageHeigth; m_ImageWidth = bmp1.getWidth(); m_ImageHeigth = bmp1.getHeight(); int m_Bmp1Pixel[], m_Bmp2Pixel[], m_Bmp3Pixel[]; m_Bmp1Pixel = new int[m_ImageWidth * m_ImageHeigth]; m_Bmp2Pixel = new int[m_ImageWidth * m_ImageHeigth]; m_Bmp3Pixel = new int[m_ImageWidth * m_ImageHeigth]; bmp1.getPixels(m_Bmp1Pixel, 0, m_ImageWidth, 0, 0, m_ImageWidth, m_ImageHeigth); bmp2.getPixels(m_Bmp2Pixel, 0, m_ImageWidth, 0, 0, m_ImageWidth, m_ImageHeigth); System.out.println("AAAAAAAAAAAA2"); for (int i = 0; i < m_ImageWidth * m_ImageHeigth; i++) { if (m_Bmp1Pixel[i] != m_Bmp2Pixel[i]) { m_Bmp3Pixel[i] = m_Bmp2Pixel[i]; } } System.out.println("AAAAAAAAAAAA3"); Bitmap pro = Bitmap.createBitmap(m_ImageWidth, m_ImageHeigth, Bitmap.Config.ARGB_8888); pro.setPixels(m_Bmp3Pixel, 0, m_ImageWidth, 0, 0, m_ImageWidth, m_ImageHeigth); System.out.println("AAAAAAAAAAAA4"); return pro; } //報警系統 public void getPolice(){ if(tempBitmap != null){ if (isEquals(tempBitmap, bitmap_) == true) { times++; times_2 = 0; Log.e("MainActivity", "number:" + times + ";" ); }else{ times_ = 0; } } if(tempBitmap != null && times < police_time){ if (isEquals(tempBitmap, bitmap_) == false){ times = times_; } } //Toast.makeText(MainActivity.this, "number:" + times, Toast.LENGTH_LONG).show(); tempBitmap = bitmap_; if(times == police_time){ bgBitmap = bitmap;//背景圖 bgBitmap_ = procSrc2Gray(bgBitmap);//灰度 bgBitmap_ = changeBitmap(bgBitmap);//二值 //震動提醒 // Vibrator vibrator = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE); // vibrator.vibrate(1000); //聲音提醒 NotificationManager manger = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); Notification notification = new Notification(); //自定義聲音 聲音檔案放在ram目錄下,沒有此目錄自己建立一個 //notification.sound=Uri.parse("android.resource://" + getPackageName() + "/" +R.raw.mm); notification.defaults=Notification.DEFAULT_SOUND;//手機系統提示音 manger.notify(1, notification); Toast.makeText(MainActivity.this, "開始執行程式", Toast.LENGTH_LONG).show(); } if(times >police_time){ Log.e(TAG, "action"); // 有結果,則終止執行緒 if(isEquals(bgBitmap_, bitmap_) == false){ times_2++; if(times_2 > police_time_){ // 有結果,則終止執行緒 times = times_; Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + "1111")); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); } } } } //報警確認 }

layout

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <SurfaceView
        android:id="@+id/surface_view"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

    <ImageView
        android:id="@+id/img_res"
        android:scaleType="centerInside"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:layout_gravity="center_horizontal|bottom" />


</FrameLayout>

這地方有幾個難點:

1.  如何實現相機幀預覽(即相機處於預覽狀態時,每幀圖片多能得到);

關鍵詞 :PreviewCallback
對應的方法:onPreviewFrame
// PreviewCallback
    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        // TODO Auto-generated method stub
        if (indexFrame++ % 6 != 0)//一秒六幀。可以增加圖片處理速度
            return;
        Size size = camera.getParameters().getPreviewSize();
        try {
            YuvImage image = new YuvImage(data, ImageFormat.NV21, size.width,
                    size.height, null);
            if (image != null) {
                ByteArrayOutputStream stream = new ByteArrayOutputStream();
                image.compressToJpeg(new Rect(0, 0, size.width, size.height),
                        80, stream);

                Bitmap bmp = BitmapFactory.decodeByteArray(
                        stream.toByteArray(), 0, stream.size());
                // **********************
                // 因為圖片會放生旋轉,因此要對圖片進行旋轉到和手機在一個方向上
                bmp = rotaingImageView(-90, bmp);
                // 圖片太大處理較慢,就把圖片縮放裁剪
                Matrix matrix = new Matrix();
                matrix.postScale(0.125f, 0.125f); // 長和寬縮小的比例
                bitmap = bmp.createBitmap(bmp, 0, 0, size.height, size.width,
                        matrix, true);

                bitmap_ = procSrc2Gray(bitmap);//灰度
                bitmap_ = changeBitmap(bitmap);//二值
                //報警
                getPolice();
                // **********************************
                stream.close();
            }
        } catch (Exception ex) {
            Log.e("Sys", "Error:" + ex.getMessage());
        }
    }

其中,本身手機拍照圖片會很大,圖片對應顯示就會很慢,人眼感覺就會一卡一卡的,除了處理每秒幾幀,這裡也圖片裁切,縮小了

// 圖片太大處理較慢,就把圖片縮放裁剪
                Matrix matrix = new Matrix();
                matrix.postScale(0.125f, 0.125f); // 長和寬縮小的比例
                bitmap = bmp.createBitmap(bmp, 0, 0, size.height, size.width,
                        matrix, true);

兩個方法:postScale();createBitmap()

2.  怎麼去識別相機如何不動;
思路:①將每幀處理的圖片儲存為tempbitmap ;②下一幀的圖片與之比較;③相同times++,不同times = 0;④當times = x 時,即手機不動了。
//手機畫面確認不動
if(tempBitmap != null){
            if (isEquals(tempBitmap, bitmap_) == true) {
                times++;
                times_2 = 0;
                Log.e("MainActivity", "number:" + times + ";" );
            }else{
                times_ = 0;
            }
        }
        if(tempBitmap != null && times < police_time){
            if (isEquals(tempBitmap, bitmap_) == false){
                times = times_;
            }
        }
3.  相機不動時,如何確認前方有障礙物或者手機在動;
    思路相同,就不詳細訴說了,程式碼多有

當時除了這些問題還有一些簡單的問題卡了我好久,

1.相機如何自動對焦(要用時呼叫starFocus()):

// 自動對焦
    Timer timer = new Timer(true);
    TimerTask task = new TimerTask() {
        public void run() {
            // 每次需要執行的程式碼放到這裡面
            // 實現自動對焦
            mCamera.autoFocus(null);
        }
    };

    public void startFocus() {
        timer.schedule(task, 0, 3000);
    }

2.如何比較兩組圖片是否為相同(我能力有限不能讓所有rgb值一樣,所以就取了一個約值,2%-3%)

處理最重要的地方:將比較的兩幅圖片,灰度化並且二值化,不然你會很慘的

    // 比較圖片
        public boolean isEquals(Bitmap b1, Bitmap b2) {
            int xCount = b1.getWidth();
            int yCount = b1.getHeight();
            int number = 0;
            for (int x = 0; x < xCount; x++) {
                for (int y = 0; y < yCount; y++) {
                    // 比較每個畫素點顏色
                    if (b1.getPixel(x, y) != b2.getPixel(x, y)) {
                        number++;
                    }
                }
            }
            if(number < 500) return true;
            return false;
        }

二值化,灰度化:

//灰度化
    public Bitmap procSrc2Gray(Bitmap bm){
       Mat rgbMat = new Mat();  
       Mat grayMat = new Mat();  
       Bitmap graybm = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Config.ARGB_8888);  
       Utils.bitmapToMat(bm, rgbMat);//convert original bitmap to Mat, R G B.  
       Imgproc.cvtColor(rgbMat, grayMat, Imgproc.COLOR_RGB2GRAY);//rgbMat to gray grayMat
       Utils.matToBitmap(grayMat, graybm);
       return graybm;
    }
    //二值化
    public Bitmap changeBitmap(Bitmap bm) {
        Mat rgbMat = new Mat();  
        Mat grayMat = new Mat();  
        Bitmap graybm = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Config.ARGB_8888);  
        Utils.bitmapToMat(bm,rgbMat);
        Imgproc.threshold(rgbMat,grayMat,100,255,Imgproc.THRESH_BINARY);
        Utils.matToBitmap(grayMat,graybm);
        return graybm;
    }

這裡關於opencv的運用前面的部落格有詳細說明。