影象處理(三) 檢測障礙物
阿新 • • 發佈:2019-01-08
前幾天寫了一個小的專案關於:當手機處於靜止狀態時,識別是否動或者前方有不同物體
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的運用前面的部落格有詳細說明。