4

我的项目是使用 OpenCV 库识别 Android 上的叶子。我正在使用 ORB 检测来获取图像的关键点并使用 ORB 描述符来获取关键点的特征。这是我使用的代码:

bmp=BitmapFactory.decodeResource(getResources(),R.drawable.t1);
Utils.bitmapToMat(bmp, mat);
FeatureDetector detector = FeatureDetector.create(FeatureDetector.ORB);
detector.detect(mat, keypoints);
DescriptorExtractor extractor = DescriptorExtractor.create(DescriptorExtractor.ORB);
extractor.compute(mat, keypoints, features);

来源:http ://answers.opencv.org/question/6260/orb-features/

但是每次我输入相同的图像,该图像的关键点总是不同的。如果总是不同,我可以将关键点的功能保存到数据库吗?或者我应该保存图像以保存特征数据?如果可以保存到数据库,我该怎么做?

4

4 回答 4

6

在我看来,最通用的存储关键点的方法是首先将它们转换为像 JSON 这样的数据交换格式。

在您能够进行该转换后,您可以灵活地存储它。JSON 很容易转换为字符串和/或通过网络连接发送。

使用 OpenCV C++ ,您可以将数据存储为 YAML,但这不适用于 Android。

要在 Java 中解析 JSON,您可以使用这个易于使用的库 Google GSON

这是我第一次尝试这样做:

 public static String keypointsToJson(MatOfKeyPoint mat){
    if(mat!=null && !mat.empty()){          
        Gson gson = new Gson();

        JsonArray jsonArr = new JsonArray();            

        KeyPoint[] array = mat.toArray();
        for(int i=0; i<array.length; i++){
            KeyPoint kp = array[i];

            JsonObject obj = new JsonObject();

            obj.addProperty("class_id", kp.class_id); 
            obj.addProperty("x",        kp.pt.x);
            obj.addProperty("y",        kp.pt.y);
            obj.addProperty("size",     kp.size);
            obj.addProperty("angle",    kp.angle);                          
            obj.addProperty("octave",   kp.octave);
            obj.addProperty("response", kp.response);

            jsonArr.add(obj);               
        }

        String json = gson.toJson(jsonArr);         

        return json;
    }
    return "{}";
}

public static MatOfKeyPoint keypointsFromJson(String json){
    MatOfKeyPoint result = new MatOfKeyPoint();

    JsonParser parser = new JsonParser();
    JsonArray jsonArr = parser.parse(json).getAsJsonArray();        

    int size = jsonArr.size();

    KeyPoint[] kpArray = new KeyPoint[size];

    for(int i=0; i<size; i++){
        KeyPoint kp = new KeyPoint(); 

        JsonObject obj = (JsonObject) jsonArr.get(i);

        Point point = new Point( 
                obj.get("x").getAsDouble(), 
                obj.get("y").getAsDouble() 
        );          

        kp.pt       = point;
        kp.class_id = obj.get("class_id").getAsInt();
        kp.size     =     obj.get("size").getAsFloat();
        kp.angle    =    obj.get("angle").getAsFloat();
        kp.octave   =   obj.get("octave").getAsInt();
        kp.response = obj.get("response").getAsFloat();

        kpArray[i] = kp;
    }

    result.fromArray(kpArray);

    return result;
}
于 2014-02-19T00:01:57.103 回答
1

虽然我没有运行计时,但我怀疑通过 XML 或 JSON 等格式将需要比使用原语更多的空间和计算资源。

下面是一些可用于在 Android 环境中将 MatOfKeyPoint 保存和检索到 SQLite 数据库的代码。这是使用 OpenCV 2.4.11,所以 SIFT 可用。

当您运行此应用程序时,您看到的是带有添加关键点的测试图像(您需要提供并放入可绘制文件夹中)。

siftTest()方法首先计算keyPoints哪个是MatOfKeyPoint类型。该代码将该对象的基础数据保存在数据库中,然后读取该数据并创建一个新对象keyPointsFromDb,其内容应用于原始图像并显示结果。

