将 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();
}
如果有人有更好的解决办法,欢迎补充
我正在编写使用 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();
}
如果有人有更好的解决办法,欢迎补充