Merge branch 'V2.0/single-point-calibration' into 'dev'

V2.0/single point calibration

See merge request chameleon-vision/Chameleon-Vision!28
This commit is contained in:
ori agranat
2019-10-29 20:31:00 +00:00
13 changed files with 219 additions and 96 deletions

View File

@@ -10,6 +10,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-nop:1.7.26" level="project" />
<orderEntry type="library" name="Maven: io.javalin:javalin:3.4.1" level="project" />
<orderEntry type="library" name="Maven: org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.31" level="project" />
<orderEntry type="library" name="Maven: org.jetbrains.kotlin:kotlin-stdlib:1.3.31" level="project" />
@@ -33,7 +34,7 @@
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-client:9.4.19.v20190610" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-servlet:9.4.19.v20190610" level="project" />
<orderEntry type="library" name="Maven: org.json:json:20190722" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-simple:1.7.26" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-nop:1.7.26" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-math3:3.6.1" level="project" />
<orderEntry type="library" name="Maven: com.google.code.gson:gson:2.8.5" level="project" />
<orderEntry type="library" name="Maven: org.msgpack:msgpack-core:0.8.18" level="project" />

View File

@@ -75,7 +75,7 @@
<!--slf4j for javalin -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.26</version>
</dependency>

View File

@@ -0,0 +1,5 @@
package com.chameleonvision.vision;
public enum CalibrationMode {
None,Single,Dual
}

View File

@@ -22,6 +22,7 @@ public class Pipeline {
public TargetIntersection targetIntersection = TargetIntersection.Up;
public double m = 1;
public double b = 0;
public boolean isCalibrated = false;
public List<Number> point = Arrays.asList(0,0);
public CalibrationMode calibrationMode = CalibrationMode.None;
public String nickname;
}

View File