public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("native-lib");
        System.loadLibrary("opencv_java");
        System.loadLibrary("nonfree");

    }
    private ImageView imageView;
    private Bitmap inputImage; // make bitmap from image resource
    private FeatureDetector detector = FeatureDetector.create(FeatureDetector.SIFT);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        inputImage = BitmapFactory.decodeResource(getResources(), R.drawable.test);
        imageView = (ImageView) this.findViewById(R.id.imageView);
        siftTest();
    }

    public void siftTest() {
        Mat rgba = new Mat();
        Utils.bitmapToMat(inputImage, rgba);
        MatOfKeyPoint keyPoints = new MatOfKeyPoint();
        Imgproc.cvtColor(rgba, rgba, Imgproc.COLOR_RGBA2GRAY);
        detector.detect(rgba, keyPoints);

        // Save to database
        MatchingDatabaseAdapter.addKeypoints(keyPoints, getApplicationContext());

        // Opens database cursor
        MatchingDatabaseAdapter.getAllRecordsCursor(getApplicationContext()); 

        // Gets the first item in the database (as an example... you could loop through many/all)
        MatOfKeyPoint keyPointsFromDb = MatchingDatabaseAdapter.getKeypointsFromNextCursorPosition();

        // Closes database
        MatchingDatabaseAdapter.closeDb(); 

        Features2d.drawKeypoints(rgba, keyPointsFromDb, rgba);
        Utils.matToBitmap(rgba, inputImage);
        imageView.setImageBitmap(inputImage);

    }
}

这是数据库代码,其中包含与转换为数据库所需的字节数组相关的一些细节。我没有包括与使用数据库相关的所有内容,因为这确实是一个不同的主题。

public class MatchingDatabaseAdapter {
    ...
    ...
    ...

    public static void addKeypoints(MatOfKeyPoint keyPoints, Context context) {
        float[] data = new float[(int)keyPoints.total() * keyPoints.channels()]; // make a spot to save the data
        keyPoints.get(0,0,data); // load the data;
        ByteBuffer buffer = ByteBuffer.allocate(data.length * 4);
        for (int i = 0; i < data.length; i++){
            buffer.putFloat(data[i]);
        }
        byte[] byteArray = buffer.array();
        addBlob(byteArray, keyPoints.rows(), keyPoints.cols(), keyPoints.type(), context);
    }

    public static void addBlob(byte[] blob, int rows, int columns, int mattype, Context context) throws SQLException {
        if (mDb == null) mDb = openDb(context);
        ContentValues contentValues = new ContentValues();
        contentValues.put(DatabaseHelper.BLOB_FIELD_NAME, blob);
        contentValues.put(DatabaseHelper.ROWS_FIELD_NAME, rows);
        contentValues.put(DatabaseHelper.COLUMNS_FIELD_NAME, columns);
        contentValues.put(DatabaseHelper.MATTYPE_FIELD_NAME, mattype);
        long x = mDb.insert(DatabaseHelper.TABLE_NAME, null, contentValues);
        Log.v(TAG, "insert said " + x + " and blob was " + blob.length + " long.");
        closeDb();
    }

    public static Cursor getAllRecordsCursor(Context context) {
        if (mDb == null || !mDb.isOpen()) mDb = openDb(context);
        mCursor = mDb.query(DatabaseHelper.TABLE_NAME, null, null, null, null,null, null);
        boolean hasRecords = mCursor.moveToFirst();
        Log.v(TAG, "MatchingDatabaseAdapter.getAllRecordsCursor() cursor created. " + mCursor + " and " + (hasRecords?"has records":"has NO RECORDS"));
        return mCursor;
    }

     public static MatOfKeyPoint getKeypointsFromNextCursorPosition() {
        MatOfKeyPoint keyPoints = null;
        if (mCursor != null) {
            Log.v(TAG, "mCursor has " + mCursor.getCount());
            mCursor.moveToFirst();
            int rows = mCursor.getInt(DatabaseHelper.ROWS_FIELD_POSITION);
            int columns = mCursor.getInt(DatabaseHelper.COLUMNS_FIELD_POSITION);
            int mattype = mCursor.getInt(DatabaseHelper.MATTYPE_FIELD_POSITION);
            keyPoints = new MatOfKeyPoint();
            keyPoints.create(rows, columns, mattype);
            byte[] blob = mCursor.getBlob(DatabaseHelper.BLOB_FIELD_POSITION);
            ByteBuffer buffer = ByteBuffer.wrap(blob);
            FloatBuffer floatBuffer = buffer.asFloatBuffer();
            float[] floatArray = new float[floatBuffer.limit()];
            floatBuffer.get(floatArray);
            keyPoints.put(0, 0, floatArray);
        }
        return keyPoints;
    }

