Я пытаюсь загрузить / сохранить данные калибровки OpenCV в формате YAML, используя официальные привязки OpenCV Java. Я знаю, что OpenCV (по крайней мере, версия c ++) может сериализоваться до XML и JSON, но я хотел бы поддерживать более старые файлы калибровки YAML.
Файл калибровки выглядит следующим образом:
%YAML:1.0
cameraMatrix: !!opencv-matrix
rows: 3
cols: 3
dt: d
data: [ 6.6278599887122368e+02, 0., 3.1244256016006659e+02, 0.,
6.6129276875199082e+02, 2.2747179767124251e+02, 0., 0., 1. ]
imageSize_width: 640
imageSize_height: 480
sensorSize_width: 0
sensorSize_height: 0
distCoeffs: !!opencv-matrix
rows: 5
cols: 1
dt: d
data: [ -1.8848338341464690e-01, 1.0721890419183855e+00,
-3.5244467228016116e-03, -7.0195032848241403e-04,
-2.0412827999027101e+00 ]
reprojectionError: 2.1723265945911407e-01
Я уже посмотрел несколько ответов здесь и здесь , однако я ищу элегантное решение, так как я не совсем понял лучшую карту java занятия в YAML и обратно. Я пробовал несколько библиотек, таких как jyaml, yamlbeans (обе версии 1.0 от SourceForge и 1.13 через Maven Central) и SnakeYAML.
Моя текущая попытка десериализации такого рода работ, но мне кажется, что она довольно хакерская:
CalibrationParseTest. java
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import org.opencv.core.Core;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
public class CalibrationParseTest {
public static void main(String[] args) {
// load OpenCV native
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
String yamlPath = "./data/calibration.yml";
try{
String yamlString = new String(Files.readAllBytes(Paths.get(yamlPath)), StandardCharsets.UTF_8);
// remove %YAML:1.0 to avoid scan directive error
yamlString = yamlString.replaceAll("%YAML:1.0", "");
// map custom class
yamlString = yamlString.replaceAll("opencv-matrix", "MatYAML");
System.out.println("<loaded>");
System.out.println(yamlString);
System.out.println("</loaded>");
Yaml yaml = new Yaml(new Constructor(CalibrationData.class));
CalibrationData data = yaml.load(yamlString);
// currently manually parsing data from the HashMap: can this be better ?
data.populateCV();
// double check data
System.out.println("<deserialized>");
System.out.println(data);
System.out.println("</deserialized>");
}catch (IOException e) {
e.printStackTrace();
}
}
}
CalibrationData. java
import java.util.HashMap;
import org.opencv.core.Mat;
import org.opencv.core.Size;
public class CalibrationData extends HashMap{
public Mat cameraMatrix;
public Size imageSize;
public Size sensorSize;
public Mat distCoeffs;
public float reprojectionError;
public CalibrationData(){}
public void populateCV(){
cameraMatrix = ((MatYAML)get("cameraMatrix")).toMat();
imageSize = new Size((int)get("imageSize_width"),(int)get("imageSize_height"));
sensorSize = new Size((int)get("sensorSize_width"),(int)get("sensorSize_height"));
distCoeffs = ((MatYAML)get("distCoeffs")).toMat();
reprojectionError = (float)((double)get("reprojectionError"));
}
public String toString(){
if(cameraMatrix == null){
return String.format("[CalibrationData (not parsed to CV-> call populateCV()\n\tdata: %s\n]",super.toString());
}
return String.format("[CalibrationData\n" +
"\tcalibrationMatrix: %s\n" +
"\timageSize: %s\n" +
"\tsensorSize: %s\n" +
"\tdistCoeffs: %s\n" +
"\treprojectionError: %f\n]", cameraMatrix.dump(), imageSize.toString(), sensorSize.toString(), distCoeffs.dump(), reprojectionError);
}
}
MatYAML. java
import java.util.List;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
public class MatYAML{
public int rows;
public int cols;
public String dt;
public List<Double> data;
Mat toMat(){
Mat out = new Mat(rows, cols, dt.equals("d") ? CvType.CV_64F : CvType.CV_32F);
int index = 0;
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
out.put(row, col, data.get(index++));
}
}
return out;
}
}
Выводит ожидаемый результат:
<loaded>
cameraMatrix: !!MatYAML
rows: 3
cols: 3
dt: d
data: [ 6.6278599887122368e+02, 0., 3.1244256016006659e+02, 0.,
6.6129276875199082e+02, 2.2747179767124251e+02, 0., 0., 1. ]
imageSize_width: 640
imageSize_height: 480
sensorSize_width: 0
sensorSize_height: 0
distCoeffs: !!MatYAML
rows: 5
cols: 1
dt: d
data: [ -1.8848338341464690e-01, 1.0721890419183855e+00,
-3.5244467228016116e-03, -7.0195032848241403e-04,
-2.0412827999027101e+00 ]
reprojectionError: 2.1723265945911407e-01
</loaded>
<deserialized>
[CalibrationData
calibrationMatrix: [662.7859988712237, 0, 312.4425601600666;
0, 661.2927687519908, 227.4717976712425;
0, 0, 1]
imageSize: 640x480
sensorSize: 0x0
distCoeffs: [-0.1884833834146469; 1.072189041918385; -0.003524446722801612; -0.000701950328482414; -2.04128279990271]
reprojectionError: 0.217233
]
</deserialized>
Есть ли более элегантный способ сериализации / десериализации между Java классами OpenCV и YAML без этих хаков?
Под хаки я имею в виду:
- вручную удаление директивы yaml версии
- замена opencv-матрицы строкой MatYAML
- приведение значений HashMap вручную
- , потенциально не позволяющих вручную заполнять данные OpenCV
Mat
? (если возможно?)
Обновление 2 Ответ Аманина чище и позволяет избежать случайного замещения "!! opencv-matrix", однако он не сериализует / десериализует Mat
:
OpenCVConfig{imageSize_width=640, imageSize_height=480, sensorSize_width=0, sensorSize_height=0, camerMatrix=Matrix{rows=3, cols=3, dt=d, data=[662.7859988712237, 0.0, 312.4425601600666, 0.0, 661.2927687519908, 227.4717976712425, 0.0, 0.0, 1.0]}, distCoeffs=Matrix{rows=5, cols=1, dt=d, data=[-0.1884833834146469, 1.0721890419183855, -0.0035244467228016116, -7.01950328482414E-4, -2.04128279990271]}}
---
imageSize_width: 640
imageSize_height: 480
sensorSize_width: 0
sensorSize_height: 0
reprojectionError: 0.21723265945911407
cameraMatrix:
rows: 3
cols: 3
dt: "d"
data:
- 662.7859988712237
- 0.0
- 312.4425601600666
- 0.0
- 661.2927687519908
- 227.4717976712425
- 0.0
- 0.0
- 1.0
distCoeffs:
rows: 5
cols: 1
dt: "d"
data:
- -0.1884833834146469
- 1.0721890419183855
- -0.0035244467228016116
- -7.01950328482414E-4
- -2.04128279990271
Пожалуйста, сообщите об интеграции решения с org.opencv.core.Mat