@@ -12,6 +12,7 @@ public class CameraValues {
public final double CenterX;
public final double CenterY;
public final double DiagonalView;
public final double DiagonalAspect;
public final Fraction AspectFraction;
public final int HorizontalRatio;
public final int VerticalRatio;
@@ -35,8 +36,9 @@ public class CameraValues {
AspectFraction = new Fraction(ImageWidth, ImageHeight);
HorizontalRatio = AspectFraction.getNumerator();
VerticalRatio = AspectFraction.getDenominator();
HorizontalView = FastMath.atan(FastMath.tan(DiagonalView / 2) * (HorizontalRatio / DiagonalView)) * 2;
VerticalView = FastMath.atan(FastMath.tan(DiagonalView/2) * (VerticalRatio / DiagonalView)) * 2;
DiagonalAspect = FastMath.hypot(HorizontalRatio, VerticalRatio);
HorizontalView = FastMath.atan(FastMath.tan(DiagonalView / 2) * (HorizontalRatio / DiagonalAspect)) * 2;
VerticalView = FastMath.atan(FastMath.tan(DiagonalView / 2) * (VerticalRatio / DiagonalAspect)) * 2;
HorizontalFocalLength = ImageWidth / (2 * FastMath.tan(HorizontalView /2));
VerticalFocalLength = ImageHeight / (2 * FastMath.tan(VerticalView /2));
}

View File

@@ -1,6 +1,7 @@
package com.chameleonvision.vision.process;
import com.chameleonvision.settings.SettingsManager;
import com.chameleonvision.vision.CalibrationMode;
import com.chameleonvision.vision.Orientation;
import com.chameleonvision.vision.Pipeline;
import com.chameleonvision.vision.camera.Camera;
@@ -155,15 +156,24 @@ public class VisionProcess implements Runnable {
groupedContours = cvProcess.groupTargets(deSpeckledContours, currentPipeline.targetIntersection, currentPipeline.targetGroup);
if (groupedContours.size() > 0) {
var finalRect = cvProcess.sortTargetsToOne(groupedContours, currentPipeline.sortMode);
// System.out.printf("Largest Contour Area: %.2f\n", finalRect.size.area());
pipelineResult.RawPoint = finalRect;
pipelineResult.IsValid = true;
if (!currentPipeline.isCalibrated) {
pipelineResult.CalibratedX = camera.getCamVals().CenterX;
pipelineResult.CalibratedY = camera.getCamVals().CenterY;
} else {
pipelineResult.CalibratedX = (finalRect.center.y - currentPipeline.b) / currentPipeline.m;
pipelineResult.CalibratedY = (finalRect.center.x * currentPipeline.m) + currentPipeline.b;
switch (currentPipeline.calibrationMode){
case None:
///use the center of the camera to find the pitch and yaw difference
pipelineResult.CalibratedX = camera.getCamVals().CenterX;
pipelineResult.CalibratedY = camera.getCamVals().CenterY;
break;
case Single:
// use the static point as a calibration method instead of the center
pipelineResult.CalibratedX = currentPipeline.point.get(0).doubleValue();
pipelineResult.CalibratedY = currentPipeline.point.get(1).doubleValue();
break;
case Dual:
// use the calculated line to find the difference in length between the point and the line
pipelineResult.CalibratedX = (finalRect.center.y - currentPipeline.b) / currentPipeline.m;
pipelineResult.CalibratedY = (finalRect.center.x * currentPipeline.m) + currentPipeline.b;
break;
}
pipelineResult.Pitch = camera.getCamVals().CalculatePitch(finalRect.center.y, pipelineResult.CalibratedY);
pipelineResult.Yaw = camera.getCamVals().CalculateYaw(finalRect.center.x, pipelineResult.CalibratedX);

View File

@@ -53,6 +53,7 @@ public class ServerHandler {
setField(SettingsManager.GeneralSettings, e.getKey(), e.getValue());
}
SettingsManager.saveSettings();
sendFullSettings();
break;
}
case "cameraSettings": {
@@ -61,16 +62,19 @@ public class ServerHandler {
CameraManager.getCurrentCamera().setStreamDivisor((Integer) camSettings.get("streamDivisor"));
CameraManager.getCurrentCamera().setCamVideoMode((Integer) camSettings.get("resolution"), true);
SettingsManager.saveSettings();
sendFullSettings();
break;
}
case "changeCameraName": {
CameraManager.getCurrentCamera().setNickname((String) entry.getValue());
sendFullSettings();
SettingsManager.saveSettings();
break;
}
case "changePipelineName": {
CameraManager.getCurrentPipeline().nickname = (String) entry.getValue();
sendFullSettings();
SettingsManager.saveSettings();
break;
}
case "duplicatePipeline": {
@@ -85,6 +89,7 @@ public class ServerHandler {
} else {
CameraManager.getCurrentCamera().addPipeline(origPipeline);
}
SettingsManager.saveSettings();
break;
}
case "command": {
@@ -93,6 +98,7 @@ public class ServerHandler {
case "addNewPipeline":
cam.addPipeline();
sendFullSettings();
SettingsManager.saveSettings();
break;
case "deleteCurrentPipeline":
int currentIndex = cam.getCurrentPipelineIndex();
@@ -105,6 +111,10 @@ public class ServerHandler {
cam.deletePipeline();
cam.setCurrentPipelineIndex(nextIndex);
sendFullSettings();
SettingsManager.saveSettings();
break;
case "save":
SettingsManager.saveSettings();
break;
}
// used to define all incoming commands
@@ -118,9 +128,7 @@ public class ServerHandler {
case "currentPipeline": {
var cam = CameraManager.getCurrentCamera();
cam.setCurrentPipelineIndex((Integer) entry.getValue());
HashMap<String, Object> tmp = new HashMap<>();
tmp.put("pipeline", getOrdinalPipeline());
broadcastMessage(tmp);
sendFullSettings();
try {
cam.setBrightness(cam.getCurrentPipeline().brightness);
cam.setExposure(cam.getCurrentPipeline().exposure);

View File

@@ -13493,7 +13493,8 @@
},
"code-point-at": {
"version": "1.1.0",
"bundled": true
"bundled": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
@@ -13502,7 +13503,8 @@
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true
"bundled": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
@@ -13605,7 +13607,8 @@
},
"inherits": {
"version": "2.0.3",
"bundled": true
"bundled": true,
"optional": true
},
"ini": {
"version": "1.3.5",
@@ -13615,6 +13618,7 @@
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -13640,6 +13644,7 @@
"minipass": {
"version": "2.2.4",
"bundled": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.1",
"yallist": "^3.0.0"
@@ -13656,6 +13661,7 @@
"mkdirp": {
"version": "0.5.1",
"bundled": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -13728,7 +13734,8 @@
},
"number-is-nan": {
"version": "1.0.1",
"bundled": true
"bundled": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
@@ -13738,6 +13745,7 @@
"once": {
"version": "1.4.0",
"bundled": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@@ -13843,6 +13851,7 @@
"string-width": {
"version": "1.0.2",
"bundled": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",

View File

@@ -0,0 +1,64 @@
<template>
<div>
<v-row align="center" justify="start">
<v-col style="padding-right:0" :cols="3">
<v-btn small color="#4baf62" @click="takePointA">Take Point A</v-btn>
</v-col>
<v-col style="margin-left:0" :cols="3">
<v-btn small color="#4baf62" @click="takePointB">Take Point B</v-btn>
</v-col>
<v-col>
<v-btn small @click="clearSlope" color="yellow darken-3">Clear All Points</v-btn>
</v-col>
</v-row>
</div>
</template>
<script>
export default {
name: "DualCalibration",
props: ['rawPoint'],
data() {
return {
pointA: undefined,
pointB: undefined
}
},
methods: {
takePointA() {
this.pointA = this.rawPoint;
this.calcSlope();
},
takePointB() {
this.pointB = this.rawPoint;
this.calcSlope();
},
calcSlope() {
if (this.pointA !== undefined && this.pointB !== undefined) {
let m = (this.pointB[1] - this.pointA[1]) / (this.pointB[0] - this.pointA[0]);
let b = this.pointA[1] - (m * this.pointA[0]);
if (isNaN(m) === false && isNaN(b) === false) {
this.sendSlope(m, b, true);
} else {
this.$emit('snackbar');
}
this.pointA = undefined;
this.pointB = undefined;
}
},
sendSlope(m, b, valid) {
this.handleInput('m', m);
this.handleInput('b', b);
},
clearSlope() {
this.sendSlope(1, 0, false);
this.pointA = undefined;
this.pointB = undefined;
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,31 @@
<template>
<div>
<v-row align="center" justify="start">
<v-col style="padding-right:0" :cols="3">
<v-btn small color="#4baf62" @click="takePoint">Take Point</v-btn>
</v-col>
<v-col>
<v-btn small @click="clearPoint" color="yellow darken-3">Clear Point</v-btn>
</v-col>
</v-row>
</div>
</template>
<script>
export default {
name: "SingleCalibration",
props: ['rawPoint'],
methods:{
clearPoint(){
this.handleInput('point',[0,0]);
},
takePoint(){
this.handleInput('point',this.rawPoint);
}
}
}
</script>
<style scoped>
</style>

View File

@@ -33,7 +33,8 @@ export default new Vuex.Store({
targetGrouping:0,
targetIntersection:0,
sortMode:0,
isBinary:0
isBinary:0,
calibrationMode:0
},
cameraSettings:{},
resolutionList:[],
@@ -54,7 +55,12 @@ export default new Vuex.Store({
currentPipelineIndex: set('currentPipelineIndex'),
cameraList: set('cameraList'),
pipelineList: set('pipelineList'),
point:set('point')
point:set('point'),
setPipeValues(state,obj){
for(let i in obj){
Vue.set(state.pipeline,i,obj[i]);
}
}
},
actions: {
settings: state => state.settings,
@@ -67,10 +73,5 @@ export default new Vuex.Store({
cameraList: state =>state.cameraList,
pipelineList: state =>state.pipelineList,
point: state =>state.point,
setPipeValues(state,obj){
for(let i in obj){
Vue.set(state.pipeline,i,obj[i]);
}
}
}
})

View File

@@ -4,52 +4,63 @@
<v-row align="center">
<v-col :cols="3" class="colsClass">
<div style="padding-left:30px">
<CVselect v-if="isCameraNameEdit == false" name="Camera" v-model="currentCameraIndex" :list="cameraList" @input="handleInput('currentCamera',currentCameraIndex)"></CVselect>
<CVinput v-else name="Camera" v-model="newCameraName" @Enter="saveCameraNameChange" :errorMessage="checkCameraName"></CVinput>
<CVselect v-if="isCameraNameEdit === false" name="Camera" v-model="currentCameraIndex"
:list="cameraList" @input="handleInput('currentCamera',currentCameraIndex)"/>
<CVinput v-else name="Camera" v-model="newCameraName" @Enter="saveCameraNameChange"
:errorMessage="checkCameraName"/>
</div>
</v-col>
<v-col :cols="1">
<CVicon color="#c5c5c5" v-if="isCameraNameEdit == false" hover text="edit" @click="toCameraNameChange" tooltip="Edit camera name"></CVicon>
<CVicon color="#c5c5c5" v-if="isCameraNameEdit === false" hover text="edit"
@click="toCameraNameChange" tooltip="Edit camera name"/>
<div v-else>
<CVicon color="#c5c5c5" style="display: inline-block;" hover text="save" @click="saveCameraNameChange" tooltip="Save Camera Name"></CVicon>
<CVicon color="error" style="display: inline-block;" hover text="close" @click="discardCameraNameChange" tooltip="Discard Changes"></CVicon>
<CVicon color="#c5c5c5" style="display: inline-block;" hover text="save"
@click="saveCameraNameChange" tooltip="Save Camera Name"/>
<CVicon color="error" style="display: inline-block;" hover text="close"
@click="discardCameraNameChange" tooltip="Discard Changes"/>
</div>
</v-col>
<v-col :cols="3" class="colsClass">
<CVselect v-if="isPipelineEdit == false" name="Pipeline" :list="pipelineList" v-model="currentPipelineIndex" @input="handleInput('currentPipeline',currentPipelineIndex)"></CVselect>
<CVinput v-else name="Pipeline" v-model="newPipelineName" @Enter="savePipelineNameChange"></CVinput>
<CVselect v-if="isPipelineEdit === false" name="Pipeline" :list="pipelineList"
v-model="currentPipelineIndex"
@input="handleInput('currentPipeline',currentPipelineIndex)"/>
<CVinput v-else name="Pipeline" v-model="newPipelineName" @Enter="savePipelineNameChange"/>
</v-col>
<v-col :cols="1" class="colsClass">
<v-menu v-if="isPipelineEdit == false" offset-y dark auto>
<v-menu v-if="isPipelineEdit === false" offset-y dark auto>
<template v-slot:activator="{ on }">
<v-icon color="white" v-on="on">menu</v-icon>
</template>
<v-list dense>
<v-list-item @click="toPipelineNameChange">
<v-list-item-title>
<CVicon color="#c5c5c5" :right="true" text="edit" tooltip="Edit pipeline name"></CVicon>
<CVicon color="#c5c5c5" :right="true" text="edit" tooltip="Edit pipeline name"/>
</v-list-item-title>
</v-list-item>
<v-list-item @click="handleInput('command','addNewPipeline')">
<v-list-item-title>
<CVicon color="#c5c5c5" :right="true" text="add" tooltip="Add new pipeline"></CVicon>
<CVicon color="#c5c5c5" :right="true" text="add" tooltip="Add new pipeline"/>
</v-list-item-title>
</v-list-item>
<v-list-item @click="deleteCurrentPipeline">
<v-list-item-title>
<CVicon color="red darken-2" :right="true" text="delete" tooltip="Delete pipeline"></CVicon>
<CVicon color="red darken-2" :right="true" text="delete"
tooltip="Delete pipeline"/>
</v-list-item-title>
</v-list-item>
<v-list-item @click="openDuplicateDialog">
<v-list-item-title>
<CVicon color="#c5c5c5" :right="true" text="mdi-content-copy" tooltip="Duplicate pipeline"></CVicon>
<CVicon color="#c5c5c5" :right="true" text="mdi-content-copy"
tooltip="Duplicate pipeline"/>
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
<div v-else>
<CVicon color="#c5c5c5" style="display: inline-block;" hover text="save" @click="savePipelineNameChange" tooltip="Save Pipeline Name"></CVicon>
<CVicon color="error" style="display: inline-block;" hover text="close" @click="discardPipelineNameChange" tooltip="Discard Changes"></CVicon>
<CVicon color="#c5c5c5" style="display: inline-block;" hover text="save"
@click="savePipelineNameChange" tooltip="Save Pipeline Name"/>
<CVicon color="error" style="display: inline-block;" hover text="close"
@click="discardPipelineNameChange" tooltip="Discard Changes"/>
</div>
</v-col>
@@ -66,7 +77,7 @@
</v-tabs>
<div style="padding-left:30px">
<!-- vision component -->
<component v-model="pipeline" :is="selectedComponent"></component>
<component v-model="pipeline" :is="selectedComponent"/>
</div>
</v-col>
<v-col cols="6" class="colsClass">
@@ -78,7 +89,7 @@
</v-tabs>
<!-- camera image stream -->
<div class="videoClass">
<img v-if="cameraList.length > 0" :src="steamAdress">
<img v-if="cameraList.length > 0" :src="streamAddress">
<span v-else>No Cameras Are connected</span>
<h5 id="Point">{{point}}</h5>
</div>
@@ -90,14 +101,15 @@
<v-card dark>
<v-card-title class="headline" primary-title>Duplicate Pipeline</v-card-title>
<v-card-text>
<CVselect name="Pipeline" :list="pipelineList" v-model="pipelineDuplicate.pipeline"></CVselect>
<v-checkbox v-if="cameraList.length > 1" dark :label="'To another camera'" v-model="anotherCamera"></v-checkbox>
<CVselect v-if="anotherCamera === true" name="Camera" v-model="pipelineDuplicate.camera" :list="cameraList"></CVselect>
<CVselect name="Pipeline" :list="pipelineList" v-model="pipelineDuplicate.pipeline"/>
<v-checkbox v-if="cameraList.length > 1" dark :label="'To another camera'" v-model="anotherCamera"/>
<CVselect v-if="anotherCamera === true" name="Camera" v-model="pipelineDuplicate.camera"
:list="cameraList"/>
</v-card-text>
<v-divider>
</v-divider>
<v-card-actions>
<v-spacer></v-spacer>
<v-spacer/>
<v-btn color="#4baf62" text @click="duplicatePipeline">Duplicate</v-btn>
<v-btn color="error" text @click="closeDuplicateDialog">Cancels</v-btn>
</v-card-actions>
@@ -213,7 +225,7 @@ import CVinput from '../components/cv-input'
checkCameraName(){
if(this.newCameraName !== this.cameraList[this.currentCameraIndex]){
for(let cam in this.cameraList){
if(this.newCameraName == this.cameraList[cam]){
if(this.newCameraName === this.cameraList[cam]){
return "Camera by that name already Exists"
}
}
@@ -285,7 +297,7 @@ import CVinput from '../components/cv-input'
return this.$store.state.pipeline;
}
},
steamAdress: {
streamAddress: {
get: function(){
return "http://"+location.hostname + ":"+ this.$store.state.port +"/stream.mjpg";
}

View File

@@ -1,19 +1,13 @@
<template>
<div>
<CVselect name="SortMode" v-model="value.sortMode" :list="['Largest','Smallest','Highest','Lowest','Rightmost','Leftmost','Closest']" @input="handleInput('sortMode',value.sortMode)"></CVselect>
<span>Calibrate:</span><v-divider dark color="white"></v-divider>
<v-row align="center" justify="start">
<v-col style="padding-right:0px" :cols="3">
<v-btn small color="#4baf62" @click="takePointA">Take Point A</v-btn>
</v-col>
<v-col style="margin-left:0px" :cols="3">
<v-btn small color="#4baf62" @click="takePointB">Take Point B</v-btn>
</v-col>
<v-col>
<v-btn small @click="clearSlope" color="yellow darken-3">Clear All Points</v-btn>
</v-col>
</v-row>
<CVselect name="SortMode" v-model="value.sortMode"
:list="['Largest','Smallest','Highest','Lowest','Rightmost','Leftmost','Closest']"
@input="handleInput('sortMode',value.sortMode)"/>
<span>Calibrate:</span>
<v-divider dark color="white"/>
<CVselect name="Calibration Mode" v-model="value.calibrationMode" :list="['None','Single point','Dual point']"
@input="handleInput('calibrationMode',value.calibrationMode)"/>
<component :raw-point="rawPoint" :is="selectedComponent"/>
<v-snackbar :timeout="3000" v-model="snackbar" top color="error">
<span style="color:#000">Points are too close</span>
<v-btn color="black" text @click="snackbar = false">Close</v-btn>
@@ -23,54 +17,39 @@
<script>
import CVselect from '../../components/cv-select'
import DualCalibration from "../../components/OutputTab/DualCalibration";
import SingleCalibration from "../../components/OutputTab/SingleCalibration";
export default {
name: 'Output',
props:['value'],
components:{
CVselect
CVselect,
SingleCalibration,
DualCalibration,
},
methods:{
takePointA(){
this.pointA = this.rawPoint;
this.calcSlope();
},
takePointB(){
this.pointB = this.rawPoint;
this.calcSlope();
},
calcSlope(){
if(this.pointA !== undefined && this.pointB !== undefined){
let m = (this.pointB[1] - this.pointA[1]) / (this.pointB[0] - this.pointA[0]);
let b = this.pointA[1] - (m * this.pointA[0]);
if(isNaN(m) === false && isNaN(b) === false){
this.sendSlope(m,b,true);
} else {
this.snackbar = true;
}
this.pointA = undefined;
this.pointB = undefined;
}
},
sendSlope(m,b,valid){
this.handleInput('m',m);
this.handleInput('b',b);
this.handleInput('isCalibrated',valid);
},
clearSlope(){
this.sendSlope(1,0,false);
this.pointA = undefined;
this.pointB = undefined;
}
},
data() {
return {
snackbar: false,
pointA: undefined,
pointB: undefined
}
},
computed:{
selectedComponent:{
get(){
switch (this.value.calibrationMode) {
case 0:
return "";
case 1:
return "SingleCalibration";
case 2:
return "DualCalibration"
}
return ""
}
},
rawPoint:{
get(){
return this.$store.state.point.rawPoint;