Android學習十二---在android上實現圖像匹配


一、效果圖及功能描述

效果圖

image

點擊ShowImg后

image

點擊match,然后點擊showmatch,可以不斷點擊showmatch。

image

主要功能描述:顯示在SD卡上已經存在的圖片test.jpg,根據圖片在cameraframe對於每一幀計算和test.ipg的匹配並顯示。

二、界面設計

    一個JavaCameraView用來顯示幀相當於是相機的預覽,兩個ImgView一個用來顯示要匹配的圖像,一個用來顯示最后得到的匹配圖。三個Button對應三個View,ShowImg用來顯示SD卡上的test.jpg,match開始匹配,showmatch,顯示匹配的結果。(忽略名字的不統一。。。。)

    采用線性布局的嵌套來實現布局,首先最外面是一個水平方向的線性布局,然后每行又是一個垂直方向的線性布局,一個垂直方向的線性布局里放的是JavaCameraView和match button,另一個則是兩個ImgView和另兩個button。

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

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:orientation="vertical" >

        <org.opencv.android.JavaCameraView
            android:id="@+id/objectMatch"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_weight="1" />

        <Button
            android:id="@+id/button_match"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/Match" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:orientation="vertical" >

        <ImageView
            android:id="@+id/ImgPhoto"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:src="@drawable/ic_launcher" />

        <Button
            android:id="@+id/button_show"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/showImg" />

        <ImageView
            android:id="@+id/Imgmatch"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:src="@drawable/ic_launcher" />

        <Button
            android:id="@+id/button_showmatch"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/showmatch" />
    </LinearLayout>

</LinearLayout>

 

三、功能實現

按照一般圖像匹配的流程,即提訓練和測試圖像關鍵點,計算關鍵點的特征表示,計算訓練測試圖片的匹配點數,並畫圖。這個流程需要用的OpenCV4android API有

FeatureDetector .create,detect,match見前面章節的介紹

http://blog.csdn.net/h2008066215019910120/article/details/42650231

還有就是bitmap到mat,mat到bitmap之間的轉換Utils.bitmapToMat,Utils.matToBitmap。

package com.example.objectmatch;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import org.opencv.android.Utils;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.features2d.DescriptorExtractor;
import org.opencv.features2d.DescriptorMatcher;
import org.opencv.features2d.FeatureDetector;
import org.opencv.features2d.Features2d;
import org.opencv.features2d.KeyPoint;
import org.opencv.imgproc.Imgproc;

import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;

public class MainActivity extends Activity implements CvCameraViewListener2 {
    private Bitmap testimg;
    private Bitmap matchbitmap;
    private CameraBridgeViewBase mOpenCvCameraView;
    private Mat mRgba;
    private Mat mGray;
    private Mat mByte;
    private Scalar CONTOUR_COLOR;
    private boolean isProcess = false;
    private String filepath = "/sdcard/test.jpg";
    private static final String TAG = "Dawn";
    private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
            case LoaderCallbackInterface.SUCCESS: {
                Log.i(TAG, "OpenCV loaded successfully");
                mOpenCvCameraView.enableView();
            }
                break;
            default: {
                super.onManagerConnected(status);
            }
                break;
            }
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        setContentView(R.layout.activity_main);
        mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.objectMatch);
        mOpenCvCameraView.setCvCameraViewListener(this);
        final ImageView showimg = (ImageView) findViewById(R.id.ImgPhoto);
        final ImageView matchimg = (ImageView) findViewById(R.id.Imgmatch);
        Button showButton = (Button) findViewById(R.id.button_show);
        Button matchButton = (Button) findViewById(R.id.button_match);
        Button showmatchButton = (Button) findViewById(R.id.button_showmatch);
        showButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // show img in the Imageview
                File file = new File(filepath);
                if (file.exists()) {
                    testimg = BitmapFactory.decodeFile(filepath);
                    // 將圖片顯示到ImageView中
                    showimg.setImageBitmap(testimg);
                }
            }
        });
        matchButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // show img in the Imageview
                isProcess = !isProcess;
            }
        });
        showmatchButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // show img in the Imageview
                matchimg.setImageBitmap(matchbitmap);
            }
        });
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mOpenCvCameraView != null)
            mOpenCvCameraView.disableView();

    }

    public void onResume() {
        super.onResume();
        OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_10, this,
                mLoaderCallback);
    }

    @Override
    protected void onDestroy() {
        Log.e("onDestroy", "INITIATED");
        super.onDestroy();

        if (mOpenCvCameraView != null)
            mOpenCvCameraView.disableView();

    }

    public void onCameraViewStarted(int width, int height) {
        mRgba = new Mat(height, width, CvType.CV_8UC3);
        mByte = new Mat(height, width, CvType.CV_8UC1);
       

    }

    public void onCameraViewStopped() { // Explicitly deallocate Mats
        mRgba.release();
    }

    public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
        
        Bitmap s_testimg;
        Mat testimage = new Mat();
        Mat grayimage=new Mat();
        mRgba = inputFrame.rgba();
        mGray = inputFrame.gray();
        CONTOUR_COLOR = new Scalar(255);
        MatOfDMatch matches = new MatOfDMatch();
        MatOfKeyPoint keypoint_train = new MatOfKeyPoint();
        MatOfKeyPoint keypoint_test = new MatOfKeyPoint();
         KeyPoint kpoint = new KeyPoint();
        Mat mask = Mat.zeros(mGray.size(), CvType.CV_8UC1);
        Mat output = new Mat(); // Mat train=new Mat(); Mat
        Mat test = new Mat();
        Mat train = new Mat();
        if (isProcess) {
            FeatureDetector detector_train = FeatureDetector
                    .create(FeatureDetector.ORB);
            detector_train.detect(mGray, keypoint_train);
//            Features2d.drawKeypoints(mGray, keypoint_train, output, new Scalar(
//                    2, 254, 255), Features2d.DRAW_RICH_KEYPOINTS);

            DescriptorExtractor descriptor_train = DescriptorExtractor
                    .create(DescriptorExtractor.ORB);
            descriptor_train.compute(mGray, keypoint_train, train);
            s_testimg = Bitmap.createScaledBitmap(testimg, mGray.width(), mGray.height(), false);
            
            Utils.bitmapToMat(s_testimg, testimage);
            Imgproc.cvtColor(testimage, grayimage, Imgproc.COLOR_RGB2GRAY);
            
            FeatureDetector detector_test = FeatureDetector
                    .create(FeatureDetector.ORB);
            detector_test.detect(grayimage, keypoint_test);

//            Features2d.drawKeypoints(testimage, keypoint_test, output,
//                    new Scalar(2, 254, 255), Features2d.DRAW_RICH_KEYPOINTS);
            DescriptorExtractor descriptor_test = DescriptorExtractor
                    .create(DescriptorExtractor.ORB);
            descriptor_test.compute(grayimage, keypoint_test, test);
            DescriptorMatcher descriptormatcher = DescriptorMatcher
                    .create(DescriptorMatcher.BRUTEFORCE_HAMMING);
            descriptormatcher.match(test, train, matches);
            Features2d.drawMatches(grayimage,keypoint_test,mGray, keypoint_train, matches, output);
            matchbitmap=Bitmap.createScaledBitmap(testimg, output.width(),  output.height(), false);
            Utils.matToBitmap(output, matchbitmap);
            
            return mRgba;
        }

        return mRgba;
    }

}

