将 Mat (openCV) 保存到 SharedPreferences Android

Save Mat (openCV) to SharedPreferences Android

我正在编写使用 KNearest 的应用程序。我写了代码来训练模型,但是每次重启应用程序,我都必须重新训练数据,所以我想把训练数据保存一次到SharedPreferences,然后再使用。

我知道我必须将 Mat 转换为 byte[],然后再转换为 String,但是解码不起作用,我收到错误消息:

(layout == ROW_SAMPLE && responses.rows == nsamples) 
|| (layout == COL_SAMPLE && responses.cols == nsamples) 
in function void cv::ml::TrainDataImpl::setData(cv::InputArray, 
int, cv::InputArray, cv::InputArray, 
cv::InputArray, cv::InputArray, cv::InputArray, cv::InputArray)

代码:

protected Void doInBackground(Void... args) {
        // Constants.TRAIN_SAMPLES = 10

        Mat trainData = new Mat(0, 200 * 200, CvType.CV_32FC1); // 0 x 40 000
        Mat trainClasses = new Mat(Constants.TRAIN_SAMPLES, 1, CvType.CV_32FC1); // 10 x 1
        float[] myint = new float[Constants.TRAIN_SAMPLES + 1];
        for (i = 1; i <= Constants.TRAIN_SAMPLES; i++)
            myint[i] = (float) i;

        trainClasses.put(0, 0, myint);
        KNearest knn = KNearest.create();

        String val = " ";
        val = sharedPref.getString("key", " ");

        // empty SharedPreferences
        if (val.equals(" ")) {

            // get all images from external storage
            for (i = 1; i <= Constants.TRAIN_SAMPLES; i++) {

                String photoPath = Environment.getExternalStorageDirectory().toString() + "/ramki/ramka_" + i + ".png";
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inPreferredConfig = Bitmap.Config.ARGB_8888;
                Bitmap bitmap = BitmapFactory.decodeFile(photoPath, options);

                Utils.bitmapToMat(bitmap, img);

                if (img.channels() == 3) {
                    Imgproc.cvtColor(img, img, Imgproc.COLOR_RGB2GRAY);
                } else if (img.channels() == 4) {
                    Imgproc.cvtColor(img, img, Imgproc.COLOR_RGBA2GRAY);
                }

                Imgproc.resize(img, img, new Size(200, 200));
                img.convertTo(img, CvType.CV_32FC1);
                img = img.reshape(1, 1); //  1 x 40 000 ( 200x200 )

                trainData.push_back(img);
                publishProgress(i);
            }


            trainData.convertTo(trainData, CvType.CV_8U);
            // save this trainData (Mat) to SharedPreferences
            saveMatToPref(trainData);

        } else {
            // get trainData from SharedPreferences
            val = sharedPref.getString("key", " ");

            byte[] data = Base64.decode(val, Base64.DEFAULT);

            trainData.convertTo(trainData, CvType.CV_8U);
            trainData.put(0, 0, data);
        }

        trainData.convertTo(trainData, CvType.CV_32FC1);
        knn.train(trainData, Ml.ROW_SAMPLE, trainClasses);

        trainClasses.release();
        trainData.release();
        img.release();

        onPostExecute();
        return null;
    }


 public void saveMatToPref(Mat mat) {

    if (mat.isContinuous()) {
        int cols = mat.cols();
        int rows = mat.rows();
        byte[] data = new byte[cols * rows];

        // there, data contains {0,0,0,0,0,0 ..... } 400 000 items
        mat.get(0, 0, data);

        String dataString = new String(Base64.encode(data, Base64.DEFAULT));

        SharedPreferences.Editor mEdit1 = sharedPref.edit();
        mEdit1.putString("key", dataString);

        mEdit1.commit();

    } else {
        Log.i(TAG, "Mat not continuous.");
    }
 }

当我解码时,我的 trainData 看起来像这样:

Mat [ 0*40000*CV_32FC1 ..]

但应该:Mat [ 10*40000*CV_32FC1 ..]

谁能帮我编解码Mat?感谢您的帮助。

正如@Miki 提到的,问题出在类型上。现在它可以工作了,但在我的例子中只有大约 200 x 40 000 的 Mat 大小,如果它更大,我有 outOfMemory excepion...

            String val = " ";
        val = sharedPref.getString("key", " ");

        // empty SharedPreferences
        if ( ! val.equals(" ")) {

            // get all images from external storage
            for (i = 1; i <= Constants.TRAIN_SAMPLES; i++) {

                String photoPath = Environment.getExternalStorageDirectory().toString() + "/ramki/ramka_" + i + ".png";
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inPreferredConfig = Bitmap.Config.ARGB_8888;
                Bitmap bitmap = BitmapFactory.decodeFile(photoPath, options);

                Utils.bitmapToMat(bitmap, img);

                if (img.channels() == 3) {
                    Imgproc.cvtColor(img, img, Imgproc.COLOR_RGB2GRAY);
                } else if (img.channels() == 4) {
                    Imgproc.cvtColor(img, img, Imgproc.COLOR_RGBA2GRAY);
                }

                Imgproc.resize(img, img, new Size(200, 200));
                img.convertTo(img, CvType.CV_32FC1);
                img = img.reshape(1, 1); //  1 x 40 000 ( 200x200 )

                trainData.push_back(img);
                publishProgress(i);
            }

            // save this trainData (Mat) to SharedPreferences
            saveMatToPref(trainData);

        } else {
            // get trainData from SharedPreferences
            val = sharedPref.getString("key", " ");

            byte[] data = Base64.decode(val, Base64.DEFAULT);

            trainData = new Mat(Constants.TRAIN_SAMPLES, 200 * 200, CvType.CV_32FC1); 

            float[] f = toFloatArray(data);

            trainData.put(0, 0, f);
        }

        knn.train(trainData, Ml.ROW_SAMPLE, trainClasses);



  public void saveMatToPref(Mat mat) {

    if (mat.isContinuous()) {

        int size = (int)( mat.total() * mat.channels() );

        float[] data = new float[ size ];

        byte[] b = new byte[ size ];

        mat.get(0, 0, data);

        b = FloatArray2ByteArray(data);

        String dataString = new String(Base64.encode(b, Base64.DEFAULT));

        SharedPreferences.Editor mEdit1 = sharedPref.edit();
        mEdit1.putString("key", dataString);

        mEdit1.commit();

    } else {
        Log.i(TAG, "Mat not continuous.");
    }
}

private static float[] toFloatArray(byte[] bytes) {
    ByteBuffer buffer = ByteBuffer.wrap(bytes);
    FloatBuffer fb = buffer.asFloatBuffer();
    float[] floatArray = new float[fb.limit()];
    fb.get(floatArray);
    return floatArray;
}

public static byte[] FloatArray2ByteArray(float[] values){
    ByteBuffer buffer = ByteBuffer.allocate(4 * values.length);

    for (float value : values)
        buffer.putFloat(value);

    return buffer.array();
}

如果有人有更好的解决办法,欢迎补充