diff --git a/chameleon-server/chameleon-vision.iml b/chameleon-server/chameleon-vision.iml index 11b1a05d0..e14bee233 100644 --- a/chameleon-server/chameleon-vision.iml +++ b/chameleon-server/chameleon-vision.iml @@ -12,6 +12,20 @@ + + + + + + + + + + + + + + diff --git a/chameleon-server/src/main/java/com/chameleonvision/config/CameraConfig.java b/chameleon-server/src/main/java/com/chameleonvision/config/CameraConfig.java index 66d9df0da..b4176e3fa 100644 --- a/chameleon-server/src/main/java/com/chameleonvision/config/CameraConfig.java +++ b/chameleon-server/src/main/java/com/chameleonvision/config/CameraConfig.java @@ -51,7 +51,7 @@ public class CameraConfig { private CameraJsonConfig loadConfig() { CameraJsonConfig config = preliminaryConfig; try { - config = JacksonHelper.deserializer(configPath, CameraJsonConfig.class); + config = JacksonHelper.deserialize(configPath, CameraJsonConfig.class); } catch (IOException e) { System.err.printf("Failed to load camera config: %s - using default.\n", configPath.toString()); } @@ -61,7 +61,7 @@ public class CameraConfig { private CVPipelineSettings loadDriverMode() { CVPipelineSettings driverMode = new CVPipelineSettings(); try { - driverMode = JacksonHelper.deserializer(driverModePath, CVPipelineSettings.class); + driverMode = JacksonHelper.deserialize(driverModePath, CVPipelineSettings.class); } catch (IOException e) { System.err.println("Failed to load camera drivermode: " + driverModePath.toString()); } @@ -75,7 +75,7 @@ public class CameraConfig { private List loadCalibration() { List calibrations = new ArrayList<>(); try { - calibrations = List.of(Objects.requireNonNull(JacksonHelper.deserializer(calibrationPath, CameraCalibrationConfig[].class))); + calibrations = List.of(Objects.requireNonNull(JacksonHelper.deserialize(calibrationPath, CameraCalibrationConfig[].class))); } catch (Exception e) { System.err.println("Failed to load camera calibration: " + driverModePath.toString()); } diff --git a/chameleon-server/src/main/java/com/chameleonvision/config/ConfigManager.java b/chameleon-server/src/main/java/com/chameleonvision/config/ConfigManager.java index 18da8285e..fd0f5dc5d 100644 --- a/chameleon-server/src/main/java/com/chameleonvision/config/ConfigManager.java +++ b/chameleon-server/src/main/java/com/chameleonvision/config/ConfigManager.java @@ -1,6 +1,5 @@ package com.chameleonvision.config; -import com.chameleonvision.Main; import com.chameleonvision.util.*; import com.chameleonvision.vision.pipeline.CVPipelineSettings; @@ -54,7 +53,7 @@ public class ConfigManager { } } else { try { - settings = JacksonHelper.deserializer(settingsFilePath, GeneralSettings.class); + settings = JacksonHelper.deserialize(settingsFilePath, GeneralSettings.class); } catch (IOException e) { System.err.println("Failed to load settings.json, using defaults."); } diff --git a/chameleon-server/src/main/java/com/chameleonvision/config/PipelineConfig.java b/chameleon-server/src/main/java/com/chameleonvision/config/PipelineConfig.java index e4d1c4757..256c6b784 100644 --- a/chameleon-server/src/main/java/com/chameleonvision/config/PipelineConfig.java +++ b/chameleon-server/src/main/java/com/chameleonvision/config/PipelineConfig.java @@ -1,5 +1,7 @@ package com.chameleonvision.config; +import com.chameleonvision.config.serializers.StandardCVPipelineSettingsDeserializer; +import com.chameleonvision.config.serializers.StandardCVPipelineSettingsSerializer; import com.chameleonvision.util.FileHelper; import com.chameleonvision.util.JacksonHelper; import com.chameleonvision.vision.pipeline.*; @@ -71,11 +73,20 @@ public class PipelineConfig { var path = getPipelinePath(settings); - try { - JacksonHelper.serializer(path, settings); - FileHelper.setFilePerms(path); - } catch (IOException e) { - e.printStackTrace(); + if (settings instanceof StandardCVPipelineSettings) { + try { + JacksonHelper.serialize(path, (StandardCVPipelineSettings)settings, StandardCVPipelineSettings.class, new StandardCVPipelineSettingsSerializer()); + FileHelper.setFilePerms(path); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + try { + JacksonHelper.serializer(path, settings); + FileHelper.setFilePerms(path); + } catch (IOException e) { + e.printStackTrace(); + } } } @@ -119,7 +130,7 @@ public class PipelineConfig { } else { for(File pipelineFile : pipelineFiles) { try { - var pipe = JacksonHelper.deserializer(Paths.get(pipelineFile.getPath()), StandardCVPipelineSettings.class); + var pipe = JacksonHelper.deserialize(Paths.get(pipelineFile.getPath()), StandardCVPipelineSettings.class, new StandardCVPipelineSettingsDeserializer()); deserializedList.add(pipe); } catch (IOException e) { System.err.println("couldn't load cvpipeline2d"); diff --git a/chameleon-server/src/main/java/com/chameleonvision/config/serializers/BaseDeserializer.java b/chameleon-server/src/main/java/com/chameleonvision/config/serializers/BaseDeserializer.java new file mode 100644 index 000000000..69c5b86b3 --- /dev/null +++ b/chameleon-server/src/main/java/com/chameleonvision/config/serializers/BaseDeserializer.java @@ -0,0 +1,110 @@ +package com.chameleonvision.config.serializers; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.type.CollectionType; +import com.fasterxml.jackson.databind.type.TypeFactory; +import org.jetbrains.annotations.NotNull; +import org.opencv.core.MatOfPoint3f; +import org.opencv.core.Point3; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public abstract class BaseDeserializer extends StdDeserializer { + protected BaseDeserializer(Class vc) { + super(vc); + } + + JsonNode baseNode; + + private static final CollectionType numberListColType = TypeFactory.defaultInstance().constructCollectionType(List.class, Number.class); + private CollectionType pointListColType = TypeFactory.defaultInstance().constructCollectionType(List.class, Object.class); + private static final ObjectMapper mapper = new ObjectMapper(); + private static boolean nodeGood(JsonNode node) { + return node != null && !node.toString().equals(""); + } + + List getNumberList(String name, List defaultValue) throws JsonProcessingException { + JsonNode node = baseNode.get(name); + + if (nodeGood(node)) { + return mapper.readValue(node.toString(), numberListColType); + } + return defaultValue; + } + + boolean getBoolean(String name, boolean defaultValue) { + JsonNode node = baseNode.get(name); + + if (nodeGood(node)) { + return node.booleanValue(); + } + + return defaultValue; + } + + int getInt(String name, int defaultValue) { + return (int) getDouble(name, defaultValue); + } + + double getDouble(String name, double defaultValue) { + JsonNode node = baseNode.get(name); + + if (nodeGood(node)) { + return node.numberValue().doubleValue(); + } + + return defaultValue; + } + + String getString(String name, String defaultValue) { + JsonNode node = baseNode.get(name); + + if (nodeGood(node)) { + return node.asText(); + } + + return defaultValue; + } + + > E getEnum(String name, Class enumClass, E defaultValue) throws IOException { + JsonNode node = baseNode.get(name); + + if (nodeGood(node)) { + E[] possibleVals = enumClass.getEnumConstants(); + String jsonVal = baseNode.get(name).asText(); + + for (E val : possibleVals) { + if (val.name().equals(jsonVal)) { + return val; + } + } + } + + return defaultValue; + } + MatOfPoint3f getMatOfPoint3f(String name, MatOfPoint3f defaultValue) throws JsonProcessingException { + JsonNode node = baseNode.get(name); + if (nodeGood(node)){ + List> numberList = mapper.readValue(node.toString(), pointListColType); + List point3List = new ArrayList<>(); + for (List tmp : numberList){ + Point3 p = new Point3(); + p.x = tmp.get(0).doubleValue(); + p.y = tmp.get(1).doubleValue(); + p.z = tmp.get(2).doubleValue(); + point3List.add(p); + } + MatOfPoint3f mat = new MatOfPoint3f(); + mat.fromList(point3List); + return mat; + } + + return defaultValue; + } +} diff --git a/chameleon-server/src/main/java/com/chameleonvision/config/serializers/BaseSerializer.java b/chameleon-server/src/main/java/com/chameleonvision/config/serializers/BaseSerializer.java new file mode 100644 index 000000000..fdfbb9db8 --- /dev/null +++ b/chameleon-server/src/main/java/com/chameleonvision/config/serializers/BaseSerializer.java @@ -0,0 +1,43 @@ +package com.chameleonvision.config.serializers; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import org.opencv.core.MatOfPoint3f; +import org.opencv.core.Point; +import org.opencv.core.Point3; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public abstract class BaseSerializer extends StdSerializer { + protected BaseSerializer(Class t) { + super(t); + } + + JsonGenerator generator; + + void writeNumberListAsNumberArray(String name, List list) throws IOException { + generator.writeArrayFieldStart(name); + for (Number i : list) { + generator.writeObject(i); + } + generator.writeEndArray(); + } + + > void writeEnum(String name, E num) throws IOException { + generator.writeFieldName(name); + generator.writeString(num.name()); + } + + void writeMatOfPoint3f(String name, MatOfPoint3f mat) throws IOException { + List point3List = mat.toList(); + generator.writeArrayFieldStart(name); + + for (Point3 point3 : point3List) { + double[] tmp = {point3.x, point3.y, point3.z}; + generator.writeObject(tmp); + } + generator.writeEndArray(); + } +} diff --git a/chameleon-server/src/main/java/com/chameleonvision/config/serializers/StandardCVPipelineSettingsDeserializer.java b/chameleon-server/src/main/java/com/chameleonvision/config/serializers/StandardCVPipelineSettingsDeserializer.java new file mode 100644 index 000000000..ec909e477 --- /dev/null +++ b/chameleon-server/src/main/java/com/chameleonvision/config/serializers/StandardCVPipelineSettingsDeserializer.java @@ -0,0 +1,80 @@ +package com.chameleonvision.config.serializers; + +import com.chameleonvision.vision.enums.*; +import com.chameleonvision.vision.pipeline.impl.StandardCVPipelineSettings; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.IntNode; + +import java.io.IOException; + +public class StandardCVPipelineSettingsDeserializer extends BaseDeserializer { + public StandardCVPipelineSettingsDeserializer() { + this(null); + } + + private StandardCVPipelineSettingsDeserializer(Class vc) { + super(vc); + } + + @Override + public StandardCVPipelineSettings deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException { + // set BaseDeserializer parser reference. + baseNode = jsonParser.getCodec().readTree(jsonParser); + + StandardCVPipelineSettings pipeline = new StandardCVPipelineSettings(); + + pipeline.index = getInt("index", pipeline.index); + + pipeline.flipMode = getEnum("flipMode", ImageFlipMode.class, pipeline.flipMode); + pipeline.rotationMode = getEnum("rotationMode", ImageRotationMode.class, pipeline.rotationMode); + + pipeline.nickname = getString("nickname", pipeline.nickname); + + pipeline.exposure = getDouble("exposure", pipeline.exposure); + pipeline.brightness = getDouble("brightness", pipeline.brightness); + pipeline.gain = getDouble("gain", pipeline.gain); + + pipeline.videoModeIndex = getInt("videoModeIndex", pipeline.videoModeIndex); + + pipeline.streamDivisor = getEnum("streamDivisor", StreamDivisor.class, pipeline.streamDivisor); + + pipeline.hue = getNumberList("hue", pipeline.hue); + pipeline.saturation = getNumberList("saturation", pipeline.saturation); + pipeline.value = getNumberList("value", pipeline.value); + + pipeline.erode = getBoolean("erode", pipeline.erode); + pipeline.dilate = getBoolean("dilate", pipeline.dilate); + + pipeline.area = getNumberList("area", pipeline.area); + pipeline.ratio = getNumberList("ratio", pipeline.ratio); + pipeline.extent = getNumberList("extent", pipeline.extent); + + pipeline.speckle = getInt("speckle", (Integer) pipeline.speckle); + + pipeline.isBinary = getBoolean("isBinary", pipeline.isBinary); + + pipeline.sortMode = getEnum("sortMode", SortMode.class, pipeline.sortMode); + pipeline.targetRegion = getEnum("targetRegion", TargetRegion.class, pipeline.targetRegion); + pipeline.targetOrientation = getEnum("targetOrientation", TargetOrientation.class, pipeline.targetOrientation); + + pipeline.multiple = getBoolean("multiple", pipeline.multiple); + + pipeline.targetGroup = getEnum("targetGroup", TargetGroup.class, pipeline.targetGroup); + pipeline.targetIntersection = getEnum("targetIntersection", TargetIntersection.class, pipeline.targetIntersection); + + pipeline.point = getNumberList("point", pipeline.point); + + pipeline.calibrationMode = getEnum("calibrationMode", CalibrationMode.class, pipeline.calibrationMode); + + pipeline.dualTargetCalibrationM = getDouble("dualTargetCalibrationM", pipeline.dualTargetCalibrationM); + pipeline.dualTargetCalibrationB = getDouble("dualTargetCalibrationB", pipeline.dualTargetCalibrationB); + + pipeline.is3D = getBoolean("is3D", pipeline.is3D); + pipeline.targetCornerMat = getMatOfPoint3f("targetCornerMat", pipeline.targetCornerMat); + + return pipeline; + } +} diff --git a/chameleon-server/src/main/java/com/chameleonvision/config/serializers/StandardCVPipelineSettingsSerializer.java b/chameleon-server/src/main/java/com/chameleonvision/config/serializers/StandardCVPipelineSettingsSerializer.java new file mode 100644 index 000000000..c787d3145 --- /dev/null +++ b/chameleon-server/src/main/java/com/chameleonvision/config/serializers/StandardCVPipelineSettingsSerializer.java @@ -0,0 +1,82 @@ +package com.chameleonvision.config.serializers; + +import com.chameleonvision.vision.pipeline.impl.StandardCVPipelineSettings; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +public class StandardCVPipelineSettingsSerializer extends BaseSerializer { + public StandardCVPipelineSettingsSerializer() { + this(null); + } + + private StandardCVPipelineSettingsSerializer(Class t) { + super(t); + } + + @Override + public void serialize(StandardCVPipelineSettings pipeline, JsonGenerator gen, SerializerProvider provider) throws IOException { + // set BaseSerializer generator reference. + generator = gen; + + gen.writeStartObject(); + + gen.writeNumberField("index", pipeline.index); + + writeEnum("flipMode", pipeline.flipMode); + writeEnum("rotationMode", pipeline.rotationMode); + + gen.writeStringField("nickname", pipeline.nickname); + + gen.writeNumberField("exposure", pipeline.exposure); + gen.writeNumberField("brightness", pipeline.brightness); + gen.writeNumberField("gain", pipeline.gain); + + gen.writeNumberField("videoModeIndex", pipeline.videoModeIndex); + + writeEnum("streamDivisor", pipeline.streamDivisor); + + writeNumberListAsNumberArray("hue", pipeline.hue); + writeNumberListAsNumberArray("saturation", pipeline.saturation); + writeNumberListAsNumberArray("value", pipeline.value); + + gen.writeBooleanField("erode", pipeline.erode); + gen.writeBooleanField("dilate", pipeline.dilate); + + writeNumberListAsNumberArray("area", pipeline.area); + writeNumberListAsNumberArray("ratio", pipeline.ratio); + writeNumberListAsNumberArray("extent", pipeline.extent); + + // speckle rejection + gen.writeNumberField("speckle", (Integer) pipeline.speckle); + + // stream output (camera feed, or thresholded feed) + gen.writeBooleanField("isBinary", pipeline.isBinary); + + writeEnum("sortMode", pipeline.sortMode); + writeEnum("targetRegion", pipeline.targetRegion); + writeEnum("targetOrientation", pipeline.targetOrientation); + + // show multiple targets when drawing + gen.writeBooleanField("multiple", pipeline.multiple); + + writeEnum("targetGroup", pipeline.targetGroup); + writeEnum("targetIntersection", pipeline.targetIntersection); + + // single calibration point + writeNumberListAsNumberArray("point", pipeline.point); + + // target X/Y calibration + writeEnum("calibrationMode", pipeline.calibrationMode); + + // TODO: better names? or use an array? + gen.writeNumberField("dualTargetCalibrationM", pipeline.dualTargetCalibrationM); + gen.writeNumberField("dualTargetCalibrationB", pipeline.dualTargetCalibrationB); + + + gen.writeBooleanField("is3D", pipeline.is3D); + writeMatOfPoint3f("targetCornerMat", pipeline.targetCornerMat); + gen.writeEndObject(); + } +} diff --git a/chameleon-server/src/main/java/com/chameleonvision/scripting/ScriptManager.java b/chameleon-server/src/main/java/com/chameleonvision/scripting/ScriptManager.java index 9385a13c3..f85e3fb61 100644 --- a/chameleon-server/src/main/java/com/chameleonvision/scripting/ScriptManager.java +++ b/chameleon-server/src/main/java/com/chameleonvision/scripting/ScriptManager.java @@ -1,24 +1,18 @@ package com.chameleonvision.scripting; import com.chameleonvision.Debug; -import com.chameleonvision.Main; import com.chameleonvision.config.ConfigManager; import com.chameleonvision.util.JacksonHelper; import com.chameleonvision.util.LoopingRunnable; import com.chameleonvision.util.Platform; -import com.chameleonvision.util.ProgramDirectoryUtilities; -import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; -import java.util.Objects; -import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; -import java.util.concurrent.LinkedBlockingQueue; public class ScriptManager { @@ -94,7 +88,7 @@ public class ScriptManager { static List loadConfig() { try { - var raw = JacksonHelper.deserializer(scriptConfigPath, ScriptConfig[].class); + var raw = JacksonHelper.deserialize(scriptConfigPath, ScriptConfig[].class); if (raw != null) { return List.of(raw); } diff --git a/chameleon-server/src/main/java/com/chameleonvision/util/FileHelper.java b/chameleon-server/src/main/java/com/chameleonvision/util/FileHelper.java index 13ff9e3a2..04d97fc2c 100644 --- a/chameleon-server/src/main/java/com/chameleonvision/util/FileHelper.java +++ b/chameleon-server/src/main/java/com/chameleonvision/util/FileHelper.java @@ -1,5 +1,7 @@ package com.chameleonvision.util; +import com.chameleonvision.Debug; + import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -20,7 +22,7 @@ public class FileHelper { File thisFile = path.toFile(); Set perms = Files.readAttributes(path, PosixFileAttributes.class).permissions(); if (!perms.equals(allReadWriteExecutePerms)) { - System.out.printf("setting perms on %s\n", path.toString()); + Debug.printInfo("Setting perms on" + path.toString()); Files.setPosixFilePermissions(path, perms); if (thisFile.isDirectory()) { for (File subfile : thisFile.listFiles()) { diff --git a/chameleon-server/src/main/java/com/chameleonvision/util/JacksonHelper.java b/chameleon-server/src/main/java/com/chameleonvision/util/JacksonHelper.java index 8979b4621..d6da705a7 100644 --- a/chameleon-server/src/main/java/com/chameleonvision/util/JacksonHelper.java +++ b/chameleon-server/src/main/java/com/chameleonvision/util/JacksonHelper.java @@ -1,9 +1,14 @@ package com.chameleonvision.util; +import com.chameleonvision.config.serializers.StandardCVPipelineSettingsDeserializer; +import com.chameleonvision.vision.pipeline.impl.StandardCVPipelineSettings; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator; import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; import java.io.File; import java.io.IOException; @@ -18,7 +23,7 @@ public class JacksonHelper { objectMapper.writerWithDefaultPrettyPrinter().writeValue(new File(path.toString()), object); } - public static T deserializer(Path path, Class ref) throws IOException { + public static T deserialize(Path path, Class ref) throws IOException { PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder().allowIfBaseType(ref).build(); ObjectMapper objectMapper = JsonMapper.builder().activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT).build(); File jsonFile = new File(path.toString()); @@ -27,4 +32,26 @@ public class JacksonHelper { } return null; } + + public static T deserialize(Path path, Class ref, StdDeserializer deserializer) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + SimpleModule module = new SimpleModule(); + module.addDeserializer(ref, deserializer); + objectMapper.registerModule(module); + + File jsonFile = new File(path.toString()); + if (jsonFile.exists() && jsonFile.length() > 0) { + return objectMapper.readValue(jsonFile, ref); + } + return null; + } + + public static void serialize(Path path, T object, Class ref, StdSerializer serializer) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + SimpleModule module = new SimpleModule(); + module.addSerializer(ref, serializer); + objectMapper.registerModule(module); + + objectMapper.writerWithDefaultPrettyPrinter().writeValue(new File(path.toString()), object); + } } diff --git a/chameleon-server/src/main/java/com/chameleonvision/vision/pipeline/impl/StandardCVPipelineSettings.java b/chameleon-server/src/main/java/com/chameleonvision/vision/pipeline/impl/StandardCVPipelineSettings.java index 8b688abd5..dd8cd32d7 100644 --- a/chameleon-server/src/main/java/com/chameleonvision/vision/pipeline/impl/StandardCVPipelineSettings.java +++ b/chameleon-server/src/main/java/com/chameleonvision/vision/pipeline/impl/StandardCVPipelineSettings.java @@ -17,7 +17,7 @@ public class StandardCVPipelineSettings extends CVPipelineSettings { public boolean dilate = false; public List area = Arrays.asList(0.0, 100.0); public List ratio = Arrays.asList(0.0, 20.0); - public List extent = Arrays.asList(0, 100); + public List extent = Arrays.asList(0.0, 100.0); public Number speckle = 5; public boolean isBinary = false; public SortMode sortMode = SortMode.Largest;