    // INNER CLASS DatabaseHelper
    static class DatabaseHelper extends SQLiteOpenHelper {
        private static DatabaseHelper mDatabaseHelper;
        private static final String DB_NAME = "blobDb";
        private static final String TABLE_NAME = "blobTable";
        private static final String ROWS_FIELD_NAME = "rowsField";
        public static final int ROWS_FIELD_POSITION = 1;
        private static final String COLUMNS_FIELD_NAME = "columnsField";
        public static final int COLUMNS_FIELD_POSITION = 2;
        private static final String MATTYPE_FIELD_NAME = "mattypeField";
        public static final int MATTYPE_FIELD_POSITION = 3;
        private static final String BLOB_FIELD_NAME = "blobField";
        private static final int BLOB_FIELD_POSITION = 4;

        private static final java.lang.String CREATE_TABLE_SQL =
                "CREATE TABLE " + TABLE_NAME + " ("
                        + "_id INTEGER PRIMARY KEY AUTOINCREMENT, "
                        + ROWS_FIELD_NAME + " INTEGER, "
                        + COLUMNS_FIELD_NAME + " INTEGER, "
                        + MATTYPE_FIELD_NAME + " INTEGER, "
                        + BLOB_FIELD_NAME + " BLOB"
                        + ");";

        private DatabaseHelper(Context context) {
            super(context, DB_NAME, null, DB_VERSION);
            mDatabaseHelper = this;
        }

        static synchronized DatabaseHelper getInstance(Context context) {
            if (mDatabaseHelper == null) {
                mDatabaseHelper = new DatabaseHelper(context.getApplicationContext());
            }
            return mDatabaseHelper;
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            Log.v(TAG, "Creating table: " + CREATE_TABLE_SQL);
            db.execSQL(CREATE_TABLE_SQL);
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            Log.v(TAG, "onUpgrade() from " + oldVersion + " to " + newVersion);
            Log.v(TAG, "ALL DATA BEING REMOVED FROM THE DATABASE!!");
            db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME + ";");
            onCreate(db);
        }
    }
}

完整的示例项目运行并显示 surf 关键点,因此如果您想将关键点保存到数据库,此代码应该可以让您到达您想去的地方。

于 2017-05-20T18:27:26.067 回答
0

最近我完成了一个关于识别人类手势的项目。但我使用了来自 openCv 的 svm。为您通知每个图像中的关键点编号是其突出点(根据 openCv 定义)。为了识别或换句话说“预测”,您必须保存作为垫子的关键点(关键点属性)的特征。我使用了 SURF,它为每个关键点计算了 64 个特征。我希望它有所帮助。

于 2015-07-01T06:23:03.493 回答
0

如果要将 MatOfKeypoints 保存在磁盘而不是数据库上。以下是我的解决方案,如果您要保存 1000 个关键点,您可以压缩和解压缩关键点文件以节省磁盘空间。

public final static void save(String path, MatOfKeyPoint mat) {
    File file = new File(path).getAbsoluteFile();
    file.getParentFile().mkdirs();
    try {
        int cols = mat.cols();
        int rows = mat.rows();
        int type = mat.type();
        float[] data = new float[(int) mat.total() * mat.channels()];
        mat.get(0, 0, data);
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path))) {
            oos.writeInt(cols);
            oos.writeInt(rows);
            oos.writeInt(type);
            oos.writeObject(data);
            oos.close();
        }
    } catch (IOException | ClassCastException ex) {
        System.err.println("ERROR: Could not save mat to file: " + path);
    }
}

public static final MatOfKeyPoint load(String path) {
    try {
        int cols;
        int rows;
        int type;
        float[] data;
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path))) {
            cols = (int) ois.readInt();
            rows = (int) ois.readInt();
            type = (int) ois.readInt();
            data = (float[]) ois.readObject();
        }
        
        MatOfKeyPoint keyPoints = new MatOfKeyPoint();
        keyPoints.create(rows, cols, type);
        keyPoints.put(0, 0, data);
        return keyPoints;
    } catch (IOException | ClassNotFoundException | ClassCastException ex) {
        ex.printStackTrace();
        System.err.println("ERROR: Could not load mat from file: " + path);
    }
    return null;
}
于 2020-07-22T07:01:22.860 回答