三、實現過程中問題描述

問題1

android4OpenCV中無法使用SIFT API ,調試運行到這一位置出錯。

原因:可能是SIFT這個屬於nonfree模塊,在手機上無法運行,也可能是SIFT計算時間長,存儲空間太大。每個關鍵點128維,每維需要4個字節(float型)來存儲,而一張圖片可能有上百或者上千個關鍵點。

解決:改用其他特征來進行識別

可選項

static int
BRIEF

static int
BRISK

static int
FREAK

static int
OPPONENT_BRIEF

static int
OPPONENT_BRISK

static int
OPPONENT_FREAK

static int
OPPONENT_ORB

static int
OPPONENT_SIFT

static int
OPPONENT_SURF

static int
ORB

 

BRIEF:主要思路就是在特征點附近隨機選取若干點對,將這些點對的灰度值的大小,組合成一個二進制串,並將這個二進制串作為該特征點的特征描述子

分析見:http://www.cnblogs.com/ronny/p/4081362.html

ORB:RIEF的優點在於速度,缺點也相當明顯:1:不具備旋轉不變性。2:對噪聲敏感3:不具備尺度不變性。ORB就解決上述1和2兩個問題。

分析見:http://www.cnblogs.com/ronny/p/4083537.html

這些特征區別於SIFT在於:

SIFT特征采用了128維的特征描述子,由於描述子用的浮點數,所以它將會占用512 bytes的空間。類似地,對於SURF特征,常見的是64維的描述子,它也將占用256bytes的空間。如果一幅圖像中有1000個特征點(不要驚訝,這是很正常的事),那么SIFT或SURF特征描述子將占用大量的內存空間,對於那些資源緊張的應用,尤其是嵌入式的應用,這樣的特征描述子顯然是不可行的。而且,越占有越大的空間,意味着越長的匹配時間。但是實際上SFIT或SURF的特征描述子中,並不是所有維都在匹配中有着實質性的作用。我們可以用PCA、LDA等特征降維的方法來壓縮特征描述子的維度。還有一些算法,例如LSH,將SIFT的特征描述子轉換為一個二值的碼串,然后這個碼串用漢明距離進行特征點之間的匹配。這種方法將大大提高特征之間的匹配,因為漢明距離的計算可以用異或操作然后計算二進制位數來實現,在現代計算機結構中很方便

問題二:

 

 

 

01-24 10:23:58.251: E/AndroidRuntime(3535): CvException [org.opencv.core.CvException: cv::Exception: /hdd2/buildbot/slaves/slave_ardbeg1/50-SDK/opencv/modules/features2d/src/draw.cpp:208: error: (-215) i2 >= 0 && i2 < static_cast<int>(keypoints2.size()) in function void cv::drawMatches(const cv::Mat&, const std::vector<cv::KeyPoint>&, const cv::Mat&, const std::vector<cv::KeyPoint>&, const std::vector<cv::DMatch>&, cv::Mat&, const Scalar&, const Scalar&, const std::vector<char>&, int)

原因:descriptormatcher.match(test, train, matches);

Features2d.drawMatches(grayimage,keypoint_test,mGray, keypoint_train, matches, output);的參數寫反了

第一個參數應該是test的圖片,第二個參數是train的圖片。要跟前面的match函數匹配起來。

兩個參數寫反了

 

 

五、總結和展望

OpenCV4android的資料確實太少,API和文檔都不是很完善,需要先找到C++的實現然后轉換到java上來。

這個程序還存在不少問題,很多功能都沒有完善待添加功能

1.能夠從圖庫中選取或者拍照得到測試的圖片。

2.但測試圖片從幀圖像中找到匹配后,停止並在界面上顯示匹配的圖像。

代碼地址:https://github.com/dawnminghuang/objectMatch


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM