Fix .gitignore, move vision package out of common

This commit is contained in:
Banks Troutman
2020-06-28 04:40:43 -04:00
parent bdbd6b9d18
commit f2fbe9dd6e
214 changed files with 485 additions and 7768 deletions

24
.gitignore vendored
View File

@@ -105,21 +105,21 @@ fabric.properties
.idea/caches/build_file_checksums.ser
chameleon-server/.gradle
chameleon-server/target
chameleon-server/src/main/java/META-INF
chameleon-server/.settings
chameleon-server/.classpath
chameleon-server/.project
chameleon-server/settings
chameleon-server/dependency-reduced-pom.xml
# chameleon-server/chameleon-vision.iml
photon-server/.gradle
photon-server/target
photon-server/src/main/java/META-INF
photon-server/.settings
photon-server/.classpath
photon-server/.project
photon-server/settings
photon-server/dependency-reduced-pom.xml
# photon-server/photon-vision.iml
New client/chameleon-client/*
New client/photon-client/*
*.prefs
*.jfr
.DS_Store
# *.iml
chameleon-server/build
chameleon-server/chameleon-vision
photon-server/build
photon-server/photon-vision

View File

@@ -4,12 +4,12 @@ plugins {
id "com.diffplug.gradle.spotless" version "3.28.0"
}
group 'com.chameleonvision'
version '3.0-SNAPSHOT'
group 'org.photonvision'
version '2020.6.1'
shadowJar {
configurations = [project.configurations.compile]
archiveFileName.set("chameleonvision-${project.version}.jar")
archiveFileName.set("photonvision-${project.version}.jar")
}
sourceCompatibility = 11
@@ -73,14 +73,6 @@ dependencies {
testImplementation('org.junit.jupiter:junit-jupiter:5.6.0')
}
sourceSets {
main {
java {
exclude '**/_2/**'
}
}
}
test {
useJUnitPlatform()
testLogging {

View File

@@ -1,336 +0,0 @@
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
src: local('Roboto Thin'), local('Roboto-Thin'), url(https://fonts.gstatic.com/s/roboto/v20/KFOkCnqEu92Fr1MmgVxFIzIFKw.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
src: local('Roboto Thin'), local('Roboto-Thin'), url(https://fonts.gstatic.com/s/roboto/v20/KFOkCnqEu92Fr1MmgVxMIzIFKw.woff2) format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
src: local('Roboto Thin'), local('Roboto-Thin'), url(https://fonts.gstatic.com/s/roboto/v20/KFOkCnqEu92Fr1MmgVxEIzIFKw.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
src: local('Roboto Thin'), local('Roboto-Thin'), url(https://fonts.gstatic.com/s/roboto/v20/KFOkCnqEu92Fr1MmgVxLIzIFKw.woff2) format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
src: local('Roboto Thin'), local('Roboto-Thin'), url(https://fonts.gstatic.com/s/roboto/v20/KFOkCnqEu92Fr1MmgVxHIzIFKw.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
src: local('Roboto Thin'), local('Roboto-Thin'), url(https://fonts.gstatic.com/s/roboto/v20/KFOkCnqEu92Fr1MmgVxGIzIFKw.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
src: local('Roboto Thin'), local('Roboto-Thin'), url(https://fonts.gstatic.com/s/roboto/v20/KFOkCnqEu92Fr1MmgVxIIzI.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2) format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmSU5fCBc4EsA.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmSU5fBxc4EsA.woff2) format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmSU5fCxc4EsA.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmSU5fBBc4.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu72xKOzY.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu5mxKOzY.woff2) format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu7mxKOzY.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu4WxKOzY.woff2) format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu7WxKOzY.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu7GxKOzY.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu4mxK.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmEU9fCRc4EsA.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmEU9fABc4EsA.woff2) format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmEU9fCBc4EsA.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmEU9fBxc4EsA.woff2) format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmEU9fCxc4EsA.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmEU9fBBc4.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2) format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmWUlfCBc4EsA.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmWUlfBxc4EsA.woff2) format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmWUlfCxc4EsA.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmWUlfBBc4.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
src: local('Roboto Black'), local('Roboto-Black'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmYUtfCRc4EsA.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
src: local('Roboto Black'), local('Roboto-Black'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmYUtfABc4EsA.woff2) format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
src: local('Roboto Black'), local('Roboto-Black'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmYUtfCBc4EsA.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
src: local('Roboto Black'), local('Roboto-Black'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmYUtfBxc4EsA.woff2) format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
src: local('Roboto Black'), local('Roboto-Black'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmYUtfCxc4EsA.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
src: local('Roboto Black'), local('Roboto-Black'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmYUtfChc4EsA.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
src: local('Roboto Black'), local('Roboto-Black'), url(https://fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmYUtfBBc4.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

View File

@@ -1 +0,0 @@
html{overflow-y:hidden!important}.imgClass{width:auto;height:45px;vertical-align:middle;padding-right:5px}.loggerClass{position:absolute;bottom:0;height:25%!important;left:0;right:0;-webkit-box-shadow:#282828 0 0 5px 1px;box-shadow:0 0 5px 1px #282828;background-color:#2b2b2b}::-webkit-scrollbar{width:.5em;border-radius:5px}::-webkit-scrollbar-track{-webkit-box-shadow:inset 0 0 6px rgba(0,0,0,.3);border-radius:10px}::-webkit-scrollbar-thumb{background-color:#4baf62;border-radius:10px}.container{background-color:#212121;padding:0!important}#title{color:#4baf62}span{color:#fff}.hover[data-v-6fe754e4]:hover{color:#fff!important}#canvasId[data-v-7cadfa0d]{width:400px;height:400px;background-color:#2b2b2b;border-radius:5px;border:2px solid grey;-webkit-box-shadow:0 0 5px 1px;box-shadow:0 0 5px 1px}th[data-v-7cadfa0d]{width:80px;text-align:center}.miniMapClass[data-v-383a7992]{width:50%!important;height:50%!important}.colsClass[data-v-9e4d1488]{padding:0!important}.videoClass[data-v-9e4d1488]{text-align:center}th[data-v-9e4d1488]{width:80px;text-align:center}.videoClass[data-v-4625d12f]{text-align:center}.videoClass img[data-v-4625d12f]{padding-top:10px;height:auto!important;vertical-align:middle}.colsClass[data-v-4625d12f]{padding:0!important}

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

View File

@@ -1 +0,0 @@
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=/favicon.png><title>Chameleon Vision</title><link rel=stylesheet href=/Roboto.css><link href=/css/app.e8004665.css rel=preload as=style><link href=/css/chunk-vendors.0ad5867f.css rel=preload as=style><link href=/js/app.2e103153.js rel=preload as=script><link href=/js/chunk-vendors.7c313850.js rel=preload as=script><link href=/css/chunk-vendors.0ad5867f.css rel=stylesheet><link href=/css/app.e8004665.css rel=stylesheet></head><body><noscript><strong>We're sorry but Chameleon Vision doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/js/chunk-vendors.7c313850.js></script><script src=/js/app.2e103153.js></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,191 +0,0 @@
package org.photonvision._2;
import static org.photonvision.common.util.Platform.CurrentPlatform;
import org.photonvision._2.config.ConfigManager;
import org.photonvision._2.vision.VisionManager;
import org.photonvision._2.web.Server;
import org.photonvision.common.datatransfer.networktables.NetworkTablesManager;
import org.photonvision.common.networking.NetworkManager;
import org.photonvision.common.scripting.ScriptEventType;
import org.photonvision.common.scripting.ScriptManager;
import org.photonvision.common.util.Platform;
import org.photonvision.common.util.math.IPUtils;
import edu.wpi.cscore.CameraServerCvJNI;
import edu.wpi.cscore.CameraServerJNI;
import java.io.IOException;
public class Main {
private static final String NT_SERVERMODE_KEY = "--nt-servermode"; // no args for this setting
private static final String NT_CLIENTMODESERVER_KEY =
"--nt-client-server"; // expects String representing an IP address (hostnames will be
// rejected!)
private static final String NETWORK_MANAGE_KEY = "--unmanage-network"; // no args for this setting
private static final String IGNORE_ROOT_KEY = "--ignore-root"; // no args for this setting
private static final String TEST_MODE_KEY = "--cv-development";
private static final String UI_PORT_KEY = "--ui-port";
private static final int DEFAULT_PORT = 5800;
private static boolean ntServerMode = false;
private static boolean manageNetwork = true;
private static boolean ignoreRoot = false;
private static String ntClientModeServer = null;
public static boolean testMode = false;
public static int uiPort = DEFAULT_PORT;
private static void handleArgs(String[] args) {
for (int i = 0; i < args.length; i++) {
var key = args[i].toLowerCase();
String value = null;
// this switch handles arguments with a value. Add any settings with a value here.
switch (key) {
case NT_CLIENTMODESERVER_KEY:
var potentialValue = args[i + 1];
// ensures this "value" isnt null, blank, nor another argument
if (potentialValue != null
&& !potentialValue.isBlank()
&& !potentialValue.startsWith("-") & !potentialValue.startsWith("--")) {
value = potentialValue.toLowerCase();
}
i++; // increment to skip an 'arg' next go-around of for loop, as that would be this value
break;
case UI_PORT_KEY:
var potentialPort = args[i + 1];
if (potentialPort != null
&& !potentialPort.isBlank()
&& !potentialPort.startsWith("-") & !potentialPort.startsWith("--")) {
value = potentialPort;
}
i++;
break;
case NT_SERVERMODE_KEY:
case NETWORK_MANAGE_KEY:
case IGNORE_ROOT_KEY:
case TEST_MODE_KEY:
// nothing
break;
}
// this switch actually handles the arguments.
switch (key) {
case NT_SERVERMODE_KEY:
ntServerMode = true;
break;
case NT_CLIENTMODESERVER_KEY:
if (value != null) {
if (value.equals("localhost")) {
ntClientModeServer = "127.0.0.1";
continue;
}
if (IPUtils.isValidIPV4(value)) {
ntClientModeServer = value;
continue;
}
}
System.err.println(
"Argument for NT Server Host was invalid, defaulting to team number host");
break;
case NETWORK_MANAGE_KEY:
manageNetwork = false;
break;
case IGNORE_ROOT_KEY:
ignoreRoot = true;
break;
case TEST_MODE_KEY:
testMode = true;
break;
case UI_PORT_KEY:
if (value != null) {
try {
uiPort = Integer.parseInt(value);
} catch (NumberFormatException e) {
System.err.println("ui Port was not a valid number using port 5800");
}
}
break;
}
}
}
public static void main(String[] args) {
Runtime.getRuntime()
.addShutdownHook(new Thread(() -> ScriptManager.queueEvent(ScriptEventType.kProgramExit)));
if (CurrentPlatform.equals(Platform.UNSUPPORTED)) {
System.err.printf(
"Sorry, this platform is not supported. Give these details to the developers.\n%s\n",
CurrentPlatform.toString());
return;
} else {
System.out.printf("Starting Chameleon Vision on platform %s\n", CurrentPlatform.toString());
}
handleArgs(args);
if (!CurrentPlatform.isRoot) {
if (ignoreRoot) {
// manageNetwork = false;
System.out.println("Ignoring root, network will not be managed!");
} else {
System.err.println("This program must be run as root!");
return;
}
}
// Attempt to load the JNI Libraries
System.out.println("Loading CameraServer...");
try {
CameraServerJNI.forceLoad();
CameraServerCvJNI.forceLoad();
} catch (UnsatisfiedLinkError | IOException e) {
if (CurrentPlatform.isWindows()) {
System.err.println(
"Try to download the VC++ Redistributable, https://aka.ms/vs/16/release/vc_redist.x64.exe");
}
throw new RuntimeException("Failed to load JNI Libraries!");
}
System.out.println("Checking Settings...");
ConfigManager.initializeSettings();
if (!CurrentPlatform.isWindows()) {
System.out.println("Initializing Script Manager...");
ScriptManager.initialize();
} else {
System.out.println("Scripts not yet supported on Windows. ScriptEvents will be ignored.");
}
NetworkManager.getInstance().initialize(manageNetwork);
if (ntServerMode) {
NetworkTablesManager.setServerMode();
} else {
NetworkTablesManager.setClientMode(ntClientModeServer);
}
ScriptManager.queueEvent(ScriptEventType.kProgramInit);
boolean visionSourcesOk = VisionManager.initializeSources();
if (!visionSourcesOk) {
System.err.println("No cameras connected!");
return;
}
boolean visionProcessesOk = VisionManager.initializeProcesses();
if (!visionProcessesOk) {
System.err.println("Failed to initialize vision processes!");
return;
}
System.out.println("Starting vision processes...");
VisionManager.startProcesses();
System.out.printf("Starting Web server at port %d\n", uiPort);
Server.main(uiPort);
}
}

View File

@@ -1,69 +0,0 @@
package org.photonvision._2.config;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreType;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.opencv.core.Mat;
import org.opencv.core.MatOfDouble;
import org.opencv.core.Size;
/** A class that holds a camera matrix and distortion coefficients for a given resolution */
public class CameraCalibrationConfig {
@JsonProperty("resolution")
public final Size resolution;
@JsonProperty("cameraMatrix")
public final JsonMat cameraMatrix;
@JsonProperty("distortionCoeffs")
public final JsonMat distortionCoeffs;
@JsonProperty("squareSize")
public final double squareSize;
@JsonCreator
public CameraCalibrationConfig(
@JsonProperty("resolution") Size resolution,
@JsonProperty("cameraMatrix") JsonMat cameraMatrix,
@JsonProperty("distortionCoeffs") JsonMat distortionCoeffs,
@JsonProperty("squareSize") double squareSize) {
this.resolution = resolution;
this.cameraMatrix = cameraMatrix;
this.distortionCoeffs = distortionCoeffs;
this.squareSize = squareSize;
}
public CameraCalibrationConfig(
Size resolution, Mat cameraMatrix, Mat distortionCoeffs, double squareSize) {
this.resolution = resolution;
this.cameraMatrix = JsonMat.fromMat(cameraMatrix);
this.distortionCoeffs = JsonMat.fromMat(distortionCoeffs);
this.squareSize = squareSize;
}
@JsonIgnoreType
public static class UICameraCalibrationConfig {
public final int width;
public final int height;
public final double[] cameraMatrix;
public final double[] distortionCoeffs;
public UICameraCalibrationConfig(CameraCalibrationConfig config) {
width = (int) config.resolution.width;
height = (int) config.resolution.height;
cameraMatrix = config.cameraMatrix.data;
distortionCoeffs = config.distortionCoeffs.data;
}
}
@JsonIgnore
public Mat getCameraMatrixAsMat() {
return cameraMatrix.getAsMat();
}
@JsonIgnore
public MatOfDouble getDistortionCoeffsAsMat() {
return new MatOfDouble(distortionCoeffs.getAsMat());
}
}

View File

@@ -1,187 +0,0 @@
package org.photonvision._2.config;
import org.photonvision._2.vision.pipeline.CVPipelineSettings;
import org.photonvision.common.util.file.FileUtils;
import com.chameleonvision.common.util.jackson.JacksonUtils;
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;
public class CameraConfig {
private static final Path camerasConfigFolderPath =
Path.of(ConfigManager.SettingsPath.toString(), "cameras");
private final CameraJsonConfig preliminaryConfig;
private final Path configFolderPath;
private final Path configPath;
private final Path driverModePath;
private final Path calibrationPath;
final Path pipelineFolderPath;
public final PipelineConfig pipelineConfig;
CameraConfig(CameraJsonConfig config) {
preliminaryConfig = config;
String cameraConfigName = preliminaryConfig.name.replace(' ', '_');
pipelineConfig = new PipelineConfig(this);
configFolderPath = Path.of(camerasConfigFolderPath.toString(), cameraConfigName);
configPath = Path.of(configFolderPath.toString(), "camera.json");
driverModePath = Path.of(configFolderPath.toString(), "drivermode.json");
calibrationPath = Path.of(configFolderPath.toString(), "calibration.json");
pipelineFolderPath = Paths.get(configFolderPath.toString(), "pipelines");
}
public FullCameraConfiguration load() {
checkFolder();
checkConfig();
checkDriverMode();
checkCalibration();
pipelineConfig.check();
return new FullCameraConfiguration(
loadConfig(), pipelineConfig.load(), loadDriverMode(), loadCalibration(), this);
}
private CameraJsonConfig loadConfig() {
CameraJsonConfig config = preliminaryConfig;
try {
config = JacksonUtils.deserialize(configPath, CameraJsonConfig.class);
} catch (IOException e) {
System.err.printf(
"Failed to load camera config: %s - using default.\n", configPath.toString());
}
return config;
}
private CVPipelineSettings loadDriverMode() {
CVPipelineSettings driverMode = new CVPipelineSettings();
try {
driverMode = JacksonUtils.deserialize(driverModePath, CVPipelineSettings.class);
} catch (IOException e) {
System.err.println("Failed to load camera drivermode: " + driverModePath.toString());
}
if (driverMode != null) {
driverMode.nickname = "DRIVERMODE";
driverMode.index = -1;
}
return driverMode;
}
private List<CameraCalibrationConfig> loadCalibration() {
List<CameraCalibrationConfig> calibrations = new ArrayList<>();
try {
calibrations =
List.of(
Objects.requireNonNull(
JacksonUtils.deserialize(calibrationPath, CameraCalibrationConfig[].class)));
} catch (Exception e) {
System.err.println("Failed to load camera calibration: " + driverModePath.toString());
}
return calibrations;
}
void saveConfig(CameraJsonConfig config) {
try {
JacksonUtils.serializer(configPath, config, true);
FileUtils.setFilePerms(configPath);
} catch (IOException e) {
System.err.println("Failed to save camera config file: " + configPath.toString());
}
}
void savePipelines(List<CVPipelineSettings> pipelines) {
pipelineConfig.save(pipelines);
}
public void saveDriverMode(CVPipelineSettings driverMode) {
try {
JacksonUtils.serializer(driverModePath, driverMode, true);
FileUtils.setFilePerms(driverModePath);
} catch (IOException e) {
System.err.println("Failed to save camera drivermode file: " + driverModePath.toString());
}
}
public void saveCalibration(List<CameraCalibrationConfig> cal) {
CameraCalibrationConfig[] configs = cal.toArray(new CameraCalibrationConfig[0]);
try {
JacksonUtils.serializer(calibrationPath, configs, true);
FileUtils.setFilePerms(calibrationPath);
} catch (IOException e) {
System.err.println("Failed to save camera calibration file: " + calibrationPath.toString());
}
}
void checkFolder() {
if (!configFolderExists()) {
try {
if (!(new File(configFolderPath.toUri()).mkdirs())) {
System.err.println(
"Failed to create camera config folder: " + configFolderPath.toString());
}
FileUtils.setFilePerms(configFolderPath);
} catch (Exception e) {
System.err.println("Failed to create camera config folder: " + configFolderPath.toString());
}
}
}
private void checkConfig() {
if (!configExists()) {
try {
JacksonUtils.serializer(configPath, preliminaryConfig, true);
FileUtils.setFilePerms(configPath);
} catch (IOException e) {
System.err.println("Failed to create camera config file: " + configPath.toString());
}
}
}
private void checkDriverMode() {
if (!driverModeExists()) {
try {
CVPipelineSettings newDriverModeSettings = new CVPipelineSettings();
newDriverModeSettings.nickname = "DRIVERMODE";
JacksonUtils.serializer(driverModePath, newDriverModeSettings, true);
FileUtils.setFilePerms(driverModePath);
} catch (IOException e) {
System.err.println("Failed to create camera drivermode file: " + driverModePath.toString());
}
}
}
private void checkCalibration() {
if (!calibrationExists()) {
try {
List<CameraCalibrationConfig> calibrations = new ArrayList<>();
JacksonUtils.serializer(calibrationPath, calibrations.toArray(), true);
} catch (IOException e) {
System.err.println(
"Failed to create camera calibration file: " + calibrationPath.toString());
}
}
}
private boolean configFolderExists() {
return Files.exists(configFolderPath);
}
private boolean configExists() {
return configFolderExists() && Files.exists(configPath);
}
private boolean driverModeExists() {
return configFolderExists() && Files.exists(driverModePath);
}
private boolean calibrationExists() {
return configFolderExists() && Files.exists(calibrationPath);
}
}

View File

@@ -1,60 +0,0 @@
package org.photonvision._2.config;
import org.photonvision._2.vision.VisionProcess;
import org.photonvision._2.vision.camera.USBCaptureProperties;
import org.photonvision._2.vision.enums.StreamDivisor;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
public class CameraJsonConfig {
public final double fov;
public final String path;
public final String name;
public final String nickname;
public final double tilt;
public final int videomode;
public final StreamDivisor streamDivisor;
@JsonCreator
public CameraJsonConfig(
@JsonProperty("fov") double fov,
@JsonProperty("path") String path,
@JsonProperty("name") String name,
@JsonProperty("nickname") String nickname,
@JsonProperty("videomode") int videomode,
@JsonProperty("streamDivisor") StreamDivisor streamDivisor,
@JsonProperty("tilt") double tilt) {
this.fov = fov;
this.path = path;
this.name = name;
this.nickname = nickname;
this.videomode = videomode;
this.streamDivisor = streamDivisor;
this.tilt = tilt;
}
public CameraJsonConfig(String path, String name) {
this.fov = USBCaptureProperties.DEFAULT_FOV;
this.path = path;
this.name = name;
this.nickname = name;
this.videomode = 0;
this.streamDivisor = StreamDivisor.NONE;
this.tilt = 0;
}
public static CameraJsonConfig fromVisionProcess(VisionProcess process) {
USBCaptureProperties camProps = process.getCamera().getProperties();
int videomode = camProps.getCurrentVideoModeIndex();
StreamDivisor streamDivisor = process.cameraStreamer.getDivisor();
double tilt = process.getCamera().getProperties().getTilt().getDegrees();
return new CameraJsonConfig(
camProps.getFOV(),
camProps.path,
camProps.name,
camProps.getNickname(),
videomode,
streamDivisor,
tilt);
}
}

View File

@@ -1,128 +0,0 @@
package org.photonvision._2.config;
import org.photonvision._2.util.ProgramDirectoryUtilities;
import org.photonvision._2.vision.pipeline.CVPipelineSettings;
import org.photonvision.common.util.Platform;
import org.photonvision.common.util.ShellExec;
import org.photonvision.common.util.file.FileUtils;
import org.photonvision.common.util.file.JacksonUtils;
import com.chameleonvision.common.util.jackson.JacksonUtils;
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.LinkedHashMap;
import java.util.List;
public class ConfigManager {
private ConfigManager() {}
public static final Path SettingsPath =
Paths.get(ProgramDirectoryUtilities.getProgramDirectory(), "settings");
private static final Path settingsFilePath = Paths.get(SettingsPath.toString(), "settings.json");
private static final LinkedHashMap<String, CameraConfig> cameraConfigs = new LinkedHashMap<>();
public static GeneralSettings settings = new GeneralSettings();
private static boolean settingsFolderExists() {
return Files.exists(SettingsPath);
}
private static boolean settingsFileExists() {
return settingsFolderExists() && Files.exists(settingsFilePath);
}
private static void checkSettingsFolder() {
if (!settingsFolderExists()) {
try {
if (!(new File(SettingsPath.toUri()).mkdirs())) {
System.err.println("Failed to create settings folder: " + SettingsPath.toString());
}
Files.createDirectory(SettingsPath);
if (!Platform.CurrentPlatform.isWindows()) {
new ShellExec().executeBashCommand("sudo chmod -R 0777 " + SettingsPath.toString());
}
} catch (IOException e) {
if (!(e instanceof java.nio.file.FileAlreadyExistsException)) e.printStackTrace();
}
}
}
private static void checkSettingsFile() {
boolean settingsFileEmpty =
settingsFileExists() && new File(settingsFilePath.toString()).length() == 0;
if (settingsFileEmpty || !settingsFileExists()) {
try {
JacksonUtils.serializer(settingsFilePath, settings, true);
FileUtils.setFilePerms(settingsFilePath);
} catch (IOException e) {
e.printStackTrace();
}
} else {
try {
settings = JacksonUtils.deserialize(settingsFilePath, GeneralSettings.class);
} catch (IOException e) {
System.err.println("Failed to load settings.json, using defaults.");
}
}
}
public static void initializeSettings() {
System.out.println("Settings folder: " + SettingsPath.toString());
checkSettingsFolder();
checkSettingsFile();
FileUtils.setAllPerms(SettingsPath);
}
private static void saveSettingsFile() {
try {
JacksonUtils.serializer(settingsFilePath, settings, true);
FileUtils.setFilePerms(settingsFilePath);
} catch (IOException e) {
System.err.println("Failed to save settings.json!");
}
}
public static void saveGeneralSettings() {
checkSettingsFolder();
saveSettingsFile();
}
public static List<FullCameraConfiguration> initializeCameras(
List<CameraJsonConfig> preliminaryConfigs) {
List<FullCameraConfiguration> configList = new ArrayList<>();
checkSettingsFolder();
// loop over all the camera names and try to create settings folders for it
for (CameraJsonConfig preliminaryConfig : preliminaryConfigs) {
CameraConfig cameraConfiguration = new CameraConfig(preliminaryConfig);
cameraConfigs.put(preliminaryConfig.name, cameraConfiguration);
FullCameraConfiguration camJsonConfig = cameraConfiguration.load();
configList.add(camJsonConfig);
}
return configList;
}
public static void saveCameraConfig(String cameraName, CameraJsonConfig config) {
var camConf = cameraConfigs.get(cameraName);
camConf.saveConfig(config);
}
public static void saveCameraPipelines(String cameraName, List<CVPipelineSettings> pipelines) {
var camConf = cameraConfigs.get(cameraName);
camConf.savePipelines(pipelines);
}
public static void saveCameraDriverMode(String cameraName, CVPipelineSettings driverMode) {
var camConf = cameraConfigs.get(cameraName);
camConf.saveDriverMode(driverMode);
}
}

View File

@@ -1,25 +0,0 @@
package org.photonvision._2.config;
import org.photonvision._2.vision.pipeline.CVPipelineSettings;
import java.util.List;
public class FullCameraConfiguration {
public final CameraJsonConfig cameraConfig;
public final List<CVPipelineSettings> pipelines;
public final CVPipelineSettings driverMode;
public final List<CameraCalibrationConfig> calibration;
public final CameraConfig fileConfig;
FullCameraConfiguration(
CameraJsonConfig cameraConfig,
List<CVPipelineSettings> pipelines,
CVPipelineSettings driverMode,
List<CameraCalibrationConfig> calibration,
CameraConfig fileConfig) {
this.cameraConfig = cameraConfig;
this.pipelines = pipelines;
this.driverMode = driverMode;
this.calibration = calibration;
this.fileConfig = fileConfig;
}
}

View File

@@ -1,14 +0,0 @@
package org.photonvision._2.config;
import org.photonvision.common.networking.NetworkMode;
public class GeneralSettings {
public int teamNumber = 1577;
public NetworkMode connectionType = NetworkMode.DHCP;
public String ip = "";
public String gateway = "";
public String netmask = "";
public String hostname = "Chameleon-vision";
public String currentCamera = "";
public Integer currentPipeline = null;
}

View File

@@ -1,152 +0,0 @@
package org.photonvision._2.config;
import org.photonvision._2.config.serializers.StandardCVPipelineSettingsDeserializer;
import org.photonvision._2.config.serializers.StandardCVPipelineSettingsSerializer;
import org.photonvision._2.vision.pipeline.CVPipelineSettings;
import org.photonvision._2.vision.pipeline.impl.StandardCVPipelineSettings;
import org.photonvision.common.util.file.FileUtils;
import com.chameleonvision.common.util.jackson.JacksonUtils;
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;
public class PipelineConfig {
private final CameraConfig cameraConfig;
/**
* Construct a new PipelineConfig
*
* @param cameraConfig the CameraConfig (parent folder, kinda?)
*/
PipelineConfig(CameraConfig cameraConfig) {
this.cameraConfig = cameraConfig;
}
private void checkFolder() {
if (!(new File(cameraConfig.pipelineFolderPath.toUri()).mkdirs())) {
if (Files.notExists(cameraConfig.pipelineFolderPath)) {
System.err.println("Failed to create pipelines folder.");
}
}
try {
FileUtils.setFilePerms(cameraConfig.pipelineFolderPath);
} catch (IOException e) {
// ignored
}
}
private File[] getPipelineFiles() {
return new File(cameraConfig.pipelineFolderPath.toUri()).listFiles();
}
private boolean folderHasPipelines() {
File[] folderContents = getPipelineFiles();
if (folderContents == null) return false;
return folderContents.length > 0;
}
void check() {
cameraConfig.checkFolder();
checkFolder();
// Check if there's at least one pipe
if (!folderHasPipelines()) {
save(new StandardCVPipelineSettings());
}
}
private Path getPipelinePath(CVPipelineSettings setting) {
String pipelineName = setting.nickname.replace(' ', '_');
String fullFileName = pipelineName + ".json";
return Path.of(cameraConfig.pipelineFolderPath.toString(), fullFileName);
}
private boolean pipelineExists(CVPipelineSettings setting) {
return Files.exists(getPipelinePath(setting));
}
public void save(CVPipelineSettings settings) {
var path = getPipelinePath(settings);
if (settings instanceof StandardCVPipelineSettings) {
try {
JacksonUtils.serialize(
path,
(StandardCVPipelineSettings) settings,
StandardCVPipelineSettings.class,
new StandardCVPipelineSettingsSerializer(),
true);
FileUtils.setFilePerms(path);
} catch (IOException e) {
e.printStackTrace();
}
} else {
try {
JacksonUtils.serializer(path, settings, true);
FileUtils.setFilePerms(path);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void save(List<CVPipelineSettings> settings) {
for (CVPipelineSettings setting : settings) {
save(setting);
}
}
public void delete(CVPipelineSettings setting) {
if (pipelineExists(setting)) {
try {
Files.delete(getPipelinePath(setting));
} catch (IOException e) {
System.err.println("Failed to delete pipeline!");
}
}
}
public CVPipelineSettings rename(CVPipelineSettings setting, String newName) {
if (pipelineExists(setting)) {
delete(setting);
setting.nickname = newName;
save(setting);
} else {
setting.nickname = newName;
save(setting);
}
return setting;
}
public List<CVPipelineSettings> load() {
check(); // TODO: this ensures there will be a default pipeline. is the check later necessary?
File[] pipelineFiles = getPipelineFiles();
List<CVPipelineSettings> deserializedList = new ArrayList<>();
if (pipelineFiles == null || pipelineFiles.length < 1) {
// TODO handle no pipelines to load
System.err.println("no pipes to load! loading default");
} else {
for (File pipelineFile : pipelineFiles) {
try {
var pipe =
JacksonUtils.deserialize(
Paths.get(pipelineFile.getPath()),
StandardCVPipelineSettings.class,
new StandardCVPipelineSettingsDeserializer());
deserializedList.add(pipe);
} catch (IOException e) {
System.err.println("couldn't load cvpipeline2d");
}
}
}
return deserializedList;
}
}

View File

@@ -1,138 +0,0 @@
package org.photonvision._2.config.serializers;
import org.photonvision.common.util.numbers.DoubleCouple;
import org.photonvision.common.util.numbers.IntegerCouple;
import com.fasterxml.jackson.core.JsonProcessingException;
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 java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.opencv.core.MatOfPoint3f;
import org.opencv.core.Point3;
public abstract class BaseDeserializer<T> extends StdDeserializer<T> {
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("");
}
IntegerCouple getNumberCouple(String name, IntegerCouple defaultValue)
throws JsonProcessingException {
JsonNode node = baseNode.get(name);
if (nodeGood(node)) {
List<Integer> mapped = mapper.readValue(node.toString(), numberListColType);
return new IntegerCouple(mapped.get(0), mapped.get(1));
}
return defaultValue;
}
DoubleCouple getNumberCouple(String name, DoubleCouple defaultValue)
throws JsonProcessingException {
JsonNode node = baseNode.get(name);
if (nodeGood(node)) {
List<Double> mapped = mapper.readValue(node.toString(), numberListColType);
return new DoubleCouple(mapped.get(0), mapped.get(1));
}
return defaultValue;
}
List<Number> getNumberList(String name, List<Number> 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 extends Enum<E>> E getEnum(String name, Class<E> 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<List<Number>> numberList = mapper.readValue(node.toString(), pointListColType);
List<Point3> point3List = new ArrayList<>();
for (List<Number> 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;
}
}

View File

@@ -1,49 +0,0 @@
package org.photonvision._2.config.serializers;
import org.photonvision.common.util.numbers.NumberCouple;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;
import java.util.List;
import org.opencv.core.MatOfPoint3f;
import org.opencv.core.Point3;
public abstract class BaseSerializer<T> extends StdSerializer<T> {
protected BaseSerializer(Class<T> t) {
super(t);
}
JsonGenerator generator;
<N extends NumberCouple> void writeNumberCoupleAsNumberArray(String name, N couple)
throws IOException {
generator.writeArrayFieldStart(name);
generator.writeObject(couple.getFirst());
generator.writeObject(couple.getSecond());
generator.writeEndArray();
}
void writeNumberListAsNumberArray(String name, List<Number> list) throws IOException {
generator.writeArrayFieldStart(name);
for (Number i : list) {
generator.writeObject(i);
}
generator.writeEndArray();
}
<E extends Enum<E>> void writeEnum(String name, E num) throws IOException {
generator.writeFieldName(name);
generator.writeString(num.name());
}
void writeMatOfPoint3f(String name, MatOfPoint3f mat) throws IOException {
List<Point3> point3List = mat.toList();
generator.writeArrayFieldStart(name);
for (Point3 point3 : point3List) {
double[] tmp = {point3.x, point3.y, point3.z};
generator.writeObject(tmp);
}
generator.writeEndArray();
}
}

View File

@@ -1,86 +0,0 @@
package org.photonvision._2.config.serializers;
import com.chameleonvision._2.vision.enums.*;
import org.photonvision._2.vision.enums.*;
import org.photonvision._2.vision.pipeline.impl.StandardCVPipelineSettings;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import java.io.IOException;
public class StandardCVPipelineSettingsDeserializer
extends BaseDeserializer<StandardCVPipelineSettings> {
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 = getNumberCouple("hue", pipeline.hue);
pipeline.saturation = getNumberCouple("saturation", pipeline.saturation);
pipeline.value = getNumberCouple("value", pipeline.value);
pipeline.erode = getBoolean("erode", pipeline.erode);
pipeline.dilate = getBoolean("dilate", pipeline.dilate);
pipeline.area = getNumberCouple("area", pipeline.area);
pipeline.ratio = getNumberCouple("ratio", pipeline.ratio);
pipeline.extent = getNumberCouple("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 = getNumberCouple("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);
pipeline.accuracy = getDouble("accuracy", pipeline.accuracy.doubleValue());
return pipeline;
}
}

View File

@@ -1,84 +0,0 @@
package org.photonvision._2.config.serializers;
import org.photonvision._2.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<StandardCVPipelineSettings> {
public StandardCVPipelineSettingsSerializer() {
this(null);
}
private StandardCVPipelineSettingsSerializer(Class<StandardCVPipelineSettings> 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);
writeNumberCoupleAsNumberArray("hue", pipeline.hue);
writeNumberCoupleAsNumberArray("saturation", pipeline.saturation);
writeNumberCoupleAsNumberArray("value", pipeline.value);
gen.writeBooleanField("erode", pipeline.erode);
gen.writeBooleanField("dilate", pipeline.dilate);
writeNumberCoupleAsNumberArray("area", pipeline.area);
writeNumberCoupleAsNumberArray("ratio", pipeline.ratio);
writeNumberCoupleAsNumberArray("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
writeNumberCoupleAsNumberArray("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.writeNumberField("accuracy", pipeline.accuracy.doubleValue());
gen.writeEndObject();
}
}

View File

@@ -1,101 +0,0 @@
package org.photonvision._2.network;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.io.FileUtils;
public class LinuxNetworking extends SysNetworking {
private static final String PATH = "/etc/dhcpcd.conf";
@Override
public boolean setDHCP() {
File dhcpConf = new File(PATH);
if (dhcpConf.exists()) {
try {
List<String> lines = FileUtils.readLines(dhcpConf, StandardCharsets.UTF_8);
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
if (line.startsWith("interface " + networkInterface.name)) {
lines.remove(i);
for (int j = i; j < lines.size(); j++) {
String subInterface = lines.get(j);
if (subInterface.contains("static ip_address")
|| subInterface.contains("static routers")) {
lines.remove(j);
j--;
}
if (subInterface.contains("interface")) {
break;
}
}
FileUtils.writeLines(dhcpConf, lines);
return true;
}
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
} else {
System.err.println("dhcpcd5 is not installed cant set ip");
return false;
}
return true;
}
@Override
public boolean setHostname(String newHostname) {
String[] setHostnameArgs = {"set-hostname", newHostname};
try {
var setHostnameRetCode = shell.execute("hostnamectl", setHostnameArgs);
return setHostnameRetCode == 0;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@Override
public boolean setStatic(String ipAddress, String netmask, String gateway) {
setDHCP(); // clean up old static interface
File dhcpConf = new File(PATH);
try {
List<String> lines = FileUtils.readLines(dhcpConf, StandardCharsets.UTF_8);
lines.add("interface " + networkInterface.name);
InetAddress iNetMask = InetAddress.getByName(netmask);
int prefix = NetmaskToCIDR.convertNetmaskToCIDR(iNetMask);
lines.add("static ip_address=" + ipAddress + "/" + prefix);
lines.add("static routers=" + gateway);
FileUtils.writeLines(dhcpConf, lines);
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
@Override
public List<java.net.NetworkInterface> getNetworkInterfaces() throws SocketException {
List<java.net.NetworkInterface> netInterfaces;
try {
netInterfaces = Collections.list(java.net.NetworkInterface.getNetworkInterfaces());
} catch (SocketException e) {
return null;
}
List<java.net.NetworkInterface> goodInterfaces = new ArrayList<>();
for (var netInterface : netInterfaces) {
if (netInterface.getDisplayName().contains("lo")) continue;
if (!netInterface.isUp()) continue;
goodInterfaces.add(netInterface);
}
return goodInterfaces;
}
}

View File

@@ -1,30 +0,0 @@
package org.photonvision._2.network;
import java.net.InetAddress;
public class NetmaskToCIDR {
// code belongs to
// https://stackoverflow.com/questions/19531411/calculate-cidr-from-a-given-netmask-java
public static int convertNetmaskToCIDR(InetAddress netmask) {
byte[] netmaskBytes = netmask.getAddress();
int cidr = 0;
boolean zero = false;
for (byte b : netmaskBytes) {
int mask = 0x80;
for (int i = 0; i < 8; i++) {
int result = b & mask;
if (result == 0) {
zero = true;
} else if (zero) {
throw new IllegalArgumentException("Invalid netmask.");
} else {
cidr++;
}
mask >>>= 1;
}
}
return cidr;
}
}

View File

@@ -1,59 +0,0 @@
package org.photonvision._2.network;
import java.net.InterfaceAddress;
@SuppressWarnings("WeakerAccess")
public class NetworkInterface {
public final String name;
public final String displayName;
public final String IPAddress;
public final String Netmask;
public final String Gateway;
public final String Broadcast;
public NetworkInterface(java.net.NetworkInterface inetface, InterfaceAddress ifaceAddress) {
name = inetface.getName();
displayName = inetface.getDisplayName();
var inetAddress = ifaceAddress.getAddress();
IPAddress = inetAddress.getHostAddress();
Netmask = getIPv4LocalNetMask(ifaceAddress);
// TODO: (low) hack to "get" gateway, this is gross and bad, pls fix
var splitIPAddr = IPAddress.split("\\.");
splitIPAddr[3] = "1";
Gateway = String.join(".", splitIPAddr);
splitIPAddr[3] = "255";
Broadcast = String.join(".", splitIPAddr);
}
private static String getIPv4LocalNetMask(InterfaceAddress interfaceAddress) {
var netPrefix = interfaceAddress.getNetworkPrefixLength();
try {
// Since this is for IPv4, it's 32 bits, so set the sign value of
// the int to "negative"...
int shiftby = (1 << 31);
// For the number of bits of the prefix -1 (we already set the sign bit)
for (int i = netPrefix - 1; i > 0; i--) {
// Shift the sign right... Java makes the sign bit sticky on a shift...
// So no need to "set it back up"...
shiftby = (shiftby >> 1);
}
// Transform the resulting value in xxx.xxx.xxx.xxx format, like if
/// it was a standard address...
// Return the address thus created...
return ((shiftby >> 24) & 255)
+ "."
+ ((shiftby >> 16) & 255)
+ "."
+ ((shiftby >> 8) & 255)
+ "."
+ (shiftby & 255);
// return InetAddress.getByName(maskString);
} catch (Exception e) {
e.printStackTrace();
}
// Something went wrong here...
return null;
}
}

View File

@@ -1,104 +0,0 @@
package org.photonvision._2.network;
import org.photonvision._2.config.ConfigManager;
import org.photonvision.common.util.Platform;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
public class NetworkManager {
private NetworkManager() {}
private static SysNetworking networking;
private static boolean isManaged = false;
public static void initialize(boolean manage) {
isManaged = manage;
if (!isManaged) {
return;
}
Platform platform = Platform.CurrentPlatform;
if (platform.isLinux()) {
networking = new LinuxNetworking();
} else if (platform.isWindows()) {
// networking = new WindowsNetworking();
System.out.println("Windows networking is not yet supported. Running unmanaged.");
return;
}
if (networking == null) {
throw new RuntimeException("Failed to detect platform!");
}
List<java.net.NetworkInterface> interfaces = new ArrayList<>();
List<NetworkInterface> goodInterfaces = new ArrayList<>();
try {
interfaces = networking.getNetworkInterfaces();
} catch (SocketException e) {
e.printStackTrace();
}
var teamBytes = NetworkManager.GetTeamNumberIPBytes(ConfigManager.settings.teamNumber);
if (interfaces.size() > 0) {
for (var inetface : interfaces) {
for (var inetfaceAddr : inetface.getInterfaceAddresses()) {
var rawAddr = inetfaceAddr.getAddress().getAddress();
if (rawAddr.length > 4) continue;
if (rawAddr[1] == teamBytes[0] && rawAddr[2] == teamBytes[1]) {
goodInterfaces.add(new NetworkInterface(inetface, inetfaceAddr));
}
}
}
if (goodInterfaces.size() == 0) {
isManaged = false;
System.err.println("No valid network interfaces found! Staying unmanaged.");
return;
}
NetworkInterface botInterface = goodInterfaces.get(0);
networking.setNetworkInterface(botInterface);
} else {
isManaged = false;
System.err.println("No valid network interfaces found! Staying unmanaged.");
}
}
private static byte[] GetTeamNumberIPBytes(int teamNumber) {
return new byte[] {(byte) (teamNumber / 100), (byte) (teamNumber % 100)};
}
private static boolean setDHCP() {
if (!isManaged) {
return true;
}
return networking.setDHCP();
}
private static boolean setStatic(String ipAddress, String netmask, String gateway) {
if (!isManaged) {
return true;
}
return networking.setStatic(ipAddress, netmask, gateway);
}
public static boolean setHostname(String hostname) {
if (!isManaged) {
return true;
}
return networking.setHostname(hostname);
}
public static boolean setNetwork(boolean isStatic, String ip, String netmask, String gateway) {
if (isStatic) {
return setStatic(ip, netmask, gateway);
} else {
return setDHCP();
}
}
}

View File

@@ -1,38 +0,0 @@
package org.photonvision._2.network;
import org.photonvision.common.util.ShellExec;
import java.io.IOException;
import java.net.SocketException;
import java.util.List;
public abstract class SysNetworking {
NetworkInterface networkInterface;
ShellExec shell = new ShellExec(true, true);
public String getHostname() {
try {
var retCode = shell.execute("hostname", null, true);
if (retCode == 0) {
while (!shell.isOutputCompleted()) {}
return shell.getOutput();
} else {
return null;
}
} catch (IOException e) {
return null;
}
}
public void setNetworkInterface(NetworkInterface networkInterface) {
this.networkInterface = networkInterface;
}
public abstract boolean setDHCP();
public abstract boolean setHostname(String hostname);
public abstract boolean setStatic(String ipAddress, String netmask, String gateway);
public abstract List<java.net.NetworkInterface> getNetworkInterfaces() throws SocketException;
}

View File

@@ -1,57 +0,0 @@
package org.photonvision._2.network;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class WindowsNetworking extends SysNetworking {
@Override
public boolean setDHCP() {
return false;
}
@Override
public boolean setHostname(String newHostname) {
var currentHostname = getHostname();
if (getHostname() == null) {
return false;
}
String command =
String.format(
"wmic computersystem where name=\"%s\" call rename name=\"%s\"",
currentHostname, newHostname);
try {
var process = Runtime.getRuntime().exec(command);
var returnCode = process.waitFor();
return returnCode == 0;
} catch (Exception e) {
return false;
}
}
@Override
public boolean setStatic(String ipAddress, String netmask, String gateway) {
return false;
}
@Override
public List<java.net.NetworkInterface> getNetworkInterfaces() throws SocketException {
var netInterfaces = Collections.list(java.net.NetworkInterface.getNetworkInterfaces());
List<java.net.NetworkInterface> goodInterfaces = new ArrayList<>();
for (var netInterface : netInterfaces) {
if (netInterface.getDisplayName().toLowerCase().contains("bluetooth")) continue;
if (netInterface.getDisplayName().toLowerCase().contains("virtual")) continue;
if (netInterface.getDisplayName().toLowerCase().contains("loopback")) continue;
if (!netInterface.isUp()) continue;
goodInterfaces.add(netInterface);
}
return goodInterfaces;
}
}

View File

@@ -1,54 +0,0 @@
package org.photonvision._2.util;
import edu.wpi.cscore.VideoMode;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.nio.file.Path;
import java.util.HashMap;
public class Helpers {
// TODO: MOVE
public static HashMap VideoModeToHashMap(VideoMode videoMode) {
return new HashMap<String, Object>() {
{
put("width", videoMode.width);
put("height", videoMode.height);
put("fps", videoMode.fps);
put("pixelFormat", videoMode.pixelFormat.toString());
}
};
}
// TODO: MOVE
private static final String kServicePath = "/etc/systemd/system/chameleonVision.service";
private static final String kServiceString =
"[Unit]\n"
+ "Description=chameleon vision\n"
+ "\n"
+ "[Service]\n"
+ "ExecStart=/usr/bin/java -jar %s \n"
+ "StandardOutput=file:/var/log/chameleon.out.txt\n"
+ "StandardError=file:/var/log/chameleon.err.txt\n"
+ "Type=simple\n"
+ "WorkingDirectory=/usr/local/bin\n"
+ "\n"
+ "[Install]\n"
+ "WantedBy=multi-user.target\n"
+ "\n";
public static void setService(Path filePath) throws IOException, InterruptedException {
String newService = String.format(kServiceString, filePath.toString());
File file = new File(kServicePath);
if (file.exists()) {
file.delete();
}
Writer writer = new FileWriter(file, false);
writer.write(newService);
writer.close();
Process p = Runtime.getRuntime().exec("systemctl enable chameleonVision.service");
p.waitFor();
}
}

View File

@@ -1,46 +0,0 @@
package org.photonvision._2.util;
import java.io.File;
import java.net.URISyntaxException;
public class ProgramDirectoryUtilities {
private static String getJarName() {
return new File(
ProgramDirectoryUtilities.class
.getProtectionDomain()
.getCodeSource()
.getLocation()
.getPath())
.getName();
}
private static boolean runningFromJAR() {
String jarName = getJarName();
return jarName.contains(".jar");
}
public static String getProgramDirectory() {
if (runningFromJAR()) {
return getCurrentJARDirectory();
} else {
return System.getProperty("user.dir");
}
}
private static String getCurrentJARDirectory() {
try {
return new File(
ProgramDirectoryUtilities.class
.getProtectionDomain()
.getCodeSource()
.getLocation()
.toURI()
.getPath())
.getParent();
} catch (URISyntaxException exception) {
exception.printStackTrace();
}
return null;
}
}

View File

@@ -1,220 +0,0 @@
package org.photonvision._2.vision;
import com.chameleonvision._2.config.*;
import org.photonvision._2.util.Helpers;
import org.photonvision._2.vision.camera.USBCameraCapture;
import org.photonvision._2.vision.pipeline.CVPipeline;
import org.photonvision._2.vision.pipeline.CVPipelineSettings;
import org.photonvision.common.util.Platform;
import edu.wpi.cscore.UsbCamera;
import edu.wpi.cscore.UsbCameraInfo;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.opencv.videoio.VideoCapture;
import org.photonvision._2.config.CameraConfig;
import org.photonvision._2.config.CameraJsonConfig;
import org.photonvision._2.config.ConfigManager;
import org.photonvision._2.config.FullCameraConfiguration;
@SuppressWarnings("rawtypes")
public class VisionManager {
private VisionManager() {}
private static final LinkedHashMap<String, UsbCameraInfo> usbCameraInfosByCameraName =
new LinkedHashMap<>();
private static final LinkedList<FullCameraConfiguration> loadedCameraConfigs = new LinkedList<>();
private static final LinkedList<VisionProcessManageable> visionProcesses = new LinkedList<>();
@SuppressWarnings("WeakerAccess")
private static class VisionProcessManageable {
public final int index;
public final String name;
public final VisionProcess visionProcess;
public VisionProcessManageable(int index, String name, VisionProcess visionProcess) {
this.index = index;
this.name = name;
this.visionProcess = visionProcess;
}
}
private static VisionProcess currentUIVisionProcess;
public static boolean initializeSources() {
int suffix = 0;
for (UsbCameraInfo info : UsbCamera.enumerateUsbCameras()) {
VideoCapture cap = new VideoCapture(info.dev);
if (cap.isOpened()) {
cap.release();
// Filter non-ascii characters because ext4 doesn't play nice with unicode in directory
// names
String name = info.name.replaceAll("[^\\x00-\\x7F]", "");
while (usbCameraInfosByCameraName.containsKey(name)) {
suffix++;
name = String.format("%s (%d)", name, suffix);
}
usbCameraInfosByCameraName.put(name, info);
}
}
if (usbCameraInfosByCameraName.isEmpty()) {
return false;
}
System.out.printf("[VisionManager] Found %s cameras!\n", usbCameraInfosByCameraName.size());
// load the config
List<CameraJsonConfig> preliminaryConfigs = new ArrayList<>();
usbCameraInfosByCameraName.forEach(
(suffixedName, cameraInfo) -> {
String truePath;
if (Platform.CurrentPlatform.isWindows()) {
truePath = cameraInfo.path;
} else {
truePath =
Arrays.stream(cameraInfo.otherPaths)
.filter(x -> x.contains("/dev/v4l/by-path"))
.findFirst()
.orElse(cameraInfo.path);
}
preliminaryConfigs.add(new CameraJsonConfig(truePath, suffixedName));
});
loadedCameraConfigs.addAll(ConfigManager.initializeCameras(preliminaryConfigs));
System.out.printf("[VisionManager] Loaded %s cameras!\n", loadedCameraConfigs.size());
return true;
}
public static boolean initializeProcesses() {
for (int i = 0; i < loadedCameraConfigs.size(); i++) {
FullCameraConfiguration config = loadedCameraConfigs.get(i);
CameraJsonConfig cameraJsonConfig = config.cameraConfig;
USBCameraCapture camera = new USBCameraCapture(config);
VisionProcess process = new VisionProcess(camera, config);
process.pipelineManager.driverModePipeline.settings = config.driverMode;
visionProcesses.add(new VisionProcessManageable(i, cameraJsonConfig.name, process));
}
currentUIVisionProcess = getVisionProcessByIndex(0);
ConfigManager.settings.currentCamera = visionProcesses.get(0).name;
System.out.printf(
"[VisionManager] Loaded %s vision processes! Current process: %s\n",
visionProcesses.size(), visionProcesses.get(0).name);
return true;
}
public static void startProcesses() {
visionProcesses.forEach((vpm) -> vpm.visionProcess.start());
}
public static VisionProcess getCurrentUIVisionProcess() {
return currentUIVisionProcess;
}
public static CameraConfig getCurrentCameraConfig() {
return getCameraConfig(currentUIVisionProcess);
}
public static CameraConfig getCameraConfig(VisionProcess process) {
String cameraName = process.getCamera().getProperties().name;
return Objects.requireNonNull(
loadedCameraConfigs.stream()
.filter(x -> x.cameraConfig.name.equals(cameraName))
.findFirst()
.orElse(null))
.fileConfig;
}
public static void setCurrentProcessByIndex(int processIndex) {
if (processIndex > visionProcesses.size() - 1) {
return;
}
currentUIVisionProcess = getVisionProcessByIndex(processIndex);
ConfigManager.settings.currentCamera = visionProcesses.get(processIndex).name;
}
public static VisionProcess getVisionProcessByIndex(int processIndex) {
if (processIndex > visionProcesses.size() - 1) {
return null;
}
VisionProcessManageable vpm =
visionProcesses.stream()
.filter(manageable -> manageable.index == processIndex)
.findFirst()
.orElse(null);
return vpm != null ? vpm.visionProcess : null;
}
public static List<String> getAllCameraNicknames() {
return visionProcesses.stream()
.map(vpm -> vpm.visionProcess.getCamera().getProperties().getNickname())
.collect(Collectors.toList());
}
public static List<String> getCurrentCameraPipelineNicknames() {
return currentUIVisionProcess.pipelineManager.pipelines.stream()
.map(cvPipeline -> cvPipeline.settings.nickname)
.collect(Collectors.toList());
}
public static void saveAllCameras() {
visionProcesses.forEach(
(vpm) -> {
VisionProcess process = vpm.visionProcess;
String cameraName = process.getCamera().getProperties().name;
Stream<CVPipeline> pipelineStream = process.pipelineManager.pipelines.stream();
List<CVPipelineSettings> pipelines =
process.pipelineManager.pipelines.stream()
.map(cvPipeline -> cvPipeline.settings)
.collect(Collectors.toList());
CVPipelineSettings driverMode = process.getDriverModeSettings();
CameraJsonConfig config = CameraJsonConfig.fromVisionProcess(process);
ConfigManager.saveCameraPipelines(cameraName, pipelines);
ConfigManager.saveCameraDriverMode(cameraName, driverMode);
ConfigManager.saveCameraConfig(cameraName, config);
});
}
private static String getCurrentCameraName() {
return currentUIVisionProcess.getCamera().getProperties().name;
}
public static void saveCurrentCameraSettings() {
CameraJsonConfig config = CameraJsonConfig.fromVisionProcess(currentUIVisionProcess);
ConfigManager.saveCameraConfig(getCurrentCameraName(), config);
}
public static void saveCurrentCameraPipelines() {
currentUIVisionProcess.pipelineManager.saveAllPipelines();
}
public static void saveCurrentCameraDriverMode() {
currentUIVisionProcess.pipelineManager.saveDriverModeConfig();
}
private static List<HashMap> getCameraResolutionList(USBCameraCapture capture) {
return capture.getProperties().getVideoModes().stream()
.map(Helpers::VideoModeToHashMap)
.collect(Collectors.toList());
}
public static List<HashMap> getCurrentCameraResolutionList() {
return getCameraResolutionList(currentUIVisionProcess.getCamera());
}
public static int getCurrentUIVisionProcessIndex() {
VisionProcessManageable vpm =
visionProcesses.stream()
.filter(v -> v.visionProcess == currentUIVisionProcess)
.findFirst()
.orElse(null);
return vpm != null ? vpm.index : -1;
}
}

View File

@@ -1,420 +0,0 @@
package org.photonvision._2.vision;
import org.photonvision._2.config.CameraCalibrationConfig;
import org.photonvision._2.config.CameraConfig;
import org.photonvision._2.config.ConfigManager;
import org.photonvision._2.config.FullCameraConfiguration;
import org.photonvision._2.vision.camera.CameraStreamer;
import org.photonvision._2.vision.camera.USBCameraCapture;
import org.photonvision._2.vision.pipeline.CVPipelineResult;
import org.photonvision._2.vision.pipeline.CVPipelineSettings;
import org.photonvision._2.vision.pipeline.PipelineManager;
import org.photonvision._2.vision.pipeline.impl.DriverVisionPipeline;
import org.photonvision._2.vision.pipeline.impl.StandardCVPipeline;
import org.photonvision._2.vision.pipeline.impl.StandardCVPipelineSettings;
import org.photonvision._2.web.SocketHandler;
import org.photonvision.common.datatransfer.networktables.NetworkTablesManager;
import org.photonvision.common.scripting.ScriptEventType;
import org.photonvision.common.scripting.ScriptManager;
import org.photonvision.common.util.math.MathUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.wpi.cscore.VideoMode;
import edu.wpi.first.networktables.*;
import edu.wpi.first.wpilibj.geometry.Pose2d;
import edu.wpi.first.wpiutil.CircularBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.tuple.Pair;
import org.opencv.core.Mat;
@SuppressWarnings("rawtypes")
public class VisionProcess {
public final USBCameraCapture cameraCapture;
private final VisionProcessRunnable visionRunnable;
private final CameraConfig fileConfig;
public final CameraStreamer cameraStreamer;
public PipelineManager pipelineManager;
private volatile CVPipelineResult lastPipelineResult;
// network table stuff
private final NetworkTable defaultTable;
private NetworkTableInstance tableInstance;
private NetworkTableEntry ntPipelineEntry;
public NetworkTableEntry ntDriverModeEntry;
private int ntDriveModeListenerID;
private int ntPipelineListenerID;
private NetworkTableEntry ntYawEntry;
private NetworkTableEntry ntPitchEntry;
private NetworkTableEntry ntAuxListEntry;
private NetworkTableEntry ntAreaEntry;
private NetworkTableEntry ntLatencyEntry;
private NetworkTableEntry ntValidEntry;
private NetworkTableEntry ntPoseEntry;
private NetworkTableEntry ntFittedHeightEntry;
private NetworkTableEntry ntFittedWidthEntry;
private NetworkTableEntry ntBoundingHeightEntry;
private NetworkTableEntry ntBoundingWidthEntry;
private NetworkTableEntry ntTargetRotation;
private ObjectMapper objectMapper = new ObjectMapper();
private long lastUIUpdateMs = 0;
VisionProcess(USBCameraCapture cameraCapture, FullCameraConfiguration config) {
this.cameraCapture = cameraCapture;
fileConfig = config.fileConfig;
pipelineManager = new PipelineManager(this, config.pipelines);
// Thread to put frames on the dashboard
this.cameraStreamer =
new CameraStreamer(
cameraCapture,
config.cameraConfig.name,
pipelineManager.getCurrentPipeline().settings.streamDivisor);
// Thread to process vision data
this.visionRunnable = new VisionProcessRunnable();
// network table
defaultTable =
NetworkTableInstance.getDefault()
.getTable("/chameleon-vision/" + cameraCapture.getProperties().getNickname());
}
public void start() {
System.out.printf(
"[%s Process] Creating network table...\n", getCamera().getProperties().getNickname());
initNT(defaultTable);
System.out.printf(
"[%s Process] Starting vision thread...\n", getCamera().getProperties().getNickname());
var visionThread = new Thread(visionRunnable);
visionThread.setName(getCamera().getProperties().name + " - Vision Thread");
visionThread.start();
}
/**
* Removes the old value change listeners calls {@link #initNT}
*
* @param newTable passed to {@link #initNT}
*/
public void resetNT(NetworkTable newTable) {
ntDriverModeEntry.removeListener(ntDriveModeListenerID);
ntPipelineEntry.removeListener(ntPipelineListenerID);
initNT(newTable);
}
public void setCameraNickname(String newName) {
getCamera().getProperties().setNickname(newName);
NetworkTable camTable = NetworkTablesManager.kRootTable.getSubTable(newName);
resetNT(camTable);
}
private void initNT(NetworkTable camTable) {
tableInstance = camTable.getInstance();
ntPipelineEntry = camTable.getEntry("pipeline");
ntDriverModeEntry = camTable.getEntry("driverMode");
ntPitchEntry = camTable.getEntry("targetPitch");
ntYawEntry = camTable.getEntry("targetYaw");
ntAreaEntry = camTable.getEntry("targetArea");
ntLatencyEntry = camTable.getEntry("latency");
ntValidEntry = camTable.getEntry("isValid");
ntAuxListEntry = camTable.getEntry("auxTargets");
ntPoseEntry = camTable.getEntry("targetPose");
ntFittedHeightEntry = camTable.getEntry("targetFittedHeight");
ntFittedWidthEntry = camTable.getEntry("targetFittedWidth");
ntBoundingHeightEntry = camTable.getEntry("targetBoundingHeight");
ntBoundingWidthEntry = camTable.getEntry("targetBoundingWidth");
ntTargetRotation = camTable.getEntry("targetRotation");
ntDriveModeListenerID =
ntDriverModeEntry.addListener(this::setDriverMode, EntryListenerFlags.kUpdate);
ntPipelineListenerID =
ntPipelineEntry.addListener(this::setPipeline, EntryListenerFlags.kUpdate);
ntDriverModeEntry.setBoolean(false);
ntPipelineEntry.setNumber(pipelineManager.getCurrentPipelineIndex());
pipelineManager.ntIndexEntry = ntPipelineEntry;
}
private void setDriverMode(EntryNotification driverModeEntryNotification) {
setDriverMode(driverModeEntryNotification.value.getBoolean());
}
public void setDriverMode(boolean driverMode) {
pipelineManager.setDriverMode(driverMode);
ScriptManager.queueEvent(
driverMode ? ScriptEventType.kEnterDriverMode : ScriptEventType.kExitDriverMode);
SocketHandler.sendFullSettings();
}
/**
* Method called by the nt entry listener to update the next pipeline.
*
* @param notification the notification
*/
private void setPipeline(EntryNotification notification) {
var wantedPipelineIndex = (int) notification.value.getDouble();
if (pipelineManager.pipelines.size() - 1 < wantedPipelineIndex) {
ntPipelineEntry.setDouble(pipelineManager.getCurrentPipelineIndex());
} else {
pipelineManager.setCurrentPipeline(wantedPipelineIndex);
}
}
public void setDriverModeEntry(boolean isDriverMode) {
// if it's null, we haven't even started the program yet, so just return
// otherwise, set it.
if (ntDriverModeEntry != null) {
ntDriverModeEntry.setBoolean(isDriverMode);
}
}
private void updateUI(CVPipelineResult data) {
// 30 "FPS" update rate
long currentMillis = System.currentTimeMillis();
if (currentMillis - lastUIUpdateMs > 1000 / 30) {
lastUIUpdateMs = currentMillis;
if (cameraCapture.getProperties().name.equals(ConfigManager.settings.currentCamera)) {
HashMap<String, Object> WebSend = new HashMap<>();
HashMap<String, Object> point = new HashMap<>();
HashMap<String, Object> pointMap = new HashMap<>();
ArrayList<Object> webTargets = new ArrayList<>();
List<Double> center = new ArrayList<>();
if (data.hasTarget) {
if (data instanceof StandardCVPipeline.StandardCVPipelineResult) {
StandardCVPipeline.StandardCVPipelineResult result =
(StandardCVPipeline.StandardCVPipelineResult) data;
StandardCVPipeline.TrackedTarget bestTarget = result.targets.get(0);
try {
if (((StandardCVPipelineSettings) pipelineManager.getCurrentPipeline().settings)
.multiple) {
for (var target : result.targets) {
pointMap = new HashMap<>();
pointMap.put("pitch", target.pitch);
pointMap.put("yaw", target.yaw);
pointMap.put("area", target.area);
pointMap.put("pose", target.cameraRelativePose);
webTargets.add(pointMap);
}
} else {
pointMap.put("pitch", bestTarget.pitch);
pointMap.put("yaw", bestTarget.yaw);
pointMap.put("area", bestTarget.area);
pointMap.put("pose", bestTarget.cameraRelativePose);
webTargets.add(pointMap);
}
center.add(bestTarget.minAreaRect.center.x);
center.add(bestTarget.minAreaRect.center.y);
} catch (ClassCastException ignored) {
}
} else {
pointMap.put("pitch", null);
pointMap.put("yaw", null);
pointMap.put("area", null);
pointMap.put("pose", new Pose2d());
webTargets.add(pointMap);
center.add(null);
center.add(null);
}
point.put("fps", visionRunnable.fps);
point.put("targets", webTargets);
point.put("rawPoint", center);
} else {
point.put("fps", visionRunnable.fps);
}
WebSend.put("point", point);
SocketHandler.broadcastMessage(WebSend);
}
}
}
private void updateNetworkTableData(CVPipelineResult data) {
ntValidEntry.setBoolean(data.hasTarget);
if (data.hasTarget && !(data instanceof DriverVisionPipeline.DriverPipelineResult)) {
if (data instanceof StandardCVPipeline.StandardCVPipelineResult) {
//noinspection unchecked
List<StandardCVPipeline.TrackedTarget> targets =
(List<StandardCVPipeline.TrackedTarget>) data.targets;
StandardCVPipeline.TrackedTarget bestTarget = targets.get(0);
ntLatencyEntry.setDouble(MathUtils.roundTo(data.processTime * 1e-6, 3));
ntPitchEntry.setDouble(bestTarget.pitch);
ntYawEntry.setDouble(bestTarget.yaw);
ntAreaEntry.setDouble(bestTarget.area);
ntBoundingHeightEntry.setDouble(bestTarget.boundingRect.height);
ntBoundingWidthEntry.setDouble(bestTarget.boundingRect.width);
ntFittedHeightEntry.setDouble(bestTarget.minAreaRect.size.height);
ntFittedWidthEntry.setDouble(bestTarget.minAreaRect.size.width);
ntTargetRotation.setDouble(bestTarget.minAreaRect.angle);
try {
Pose2d targetPose = targets.get(0).cameraRelativePose;
double[] targetArray = {
targetPose.getTranslation().getX(),
targetPose.getTranslation().getY(),
targetPose.getRotation().getDegrees()
};
ntPoseEntry.setDoubleArray(targetArray);
//
// ntPoseEntry.setString(objectMapper.writeValueAsString(targets.get(0).cameraRelativePose));
ntAuxListEntry.setString(
objectMapper.writeValueAsString(
targets.stream()
.map(
it ->
List.of(
it.pitch,
it.yaw,
it.area,
it.boundingRect.width,
it.boundingRect.height,
it.minAreaRect.size.width,
it.minAreaRect.size.height,
it.minAreaRect.angle,
it.cameraRelativePose))
.collect(Collectors.toList())));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
} else {
ntPitchEntry.setDouble(0.0);
ntYawEntry.setDouble(0.0);
ntAreaEntry.setDouble(0.0);
ntLatencyEntry.setDouble(0.0);
ntAuxListEntry.setString("");
}
}
tableInstance.flush();
}
public void setVideoMode(VideoMode newMode) {
cameraCapture.setVideoMode(newMode);
cameraStreamer.setNewVideoMode(newMode);
}
public VideoMode getCurrentVideoMode() {
return cameraCapture.getCurrentVideoMode();
}
public List<VideoMode> getPossibleVideoModes() {
return cameraCapture.getProperties().videoModes;
}
public USBCameraCapture getCamera() {
return cameraCapture;
}
public CVPipelineSettings getDriverModeSettings() {
return pipelineManager.driverModePipeline.settings;
}
public void addCalibration(CameraCalibrationConfig cal) {
cameraCapture.addCalibrationData(cal);
System.out.println("saving to file");
fileConfig.saveCalibration(cameraCapture.getAllCalibrationData());
}
public void setIs3d(Boolean value) {
var settings = pipelineManager.getCurrentPipeline().settings;
if (settings instanceof StandardCVPipelineSettings) {
((StandardCVPipelineSettings) settings).is3D = value;
}
}
public boolean getIs3d() {
var settings = pipelineManager.getCurrentPipeline().settings;
if (settings instanceof StandardCVPipelineSettings) {
return ((StandardCVPipelineSettings) settings).is3D;
}
return false;
}
/** VisionProcessRunnable will process images as quickly as possible */
private class VisionProcessRunnable implements Runnable {
volatile Double fps = 0.0;
private CircularBuffer fpsAveragingBuffer = new CircularBuffer(7);
@Override
public void run() {
var lastUpdateTimeNanos = System.nanoTime();
var lastStreamTimeMs = System.currentTimeMillis();
System.out.printf(
"[%s Process] Vision Process Thread -- first run!\n",
getCamera().getProperties().getNickname());
while (!Thread.interrupted()) {
// blocking call, will block until camera has a new frame.
Pair<Mat, Long> camData = cameraCapture.getFrame();
Mat camFrame = camData.getLeft();
if (camFrame.cols() > 0 && camFrame.rows() > 0) {
CVPipelineResult result = null;
try {
result = pipelineManager.getCurrentPipeline().runPipeline(camFrame);
} catch (Exception e) {
System.err.println(
"Exception in vision process " + getCamera().getProperties().getNickname() + "!");
e.printStackTrace();
}
camFrame.release();
if (result != null) {
result.setTimestamp(camData.getRight());
lastPipelineResult = result;
updateNetworkTableData(lastPipelineResult);
updateUI(lastPipelineResult);
}
}
try {
var currentTime = System.currentTimeMillis();
if ((currentTime - lastStreamTimeMs) / 1000d > 1.0 / 30.0) {
if (lastPipelineResult != null) {
cameraStreamer.runStream(lastPipelineResult.outputMat);
lastStreamTimeMs = currentTime;
lastPipelineResult.outputMat.release();
} else {
System.err.printf(
"[%s Process] Last pipeline result was null!\n",
getCamera().getProperties().getNickname());
}
}
} catch (Exception e) {
// Debug.printInfo("Vision running faster than stream.");
System.err.printf(
"[%s Process] Exception in vision thread!\n",
getCamera().getProperties().getNickname());
e.printStackTrace();
}
var deltaTimeNanos = System.nanoTime() - lastUpdateTimeNanos;
fpsAveragingBuffer.addFirst(1.0 / (deltaTimeNanos * 1E-09));
lastUpdateTimeNanos = System.nanoTime();
fps = getAverageFPS();
}
}
double getAverageFPS() {
var temp = 0.0;
for (int i = 0; i < 7; i++) {
temp += fpsAveragingBuffer.get(i);
}
temp /= 7.0;
return temp;
}
}
}

View File

@@ -1,53 +0,0 @@
package org.photonvision._2.vision.camera;
import org.photonvision._2.config.CameraCalibrationConfig;
import org.photonvision._2.vision.image.CaptureProperties;
import org.photonvision._2.vision.image.ImageCapture;
import edu.wpi.cscore.VideoMode;
import java.util.List;
public interface CameraCapture extends ImageCapture {
CaptureProperties getProperties();
VideoMode getCurrentVideoMode();
/**
* Set the exposure of the camera
*
* @param exposure the new exposure to set the camera to
*/
void setExposure(int exposure);
/**
* Set the brightness of the camera
*
* @param brightness the new brightness to set the camera to
*/
void setBrightness(int brightness);
/**
* Set the video mode (fps and resolution) of the camera
*
* @param mode the desired mode
*/
void setVideoMode(VideoMode mode);
/**
* Set the video mode (fps and resolution) of the camera
*
* @param index the index of the desired mode
*/
void setVideoMode(int index);
/**
* Set the gain of the camera NOTE - Not all cameras support this.
*
* @param gain the new gain to set the camera to
*/
void setGain(int gain);
CameraCalibrationConfig getCurrentCalibrationData();
List<CameraCalibrationConfig> getAllCalibrationData();
}

View File

@@ -1,110 +0,0 @@
package org.photonvision._2.vision.camera;
import org.photonvision._2.vision.enums.StreamDivisor;
import org.photonvision._2.web.SocketHandler;
import edu.wpi.cscore.CvSource;
import edu.wpi.cscore.MjpegServer;
import edu.wpi.cscore.VideoMode;
import edu.wpi.first.cameraserver.CameraServer;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
public class CameraStreamer {
private final CameraCapture cameraCapture;
private final String name;
private StreamDivisor divisor;
private CvSource cvSource;
private final Object streamBufferLock = new Object();
private Mat streamBuffer = new Mat();
private Size size;
public CameraStreamer(CameraCapture cameraCapture, String name, StreamDivisor div) {
this.divisor = div;
this.cameraCapture = cameraCapture;
this.name = name;
this.cvSource =
CameraServer.getInstance()
.putVideo(
name,
cameraCapture.getProperties().getStaticProperties().imageWidth / divisor.value,
cameraCapture.getProperties().getStaticProperties().imageHeight / divisor.value);
//noinspection IntegerDivisionInFloatingPointContext
this.size =
new Size(
cameraCapture.getProperties().getStaticProperties().imageWidth / divisor.value,
cameraCapture.getProperties().getStaticProperties().imageHeight / divisor.value);
setDivisor(divisor, false);
}
public void setDivisor(StreamDivisor newDivisor, boolean updateUI) {
this.divisor = newDivisor;
var camValues = cameraCapture.getProperties();
var newWidth = camValues.getStaticProperties().imageWidth / newDivisor.value;
var newHeight = camValues.getStaticProperties().imageHeight / newDivisor.value;
this.size = new Size(newWidth, newHeight);
synchronized (streamBufferLock) {
this.streamBuffer = new Mat(newWidth, newHeight, CvType.CV_8UC3);
VideoMode oldVideoMode = cvSource.getVideoMode();
cvSource.setVideoMode(
new VideoMode(
oldVideoMode.pixelFormat,
cameraCapture.getProperties().getStaticProperties().imageWidth / divisor.value,
cameraCapture.getProperties().getStaticProperties().imageHeight / divisor.value,
oldVideoMode.fps));
}
if (updateUI) {
SocketHandler.sendFullSettings();
}
}
public StreamDivisor getDivisor() {
return divisor;
}
public void recalculateDivision() {
setDivisor(this.divisor, false);
}
public void setNewVideoMode(VideoMode newVideoMode) {
// Trick to update cvSource and streamBuffer to the new resolution
// Must change the cameraProcess resolution first
setDivisor(divisor, true);
}
public int getStreamPort() {
var s = (MjpegServer) CameraServer.getInstance().getServer("serve_" + name);
return s.getPort();
}
public void runStream(Mat image) {
synchronized (streamBufferLock) {
image.copyTo(streamBuffer);
}
if (divisor.value != 1) {
// var camVal = cameraProcess.getProperties().staticProperties;
// var newWidth = camVal.imageWidth / divisor.value;
// var newHeight = camVal.imageHeight / divisor.value;
// Size newSize = new Size(newWidth, newHeight);
Imgproc.resize(streamBuffer, streamBuffer, this.size);
}
var sourceVideoMode = cvSource.getVideoMode();
var imageSize = streamBuffer.size();
if (sourceVideoMode.width != (int) imageSize.width
|| sourceVideoMode.height != (int) imageSize.height) {
synchronized (streamBufferLock) {
cvSource.setVideoMode(
new VideoMode(
sourceVideoMode.pixelFormat,
(int) imageSize.width,
(int) imageSize.height,
sourceVideoMode.fps));
}
}
cvSource.putFrame(streamBuffer);
}
}

View File

@@ -1,41 +0,0 @@
package org.photonvision._2.vision.camera;
import edu.wpi.cscore.VideoMode;
import org.apache.commons.math3.fraction.Fraction;
import org.apache.commons.math3.util.FastMath;
public class CaptureStaticProperties {
public final int imageWidth;
public final int imageHeight;
public final double fov;
public final double imageArea;
public final double centerX;
public final double centerY;
public final double horizontalFocalLength;
public final double verticalFocalLength;
public final VideoMode mode;
public CaptureStaticProperties(VideoMode mode, double fov) {
this.mode = mode;
this.imageWidth = mode.width;
this.imageHeight = mode.height;
this.fov = fov;
imageArea = this.imageWidth * this.imageHeight;
centerX = ((double) this.imageWidth / 2) - 0.5;
centerY = ((double) this.imageHeight / 2) - 0.5;
// pinhole model calculations
double diagonalView = FastMath.toRadians(this.fov);
Fraction aspectFraction = new Fraction(this.imageWidth, this.imageHeight);
int horizontalRatio = aspectFraction.getNumerator();
int verticalRatio = aspectFraction.getDenominator();
double diagonalAspect = FastMath.hypot(horizontalRatio, verticalRatio);
double horizontalView =
FastMath.atan(FastMath.tan(diagonalView / 2) * (horizontalRatio / diagonalAspect)) * 2;
double verticalView =
FastMath.atan(FastMath.tan(diagonalView / 2) * (verticalRatio / diagonalAspect)) * 2;
horizontalFocalLength = this.imageWidth / (2 * FastMath.tan(horizontalView / 2));
verticalFocalLength = this.imageHeight / (2 * FastMath.tan(verticalView / 2));
}
}

View File

@@ -1,149 +0,0 @@
package org.photonvision._2.vision.camera;
import org.photonvision._2.config.CameraCalibrationConfig;
import org.photonvision._2.config.FullCameraConfiguration;
import org.photonvision._2.util.Helpers;
import edu.wpi.cscore.CvSink;
import edu.wpi.cscore.UsbCamera;
import edu.wpi.cscore.VideoException;
import edu.wpi.cscore.VideoMode;
import edu.wpi.first.cameraserver.CameraServer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import org.opencv.core.Mat;
import org.opencv.core.Size;
public class USBCameraCapture implements CameraCapture {
private final UsbCamera baseCamera;
private final CvSink cvSink;
private List<CameraCalibrationConfig> calibrationList;
private Mat imageBuffer = new Mat();
private USBCaptureProperties properties;
public USBCameraCapture(FullCameraConfiguration fullCameraConfiguration) {
var config = fullCameraConfiguration.cameraConfig;
this.calibrationList = new ArrayList<>(); // fullCameraConfiguration.calibration;
calibrationList.addAll(fullCameraConfiguration.calibration);
baseCamera = new UsbCamera(config.name, config.path);
cvSink = CameraServer.getInstance().getVideo(baseCamera);
try {
properties = new USBCaptureProperties(baseCamera, config);
} catch (VideoException e) {
System.err.println(
"Camera cannot be found on the saved USB port!"
+ " Ensure that the camera has not been plugged into a different USB port, and if so, correct it.");
e.printStackTrace();
}
var videoModes = properties.getVideoModes();
if (videoModes.size() < 1) {
throw new VideoException(
"0 video modes are valid! Full list provided by camera: \n\n"
+ Arrays.stream(baseCamera.enumerateVideoModes())
.map(Helpers::VideoModeToHashMap)
.toString());
}
int videoMode = properties.videoModes.size() - 1 <= config.videomode ? config.videomode : 0;
setVideoMode(videoMode);
}
public CameraCalibrationConfig getCalibration(Size size) {
for (var calibration : calibrationList) {
if (calibration.resolution.equals(size)) return calibration;
}
return null;
}
public CameraCalibrationConfig getCalibration(VideoMode mode) {
return getCalibration(new Size(mode.width, mode.height));
}
public void addCalibrationData(CameraCalibrationConfig newConfig) {
calibrationList.removeIf(
c ->
newConfig.resolution.height == c.resolution.height
&& newConfig.resolution.width == c.resolution.width);
calibrationList.add(newConfig);
}
@Override
public USBCaptureProperties getProperties() {
return properties;
}
@Override
public VideoMode getCurrentVideoMode() {
return baseCamera.getVideoMode();
}
@Override
public Pair<Mat, Long> getFrame() {
Long deltaTime;
// TODO: Why multiply by 1000 here?
Mat tempMat = new Mat();
deltaTime = cvSink.grabFrame(tempMat) * 1000L;
// tempMat =
// Imgcodecs.imread("C:\\Users\\imadu\\Documents\\GitHub\\chameleon-vision\\chameleon-server\\testimages\\2020\\image.png");
tempMat.copyTo(imageBuffer);
tempMat.release();
return Pair.of(imageBuffer, deltaTime);
}
@Override
public void setExposure(int exposure) {
try {
baseCamera.setExposureManual(exposure);
} catch (VideoException e) {
System.err.println("Failed to change camera exposure!");
}
}
@Override
public void setBrightness(int brightness) {
try {
baseCamera.setBrightness(brightness);
} catch (VideoException e) {
System.err.println("Failed to change camera brightness!");
}
}
@Override
public void setVideoMode(VideoMode mode) {
try {
baseCamera.setVideoMode(mode);
properties.updateVideoMode(mode);
} catch (VideoException e) {
System.err.println("Failed to change camera video mode!");
}
}
public void setVideoMode(int index) {
VideoMode mode = properties.getVideoModes().get(index);
setVideoMode(mode);
}
@Override
public void setGain(int gain) {
if (properties.isPS3Eye) {
try {
baseCamera.getProperty("gain_automatic").set(0);
baseCamera.getProperty("gain").set(gain);
} catch (Exception e) {
System.err.println("Failed to change camera gain!");
}
}
}
@Override
public CameraCalibrationConfig getCurrentCalibrationData() {
return getCalibration(getCurrentVideoMode());
}
@Override
public List<CameraCalibrationConfig> getAllCalibrationData() {
return calibrationList;
}
}

View File

@@ -1,122 +0,0 @@
package org.photonvision._2.vision.camera;
import org.photonvision._2.config.CameraJsonConfig;
import org.photonvision._2.vision.image.CaptureProperties;
import org.photonvision.common.util.Platform;
import edu.wpi.cscore.UsbCamera;
import edu.wpi.cscore.VideoMode;
import edu.wpi.first.wpilibj.geometry.Rotation2d;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class USBCaptureProperties extends CaptureProperties {
public static final double DEFAULT_FOV = 70;
private static final int DEFAULT_EXPOSURE = 50;
private static final int DEFAULT_BRIGHTNESS = 50;
private static final int MINIMUM_FPS = 21;
private static final int MINIMUM_WIDTH = 320;
private static final int MINIMUM_HEIGHT = 200;
private static final int MAX_INIT_MS = 1500;
private static final int PS3EYE_VID = 0x1415;
private static final int PS3EYE_PID = 0x2000;
private static final List<VideoMode.PixelFormat> ALLOWED_PIXEL_FORMATS =
Arrays.asList(
VideoMode.PixelFormat.kYUYV, VideoMode.PixelFormat.kMJPEG, VideoMode.PixelFormat.kBGR);
private static final Predicate<VideoMode> kMinFPSPredicate =
(videoMode -> videoMode.fps >= MINIMUM_FPS);
private static final Predicate<VideoMode> kMinSizePredicate =
(videoMode -> videoMode.width >= MINIMUM_WIDTH && videoMode.height >= MINIMUM_HEIGHT);
private static final Predicate<VideoMode> kPixelFormatPredicate =
(videoMode -> ALLOWED_PIXEL_FORMATS.contains(videoMode.pixelFormat));
public final String name;
public final String path;
public final List<VideoMode> videoModes;
private final UsbCamera baseCamera;
public final boolean isPS3Eye;
private String nickname;
private double FOV;
USBCaptureProperties(UsbCamera baseCamera, CameraJsonConfig config) {
FOV = config.fov;
name = config.name;
path = config.path;
setTilt(Rotation2d.fromDegrees(config.tilt));
nickname = config.nickname;
this.baseCamera = baseCamera;
int usbVID = baseCamera.getInfo().vendorId;
int usbPID = baseCamera.getInfo().productId;
// wait for camera USB init on Windows, Windows USB is slow...
if (Platform.CurrentPlatform == Platform.WINDOWS_64 && !baseCamera.isConnected()) {
System.out.print("Waiting on camera... ");
long initTimeout = System.nanoTime();
while (!baseCamera.isConnected()) {
if (((System.nanoTime() - initTimeout) / 1e6) >= MAX_INIT_MS) {
break;
}
}
var initTimeMs = (System.nanoTime() - initTimeout) / 1e6;
System.out.printf("USBCameraProcess initialized in %.2fms\n", initTimeMs);
}
isPS3Eye = (usbVID == PS3EYE_VID && usbPID == PS3EYE_PID);
videoModes = filterVideoModes(baseCamera.enumerateVideoModes());
}
public void setFOV(double FOV) {
if (this.FOV != FOV) {
this.FOV = FOV;
staticProperties = new CaptureStaticProperties(staticProperties.mode, FOV);
}
}
public double getFOV() {
return FOV;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getNickname() {
return nickname;
}
private List<VideoMode> filterVideoModes(VideoMode[] videoModes) {
Predicate<VideoMode> fullPredicate =
kMinFPSPredicate.and(kMinSizePredicate).and(kPixelFormatPredicate);
Stream<VideoMode> validModes = Arrays.stream(videoModes).filter(fullPredicate);
return validModes.collect(Collectors.toList());
}
void updateVideoMode(VideoMode videoMode) {
staticProperties = new CaptureStaticProperties(videoMode, FOV);
}
public List<VideoMode> getVideoModes() {
return videoModes;
}
public VideoMode getVideoMode(int index) {
return videoModes.get(index);
}
public VideoMode getCurrentVideoMode() {
return staticProperties.mode;
}
public int getCurrentVideoModeIndex() {
return getVideoModes().indexOf(getCurrentVideoMode());
}
}

View File

@@ -1,7 +0,0 @@
package org.photonvision._2.vision.enums;
public enum CalibrationMode {
None,
Single,
Dual
}

View File

@@ -1,11 +0,0 @@
package org.photonvision._2.vision.enums;
public enum SortMode {
Largest,
Smallest,
Highest,
Lowest,
Rightmost,
Leftmost,
Centermost
}

View File

@@ -1,14 +0,0 @@
package org.photonvision._2.vision.enums;
public enum StreamDivisor {
NONE(1),
HALF(2),
QUARTER(4),
SIXTH(6);
public final Integer value;
StreamDivisor(int value) {
this.value = value;
}
}

View File

@@ -1,6 +0,0 @@
package org.photonvision._2.vision.enums;
public enum TargetGroup {
Single,
Dual
}

View File

@@ -1,9 +0,0 @@
package org.photonvision._2.vision.enums;
public enum TargetIntersection {
None,
Up,
Down,
Left,
Right
}

View File

@@ -1,9 +0,0 @@
package org.photonvision._2.vision.enums;
public enum TargetRegion {
Center,
Top,
Bottom,
Left,
Right
}

View File

@@ -1,33 +0,0 @@
package org.photonvision._2.vision.image;
import org.photonvision._2.vision.camera.CaptureStaticProperties;
import edu.wpi.cscore.VideoMode;
import edu.wpi.first.wpilibj.geometry.Rotation2d;
public class CaptureProperties {
protected CaptureStaticProperties staticProperties;
private Rotation2d tilt = new Rotation2d();
protected CaptureProperties() {}
public CaptureProperties(VideoMode videoMode, double fov) {
staticProperties = new CaptureStaticProperties(videoMode, fov);
}
public void setStaticProperties(CaptureStaticProperties staticProperties) {
this.staticProperties = staticProperties;
}
public CaptureStaticProperties getStaticProperties() {
return staticProperties;
}
public Rotation2d getTilt() {
return tilt;
}
public void setTilt(Rotation2d tilt) {
this.tilt = tilt;
}
}

View File

@@ -1,13 +0,0 @@
package org.photonvision._2.vision.image;
import org.apache.commons.lang3.tuple.Pair;
import org.opencv.core.Mat;
public interface ImageCapture {
/**
* Get the next camera frame
*
* @return a Pair of the captured image and the Linux epoch of when the frame was grabbed (in uS)
*/
Pair<Mat, Long> getFrame();
}

View File

@@ -1,86 +0,0 @@
package org.photonvision._2.vision.image;
import org.photonvision._2.config.CameraCalibrationConfig;
import org.photonvision._2.vision.camera.CameraCapture;
import edu.wpi.cscore.VideoMode;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;
public class StaticImageCapture implements CameraCapture {
private Mat image = new Mat();
private final VideoMode fakeVideoMode;
private final CaptureProperties properties;
public StaticImageCapture(Path imagePath) {
this(imagePath, 70);
}
public StaticImageCapture(Path imagePath, double FOV) {
if (!Files.exists(imagePath)) throw new RuntimeException("Invalid path for image!");
Mat loadedImage = Imgcodecs.imread(imagePath.toString());
loadedImage.copyTo(image);
if (image.cols() > 0 && image.rows() > 0) {
fakeVideoMode = new VideoMode(VideoMode.PixelFormat.kBGR, image.cols(), image.rows(), 60);
} else {
throw new RuntimeException("Failed to load image!");
}
properties = new CaptureProperties(fakeVideoMode, FOV);
}
@Override
public Pair<Mat, Long> getFrame() {
return Pair.of(image, System.nanoTime());
}
@Override
public CaptureProperties getProperties() {
return properties;
}
@Override
public VideoMode getCurrentVideoMode() {
return fakeVideoMode;
}
@Override
public void setExposure(int exposure) {
// do nothing
}
@Override
public void setBrightness(int brightness) {
// do nothing
}
@Override
public void setVideoMode(VideoMode mode) {
// do nothing
}
@Override
public void setVideoMode(int index) {
// do nothing
}
@Override
public void setGain(int gain) {
// do nothing
}
@Override
public CameraCalibrationConfig getCurrentCalibrationData() {
return null;
}
@Override
public List<CameraCalibrationConfig> getAllCalibrationData() {
return null;
}
}

View File

@@ -1,30 +0,0 @@
package org.photonvision._2.vision.pipeline;
import org.photonvision._2.vision.camera.CameraCapture;
import org.opencv.core.Mat;
/** @param <R> Pipeline result type */
public abstract class CVPipeline<R extends CVPipelineResult, S extends CVPipelineSettings> {
protected Mat outputMat = new Mat();
protected CameraCapture cameraCapture;
public S settings;
protected CVPipeline(S settings) {
this.settings = settings;
}
protected CVPipeline(String pipelineName, S settings) {
this.settings = settings;
settings.nickname = pipelineName;
}
public void initPipeline(CameraCapture camera) {
cameraCapture = camera;
cameraCapture.setVideoMode(settings.videoModeIndex);
cameraCapture.setExposure((int) settings.exposure);
cameraCapture.setBrightness((int) settings.brightness);
cameraCapture.setGain((int) settings.gain);
}
public abstract R runPipeline(Mat inputMat);
}

View File

@@ -1,25 +0,0 @@
package org.photonvision._2.vision.pipeline;
import java.util.List;
import org.opencv.core.Mat;
public abstract class CVPipelineResult<T> {
public final List<T> targets;
public final boolean hasTarget;
public final Mat outputMat = new Mat();
public final long processTime;
public long imageTimestamp = 0;
public CVPipelineResult(List<T> targets, Mat outputMat, long processTime) {
this.targets = targets;
hasTarget = targets != null && !targets.isEmpty();
// this.outputMat = outputMat;
outputMat.copyTo(this.outputMat);
outputMat.release();
this.processTime = processTime;
}
public void setTimestamp(long timestamp) {
imageTimestamp = timestamp;
}
}

View File

@@ -1,18 +0,0 @@
package org.photonvision._2.vision.pipeline;
import org.photonvision._2.vision.enums.ImageFlipMode;
import org.photonvision._2.vision.enums.ImageRotationMode;
import org.photonvision._2.vision.enums.StreamDivisor;
@SuppressWarnings("ALL")
public class CVPipelineSettings {
public int index = 0;
public ImageFlipMode flipMode = ImageFlipMode.NONE;
public ImageRotationMode rotationMode = ImageRotationMode.DEG_0;
public String nickname = "New Pipeline";
public double exposure = 50.0;
public double brightness = 50.0;
public double gain = 0;
public int videoModeIndex = 0;
public StreamDivisor streamDivisor = StreamDivisor.NONE;
}

View File

@@ -1,11 +0,0 @@
package org.photonvision._2.vision.pipeline;
import org.apache.commons.lang3.tuple.Pair;
public interface Pipe<I, O> {
/**
* @param input Input object for pipe
* @return Returns a Pair containing the process time in Nanoseconds, and the output object
*/
Pair<O, Long> run(I input);
}

View File

@@ -1,254 +0,0 @@
package org.photonvision._2.vision.pipeline;
import org.photonvision._2.config.CameraConfig;
import org.photonvision._2.config.ConfigManager;
import org.photonvision._2.vision.VisionManager;
import org.photonvision._2.vision.VisionProcess;
import org.photonvision._2.vision.pipeline.impl.Calibrate3dPipeline;
import org.photonvision._2.vision.pipeline.impl.DriverVisionPipeline;
import org.photonvision._2.vision.pipeline.impl.StandardCVPipeline;
import org.photonvision._2.vision.pipeline.impl.StandardCVPipelineSettings;
import org.photonvision._2.web.SocketHandler;
import org.photonvision.common.scripting.ScriptEventType;
import org.photonvision.common.scripting.ScriptManager;
import edu.wpi.cscore.VideoMode;
import edu.wpi.first.networktables.NetworkTableEntry;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@SuppressWarnings("WeakerAccess")
public class PipelineManager {
private static final int DRIVERMODE_INDEX = -1;
private static final int CAL_3D_INDEX = -2;
public final LinkedList<CVPipeline> pipelines = new LinkedList<>();
public final CVPipeline driverModePipeline = new DriverVisionPipeline(new CVPipelineSettings());
public final Calibrate3dPipeline calib3dPipe =
new Calibrate3dPipeline(new StandardCVPipelineSettings());
private final VisionProcess parentProcess;
private int lastPipelineIndex;
private int currentPipelineIndex;
public NetworkTableEntry ntIndexEntry;
public PipelineManager(
VisionProcess visionProcess, List<CVPipelineSettings> loadedPipelineSettings) {
parentProcess = visionProcess;
if (loadedPipelineSettings == null || loadedPipelineSettings.size() == 0) {
pipelines.add(new StandardCVPipeline("New Pipeline"));
} else {
for (CVPipelineSettings setting : loadedPipelineSettings) {
addInternalPipeline(setting);
}
}
driverModePipeline.initPipeline(visionProcess.getCamera());
setCurrentPipeline(0);
}
private void reassignIndexes() {
pipelines.sort(IndexComparator);
for (int i = 0; i < pipelines.size(); i++) {
pipelines.get(i).settings.index = i;
}
}
private CameraConfig getConfig(VisionProcess process) {
return VisionManager.getCameraConfig(process);
}
private CameraConfig getConfig() {
return getConfig(parentProcess);
}
private void savePipelineConfig(CVPipelineSettings setting) {
getConfig().pipelineConfig.save(setting);
}
private void deletePipelineConfig(CVPipelineSettings setting) {
getConfig().pipelineConfig.delete(setting);
}
private void renamePipelineConfig(CVPipelineSettings setting, String newName) {
getConfig().pipelineConfig.rename(setting, newName);
}
public void saveAllPipelines() {
pipelines.parallelStream().map(pipeline -> pipeline.settings).forEach(this::savePipelineConfig);
}
private void addInternalPipeline(CVPipelineSettings setting) {
if (setting instanceof StandardCVPipelineSettings) {
pipelines.add(new StandardCVPipeline((StandardCVPipelineSettings) setting));
} else {
System.out.println("Non 2D/3D pipelines not supported!");
}
reassignIndexes();
}
public void setDriverMode(boolean driverMode) {
if (driverMode) setCurrentPipeline(DRIVERMODE_INDEX);
else setCurrentPipeline(lastPipelineIndex);
}
public void setCalibrationMode(boolean calibrationMode) {
setCurrentPipeline((calibrationMode ? CAL_3D_INDEX : lastPipelineIndex));
}
public void enableCalibrationMode(VideoMode mode) {
parentProcess.setVideoMode(mode);
calib3dPipe.setVideoMode(mode);
setCalibrationMode(true);
}
public boolean getDriverMode() {
return currentPipelineIndex == DRIVERMODE_INDEX;
}
public int getCurrentPipelineIndex() {
return currentPipelineIndex;
}
public CVPipeline getCurrentPipeline() {
if (currentPipelineIndex == DRIVERMODE_INDEX) {
return driverModePipeline;
} else if (currentPipelineIndex <= CAL_3D_INDEX) {
return calib3dPipe;
} else {
return pipelines.get(currentPipelineIndex);
}
}
public void setCurrentPipeline(int index) {
CVPipeline newPipeline = null;
if (index == DRIVERMODE_INDEX) {
ScriptManager.queueEvent(ScriptEventType.kLEDOff);
newPipeline = driverModePipeline;
// if we're changing into driver mode, try to set the nt entry to true
parentProcess.setDriverModeEntry(true);
} else if (index == CAL_3D_INDEX) {
parentProcess.setDriverModeEntry(true);
newPipeline = calib3dPipe;
} else {
if (index < pipelines.size() && index >= 0) {
newPipeline = pipelines.get(index);
// if we're switching out of driver mode, try to set the nt entry to false
parentProcess.setDriverModeEntry(false);
ScriptManager.queueEvent(ScriptEventType.kLEDOn);
} else {
// TODO alert/warn user that pipeline doesnt exsits
System.err.println("Index is out of bounds");
}
}
if (newPipeline != null) {
lastPipelineIndex = currentPipelineIndex;
currentPipelineIndex = index;
getCurrentPipeline().initPipeline(parentProcess.getCamera());
if (ConfigManager.settings.currentCamera.equals(
parentProcess.getCamera().getProperties().name)) {
ConfigManager.settings.currentPipeline = currentPipelineIndex;
HashMap<String, Object> pipeChange = new HashMap<>();
pipeChange.put("currentPipeline", currentPipelineIndex);
SocketHandler.broadcastMessage(pipeChange);
try {
SocketHandler.sendFullSettings();
} catch (Exception e) {
// avoid NullPointerException when run before threads start
}
}
newPipeline.initPipeline(parentProcess.getCamera());
if (parentProcess.cameraStreamer != null)
parentProcess.cameraStreamer.setDivisor(newPipeline.settings.streamDivisor, true);
if (ntIndexEntry != null) {
ntIndexEntry.setDouble(index);
}
}
// gain setting quirk
if (!parentProcess.cameraCapture.getProperties().isPS3Eye) {
getCurrentPipeline().settings.gain = -1;
}
}
public void addPipeline(CVPipelineSettings setting) {
addInternalPipeline(setting);
savePipelineConfig(setting);
}
public void addPipeline(CVPipeline pipeline) {
pipelines.add(pipeline);
reassignIndexes();
savePipelineConfig(pipeline.settings);
}
public void addNewPipeline(String piplineName) {
StandardCVPipeline newPipeline = new StandardCVPipeline();
newPipeline.settings.nickname = piplineName;
newPipeline.settings.index = pipelines.size();
addPipeline(newPipeline);
}
public CVPipeline getPipeline(int index) {
return pipelines.get(index);
}
public void duplicatePipeline(CVPipelineSettings pipeline) {
duplicatePipeline(pipeline, parentProcess);
}
public void duplicatePipeline(CVPipelineSettings pipeline, VisionProcess destinationProcess) {
pipeline.index = destinationProcess.pipelineManager.pipelines.size();
pipeline.nickname += "(Copy)";
if (destinationProcess.pipelineManager.pipelines.stream()
.anyMatch(c -> c.settings.nickname.equals(pipeline.nickname))) {
// throw new DuplicatedKeyException("key Already exists");
} else {
destinationProcess.pipelineManager.addPipeline(pipeline);
}
}
public void renameCurrentPipeline(String newName) {
CVPipelineSettings settings = getCurrentPipeline().settings;
renamePipelineConfig(settings, newName);
}
public void deleteCurrentPipeline() {
deletePipeline(currentPipelineIndex);
}
private void deletePipeline(int index) {
if (index == currentPipelineIndex) {
currentPipelineIndex -= 1;
}
deletePipelineConfig(getPipeline(index).settings);
pipelines.remove(index);
reassignIndexes();
}
public void saveDriverModeConfig() {
getConfig().saveDriverMode(driverModePipeline.settings);
}
private static final Comparator<CVPipeline> IndexComparator =
(o1, o2) -> {
int o1Index = o1.settings.index;
int o2Index = o2.settings.index;
if (o1Index == o2Index) {
return 0;
} else if (o1Index < o2Index) {
return -1;
}
return 1;
};
}

View File

@@ -1,178 +0,0 @@
package org.photonvision._2.vision.pipeline.impl;
import org.photonvision._2.config.CameraCalibrationConfig;
import org.photonvision._2.config.ConfigManager;
import org.photonvision._2.vision.VisionManager;
import org.photonvision._2.vision.camera.CameraCapture;
import org.photonvision._2.vision.pipeline.CVPipeline;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.wpi.cscore.VideoMode;
import edu.wpi.first.wpilibj.util.Units;
import java.util.ArrayList;
import java.util.List;
import org.opencv.calib3d.Calib3d;
import org.opencv.core.*;
import org.opencv.imgproc.Imgproc;
public class Calibrate3dPipeline
extends CVPipeline<DriverVisionPipeline.DriverPipelineResult, StandardCVPipelineSettings> {
private int checkerboardSquaresHigh = 7;
private int checkerboardSquaresWide = 7;
private MatOfPoint3f objP; // new MatOfPoint3f(checkerboardSquaresHigh + checkerboardSquaresWide,
// 3);//(checkerboardSquaresWide * checkerboardSquaresHigh, 3);
private Size patternSize = new Size(checkerboardSquaresHigh, checkerboardSquaresWide);
private Size imageSize;
double checkerboardSquareSize = 1; // inches!
private MatOfPoint2f calibrationOutput = new MatOfPoint2f();
private List<Mat> objpoints = new ArrayList<>();
private List<Mat> imgpoints = new ArrayList<>();
public static double checkerboardSquareSizeUnits = Units.inchesToMeters(1.0);
public static final int MIN_COUNT = 15;
private VideoMode calibrationMode;
private final Size windowSize = new Size(11, 11);
private final Size zeroZone = new Size(-1, -1);
private TermCriteria criteria =
new TermCriteria(
3, 30, 0.001); // (Imgproc.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
private int captureCount = 0;
private double calibrationAccuracy = 0;
private boolean wantsSnapshot = false;
private double squareSizeInches;
public Calibrate3dPipeline(StandardCVPipelineSettings settings) {
super(settings);
objP = new MatOfPoint3f();
for (int i = 0; i < checkerboardSquaresHigh * checkerboardSquaresWide; i++) {
objP.push_back(
new MatOfPoint3f(
new Point3(i / checkerboardSquaresWide, i % checkerboardSquaresHigh, 0.0f)));
}
setSquareSize(checkerboardSquareSizeUnits);
objpoints.forEach(Mat::release);
imgpoints.forEach(Mat::release);
objpoints.clear();
imgpoints.clear();
}
public void setSquareSize(double size) {
this.squareSizeInches = size;
}
public void takeSnapshot() {
wantsSnapshot = true;
}
public boolean hasEnoughSnapshots() {
return captureCount >= MIN_COUNT - 1;
}
@Override
public DriverVisionPipeline.DriverPipelineResult runPipeline(Mat inputMat) {
// look for checkerboard
Imgproc.cvtColor(inputMat, inputMat, Imgproc.COLOR_BGR2GRAY);
var checkerboardFound = Calib3d.findChessboardCorners(inputMat, patternSize, calibrationOutput);
if (!checkerboardFound) {
Imgproc.cvtColor(inputMat, inputMat, Imgproc.COLOR_GRAY2BGR);
return new DriverVisionPipeline.DriverPipelineResult(null, inputMat, 0);
}
// System.out.println("[SolvePNP] checkerboard found!!");
// cool we found a checkerboard
// do corner subpixel
Imgproc.cornerSubPix(inputMat, calibrationOutput, windowSize, zeroZone, criteria);
// convert back to BGR
Imgproc.cvtColor(inputMat, inputMat, Imgproc.COLOR_GRAY2BGR);
// draw the chessboard
Calib3d.drawChessboardCorners(inputMat, patternSize, calibrationOutput, true);
if (wantsSnapshot) {
this.imageSize = new Size(inputMat.width(), inputMat.height());
var mat = new MatOfPoint3f();
calibrationOutput.copyTo(mat);
this.objpoints.add(objP);
imgpoints.add(mat);
captureCount++;
wantsSnapshot = false;
}
imageSize = new Size(inputMat.width(), inputMat.height());
return new DriverVisionPipeline.DriverPipelineResult(null, inputMat, 0);
}
@Override
public void initPipeline(CameraCapture camera) {
super.initPipeline(camera);
objpoints.clear();
imgpoints.clear();
captureCount = 0;
}
public boolean tryCalibration() {
if (!hasEnoughSnapshots()) return false;
Mat cameraMatrix = new Mat();
Mat distortionCoeffs = new Mat();
List<Mat> rvecs = new ArrayList<>();
List<Mat> tvecs = new ArrayList<>();
try {
calibrationAccuracy =
Calib3d.calibrateCamera(
objpoints, imgpoints, imageSize, cameraMatrix, distortionCoeffs, rvecs, tvecs);
} catch (Exception e) {
System.err.println("Camera calibration failed!");
initPipeline(cameraCapture);
return false;
}
VideoMode currentVidMode = cameraCapture.getCurrentVideoMode();
Size resolution = new Size(currentVidMode.width, currentVidMode.height);
CameraCalibrationConfig cal =
new CameraCalibrationConfig(resolution, cameraMatrix, distortionCoeffs, squareSizeInches);
VisionManager.getCurrentUIVisionProcess().addCalibration(cal);
try {
System.out.printf(
"CALIBRATION SUCCESS (with accuracy %s)! camMatrix: \n%s\ndistortionCoeffs:\n%s\n",
calibrationAccuracy,
new ObjectMapper().writeValueAsString(cal.cameraMatrix),
new ObjectMapper().writeValueAsString(cal.distortionCoeffs));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
ConfigManager.saveGeneralSettings();
return true;
}
public void setVideoMode(VideoMode mode) {
this.calibrationMode = mode;
}
public int getSnapshotCount() {
return captureCount + 1;
}
public double getCalibrationAccuracy() {
return calibrationAccuracy;
}
}

View File

@@ -1,57 +0,0 @@
package org.photonvision._2.vision.pipeline.impl;
import org.photonvision._2.vision.camera.CameraCapture;
import org.photonvision._2.vision.enums.CalibrationMode;
import org.photonvision._2.vision.pipeline.CVPipeline;
import org.photonvision._2.vision.pipeline.CVPipelineResult;
import org.photonvision._2.vision.pipeline.CVPipelineSettings;
import org.photonvision._2.vision.pipeline.pipes.Draw2dCrosshairPipe;
import org.photonvision._2.vision.pipeline.pipes.RotateFlipPipe;
import org.photonvision.common.util.MemoryManager;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import org.opencv.core.Mat;
public class DriverVisionPipeline
extends CVPipeline<DriverVisionPipeline.DriverPipelineResult, CVPipelineSettings> {
private RotateFlipPipe rotateFlipPipe;
private Draw2dCrosshairPipe drawCrosshairPipe;
private Draw2dCrosshairPipe.Draw2dCrosshairPipeSettings crosshairPipeSettings =
new Draw2dCrosshairPipe.Draw2dCrosshairPipeSettings();
private final MemoryManager memoryManager = new MemoryManager(200, 20000);
public DriverVisionPipeline(CVPipelineSettings settings) {
super(settings);
settings.index = -1;
}
@Override
public void initPipeline(CameraCapture capture) {
super.initPipeline(capture);
rotateFlipPipe = new RotateFlipPipe(settings.rotationMode, settings.flipMode);
crosshairPipeSettings.showCrosshair = true;
drawCrosshairPipe =
new Draw2dCrosshairPipe(crosshairPipeSettings, CalibrationMode.None, null, 0, 0);
}
@Override
public DriverPipelineResult runPipeline(Mat inputMat) {
rotateFlipPipe.setConfig(settings.rotationMode, settings.flipMode);
Pair<Mat, Long> rotateFlipResult = rotateFlipPipe.run(inputMat);
Pair<Mat, Long> draw2dCrosshairResult =
drawCrosshairPipe.run(Pair.of(rotateFlipResult.getLeft(), null));
memoryManager.run();
return new DriverPipelineResult(null, draw2dCrosshairResult.getLeft(), 0);
}
public static class DriverPipelineResult extends CVPipelineResult<Void> {
public DriverPipelineResult(List<Void> targets, Mat outputMat, long processTime) {
super(targets, outputMat, processTime);
}
}
}

View File

@@ -1,287 +0,0 @@
package org.photonvision._2.vision.pipeline.impl;
import org.photonvision._2.Main;
import org.photonvision._2.vision.camera.CameraCapture;
import org.photonvision._2.vision.camera.CaptureStaticProperties;
import org.photonvision._2.vision.pipeline.CVPipeline;
import org.photonvision._2.vision.pipeline.CVPipelineResult;
import com.chameleonvision._2.vision.pipeline.pipes.*;
import org.photonvision._2.vision.pipeline.pipes.*;
import org.photonvision.common.util.MemoryManager;
import org.photonvision.common.vision.opencv.Contour;
import edu.wpi.first.wpilibj.geometry.Pose2d;
import org.apache.commons.lang3.tuple.Pair;
import org.opencv.core.Point;
import org.opencv.core.*;
import java.awt.*;
import java.util.List;
@SuppressWarnings("WeakerAccess")
public class StandardCVPipeline extends CVPipeline<StandardCVPipeline.StandardCVPipelineResult, StandardCVPipelineSettings> {
private Mat rawCameraMat = new Mat();
private RotateFlipPipe rotateFlipPipe;
private BlurPipe blurPipe;
private ErodeDilatePipe erodeDilatePipe;
private HsvPipe hsvPipe;
private FindContoursPipe findContoursPipe;
private FilterContoursPipe filterContoursPipe;
private SpeckleRejectPipe speckleRejectPipe;
private GroupContoursPipe groupContoursPipe;
private SortContoursPipe sortContoursPipe;
private Collect2dTargetsPipe collect2dTargetsPipe;
private Draw2dContoursPipe.Draw2dContoursSettings draw2dContoursSettings;
private Draw2dContoursPipe draw2dContoursPipe;
private Draw2dCrosshairPipe draw2dCrosshairPipe;
private DrawSolvePNPPipe drawSolvePNPPipe;
private SolvePNPPipe solvePNPPipe;
private Draw2dCrosshairPipe.Draw2dCrosshairPipeSettings draw2dCrosshairPipeSettings;
private OutputMatPipe outputMatPipe;
private String pipelineTimeString = "";
private CaptureStaticProperties camProps;
private Scalar hsvLower, hsvUpper;
public StandardCVPipeline() {
super(new StandardCVPipelineSettings());
}
public StandardCVPipeline(String name) {
super(name, new StandardCVPipelineSettings());
}
public StandardCVPipeline(StandardCVPipelineSettings settings) {
super(settings);
}
@Override
public void initPipeline(CameraCapture process) {
super.initPipeline(process);
camProps = cameraCapture.getProperties().getStaticProperties();
hsvLower = new Scalar(settings.hue.getFirst(), settings.saturation.getFirst(), settings.value.getFirst());
hsvUpper = new Scalar(settings.hue.getSecond(), settings.saturation.getSecond(), settings.value.getSecond());
rotateFlipPipe = new RotateFlipPipe(settings.rotationMode, settings.flipMode);
blurPipe = new BlurPipe(5);
erodeDilatePipe = new ErodeDilatePipe(settings.erode, settings.dilate, 7);
hsvPipe = new HsvPipe(hsvLower, hsvUpper);
findContoursPipe = new FindContoursPipe();
filterContoursPipe = new FilterContoursPipe(settings.area, settings.ratio, settings.extent, camProps);
speckleRejectPipe = new SpeckleRejectPipe(settings.speckle.doubleValue());
groupContoursPipe = new GroupContoursPipe(settings.targetGroup, settings.targetIntersection);
sortContoursPipe = new SortContoursPipe(settings.sortMode, camProps, 5);
collect2dTargetsPipe = new Collect2dTargetsPipe(settings.calibrationMode, settings.targetRegion, settings.targetOrientation, settings.point, settings.dualTargetCalibrationM, settings.dualTargetCalibrationB, camProps);
draw2dContoursSettings = new Draw2dContoursPipe.Draw2dContoursSettings();
draw2dCrosshairPipeSettings = new Draw2dCrosshairPipe.Draw2dCrosshairPipeSettings();
draw2dContoursSettings.showCentroid = true;
draw2dContoursSettings.centroidColor = new Color(25, 239, 0);
draw2dContoursSettings.boxOutlineSize = 2;
draw2dContoursSettings.showRotatedBox = true;
draw2dContoursSettings.showMaximumBox = true;
draw2dContoursSettings.showMultiple = settings.multiple;
draw2dCrosshairPipeSettings.showCrosshair = true;
draw2dContoursPipe = new Draw2dContoursPipe(draw2dContoursSettings, camProps);
draw2dCrosshairPipe = new Draw2dCrosshairPipe(draw2dCrosshairPipeSettings, settings.calibrationMode, settings.point, settings.dualTargetCalibrationM, settings.dualTargetCalibrationB);
outputMatPipe = new OutputMatPipe(settings.isBinary);
}
private final MemoryManager memManager = new MemoryManager(120, 20000);
private StandardCVPipelineResult resultCache = new StandardCVPipelineResult(List.of(), new Mat(), 0L);
@Override
public StandardCVPipelineResult runPipeline(Mat inputMat) {
long totalPipelineTimeNanos = 0;
long pipelineStartTimeNanos = System.nanoTime();
resultCache.release();
if (cameraCapture == null) {
throw new RuntimeException("Pipeline was not initialized before being run!");
}
// TODO (HIGH) find the source of the random NPE
if (settings == null) {
throw new RuntimeException("settings was not initialized!");
}
if (inputMat.cols() <= 1) {
throw new RuntimeException("Input Mat is empty!");
}
pipelineTimeString = "";
// prepare pipes
camProps = cameraCapture.getProperties().getStaticProperties();
hsvLower = new Scalar(settings.hue.getFirst(), settings.saturation.getFirst(), settings.value.getFirst());
hsvUpper = new Scalar(settings.hue.getSecond(), settings.saturation.getSecond(), settings.value.getSecond());
rotateFlipPipe.setConfig(settings.rotationMode, settings.flipMode);
blurPipe.setConfig(0);
erodeDilatePipe.setConfig(settings.erode, settings.dilate, 7);
hsvPipe.setConfig(hsvLower, hsvUpper);
filterContoursPipe.setConfig(settings.area, settings.ratio, settings.extent, camProps);
speckleRejectPipe.setConfig(settings.speckle.doubleValue());
groupContoursPipe.setConfig(settings.targetGroup, settings.targetIntersection);
sortContoursPipe.setConfig(settings.sortMode, camProps, 5);
collect2dTargetsPipe.setConfig(settings.calibrationMode, settings.targetRegion, settings.targetOrientation, settings.point, settings.dualTargetCalibrationM, settings.dualTargetCalibrationB, camProps);
draw2dContoursPipe.setConfig(settings.multiple, camProps);
draw2dCrosshairPipe.setConfig(draw2dCrosshairPipeSettings, settings.calibrationMode, settings.point, settings.dualTargetCalibrationM, settings.dualTargetCalibrationB);
outputMatPipe.setConfig(settings.isBinary);
if (settings.is3D) {
if (solvePNPPipe == null)
solvePNPPipe = new SolvePNPPipe(settings, cameraCapture.getCurrentCalibrationData(), cameraCapture.getProperties().getTilt());
if (drawSolvePNPPipe == null)
drawSolvePNPPipe = new DrawSolvePNPPipe(settings, cameraCapture.getCurrentCalibrationData());
solvePNPPipe.setConfig(settings, cameraCapture.getCurrentCalibrationData(), cameraCapture.getProperties().getTilt());
drawSolvePNPPipe.setConfig(cameraCapture.getCurrentCalibrationData());
drawSolvePNPPipe.setConfig(settings);
}
long pipeInitTimeNanos = System.nanoTime() - pipelineStartTimeNanos;
// run pipes
Pair<Mat, Long> rotateFlipResult = rotateFlipPipe.run(inputMat);
totalPipelineTimeNanos += rotateFlipResult.getRight();
inputMat.copyTo(rawCameraMat);
// Pair<Mat, Long> blurResult = blurPipe.run(rotateFlipResult.getLeft());
// totalPipelineTimeNanos += blurResult.getRight();
Pair<Mat, Long> erodeDilateResult = erodeDilatePipe.run(rotateFlipResult.getLeft());
totalPipelineTimeNanos += erodeDilateResult.getRight();
Pair<Mat, Long> hsvResult = hsvPipe.run(erodeDilateResult.getLeft());
totalPipelineTimeNanos += hsvResult.getRight();
Pair<List<Contour>, Long> findContoursResult = findContoursPipe.run(hsvResult.getLeft());
totalPipelineTimeNanos += findContoursResult.getRight();
Pair<List<Contour>, Long> filterContoursResult = filterContoursPipe.run(findContoursResult.getLeft());
totalPipelineTimeNanos += filterContoursResult.getRight();
// ignore !
Pair<List<Contour>, Long> speckleRejectResult = speckleRejectPipe.run(filterContoursResult.getLeft());
totalPipelineTimeNanos += speckleRejectResult.getRight();
Pair<List<TrackedTarget>, Long> groupContoursResult = groupContoursPipe.run(speckleRejectResult.getLeft());
totalPipelineTimeNanos += groupContoursResult.getRight();
Pair<List<TrackedTarget>, Long> sortContoursResult = sortContoursPipe.run(groupContoursResult.getLeft());
totalPipelineTimeNanos += sortContoursResult.getRight();
Pair<List<TrackedTarget>, Long> collect2dTargetsResult = collect2dTargetsPipe.run(Pair.of(sortContoursResult.getLeft(), camProps));
totalPipelineTimeNanos += collect2dTargetsResult.getRight();
// takes pair of (Mat of original camera image (8UC3), Mat of HSV thresholded image(8UC1))
Pair<Mat, Long> outputMatResult = outputMatPipe.run(Pair.of(rawCameraMat, hsvResult.getLeft()));
totalPipelineTimeNanos += outputMatResult.getRight();
Pair<Mat, Long> result;
if (!settings.is3D) {
// takes pair of (Mat to draw on, List<RotatedRect> of sorted contours)
result = draw2dContoursPipe.run(Pair.of(outputMatResult.getLeft(), sortContoursResult.getLeft()));
totalPipelineTimeNanos += result.getRight();
} else {
result = outputMatResult;
}
// takes pair of (Mat to draw on, List<RotatedRect> of sorted contours)
Pair<Mat, Long> draw2dCrosshairResult = draw2dCrosshairPipe.run(Pair.of(result.getLeft(), collect2dTargetsResult.getLeft()));
totalPipelineTimeNanos += draw2dCrosshairResult.getRight();
Mat outputMat;
if (settings.is3D) {
// once we've sorted our targets, perform solvePNP. The number of "best targets" is limited by the above pipe
Pair<List<TrackedTarget>, Long> solvePNPResult = solvePNPPipe.run(Pair.of(collect2dTargetsResult.getLeft(), rotateFlipResult.getLeft()));
totalPipelineTimeNanos += solvePNPResult.getRight();
Pair<Mat, Long> draw3dContoursResult = drawSolvePNPPipe.run(Pair.of(outputMatResult.getLeft(), solvePNPResult.getLeft()));
totalPipelineTimeNanos += draw3dContoursResult.getRight();
outputMat = draw3dContoursResult.getLeft();
} else {
outputMat = draw2dCrosshairResult.getLeft();
}
if (Main.testMode) {
pipelineTimeString += String.format("PipeInit: %.2fms, ", pipeInitTimeNanos / 1000000.0);
pipelineTimeString += String.format("RotateFlip: %.2fms, ", rotateFlipResult.getRight() / 1000000.0);
// pipelineTimeString += String.format("Blur: %.2fms, ", blurResult.getRight() / 1000000.0);
pipelineTimeString += String.format("ErodeDilate: %.2fms, ", erodeDilateResult.getRight() / 1000000.0);
pipelineTimeString += String.format("HSV: %.2fms, ", hsvResult.getRight() / 1000000.0);
pipelineTimeString += String.format("FindContours: %.2fms, ", findContoursResult.getRight() / 1000000.0);
pipelineTimeString += String.format("FilterContours: %.2fms, ", filterContoursResult.getRight() / 1000000.0);
pipelineTimeString += String.format("SpeckleReject: %.2fms, ", speckleRejectResult.getRight() / 1000000.0);
pipelineTimeString += String.format("GroupContours: %.2fms, ", groupContoursResult.getRight() / 1000000.0);
pipelineTimeString += String.format("SortContours: %.2fms, ", sortContoursResult.getRight() / 1000000.0);
pipelineTimeString += String.format("Collect2dTargets: %.2fms, ", collect2dTargetsResult.getRight() / 1000000.0);
pipelineTimeString += String.format("OutputMat: %.2fms, ", outputMatResult.getRight() / 1000000.0);
pipelineTimeString += String.format("Draw2dContours: %.2fms, ", result.getRight() / 1000000.0);
pipelineTimeString += String.format("Draw2dCrosshair: %.2fms, ", draw2dCrosshairResult.getRight() / 1000000.0);
System.out.println(pipelineTimeString);
double totalPipelineTimeMillis = totalPipelineTimeNanos / 1000000.0;
double totalPipelineTimeFPS = 1.0 / (totalPipelineTimeMillis / 1000.0);
double truePipelineTimeMillis = (System.nanoTime() - pipelineStartTimeNanos) / 1000000.0;
double truePipelineFPS = 1.0 / (truePipelineTimeMillis / 1000.0);
System.out.printf("Pipeline processed in %.3fms (%.2fFPS), ", totalPipelineTimeMillis, totalPipelineTimeFPS);
System.out.printf("full pipeline run time was %.3fms (%.2fFPS)\n", truePipelineTimeMillis, truePipelineFPS);
}
// memManager.run();
resultCache = new StandardCVPipelineResult(collect2dTargetsResult.getLeft(), outputMat, totalPipelineTimeNanos);
return resultCache;
}
public static class StandardCVPipelineResult extends CVPipelineResult<TrackedTarget> {
public StandardCVPipelineResult(List<TrackedTarget> targets, Mat outputMat, long processTimeNanos) {
super(targets, outputMat, processTimeNanos);
}
public void release() {
targets.forEach(TrackedTarget::release);
outputMat.release();
}
}
public static class TrackedTarget {
public double calibratedX = 0.0;
public double calibratedY = 0.0;
public double pitch = 0.0;
public double yaw = 0.0;
public double area = 0.0;
public Point point = new Point();
public RotatedRect minAreaRect;
public Rect boundingRect;
// 3d stuff
public Pose2d cameraRelativePose = new Pose2d();
public Mat rVector = new Mat();
public Mat tVector = new Mat();
public MatOfPoint2f imageCornerPoints = new MatOfPoint2f();
public Pair<Rect, Rect> leftRightDualTargetPair = null;
public Pair<RotatedRect, RotatedRect> leftRightRotatedRect = null;
public MatOfPoint2f rawContour = kMat2f;
public MatOfPoint2f approxPoly = new MatOfPoint2f();
public void release() {
rVector.release();
tVector.release();
imageCornerPoints.release();
}
private static final MatOfPoint2f kMat2f = new MatOfPoint2f();
}
}

View File

@@ -1,49 +0,0 @@
package org.photonvision._2.vision.pipeline.impl;
import com.chameleonvision._2.vision.enums.*;
import org.photonvision._2.vision.pipeline.CVPipelineSettings;
import org.photonvision.common.util.numbers.DoubleCouple;
import org.photonvision.common.util.numbers.IntegerCouple;
import org.opencv.core.MatOfPoint3f;
import org.opencv.core.Point3;
import org.photonvision._2.vision.enums.*;
public class StandardCVPipelineSettings extends CVPipelineSettings {
public IntegerCouple hue = new IntegerCouple(50, 180);
public IntegerCouple saturation = new IntegerCouple(50, 255);
public IntegerCouple value = new IntegerCouple(50, 255);
public boolean erode = false;
public boolean dilate = false;
public DoubleCouple area = new DoubleCouple(0.0, 100.0);
public DoubleCouple ratio = new DoubleCouple(0.0, 20.0);
public DoubleCouple extent = new DoubleCouple(0.0, 100.0);
public Number speckle = 5;
public boolean isBinary = false;
public SortMode sortMode = SortMode.Largest;
public TargetRegion targetRegion = TargetRegion.Center;
public TargetOrientation targetOrientation = TargetOrientation.Landscape;
public boolean multiple = false;
public TargetGroup targetGroup = TargetGroup.Single;
public TargetIntersection targetIntersection = TargetIntersection.Up;
public DoubleCouple point = new DoubleCouple();
public CalibrationMode calibrationMode = CalibrationMode.None;
public double dualTargetCalibrationM = 1;
public double dualTargetCalibrationB = 0;
// 3d stuff
public MatOfPoint3f targetCornerMat = new MatOfPoint3f();
public Number accuracy = 5;
private static MatOfPoint3f hexTargetMat =
new MatOfPoint3f(
new Point3(-19.625, 0, 0),
new Point3(-9.819867, -17, 0),
new Point3(9.819867, -17, 0),
new Point3(19.625, 0, 0));
public StandardCVPipelineSettings() {
super();
hexTargetMat.copyTo(targetCornerMat);
}
public boolean is3D = false;
}

View File

@@ -1,43 +0,0 @@
package org.photonvision._2.vision.pipeline.pipes;
import org.photonvision._2.vision.pipeline.Pipe;
import org.apache.commons.lang3.tuple.Pair;
import org.opencv.core.Mat;
public class BlurPipe implements Pipe<Mat, Mat> {
private int blurSize;
private Mat processBuffer = new Mat();
private Mat outputMat = new Mat();
public BlurPipe(int blurSize) {
this.blurSize = blurSize;
}
public void setConfig(int blurSize) {
this.blurSize = blurSize;
}
@Override
public Pair<Mat, Long> run(Mat input) {
long processStartNanos = System.nanoTime();
// if (blurSize > 0) {
// input.copyTo(processBuffer);
// try {
// Imgproc.blur(processBuffer, processBuffer, new Size(blurSize, blurSize));
// processBuffer.copyTo(outputMat);
// processBuffer.release();
// } catch (CvException e) {
// System.err.println("(BlurPipe) Exception thrown by OpenCV: \n" +
// e.getMessage());
// }
// } else {
// input.copyTo(outputMat);
// }
long processTime = System.nanoTime() - processStartNanos;
return Pair.of(input, processTime);
}
}

View File

@@ -1,156 +0,0 @@
package org.photonvision._2.vision.pipeline.pipes;
import org.photonvision._2.vision.camera.CaptureStaticProperties;
import org.photonvision._2.vision.enums.CalibrationMode;
import org.photonvision._2.vision.enums.TargetOrientation;
import org.photonvision._2.vision.enums.TargetRegion;
import org.photonvision._2.vision.pipeline.Pipe;
import org.photonvision._2.vision.pipeline.impl.StandardCVPipeline;
import org.photonvision.common.util.numbers.DoubleCouple;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.math3.util.FastMath;
import org.opencv.core.Point;
public class Collect2dTargetsPipe
implements Pipe<
Pair<List<StandardCVPipeline.TrackedTarget>, CaptureStaticProperties>,
List<StandardCVPipeline.TrackedTarget>> {
private CaptureStaticProperties camProps;
private CalibrationMode calibrationMode;
private DoubleCouple calibrationPoint;
private double calibrationM, calibrationB;
private TargetRegion targetRegion;
private TargetOrientation targetOrientation;
private List<StandardCVPipeline.TrackedTarget> targets = new ArrayList<>();
private Point[] vertices = new Point[4];
public Collect2dTargetsPipe(
CalibrationMode calibrationMode,
TargetRegion targetRegion,
TargetOrientation targetOrientation,
DoubleCouple calibrationPoint,
double calibrationM,
double calibrationB,
CaptureStaticProperties camProps) {
setConfig(
calibrationMode,
targetRegion,
targetOrientation,
calibrationPoint,
calibrationM,
calibrationB,
camProps);
}
public void setConfig(
CalibrationMode calibrationMode,
TargetRegion targetRegion,
TargetOrientation targetOrientation,
DoubleCouple calibrationPoint,
double calibrationM,
double calibrationB,
CaptureStaticProperties camProps) {
this.calibrationMode = calibrationMode;
this.calibrationPoint = calibrationPoint;
this.calibrationM = calibrationM;
this.calibrationB = calibrationB;
this.camProps = camProps;
this.targetRegion = targetRegion;
this.targetOrientation = targetOrientation;
}
@Override
public Pair<List<StandardCVPipeline.TrackedTarget>, Long> run(
Pair<List<StandardCVPipeline.TrackedTarget>, CaptureStaticProperties> inputPair) {
long processStartNanos = System.nanoTime();
targets.clear();
var input = inputPair.getLeft();
var imageArea = inputPair.getRight().imageArea;
if (input.size() > 0) {
for (var t : input) {
t.minAreaRect.points(vertices);
Point bl = getMiddle(vertices[0], vertices[1]);
Point tl = getMiddle(vertices[1], vertices[2]);
Point tr = getMiddle(vertices[2], vertices[3]);
Point br = getMiddle(vertices[3], vertices[0]);
boolean orientation;
if (targetOrientation == TargetOrientation.Landscape) {
orientation = t.minAreaRect.size.width > t.minAreaRect.size.height;
} else {
orientation = t.minAreaRect.size.width < t.minAreaRect.size.height;
}
Point result = t.minAreaRect.center;
switch (this.targetRegion) {
case Top:
{
result = orientation ? tl : tr;
break;
}
case Bottom:
{
result = orientation ? br : bl;
break;
}
case Left:
{
result = orientation ? bl : tl;
break;
}
case Right:
{
result = orientation ? tr : br;
break;
}
}
t.point = result;
switch (this.calibrationMode) {
case Single:
if (this.calibrationPoint.equals(new DoubleCouple())) {
this.calibrationPoint.set(camProps.centerX, camProps.centerY);
}
t.calibratedX = this.calibrationPoint.getFirst();
t.calibratedY = this.calibrationPoint.getSecond();
break;
case None:
t.calibratedX = camProps.centerX;
t.calibratedY = camProps.centerY;
break;
case Dual:
t.calibratedX = (t.point.x - this.calibrationB) / this.calibrationM;
t.calibratedY = (t.point.y * this.calibrationM) + this.calibrationB;
break;
}
t.pitch = calculatePitch(t.point.y, t.calibratedY);
t.yaw = calculateYaw(t.point.x, t.calibratedX);
t.area = t.minAreaRect.size.area() / imageArea;
targets.add(t);
}
}
long processTime = System.nanoTime() - processStartNanos;
return Pair.of(targets, processTime);
}
private double calculatePitch(double pixelY, double centerY) {
double pitch =
FastMath.toDegrees(FastMath.atan((pixelY - centerY) / camProps.verticalFocalLength));
return (pitch * -1);
}
private double calculateYaw(double pixelX, double centerX) {
return FastMath.toDegrees(FastMath.atan((pixelX - centerX) / camProps.horizontalFocalLength));
}
private Point getMiddle(Point p1, Point p2) {
return new Point(((p1.x + p2.x) / 2), ((p1.y + p2.y) / 2));
}
}

View File

@@ -1,116 +0,0 @@
package org.photonvision._2.vision.pipeline.pipes;
import org.photonvision._2.vision.camera.CaptureStaticProperties;
import org.photonvision._2.vision.pipeline.Pipe;
import org.photonvision._2.vision.pipeline.impl.StandardCVPipeline;
import org.photonvision.common.util.ColorHelper;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import org.opencv.core.*;
import org.opencv.core.Point;
import org.opencv.imgproc.Imgproc;
public class Draw2dContoursPipe
implements Pipe<Pair<Mat, List<StandardCVPipeline.TrackedTarget>>, Mat> {
private final Draw2dContoursSettings settings;
private CaptureStaticProperties camProps;
private Mat processBuffer = new Mat();
private Mat outputMat = new Mat();
private Point[] vertices = new Point[4];
private List<MatOfPoint> drawnContours = new ArrayList<>();
private MatOfPoint contour = new MatOfPoint();
@SuppressWarnings("FieldCanBeLocal")
private Point xMax = new Point(), xMin = new Point(), yMax = new Point(), yMin = new Point();
public Draw2dContoursPipe(Draw2dContoursSettings settings, CaptureStaticProperties camProps) {
this.settings = settings;
this.camProps = camProps;
}
public void setConfig(boolean showMultiple, CaptureStaticProperties captureProps) {
settings.showMultiple = showMultiple;
camProps = captureProps;
}
@Override
public Pair<Mat, Long> run(Pair<Mat, List<StandardCVPipeline.TrackedTarget>> input) {
long processStartNanos = System.nanoTime();
if (settings.showCentroid || settings.showMaximumBox || settings.showRotatedBox) {
// input.getLeft().copyTo(processBuffer);
// processBuffer = input.getLeft();
if (input.getRight().size() > 0) {
for (int i = 0; i < input.getRight().size(); i++) {
if (i != 0 && !settings.showMultiple) {
break;
}
StandardCVPipeline.TrackedTarget target = input.getRight().get(i);
RotatedRect r = input.getRight().get(i).minAreaRect;
if (r == null) continue;
drawnContours.forEach(Mat::release);
drawnContours.clear();
drawnContours = new ArrayList<>();
r.points(vertices);
contour.fromArray(vertices);
// MatOfPoint contour = new MatOfPoint(vertices);
drawnContours.add(contour);
if (settings.showRotatedBox) {
Imgproc.drawContours(
input.getLeft(),
drawnContours,
0,
ColorHelper.colorToScalar(settings.rotatedBoxColor),
settings.boxOutlineSize);
}
if (settings.showMaximumBox) {
Rect box = Imgproc.boundingRect(contour);
Imgproc.rectangle(
input.getLeft(),
new Point(box.x, box.y),
new Point((box.x + box.width), (box.y + box.height)),
ColorHelper.colorToScalar(settings.maximumBoxColor),
settings.boxOutlineSize);
}
if (settings.showCentroid) {
Imgproc.circle(
input.getLeft(),
target.point,
3,
ColorHelper.colorToScalar(settings.centroidColor),
2);
}
// contour.release();
}
}
// processBuffer.copyTo(outputMat);
// processBuffer.release();
} else {
// input.getLeft().copyTo(outputMat);
}
long processTime = System.nanoTime() - processStartNanos;
return Pair.of(input.getLeft(), processTime);
}
public static class Draw2dContoursSettings {
public boolean showCentroid = false;
public boolean showMultiple = false;
public int boxOutlineSize = 0;
public boolean showRotatedBox = false;
public boolean showMaximumBox = false;
public Color centroidColor = Color.GREEN;
public Color rotatedBoxColor = Color.BLUE;
public Color maximumBoxColor = Color.RED;
}
}

View File

@@ -1,102 +0,0 @@
package org.photonvision._2.vision.pipeline.pipes;
import org.photonvision._2.vision.enums.CalibrationMode;
import org.photonvision._2.vision.pipeline.Pipe;
import org.photonvision._2.vision.pipeline.impl.StandardCVPipeline;
import org.photonvision.common.util.ColorHelper;
import org.photonvision.common.util.numbers.DoubleCouple;
import java.awt.*;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.imgproc.Imgproc;
public class Draw2dCrosshairPipe
implements Pipe<Pair<Mat, List<StandardCVPipeline.TrackedTarget>>, Mat> {
// Settings
private Draw2dCrosshairPipeSettings crosshairSettings;
private CalibrationMode calibrationMode;
private DoubleCouple calibrationPoint;
private double calibrationM, calibrationB;
private Point xMax = new Point(), xMin = new Point(), yMax = new Point(), yMin = new Point();
public Draw2dCrosshairPipe(
Draw2dCrosshairPipeSettings crosshairSettings,
CalibrationMode calibrationMode,
DoubleCouple calibrationPoint,
double calibrationM,
double calibrationB) {
setConfig(crosshairSettings, calibrationMode, calibrationPoint, calibrationM, calibrationB);
}
public void setConfig(
Draw2dCrosshairPipeSettings crosshairSettings,
CalibrationMode calibrationMode,
DoubleCouple calibrationPoint,
double calibrationM,
double calibrationB) {
this.crosshairSettings = crosshairSettings;
this.calibrationMode = calibrationMode;
this.calibrationPoint = calibrationPoint;
this.calibrationM = calibrationM;
this.calibrationB = calibrationB;
}
@Override
public Pair<Mat, Long> run(Pair<Mat, List<StandardCVPipeline.TrackedTarget>> inputPair) {
long processStartNanos = System.nanoTime();
Mat image = inputPair.getLeft();
// List<StandardCVPipeline.TrackedTarget> targets = inputPair.getRight();
double x, y;
double scale = image.cols() / 32.0;
drawCrosshair:
if (this.crosshairSettings.showCrosshair) {
x = image.cols() / 2.0;
y = image.rows() / 2.0;
switch (this.calibrationMode) {
case Single:
if (this.calibrationPoint.equals(new DoubleCouple())) {
this.calibrationPoint.set(x, y);
}
x = this.calibrationPoint.getFirst().intValue();
y = this.calibrationPoint.getSecond().intValue();
break;
case Dual:
// if (targets != null && !targets.isEmpty()) {
// x = targets.get(0).calibratedX;
// y = targets.get(0).calibratedY;
// //TODO dual point calibration crosshair checks
// } else
// break drawCrosshair;
break;
}
xMax.set(new double[] {x + scale, y});
xMin.set(new double[] {x - scale, y});
yMax.set(new double[] {x, y + scale});
yMin.set(new double[] {x, y - scale});
Imgproc.line(
inputPair.getLeft(),
xMax,
xMin,
ColorHelper.colorToScalar(this.crosshairSettings.crosshairColor),
2);
Imgproc.line(
inputPair.getLeft(),
yMax,
yMin,
ColorHelper.colorToScalar(this.crosshairSettings.crosshairColor),
2);
}
long processTime = System.nanoTime() - processStartNanos;
return Pair.of(inputPair.getLeft(), processTime);
}
public static class Draw2dCrosshairPipeSettings {
public boolean showCrosshair = true;
public Color crosshairColor = Color.GREEN;
}
}

View File

@@ -1,134 +0,0 @@
package org.photonvision._2.vision.pipeline.pipes;
import org.photonvision._2.config.CameraCalibrationConfig;
import org.photonvision._2.vision.pipeline.Pipe;
import org.photonvision._2.vision.pipeline.impl.StandardCVPipeline;
import org.photonvision._2.vision.pipeline.impl.StandardCVPipelineSettings;
import org.photonvision.common.util.ColorHelper;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.tuple.Pair;
import org.opencv.calib3d.Calib3d;
import org.opencv.core.*;
import org.opencv.core.Point;
import org.opencv.imgproc.Imgproc;
public class DrawSolvePNPPipe
implements Pipe<Pair<Mat, List<StandardCVPipeline.TrackedTarget>>, Mat> {
private MatOfPoint3f boxCornerMat = new MatOfPoint3f();
public Scalar green = ColorHelper.colorToScalar(Color.GREEN);
public Scalar blue = ColorHelper.colorToScalar(Color.BLUE);
public Scalar red = ColorHelper.colorToScalar(Color.RED);
public Scalar orange = ColorHelper.colorToScalar(Color.orange);
public DrawSolvePNPPipe(
StandardCVPipelineSettings standardCVPipelineSettings, CameraCalibrationConfig settings) {
setConfig(settings);
setBox(standardCVPipelineSettings.targetCornerMat);
}
private void setBox(MatOfPoint3f mat) {
boxCornerMat.release();
var list = mat.toList();
var auxList =
list.stream().map(it -> new Point3(it.x, it.y, it.z + 6)).collect(Collectors.toList());
var finalList = new ArrayList<>(list);
finalList.addAll(auxList);
boxCornerMat.fromList(finalList);
}
public void setConfig(StandardCVPipelineSettings settings) {
setBox(settings.targetCornerMat);
}
private Mat cameraMatrix = new Mat();
private MatOfDouble distortionCoefficients = new MatOfDouble();
public void setConfig(CameraCalibrationConfig config) {
if (config == null) {
System.err.println("got passed a null config! Returning...");
return;
}
setConfig(config.getCameraMatrixAsMat(), config.getDistortionCoeffsAsMat());
}
public void setConfig(Mat cameraMatrix_, MatOfDouble distortionMatrix_) {
this.cameraMatrix = cameraMatrix_;
this.distortionCoefficients = distortionMatrix_;
}
MatOfPoint2f imagePoints = new MatOfPoint2f();
@Override
public Pair<Mat, Long> run(Pair<Mat, List<StandardCVPipeline.TrackedTarget>> targets) {
long processStartNanos = System.nanoTime();
var image = targets.getLeft();
for (var it : targets.getRight()) {
try {
Calib3d.projectPoints(
boxCornerMat,
it.rVector,
it.tVector,
this.cameraMatrix,
this.distortionCoefficients,
imagePoints,
new Mat(),
0);
} catch (Exception e) {
e.printStackTrace();
}
var pts = imagePoints.toList();
// draw left and right targets if possible
if (it.leftRightDualTargetPair != null) {
var left = it.leftRightDualTargetPair.getLeft();
var right = it.leftRightDualTargetPair.getRight();
Imgproc.rectangle(image, left.tl(), left.br(), new Scalar(200, 200, 0), 4);
Imgproc.rectangle(image, right.tl(), right.br(), new Scalar(200, 200, 0), 2);
}
// draw poly dp
var list = it.approxPoly.toList();
for (int i = 0; i < list.size(); i++) {
var next = (i == list.size() - 1) ? list.get(0) : list.get(i + 1);
Imgproc.line(image, list.get(i), next, red, 2);
}
// draw center
Imgproc.circle(image, it.minAreaRect.center, 5, red);
// draw corners
for (int i = 0; i < it.imageCornerPoints.rows(); i++) {
var point = new Point(it.imageCornerPoints.get(i, 0));
Imgproc.circle(image, point, 4, green, 5);
}
// sketch out floor
Imgproc.line(image, pts.get(0), pts.get(1), green, 3);
Imgproc.line(image, pts.get(1), pts.get(2), green, 3);
Imgproc.line(image, pts.get(2), pts.get(3), green, 3);
Imgproc.line(image, pts.get(3), pts.get(0), green, 3);
// draw pillars
Imgproc.line(image, pts.get(0), pts.get(4), blue, 3);
Imgproc.line(image, pts.get(1), pts.get(5), blue, 3);
Imgproc.line(image, pts.get(2), pts.get(6), blue, 3);
Imgproc.line(image, pts.get(3), pts.get(7), blue, 3);
// draw top
Imgproc.line(image, pts.get(4), pts.get(5), red, 3);
Imgproc.line(image, pts.get(5), pts.get(6), red, 3);
Imgproc.line(image, pts.get(6), pts.get(7), red, 3);
Imgproc.line(image, pts.get(7), pts.get(4), red, 3);
}
long processTime = System.nanoTime() - processStartNanos;
return Pair.of(image, processTime);
}
}

View File

@@ -1,51 +0,0 @@
package org.photonvision._2.vision.pipeline.pipes;
import org.photonvision._2.vision.pipeline.Pipe;
import org.apache.commons.lang3.tuple.Pair;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
public class ErodeDilatePipe implements Pipe<Mat, Mat> {
private boolean erode;
private boolean dilate;
private Mat kernel;
public ErodeDilatePipe(boolean erode, boolean dilate, int kernelSize) {
this.erode = erode;
this.dilate = dilate;
kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(kernelSize, kernelSize));
}
public void setConfig(boolean erode, boolean dilate, int kernelSize) {
this.erode = erode;
this.dilate = dilate;
kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(kernelSize, kernelSize));
}
@Override
public Pair<Mat, Long> run(Mat input) {
long processStartNanos = System.nanoTime();
if (erode || dilate) {
// input.copyTo(processBuffer);
if (erode) {
Imgproc.erode(input, input, kernel);
}
if (dilate) {
Imgproc.dilate(input, input, kernel);
}
// processBuffer.copyTo(outputMat);
// processBuffer.release();
} else {
// input.copyTo(outputMat);
}
long processTime = System.nanoTime() - processStartNanos;
return Pair.of(input, processTime);
}
}

View File

@@ -1,97 +0,0 @@
package org.photonvision._2.vision.pipeline.pipes;
import org.photonvision._2.vision.camera.CaptureStaticProperties;
import org.photonvision._2.vision.pipeline.Pipe;
import org.photonvision.common.util.math.MathUtils;
import org.photonvision.common.util.numbers.DoubleCouple;
import org.photonvision.common.vision.opencv.Contour;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Rect;
import org.opencv.core.RotatedRect;
public class FilterContoursPipe implements Pipe<List<MatOfPoint>, List<Contour>> {
private DoubleCouple area;
private DoubleCouple ratio;
private DoubleCouple extent;
private CaptureStaticProperties camProps;
private List<Contour> filteredContours = new ArrayList<>();
public FilterContoursPipe(
DoubleCouple area,
DoubleCouple ratio,
DoubleCouple extent,
CaptureStaticProperties camProps) {
this.area = area;
this.ratio = ratio;
this.extent = extent;
this.camProps = camProps;
}
public void setConfig(
DoubleCouple area,
DoubleCouple ratio,
DoubleCouple extent,
CaptureStaticProperties camProps) {
this.area = area;
this.ratio = ratio;
this.extent = extent;
this.camProps = camProps;
}
private void filterContour(MatOfPoint contourPoints) {
Contour realContour = new Contour(contourPoints);
// Area Filtering
double contourArea = realContour.getArea();
double areaRatio = (contourArea / camProps.imageArea) * 100;
double minArea = (MathUtils.sigmoid(area.getFirst()));
double maxArea = (MathUtils.sigmoid(area.getSecond()));
if (areaRatio < minArea || areaRatio > maxArea) {
return;
}
// TargetFillPercentage filtering
RotatedRect minAreaRect = realContour.getMinAreaRect();
double minExtent = (extent.getFirst() * minAreaRect.size.area()) / 100;
double maxExtent = (extent.getSecond() * minAreaRect.size.area()) / 100;
if (contourArea <= minExtent || contourArea >= maxExtent) {
return;
}
// AspectRatio filtering
Rect boundingRect = realContour.getBoundingRect();
double aspectRatio = ((double) boundingRect.width / boundingRect.height);
if (aspectRatio < ratio.getFirst() || aspectRatio > ratio.getSecond()) {
return;
}
filteredContours.add(realContour);
}
@Override
public Pair<List<Contour>, Long> run(List<MatOfPoint> input) {
long processStartNanos = System.nanoTime();
filteredContours.clear();
if (input.size() > 0) {
for (MatOfPoint contour : input) {
try {
filterContour(contour);
} catch (Exception e) {
System.err.println("Error while filtering contours");
e.printStackTrace();
}
}
}
long processTime = System.nanoTime() - processStartNanos;
return Pair.of(filteredContours, processTime);
}
}

View File

@@ -1,32 +0,0 @@
package org.photonvision._2.vision.pipeline.pipes;
import org.photonvision._2.vision.pipeline.Pipe;
import org.photonvision.common.vision.opencv.Contour;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.tuple.Pair;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.imgproc.Imgproc;
public class FindContoursPipe implements Pipe<Mat, List<Contour>> {
private List<MatOfPoint> foundContours = new ArrayList<>();
public FindContoursPipe() {}
@Override
public Pair<List<Contour>, Long> run(Mat input) {
long processStartNanos = System.nanoTime();
foundContours.clear();
Imgproc.findContours(
input, foundContours, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_TC89_L1);
long processTime = System.nanoTime() - processStartNanos;
return Pair.of(
foundContours.stream().map(Contour::new).collect(Collectors.toList()), processTime);
}
}

View File

@@ -1,201 +0,0 @@
package org.photonvision._2.vision.pipeline.pipes;
import org.photonvision._2.vision.enums.TargetGroup;
import org.photonvision._2.vision.enums.TargetIntersection;
import org.photonvision._2.vision.pipeline.Pipe;
import org.photonvision._2.vision.pipeline.impl.StandardCVPipeline;
import org.photonvision.common.util.math.MathUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import org.opencv.core.*;
import org.opencv.imgproc.Imgproc;
import org.opencv.imgproc.Moments;
public class GroupContoursPipe
implements Pipe<List<MatOfPoint>, List<StandardCVPipeline.TrackedTarget>> {
private static final Comparator<MatOfPoint> sortByMomentsX =
Comparator.comparingDouble(GroupContoursPipe::calcMomentsX);
private TargetGroup group;
private TargetIntersection intersection;
private MatOfPoint2f contourBuffer = new MatOfPoint2f();
private List<StandardCVPipeline.TrackedTarget> groupedContours = new ArrayList<>();
private MatOfPoint2f intersectMatA = new MatOfPoint2f();
private MatOfPoint2f intersectMatB = new MatOfPoint2f();
public GroupContoursPipe(TargetGroup group, TargetIntersection intersection) {
this.group = group;
this.intersection = intersection;
}
public void setConfig(TargetGroup group, TargetIntersection intersection) {
this.group = group;
this.intersection = intersection;
}
@Override
public Pair<List<StandardCVPipeline.TrackedTarget>, Long> run(List<MatOfPoint> input) {
long processStartNanos = System.nanoTime();
groupedContours.forEach(StandardCVPipeline.TrackedTarget::release);
groupedContours.clear();
contourBuffer.release();
if (input.size() > (group.equals(TargetGroup.Single) ? 0 : 1)) {
List<MatOfPoint> sorted = new ArrayList<>(input);
sorted.sort(sortByMomentsX);
Collections.reverse(sorted);
switch (group) {
case Single:
{
input.forEach(
c -> {
contourBuffer.fromArray(c.toArray());
if (contourBuffer.cols() != 0 && contourBuffer.rows() != 0) {
RotatedRect rect = Imgproc.minAreaRect(contourBuffer);
Rect boundingRect = Imgproc.boundingRect(contourBuffer);
var target = new StandardCVPipeline.TrackedTarget();
target.minAreaRect = rect;
target.rawContour = contourBuffer;
target.boundingRect = boundingRect;
groupedContours.add(target);
}
});
break;
}
case Dual:
{
for (var i = 0; i < input.size(); i++) {
List<Point> finalContourList = new ArrayList<>(input.get(i).toList());
try {
MatOfPoint firstContour = input.get(i);
MatOfPoint secondContour = input.get(i + 1);
if (isIntersecting(firstContour, secondContour)) {
finalContourList.addAll(secondContour.toList());
} else {
finalContourList.clear();
continue;
}
intersectMatA.release();
intersectMatB.release();
contourBuffer.fromList(finalContourList);
if (contourBuffer.cols() != 0 && contourBuffer.rows() != 0) {
RotatedRect rect = Imgproc.minAreaRect(contourBuffer);
Rect boundingRect = Imgproc.boundingRect(contourBuffer);
var target = new StandardCVPipeline.TrackedTarget();
target.minAreaRect = rect;
target.boundingRect = boundingRect;
// find left and right bouding rectangles
target.leftRightDualTargetPair =
Pair.of(
Imgproc.boundingRect(firstContour), Imgproc.boundingRect(secondContour));
// find left and right min area rectangles
tempRectMat.fromArray(firstContour.toArray());
var minAreaRect1 = Imgproc.minAreaRect(tempRectMat);
tempRectMat.fromArray(secondContour.toArray());
var minAreaRect2 = Imgproc.minAreaRect(tempRectMat);
target.leftRightRotatedRect = Pair.of(minAreaRect1, minAreaRect2);
target.rawContour = contourBuffer;
groupedContours.add(target);
firstContour.release();
secondContour.release();
// skip the next contour because it's been grouped already
i += 1;
}
} catch (IndexOutOfBoundsException e) {
finalContourList.clear();
}
}
break;
}
}
}
long processTime = System.nanoTime() - processStartNanos;
return Pair.of(groupedContours, processTime);
}
MatOfPoint2f tempRectMat = new MatOfPoint2f();
private static double calcMomentsX(MatOfPoint c) {
Moments m = Imgproc.moments(c);
return (m.get_m10() / m.get_m00());
}
private boolean isIntersecting(MatOfPoint contourOne, MatOfPoint contourTwo) {
if (intersection.equals(TargetIntersection.None)) {
return true;
}
try {
intersectMatA.fromArray(contourOne.toArray());
intersectMatB.fromArray(contourTwo.toArray());
RotatedRect a = Imgproc.fitEllipse(intersectMatA);
RotatedRect b = Imgproc.fitEllipse(intersectMatB);
double mA = MathUtils.toSlope(a.angle);
double mB = MathUtils.toSlope(b.angle);
double x0A = a.center.x;
double y0A = a.center.y;
double x0B = b.center.x;
double y0B = b.center.y;
double intersectionX = ((mA * x0A) - y0A - (mB * x0B) + y0B) / (mA - mB);
double intersectionY = (mA * (intersectionX - x0A)) + y0A;
double massX = (x0A + x0B) / 2;
double massY = (y0A + y0B) / 2;
switch (intersection) {
case Up:
{
if (intersectionY < massY) {
return true;
}
break;
}
case Down:
{
if (intersectionY > massY) {
return true;
}
break;
}
case Left:
{
if (intersectionX < massX) {
return true;
}
break;
}
case Right:
{
if (intersectionX > massX) {
return true;
}
break;
}
}
return false;
} catch (Exception e) {
return false;
}
}
}

View File

@@ -1,45 +0,0 @@
package org.photonvision._2.vision.pipeline.pipes;
import org.photonvision._2.vision.pipeline.Pipe;
import org.apache.commons.lang3.tuple.Pair;
import org.opencv.core.Core;
import org.opencv.core.CvException;
import org.opencv.core.Mat;
import org.opencv.core.Scalar;
import org.opencv.imgproc.Imgproc;
public class HsvPipe implements Pipe<Mat, Mat> {
private Scalar hsvLower;
private Scalar hsvUpper;
private Mat processBuffer = new Mat();
private Mat outputMat = new Mat();
public HsvPipe(Scalar hsvLower, Scalar hsvUpper) {
this.hsvLower = hsvLower;
this.hsvUpper = hsvUpper;
}
public void setConfig(Scalar hsvLower, Scalar hsvUpper) {
this.hsvLower = hsvLower;
this.hsvUpper = hsvUpper;
}
@Override
public Pair<Mat, Long> run(Mat input) {
long processStartNanos = System.nanoTime();
input.copyTo(outputMat);
try {
Imgproc.cvtColor(outputMat, outputMat, Imgproc.COLOR_BGR2HSV, 3);
Core.inRange(outputMat, hsvLower, hsvUpper, outputMat);
} catch (CvException e) {
System.err.println("(HsvPipe) Exception thrown by OpenCV: \n" + e.getMessage());
}
long processTime = System.nanoTime() - processStartNanos;
return Pair.of(outputMat, processTime);
}
}

View File

@@ -1,49 +0,0 @@
package org.photonvision._2.vision.pipeline.pipes;
import org.photonvision._2.vision.pipeline.Pipe;
import org.apache.commons.lang3.tuple.Pair;
import org.opencv.core.CvException;
import org.opencv.core.Mat;
import org.opencv.imgproc.Imgproc;
public class OutputMatPipe implements Pipe<Pair<Mat, Mat>, Mat> {
private boolean showThresholded;
private Mat processBuffer = new Mat();
private Mat outputMat = new Mat();
public OutputMatPipe(boolean showThresholded) {
this.showThresholded = showThresholded;
}
public void setConfig(boolean showThresholded) {
this.showThresholded = showThresholded;
}
/**
* @param input Input object for pipe Left is raw camera mat (8UC3), Right is HSV threshold mat
* (8UC1)
* @return Returns desired output Mat, and processing time in nanoseconds
*/
@Override
public Pair<Mat, Long> run(Pair<Mat, Mat> input) {
long processStartNanos = System.nanoTime();
if (showThresholded) {
try {
input.getRight().copyTo(processBuffer);
Imgproc.cvtColor(processBuffer, processBuffer, Imgproc.COLOR_GRAY2BGR, 3);
processBuffer.copyTo(outputMat);
processBuffer.release();
} catch (CvException e) {
System.err.println("(OutputMat) Exception thrown by OpenCV: \n" + e.getMessage());
}
} else {
input.getLeft().copyTo(outputMat);
}
long processTime = System.nanoTime() - processStartNanos;
return Pair.of(outputMat, processTime);
}
}

View File

@@ -1,55 +0,0 @@
package org.photonvision._2.vision.pipeline.pipes;
import org.photonvision._2.vision.enums.ImageFlipMode;
import org.photonvision._2.vision.enums.ImageRotationMode;
import org.photonvision._2.vision.pipeline.Pipe;
import org.apache.commons.lang3.tuple.Pair;
import org.opencv.core.Core;
import org.opencv.core.Mat;
public class RotateFlipPipe implements Pipe<Mat, Mat> {
private ImageRotationMode rotation;
private ImageFlipMode flip;
private Mat processBuffer = new Mat();
private Mat outputMat = new Mat();
public RotateFlipPipe(ImageRotationMode rotation, ImageFlipMode flip) {
this.rotation = rotation;
this.flip = flip;
}
public void setConfig(ImageRotationMode rotation, ImageFlipMode flip) {
this.rotation = rotation;
this.flip = flip;
}
@Override
public Pair<Mat, Long> run(Mat input) {
long processStartNanos = System.nanoTime();
boolean shouldFlip = !flip.equals(ImageFlipMode.NONE);
boolean shouldRotate = !rotation.equals(ImageRotationMode.DEG_0);
if (shouldFlip || shouldRotate) {
// input.copyTo(processBuffer);
if (shouldFlip) {
Core.flip(input, input, flip.value);
}
if (shouldRotate) {
Core.rotate(input, input, rotation.value);
}
// processBuffer.copyTo(outputMat);
// processBuffer.release();
} else {
// input.copyTo(outputMat);
}
long processTime = System.nanoTime() - processStartNanos;
return Pair.of(input, processTime);
}
}

View File

@@ -1,534 +0,0 @@
package org.photonvision._2.vision.pipeline.pipes;
import org.photonvision._2.config.CameraCalibrationConfig;
import org.photonvision._2.vision.pipeline.Pipe;
import org.photonvision._2.vision.pipeline.impl.StandardCVPipeline;
import org.photonvision._2.vision.pipeline.impl.StandardCVPipelineSettings;
import edu.wpi.first.wpilibj.geometry.Pose2d;
import edu.wpi.first.wpilibj.geometry.Rotation2d;
import edu.wpi.first.wpilibj.geometry.Translation2d;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.stream.Collectors;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.math3.util.FastMath;
import org.opencv.calib3d.Calib3d;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfDouble;
import org.opencv.core.MatOfPoint;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.MatOfPoint3f;
import org.opencv.core.Point;
import org.opencv.core.Point3;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.core.TermCriteria;
import org.opencv.imgproc.Imgproc;
/** Handles detecting target corners and calculating robot-relative pose. */
public class SolvePNPPipe
implements Pipe<
Pair<List<StandardCVPipeline.TrackedTarget>, Mat>, List<StandardCVPipeline.TrackedTarget>> {
private Double tilt_angle;
private MatOfPoint3f objPointsMat = new MatOfPoint3f();
private Mat rVec = new Mat();
private Mat tVec = new Mat();
private Mat rodriguez = new Mat();
private Mat pzero_world = new Mat();
private Mat cameraMatrix = new Mat();
Mat rot_inv = new Mat();
Mat kMat = new Mat();
private MatOfDouble distortionCoefficients = new MatOfDouble();
private List<StandardCVPipeline.TrackedTarget> targetList = new ArrayList<>();
Comparator<Point> leftRightComparator = Comparator.comparingDouble(point -> point.x);
Comparator<Point> verticalComparator = Comparator.comparingDouble(point -> point.y);
private double distanceDivisor = 1.0;
Mat scaledTvec = new Mat();
MatOfPoint2f boundingBoxResultMat = new MatOfPoint2f();
MatOfPoint2f polyOutput = new MatOfPoint2f();
private Mat greyImg = new Mat();
private double accuracyPercentage = 0.2;
/**
* @param settings unused :bolb:
* @param calibration the camera intrinsics and extrinsics
* @param tilt The pitch of the camera relative to horzontal. used to account for distances in
* calculate pose
*/
public SolvePNPPipe(
StandardCVPipelineSettings settings, CameraCalibrationConfig calibration, Rotation2d tilt) {
super();
setCameraCoeffs(calibration);
// setBoundingBoxTarget(settings.targetWidth, settings.targetHeight);
// TODO add proper year differentiation
set2020Target(true);
this.tilt_angle = tilt.getRadians();
}
public void set2020Target(boolean isHighGoal) {
if (isHighGoal) {
// tl, bl, br, tr is the order
List<Point3> corners =
List.of(
new Point3(-19.625, 0, 0),
new Point3(-9.819867, -17, 0),
new Point3(9.819867, -17, 0),
new Point3(19.625, 0, 0));
setObjectCorners(corners);
} else {
setBoundingBoxTarget(7, 11);
}
}
public void setBoundingBoxTarget(double targetWidth, double targetHeight) {
// order is left top, left bottom, right bottom, right top
List<Point3> corners =
List.of(
new Point3(-targetWidth / 2.0, targetHeight / 2.0, 0.0),
new Point3(-targetWidth / 2.0, -targetHeight / 2.0, 0.0),
new Point3(targetWidth / 2.0, -targetHeight / 2.0, 0.0),
new Point3(targetWidth / 2.0, targetHeight / 2.0, 0.0));
setObjectCorners(corners);
}
public void setObjectCorners(List<Point3> objectCorners) {
objPointsMat.release();
objPointsMat = new MatOfPoint3f();
objPointsMat.fromList(objectCorners);
}
public void setConfig(
StandardCVPipelineSettings settings, CameraCalibrationConfig camConfig, Rotation2d tilt) {
setCameraCoeffs(camConfig);
// setBoundingBoxTarget(settings.targetWidth, settings.targetHeight);
// TODO add proper year differentiation
tilt_angle = tilt.getRadians();
this.objPointsMat = settings.targetCornerMat;
this.accuracyPercentage = settings.accuracy.doubleValue();
}
private void setCameraCoeffs(CameraCalibrationConfig settings) {
if (settings == null) {
System.err.println(
"SolvePNP can only run on a calibrated resolution, and this one is not!"
+ " Please calibrate to use solvePNP.");
return;
}
if (cameraMatrix != settings.getCameraMatrixAsMat()) {
cameraMatrix.release();
settings.getCameraMatrixAsMat().copyTo(cameraMatrix);
}
if (distortionCoefficients != settings.getDistortionCoeffsAsMat()) {
distortionCoefficients.release();
settings.getDistortionCoeffsAsMat().copyTo(distortionCoefficients);
}
this.distanceDivisor = settings.squareSize;
}
@Override
public Pair<List<StandardCVPipeline.TrackedTarget>, Long> run(
Pair<List<StandardCVPipeline.TrackedTarget>, Mat> imageTargetPair) {
long processStartNanos = System.nanoTime();
var targets = imageTargetPair.getLeft();
var image = imageTargetPair.getRight();
Imgproc.cvtColor(image, greyImg, Imgproc.COLOR_BGR2GRAY);
targetList.clear();
for (var target : targets) {
MatOfPoint2f corners;
// if it's a dual target use 2019, but default to 2020
if (target.leftRightRotatedRect == null) {
corners = find2020VisionTarget(target, accuracyPercentage); // , imageTargetPair.getRight
// ()); //find2020VisionTarget(target);// (target.leftRightDualTargetPair != null) ?
// findCorner2019(target) : findBoundingBoxCorners(target);
} else {
corners = findCorner2019(target);
}
// var corners = findCorner2019(target);
if (corners == null) continue;
// convert the corners into a Pose2d
var pose = calculatePose(corners, target);
targetList.add(pose); // TODO null check null poses. DO NOT ADD A NULL CHECK HERE, otherwise
// the order will be wrong.
}
long processTime = System.nanoTime() - processStartNanos;
return Pair.of(targetList, processTime);
}
/**
* basically we split the target's two tapes, find the min area rectangle for each, and take the
* outermost 4 corners out of the 2 rectangles
*
* @param target the target to use
* @return the 4 outermost corners.
*/
private MatOfPoint2f findCorner2019(StandardCVPipeline.TrackedTarget target) {
if (target.leftRightDualTargetPair == null) return null;
var left = target.leftRightDualTargetPair.getLeft();
var right = target.leftRightDualTargetPair.getRight();
// flip if the "left" target is to the right
if (left.x > right.x) {
var temp = left;
left = right;
right = temp;
}
var points = new MatOfPoint2f();
points.fromArray(
new Point(left.x, left.y + left.height),
new Point(left.x, left.y),
new Point(right.x + right.width, right.y),
new Point(right.x + right.width, right.y + right.height));
return points;
}
MatOfPoint2f target2020ResultMat = new MatOfPoint2f();
private double distanceBetween(Point a, Point b) {
return FastMath.sqrt(FastMath.pow(a.x - b.x, 2) + FastMath.pow(a.y - b.y, 2));
}
/**
* Find the target using the outermost tape corners and a 2020 target. Uses approxPolyDP to
* approximate the target outline.
*
* @param target the target.
* @return The four outermost tape corners.
*/
private MatOfPoint2f find2020VisionTarget(
StandardCVPipeline.TrackedTarget target, double accuracyPercentage) {
if (target.rawContour.cols() < 1) return null;
var centroid = target.minAreaRect.center;
Comparator<Point> distanceProvider =
Comparator.comparingDouble(
(Point point) ->
FastMath.sqrt(
FastMath.pow(centroid.x - point.x, 2) + FastMath.pow(centroid.y - point.y, 2)));
// algorithm from team 4915
// Contour perimeter
var peri = Imgproc.arcLength(target.rawContour, true);
// approximating a shape around the contours
// Can be tuned to allow/disallow hulls
// Approx is the number of vertices
// RamerDouglasPeucker algorithm
// we want a number between 0 and 0.16 out of a percentage from 0 to 100
// so take accuracy and divide by 600
Imgproc.approxPolyDP(target.rawContour, polyOutput, accuracyPercentage / 600.0 * peri, true);
var area = Imgproc.moments(polyOutput);
// if (area.get_m00() < 200) {
// return null;
// }
var polyList = polyOutput.toList();
polyOutput.copyTo(target.approxPoly);
// left top, left bottom, right bottom, right top
var boundingBoxCorners = findBoundingBoxCorners(target).toList();
try {
// top left and top right are the poly corners closest to the bouding box tl and tr
var tl =
polyList.stream()
.min(
Comparator.comparingDouble(
(Point p) -> distanceBetween(p, boundingBoxCorners.get(0))))
.get();
var tr =
polyList.stream()
.min(
Comparator.comparingDouble(
(Point p) -> distanceBetween(p, boundingBoxCorners.get(3))))
.get();
// bottom left and bottom right have to be in the correct quadrant and are the furthest
// from the center
var bl =
polyList.stream()
.filter(point -> point.x < centroid.x && point.y > centroid.y)
.max(distanceProvider)
.get();
var br =
polyList.stream()
.filter(point -> point.x > centroid.x && point.y > centroid.y)
.max(distanceProvider)
.get();
// polyList = new ArrayList<>(polyList);
// polyList.removeAll(List.of(tl, tr, bl, br));
//
// var tl2 = polyList.stream().min(Comparator.comparingDouble((Point p) ->
// distanceBetween(p, boundingBoxCorners.get(0)))).get();
// var tr2 = polyList.stream().min(Comparator.comparingDouble((Point p) ->
// distanceBetween(p, boundingBoxCorners.get(3)))).get();
//
// var bl2 = polyList.stream().filter(point -> point.x < centroid.x && point.y >
// centroid.y).max(distanceProvider).get();
// var br2 = polyList.stream().filter(point -> point.x > centroid.x && point.y >
// centroid.y).max(distanceProvider).get();
target2020ResultMat.release();
target2020ResultMat.fromList(List.of(tl, bl, br, tr)); // , tr2, br2, bl2, tl2));
return target2020ResultMat;
} catch (NoSuchElementException e) {
return null;
}
}
/**
* Find the target using the outermost tape corners and a dual target.
*
* @param target the target.
* @return The four outermost tape corners.
*/
private MatOfPoint2f findDualTargetCornerMinAreaRect(StandardCVPipeline.TrackedTarget target) {
if (target.leftRightRotatedRect == null) return null;
var centroid = target.minAreaRect.center;
Comparator<Point> distanceProvider =
Comparator.comparingDouble(
(Point point) ->
FastMath.sqrt(
FastMath.pow(centroid.x - point.x, 2) + FastMath.pow(centroid.y - point.y, 2)));
var left = target.leftRightRotatedRect.getLeft();
var right = target.leftRightRotatedRect.getRight();
// flip if the "left" target is to the right
if (left.center.x > right.center.x) {
var temp = left;
left = right;
right = temp;
}
var leftPoints = new Point[4];
left.points(leftPoints);
var rightPoints = new Point[4];
right.points(rightPoints);
ArrayList<Point> combinedList = new ArrayList<>(List.of(leftPoints));
combinedList.addAll(List.of(rightPoints));
// start looking in the top left quadrant
var tl =
combinedList.stream()
.filter(point -> point.x < centroid.x && point.y < centroid.y)
.max(distanceProvider)
.get();
var tr =
combinedList.stream()
.filter(point -> point.x > centroid.x && point.y < centroid.y)
.max(distanceProvider)
.get();
var bl =
combinedList.stream()
.filter(point -> point.x < centroid.x && point.y > centroid.y)
.max(distanceProvider)
.get();
var br =
combinedList.stream()
.filter(point -> point.x > centroid.x && point.y > centroid.y)
.max(distanceProvider)
.get();
boundingBoxResultMat.release();
boundingBoxResultMat.fromList(List.of(tl, bl, br, tr));
return boundingBoxResultMat;
}
/**
* @param target the target to find the corners of.
* @return the corners. left top, left bottom, right bottom, right top
*/
private MatOfPoint2f findBoundingBoxCorners(StandardCVPipeline.TrackedTarget target) {
// extract the corners
var points = new Point[4];
target.minAreaRect.points(points);
// find the tl/tr/bl/br corners
// first, min by left/right
var list_ = Arrays.asList(points);
list_.sort(leftRightComparator);
// of this, we now have left and right
// sort to get top and bottom
var left = new ArrayList<>(List.of(list_.get(0), list_.get(1)));
left.sort(verticalComparator);
var right = new ArrayList<>(List.of(list_.get(2), list_.get(3)));
right.sort(verticalComparator);
// tl tr bl br
var tl = left.get(0);
var bl = left.get(1);
var tr = right.get(0);
var br = right.get(1);
boundingBoxResultMat.release();
boundingBoxResultMat.fromList(List.of(tl, bl, br, tr));
return boundingBoxResultMat;
}
MatOfPoint2f goodFeatureToTrackRetval = new MatOfPoint2f();
private MatOfPoint2f refineCornersByBestTrack(
MatOfPoint2f corners, Mat greyImg, StandardCVPipeline.TrackedTarget target) {
MatOfPoint approxf1 = new MatOfPoint();
var origCornerList = new ArrayList<>(corners.toList());
approxf1.fromList(
origCornerList.stream()
.map(it -> new Point(it.x - target.boundingRect.x, it.y - target.boundingRect.y))
.collect(Collectors.toList()));
var croppedImage = greyImg.submat(target.boundingRect);
Imgproc.goodFeaturesToTrack(croppedImage, approxf1, 0, 0.1, 5);
// at this point corners is still unmodified so let's map it
List<Point> tempList = new ArrayList<>();
// shift all points back into global pose
var reshiftedList =
approxf1.toList().stream()
.map(it -> new Point(it.x + target.boundingRect.x, it.y + target.boundingRect.y))
.collect(Collectors.toList());
for (Point p : origCornerList) {
// find the goodFeaturesToTrack corner closest to me
var closestPoint =
reshiftedList.stream().min(Comparator.comparingDouble(p_ -> distanceBetween(p_, p)));
if (closestPoint.isEmpty()) {
tempList.add(p);
reshiftedList.remove(p);
} else {
tempList.add(closestPoint.get());
reshiftedList.remove(closestPoint.get());
}
}
goodFeatureToTrackRetval.fromList(tempList);
return goodFeatureToTrackRetval;
}
// Set the needed parameters to find the refined corners
Size winSize = new Size(4, 4);
Size zeroZone = new Size(-1, -1); // we don't need a zero zone
TermCriteria criteria = new TermCriteria(TermCriteria.EPS + TermCriteria.COUNT, 90, 0.001);
private boolean shouldRefineCorners = true;
/**
* Refine an estimated corner position using the cornerSubPixel algorithm.
*
* <p>TODO should this be here or before the points are chosen?
*
* @param corners the corners detected -- this mat is modified!
* @param greyImg the image taken by the camera as color
* @return the updated mat, same as the corner mat passed in.
*/
private MatOfPoint2f refineCornerEstimateSubPix(MatOfPoint2f corners, Mat greyImg) {
if (!shouldRefineCorners) return corners; // just return
Imgproc.cornerSubPix(greyImg, corners, winSize, zeroZone, criteria);
return corners;
}
// NetworkTableEntry tvecE = NetworkTableInstance.getDefault().getTable("SmartDashboard")
// .getEntry("tvec");
// NetworkTableEntry rvecE = NetworkTableInstance.getDefault().getTable("SmartDashboard")
// .getEntry("rvec");
/**
* Calculate the pose of the vision target
*
* @param imageCornerPoints the corners we found.
* @param target the target to process, mutated.
* @return the target, with the pose2d added to it.
*/
public StandardCVPipeline.TrackedTarget calculatePose(
MatOfPoint2f imageCornerPoints, StandardCVPipeline.TrackedTarget target) {
if (objPointsMat.rows() != imageCornerPoints.rows()
|| cameraMatrix.rows() < 2
|| distortionCoefficients.cols() < 4) {
System.err.println("can't do solvePNP with invalid params!");
return null;
}
imageCornerPoints.copyTo(target.imageCornerPoints);
try {
Calib3d.solvePnP(
objPointsMat, imageCornerPoints, cameraMatrix, distortionCoefficients, rVec, tVec);
} catch (Exception e) {
e.printStackTrace();
return null;
}
// tvecE.setString(tVec.dump());
// rvecE.setString(rVec.dump());
// Algorithm from team 5190 Green Hope Falcons. Can also be found in Ligerbot's vision
// whitepaper
// the left/right distance to the target, unchanged by tilt. Inches
var x = tVec.get(0, 0)[0];
// Z distance in the flat plane is given by
// Z_field = z cos theta + y sin theta.
// Z is the distance "out" of the camera (straight forward). Inches.
var z =
tVec.get(2, 0)[0] * FastMath.cos(tilt_angle) + tVec.get(1, 0)[0] * FastMath.sin(tilt_angle);
Calib3d.Rodrigues(rVec, rodriguez);
Core.transpose(rodriguez, rot_inv); // rodrigurz.t()
scaledTvec = matScale(tVec, -1);
Core.gemm(rot_inv, scaledTvec, 1, kMat, 0, pzero_world);
var angle2 = FastMath.atan2(pzero_world.get(0, 0)[0], pzero_world.get(2, 0)[0]);
// target rotation is the rotation of the target relative to straight ahead. this number
// should be unchanged if the robot purely translated left/right.
var targetRotation = -angle2; // radians
// We want a vector that is X forward and Y left.
// We have a Z_field (out of the camera projected onto the field), and an X left/right.
// so Z_field becomes X, and X becomes Y
//noinspection SuspiciousNameCombination
var targetLocation = new Translation2d(z, -x).times(25.4 / 1000d / distanceDivisor);
target.cameraRelativePose = new Pose2d(targetLocation, new Rotation2d(targetRotation));
target.rVector = rVec;
target.tVector = tVec;
return target;
}
/**
* Element-wise scale a matrix by a given factor
*
* @param src the source matrix
* @param factor by how much to scale each element
* @return the scaled matrix
*/
public Mat matScale(Mat src, double factor) {
Mat dst = new Mat(src.rows(), src.cols(), src.type());
Scalar s = new Scalar(factor); // TODO check if we need to add more elements to this
Core.multiply(src, s, dst);
return dst;
}
}

View File

@@ -1,108 +0,0 @@
package org.photonvision._2.vision.pipeline.pipes;
import org.photonvision._2.vision.camera.CaptureStaticProperties;
import org.photonvision._2.vision.enums.SortMode;
import org.photonvision._2.vision.pipeline.Pipe;
import org.photonvision._2.vision.pipeline.impl.StandardCVPipeline;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.math3.util.FastMath;
public class SortContoursPipe
implements Pipe<
List<StandardCVPipeline.TrackedTarget>, List<StandardCVPipeline.TrackedTarget>> {
private final Comparator<StandardCVPipeline.TrackedTarget> SortByCentermostComparator =
Comparator.comparingDouble(this::calcSquareCenterDistance);
private static final Comparator<StandardCVPipeline.TrackedTarget> SortByLargestComparator =
(rect1, rect2) ->
Double.compare(rect2.minAreaRect.size.area(), rect1.minAreaRect.size.area());
private static final Comparator<StandardCVPipeline.TrackedTarget> SortBySmallestComparator =
SortByLargestComparator.reversed();
private static final Comparator<StandardCVPipeline.TrackedTarget> SortByHighestComparator =
(rect1, rect2) -> Double.compare(rect1.minAreaRect.center.y, rect2.minAreaRect.center.y);
private static final Comparator<StandardCVPipeline.TrackedTarget> SortByLowestComparator =
SortByHighestComparator.reversed();
public static final Comparator<StandardCVPipeline.TrackedTarget> SortByLeftmostComparator =
Comparator.comparingDouble(target -> target.minAreaRect.center.x);
private static final Comparator<StandardCVPipeline.TrackedTarget> SortByRightmostComparator =
SortByLeftmostComparator.reversed();
private SortMode sort;
private CaptureStaticProperties camProps;
private int maxTargets;
private List<StandardCVPipeline.TrackedTarget> sortedContours = new ArrayList<>();
public SortContoursPipe(SortMode sort, CaptureStaticProperties camProps, int maxTargets) {
this.sort = sort;
this.camProps = camProps;
this.maxTargets = maxTargets;
}
public void setConfig(SortMode sort, CaptureStaticProperties camProps, int maxTargets) {
this.sort = sort;
this.camProps = camProps;
this.maxTargets = maxTargets;
}
@Override
public Pair<List<StandardCVPipeline.TrackedTarget>, Long> run(
List<StandardCVPipeline.TrackedTarget> input) {
long processStartNanos = System.nanoTime();
sortedContours.clear();
if (input.size() > 0) {
sortedContours.addAll(input);
switch (sort) {
case Largest:
sortedContours.sort(SortByLargestComparator);
break;
case Smallest:
sortedContours.sort(SortBySmallestComparator);
break;
case Highest:
sortedContours.sort(SortByHighestComparator);
break;
case Lowest:
sortedContours.sort(SortByLowestComparator);
break;
case Leftmost:
sortedContours.sort(SortByLeftmostComparator);
break;
case Rightmost:
sortedContours.sort(SortByRightmostComparator);
break;
case Centermost:
sortedContours.sort(SortByCentermostComparator);
break;
default:
break;
}
}
var sublistedContors =
new ArrayList<>(sortedContours.subList(0, Math.min(input.size(), maxTargets - 1)));
sortedContours
.subList(Math.min(input.size(), maxTargets - 1), sortedContours.size())
.forEach(StandardCVPipeline.TrackedTarget::release);
sortedContours.clear();
sortedContours = new ArrayList<>();
long processTime = System.nanoTime() - processStartNanos;
return Pair.of(sublistedContors, processTime);
}
private double calcSquareCenterDistance(StandardCVPipeline.TrackedTarget rect) {
return FastMath.sqrt(
FastMath.pow(camProps.centerX - rect.minAreaRect.center.x, 2)
+ FastMath.pow(camProps.centerY - rect.minAreaRect.center.y, 2));
}
}

View File

@@ -1,53 +0,0 @@
package org.photonvision._2.vision.pipeline.pipes;
import org.photonvision._2.vision.pipeline.Pipe;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import org.opencv.core.MatOfPoint;
import org.opencv.imgproc.Imgproc;
public class SpeckleRejectPipe implements Pipe<List<MatOfPoint>, List<MatOfPoint>> {
private double minPercentOfAvg;
private List<MatOfPoint> despeckledContours = new ArrayList<>();
public SpeckleRejectPipe(double minPercentOfAvg) {
this.minPercentOfAvg = minPercentOfAvg;
}
public void setConfig(double minPercentOfAvg) {
this.minPercentOfAvg = minPercentOfAvg;
}
@Override
public Pair<List<MatOfPoint>, Long> run(List<MatOfPoint> input) {
long processStartNanos = System.nanoTime();
despeckledContours.forEach(MatOfPoint::release);
despeckledContours.clear();
despeckledContours = new ArrayList<>();
if (input.size() > 0) {
double averageArea = 0.0;
for (MatOfPoint c : input) {
averageArea += Imgproc.contourArea(c);
}
averageArea /= input.size();
double minAllowedArea = minPercentOfAvg / 100.0 * averageArea;
for (MatOfPoint c : input) {
if (Imgproc.contourArea(c) >= minAllowedArea) {
despeckledContours.add(c);
}
}
}
long processTime = System.nanoTime() - processStartNanos;
return Pair.of(despeckledContours, processTime);
}
}

View File

@@ -1,277 +0,0 @@
package org.photonvision._2.web;
import org.photonvision._2.Main;
import org.photonvision._2.config.ConfigManager;
import org.photonvision._2.network.NetworkManager;
import org.photonvision._2.util.Helpers;
import org.photonvision._2.util.ProgramDirectoryUtilities;
import org.photonvision._2.vision.VisionManager;
import org.photonvision._2.vision.VisionProcess;
import org.photonvision._2.vision.camera.USBCameraCapture;
import org.photonvision._2.vision.pipeline.PipelineManager;
import org.photonvision._2.vision.pipeline.impl.Calibrate3dPipeline;
import org.photonvision._2.vision.pipeline.impl.StandardCVPipelineSettings;
import org.photonvision.common.datatransfer.networktables.NetworkTablesManager;
import org.photonvision.common.networking.NetworkMode;
import org.photonvision.common.util.Platform;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.wpi.first.wpilibj.geometry.Rotation2d;
import io.javalin.http.Context;
import io.javalin.http.UploadedFile;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.opencv.core.Point3;
public class RequestHandler {
private static final ObjectMapper kObjectMapper = new ObjectMapper();
public static void onGeneralSettings(Context ctx) {
ObjectMapper objectMapper = kObjectMapper;
try {
Map map = objectMapper.readValue(ctx.body(), Map.class);
// TODO: change to function, to restart NetworkTables
int newTeamNumber = (int) map.get("teamNumber");
if (newTeamNumber != ConfigManager.settings.teamNumber && !NetworkTablesManager.isServer) {
NetworkTablesManager.setTeamClientMode();
}
ConfigManager.settings.teamNumber = newTeamNumber;
ConfigManager.settings.connectionType = NetworkMode.values()[(int) map.get("connectionType")];
ConfigManager.settings.ip = (String) map.get("ip");
ConfigManager.settings.netmask = (String) map.get("netmask");
ConfigManager.settings.gateway = (String) map.get("gateway");
ConfigManager.settings.hostname = (String) map.get("hostname");
ConfigManager.saveGeneralSettings();
// setting up network config after saving
boolean isStatic = ConfigManager.settings.connectionType.equals(NetworkMode.STATIC);
boolean state =
NetworkManager.setHostname(ConfigManager.settings.hostname)
&& NetworkManager.setNetwork(
isStatic,
ConfigManager.settings.ip,
ConfigManager.settings.netmask,
ConfigManager.settings.gateway);
if (state) {
ctx.status(200);
} else {
ctx.result("Something went wrong while setting network configuration");
ctx.status(501);
}
SocketHandler.sendFullSettings();
} catch (JsonProcessingException e) {
ctx.status(500);
}
}
public static void onDuplicatePipeline(Context ctx) {
ObjectMapper objectMapper = kObjectMapper;
try {
Map data = objectMapper.readValue(ctx.body(), Map.class);
int cameraIndex = (Integer) data.getOrDefault("camera", -1);
var pipelineIndex = (Integer) data.get("pipeline");
StandardCVPipelineSettings origPipeline =
(StandardCVPipelineSettings)
VisionManager.getCurrentUIVisionProcess()
.pipelineManager
.getPipeline(pipelineIndex)
.settings;
String tmp = objectMapper.writeValueAsString(origPipeline);
StandardCVPipelineSettings newPipeline =
objectMapper.readValue(tmp, StandardCVPipelineSettings.class);
if (cameraIndex == -1) { // same camera
VisionManager.getCurrentUIVisionProcess().pipelineManager.duplicatePipeline(newPipeline);
} else { // another camera
var cam = VisionManager.getVisionProcessByIndex(cameraIndex);
if (cam != null) {
if (cam.getCamera().getProperties().videoModes.size() < newPipeline.videoModeIndex) {
newPipeline.videoModeIndex = cam.getCamera().getProperties().videoModes.size() - 1;
}
if (newPipeline.is3D) {
var calibration =
cam.getCamera()
.getCalibration(
cam.getCamera().getProperties().getVideoMode(newPipeline.videoModeIndex));
if (calibration == null) {
newPipeline.is3D = false;
}
}
VisionManager.getCurrentUIVisionProcess()
.pipelineManager
.duplicatePipeline(newPipeline, cam);
ctx.status(200);
} else {
ctx.status(500);
}
}
} catch (JsonProcessingException ex) {
ctx.status(500);
}
}
public static void onCameraSettings(Context ctx) {
ObjectMapper objectMapper = kObjectMapper;
try {
Map camSettings = objectMapper.readValue(ctx.body(), Map.class);
VisionProcess currentVisionProcess = VisionManager.getCurrentUIVisionProcess();
USBCameraCapture currentCamera = currentVisionProcess.getCamera();
double newFOV, tilt;
try {
newFOV = (Double) camSettings.get("fov");
} catch (Exception ignored) {
newFOV = (Integer) camSettings.get("fov");
}
try {
tilt = (Double) camSettings.get("tilt");
} catch (Exception ignored) {
tilt = (Integer) camSettings.get("tilt");
}
currentCamera.getProperties().setFOV(newFOV);
currentCamera.getProperties().setTilt(Rotation2d.fromDegrees(tilt));
VisionManager.saveCurrentCameraSettings();
SocketHandler.sendFullSettings();
ctx.status(200);
} catch (JsonProcessingException e) {
e.printStackTrace();
ctx.status(500);
}
}
public static void onCalibrationStart(Context ctx) throws JsonProcessingException {
PipelineManager pipeManager = VisionManager.getCurrentUIVisionProcess().pipelineManager;
ObjectMapper objectMapper = kObjectMapper;
var data = objectMapper.readValue(ctx.body(), Map.class);
int resolutionIndex = (Integer) data.get("resolution");
double squareSize;
try {
squareSize = (Double) data.get("squareSize");
} catch (Exception e) {
squareSize = (Integer) data.get("squareSize");
}
// convert from mm to meters
pipeManager.calib3dPipe.setSquareSize(squareSize);
VisionManager.getCurrentUIVisionProcess().pipelineManager.calib3dPipe.settings.videoModeIndex =
resolutionIndex;
VisionManager.getCurrentUIVisionProcess().pipelineManager.setCalibrationMode(true);
VisionManager.getCurrentUIVisionProcess().getCamera().setVideoMode(resolutionIndex);
}
public static void onSnapshot(Context ctx) {
Calibrate3dPipeline calPipe =
VisionManager.getCurrentUIVisionProcess().pipelineManager.calib3dPipe;
calPipe.takeSnapshot();
HashMap<String, Object> toSend = new HashMap<>();
toSend.put("snapshotCount", calPipe.getSnapshotCount());
toSend.put("hasEnough", calPipe.hasEnoughSnapshots());
ctx.json(toSend);
ctx.status(200);
}
public static void onCalibrationEnding(Context ctx) throws JsonProcessingException {
PipelineManager pipeManager = VisionManager.getCurrentUIVisionProcess().pipelineManager;
var data = kObjectMapper.readValue(ctx.body(), Map.class);
double squareSize;
try {
squareSize = (Double) data.get("squareSize");
} catch (Exception e) {
squareSize = (Integer) data.get("squareSize");
}
pipeManager.calib3dPipe.setSquareSize(squareSize);
System.out.println("Finishing Cal");
if (pipeManager.calib3dPipe.hasEnoughSnapshots()) {
if (pipeManager.calib3dPipe.tryCalibration()) {
HashMap<String, Double> tmp = new HashMap<String, Double>();
tmp.put("accuracy", pipeManager.calib3dPipe.getCalibrationAccuracy());
ctx.json(tmp);
ctx.status(200);
} else {
System.err.println("CALFAIL");
ctx.status(500);
}
} else {
ctx.status(201);
}
pipeManager.setCalibrationMode(false);
}
public static void onPnpModel(Context ctx) throws JsonProcessingException {
//noinspection unchecked
List<List<Number>> points = kObjectMapper.readValue(ctx.body(), List.class);
try {
// each entry should be an xy pair
var pointsList = new ArrayList<Point3>();
for (List<Number> point : points) {
double x, y;
x = point.get(0).doubleValue();
y = point.get(1).doubleValue();
var pointToAdd = new Point3(x, y, 0.0);
pointsList.add(pointToAdd);
}
System.out.println(pointsList.toString());
if (VisionManager.getCurrentUIVisionProcess().pipelineManager.getCurrentPipeline().settings
instanceof StandardCVPipelineSettings) {
var settings =
(StandardCVPipelineSettings)
VisionManager.getCurrentUIVisionProcess()
.pipelineManager
.getCurrentPipeline()
.settings;
settings.targetCornerMat.fromList(pointsList);
}
} catch (Exception e) {
ctx.status(500);
}
}
public static void onInstallOrUpdate(Context ctx) {
Platform p = Platform.CurrentPlatform;
try {
if (p == Platform.LINUX_RASPBIAN || p == Platform.LINUX_64) {
UploadedFile file = ctx.uploadedFile("file");
Path filePath;
if (file != null) {
filePath = Paths.get(ProgramDirectoryUtilities.getProgramDirectory(), file.getFilename());
File target = new File(filePath.toString());
OutputStream stream = new FileOutputStream(target);
file.getContent().transferTo(stream);
stream.close();
} else {
filePath =
Paths.get(
new File(Main.class.getProtectionDomain().getCodeSource().getLocation().toURI())
.getPath()); // quirk to get the current file directory
}
Helpers.setService(filePath);
ctx.status(200);
} else {
ctx.result("Only Linux Platforms Support this feature");
ctx.status(500);
}
} catch (Exception e) {
ctx.result(e.toString());
ctx.status(500);
}
}
}

View File

@@ -1,48 +0,0 @@
package org.photonvision._2.web;
import org.photonvision._2.config.ConfigManager;
import io.javalin.Javalin;
public class Server {
private static SocketHandler socketHandler;
public static void main(int port) {
socketHandler = new SocketHandler();
Javalin app =
Javalin.create(
javalinConfig -> {
javalinConfig.showJavalinBanner = false;
javalinConfig.addStaticFiles("web");
javalinConfig.enableCorsForAllOrigins();
});
app.ws(
"/websocket",
ws -> {
ws.onConnect(
ctx -> {
socketHandler.onConnect(ctx);
System.out.println("Socket Connected");
});
ws.onClose(
ctx -> {
socketHandler.onClose(ctx);
System.out.println("Socket Disconnected");
ConfigManager.saveGeneralSettings();
});
ws.onBinaryMessage(
ctx -> {
socketHandler.onBinaryMessage(ctx);
});
});
app.post("/api/settings/general", RequestHandler::onGeneralSettings);
app.post("/api/settings/camera", RequestHandler::onCameraSettings);
app.post("/api/vision/duplicate", RequestHandler::onDuplicatePipeline);
app.post("/api/settings/startCalibration", RequestHandler::onCalibrationStart);
app.post("/api/settings/snapshot", RequestHandler::onSnapshot);
app.post("/api/settings/endCalibration", RequestHandler::onCalibrationEnding);
app.post("/api/vision/pnpModel", RequestHandler::onPnpModel);
app.post("/api/install", RequestHandler::onInstallOrUpdate);
app.start(port);
}
}

View File

@@ -1,358 +0,0 @@
package org.photonvision._2.web;
import org.photonvision._2.config.CameraCalibrationConfig;
import org.photonvision._2.config.ConfigManager;
import org.photonvision._2.vision.VisionManager;
import org.photonvision._2.vision.VisionProcess;
import org.photonvision._2.vision.camera.CameraCapture;
import org.photonvision._2.vision.camera.CaptureStaticProperties;
import org.photonvision._2.vision.camera.USBCameraCapture;
import org.photonvision._2.vision.enums.ImageRotationMode;
import org.photonvision._2.vision.enums.StreamDivisor;
import org.photonvision._2.vision.pipeline.CVPipeline;
import org.photonvision._2.vision.pipeline.impl.StandardCVPipeline;
import org.photonvision.common.util.numbers.DoubleCouple;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.wpi.cscore.VideoMode;
import io.javalin.websocket.WsBinaryMessageContext;
import io.javalin.websocket.WsCloseContext;
import io.javalin.websocket.WsConnectContext;
import io.javalin.websocket.WsContext;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.lang3.ArrayUtils;
import org.msgpack.jackson.dataformat.MessagePackFactory;
public class SocketHandler {
private static List<WsContext> users;
private static ObjectMapper objectMapper;
private static final Object broadcastLock = new Object();
SocketHandler() {
users = new ArrayList<>();
objectMapper = new ObjectMapper(new MessagePackFactory());
}
void onConnect(WsConnectContext context) {
users.add(context);
sendFullSettings();
}
void onClose(WsCloseContext context) {
users.remove(context);
}
@SuppressWarnings("unchecked")
void onBinaryMessage(WsBinaryMessageContext context) throws Exception {
Map<String, Object> deserialized =
objectMapper.readValue(
(byte[]) ArrayUtils.toPrimitive(context.data()), new TypeReference<>() {});
for (Map.Entry<String, Object> entry : deserialized.entrySet()) {
try {
VisionProcess currentProcess = VisionManager.getCurrentUIVisionProcess();
CameraCapture currentCamera = currentProcess.getCamera();
CVPipeline currentPipeline = currentProcess.pipelineManager.getCurrentPipeline();
// System.out.println("entry.getKey()+entry.getValue()= " + entry.getKey() +
// entry.getValue());
switch (entry.getKey()) {
case "driverMode":
{
HashMap<String, Object> data = (HashMap<String, Object>) entry.getValue();
currentProcess.getDriverModeSettings().exposure =
(Integer) data.get("driverExposure");
currentProcess.getDriverModeSettings().brightness =
(Integer) data.get("driverBrightness");
currentProcess.setDriverMode((Boolean) data.get("isDriver"));
VisionManager.saveCurrentCameraDriverMode();
break;
}
case "changeCameraName":
{
currentProcess.setCameraNickname((String) entry.getValue());
sendFullSettings();
VisionManager.saveCurrentCameraSettings();
break;
}
case "changePipelineName":
{
currentProcess.pipelineManager.renameCurrentPipeline((String) entry.getValue());
sendFullSettings();
VisionManager.saveCurrentCameraPipelines();
break;
}
case "addNewPipeline":
{
// HashMap<String, Object> data = (HashMap<String, Object>)
// entry.getValue();
String pipeName = (String) entry.getValue();
// TODO: add to UI selection for new 2d/3d
currentProcess.pipelineManager.addNewPipeline(pipeName);
sendFullSettings();
VisionManager.saveCurrentCameraPipelines();
break;
}
case "command":
{
switch ((String) entry.getValue()) {
case "deleteCurrentPipeline":
currentProcess.pipelineManager.deleteCurrentPipeline();
sendFullSettings();
VisionManager.saveCurrentCameraPipelines();
break;
case "save":
ConfigManager.saveGeneralSettings();
VisionManager.saveAllCameras();
System.out.println("Saved Settings");
break;
}
// used to define all incoming commands
break;
}
case "currentCamera":
{
VisionManager.setCurrentProcessByIndex((Integer) entry.getValue());
sendFullSettings();
break;
}
case "is3D":
{
VisionManager.getCurrentUIVisionProcess().setIs3d((Boolean) entry.getValue());
break;
}
case "currentPipeline":
{
currentProcess.pipelineManager.setCurrentPipeline((Integer) entry.getValue());
sendFullSettings();
break;
}
case "isPNPCalibration":
{
currentProcess.pipelineManager.setCalibrationMode((Boolean) entry.getValue());
break;
}
case "takeCalibrationSnapshot":
{
currentProcess.pipelineManager.calib3dPipe.takeSnapshot();
}
default:
{
switch (entry.getKey()) { // Pre field value set
case "rotationMode":
{ // Create new CaptureStaticProperties with new width and height, reset crosshair
// calib
ImageRotationMode oldRot = currentPipeline.settings.rotationMode;
ImageRotationMode newRot =
ImageRotationMode.class.getEnumConstants()[(Integer) entry.getValue()];
CaptureStaticProperties prop =
currentCamera.getProperties().getStaticProperties();
int width, height;
if (oldRot.isRotated() != newRot.isRotated()) {
width = prop.mode.height;
height = prop.mode.width;
// Creates new video mode with new width and height to create new
// CaptureStaticProperties and applies it
currentCamera
.getProperties()
.setStaticProperties(
new CaptureStaticProperties(
new VideoMode(
prop.mode.pixelFormat, width, height, prop.mode.fps),
prop.fov));
}
prop = currentCamera.getProperties().getStaticProperties();
currentProcess.cameraStreamer.recalculateDivision();
if (currentPipeline instanceof StandardCVPipeline)
((StandardCVPipeline) currentPipeline)
.settings.point.set(
prop.mode.width / 2.0,
prop.mode.height / 2.0); // Reset Crosshair in single point calib
break;
}
}
if (currentProcess.pipelineManager.getDriverMode()) {
setField(
currentProcess.pipelineManager.driverModePipeline.settings,
entry.getKey(),
entry.getValue());
} else {
setField(currentPipeline.settings, entry.getKey(), entry.getValue());
}
// Post field value set
switch (entry.getKey()) {
case "exposure":
{
currentCamera.setExposure((Integer) entry.getValue());
break;
}
case "brightness":
{
currentCamera.setBrightness((Integer) entry.getValue());
break;
}
case "gain":
{
currentCamera.setGain((Integer) entry.getValue());
break;
}
case "videoModeIndex":
{
if (currentPipeline instanceof StandardCVPipeline)
((StandardCVPipeline) currentPipeline).settings.point =
new DoubleCouple(); // This will reset the calibration
currentCamera.setVideoMode((Integer) entry.getValue());
currentProcess.cameraStreamer.recalculateDivision();
break;
}
case "streamDivisor":
{
currentProcess.cameraStreamer.setDivisor(
StreamDivisor.values()[(Integer) entry.getValue()], true);
break;
}
}
VisionManager.saveCurrentCameraPipelines();
break;
}
}
} catch (Exception e) {
System.err.println(e.getMessage());
}
broadcastMessage(deserialized, context);
}
}
private void setField(Object obj, String fieldName, Object value) {
try {
Field field = obj.getClass().getField(fieldName);
if (field.getType().isEnum())
field.set(obj, field.getType().getEnumConstants()[(Integer) value]);
else field.set(obj, value);
} catch (NoSuchFieldException | IllegalAccessException ex) {
ex.printStackTrace();
}
}
private static void broadcastMessage(Object obj, WsContext userToSkip) {
synchronized (broadcastLock) {
if (users != null) {
var userList = users;
for (WsContext user : userList) {
if (userToSkip != null && user.getSessionId().equals(userToSkip.getSessionId())) {
continue;
}
try {
ByteBuffer b = ByteBuffer.wrap(objectMapper.writeValueAsBytes(obj));
user.send(b);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
}
}
}
public static void broadcastMessage(Object obj) {
broadcastMessage(obj, null); // Broadcasts the message to every user
}
private static HashMap<String, Object> getOrdinalPipeline(Class cvClass)
throws IllegalAccessException {
HashMap<String, Object> tmp = new HashMap<>();
for (Field field : cvClass.getFields()) { // iterate over every field in CVPipelineSettings
try {
if (!field
.getType()
.isEnum()) { // if the field is not an enum, get it based on the current pipeline
tmp.put(
field.getName(),
field.get(
VisionManager.getCurrentUIVisionProcess()
.pipelineManager
.getCurrentPipeline()
.settings));
} else {
var ordinal =
(Enum)
field.get(
VisionManager.getCurrentUIVisionProcess()
.pipelineManager
.getCurrentPipeline()
.settings);
tmp.put(field.getName(), ordinal.ordinal());
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
return tmp;
}
private static HashMap<String, Object> getOrdinalSettings() {
HashMap<String, Object> tmp = new HashMap<>();
tmp.put("teamNumber", ConfigManager.settings.teamNumber);
tmp.put("connectionType", ConfigManager.settings.connectionType.ordinal());
tmp.put("ip", ConfigManager.settings.ip);
tmp.put("gateway", ConfigManager.settings.gateway);
tmp.put("netmask", ConfigManager.settings.netmask);
tmp.put("hostname", ConfigManager.settings.hostname);
return tmp;
}
private static HashMap<String, Object> getOrdinalCameraSettings() {
HashMap<String, Object> tmp = new HashMap<>();
VisionProcess currentVisionProcess = VisionManager.getCurrentUIVisionProcess();
USBCameraCapture currentCamera = VisionManager.getCurrentUIVisionProcess().getCamera();
tmp.put("fov", currentCamera.getProperties().getFOV());
tmp.put("streamDivisor", currentVisionProcess.cameraStreamer.getDivisor().ordinal());
tmp.put(
"resolution", currentVisionProcess.getCamera().getProperties().getCurrentVideoModeIndex());
tmp.put("tilt", currentVisionProcess.getCamera().getProperties().getTilt().getDegrees());
List<CameraCalibrationConfig.UICameraCalibrationConfig> calibrations =
currentCamera.getAllCalibrationData().stream()
.map(CameraCalibrationConfig.UICameraCalibrationConfig::new)
.collect(Collectors.toList());
tmp.put("calibration", calibrations);
return tmp;
}
public static void sendFullSettings() {
// General settings
Map<String, Object> fullSettings = new HashMap<>();
VisionProcess currentProcess = VisionManager.getCurrentUIVisionProcess();
CVPipeline currentPipeline = currentProcess.pipelineManager.getCurrentPipeline();
try {
fullSettings.put("settings", getOrdinalSettings());
fullSettings.put("cameraSettings", getOrdinalCameraSettings());
fullSettings.put("cameraList", VisionManager.getAllCameraNicknames());
fullSettings.put("pipeline", getOrdinalPipeline(currentPipeline.settings.getClass()));
fullSettings.put("pipelineList", VisionManager.getCurrentCameraPipelineNicknames());
fullSettings.put("resolutionList", VisionManager.getCurrentCameraResolutionList());
fullSettings.put("port", currentProcess.cameraStreamer.getStreamPort());
fullSettings.put(
"currentPipelineIndex",
VisionManager.getCurrentUIVisionProcess().pipelineManager.getCurrentPipelineIndex());
fullSettings.put("currentCameraIndex", VisionManager.getCurrentUIVisionProcessIndex());
} catch (IllegalAccessException e) {
System.err.println("No camera found!");
}
broadcastMessage(fullSettings);
}
}

View File

@@ -1,92 +0,0 @@
package org.photonvision.common.calibration;
import org.photonvision.common.vision.opencv.Releasable;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Arrays;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfDouble;
public class JsonMat implements Releasable {
public final int rows;
public final int cols;
public final int type;
public final double[] data;
@JsonIgnore private Mat wrappedMat;
private MatOfDouble wrappedMatOfDouble;
public JsonMat(int rows, int cols, double[] data) {
this(rows, cols, CvType.CV_64FC1, data);
}
public JsonMat(
@JsonProperty("rows") int rows,
@JsonProperty("cols") int cols,
@JsonProperty("type") int type,
@JsonProperty("data") double[] data) {
this.rows = rows;
this.cols = cols;
this.type = type;
this.data = data;
}
private static boolean isCameraMatrixMat(Mat mat) {
return mat.type() == CvType.CV_64FC1 && mat.cols() == 3 && mat.rows() == 3;
}
private static boolean isDistortionCoeffsMat(Mat mat) {
return mat.type() == CvType.CV_64FC1 && mat.cols() == 5 && mat.rows() == 1;
}
private static boolean isCalibrationMat(Mat mat) {
return isDistortionCoeffsMat(mat) || isCameraMatrixMat(mat);
}
@JsonIgnore
public static double[] getDataFromMat(Mat mat) {
if (!isCalibrationMat(mat)) return null;
double[] data = new double[(int) (mat.total() * mat.elemSize())];
mat.get(0, 0, data);
int dataLen = -1;
if (isCameraMatrixMat(mat)) dataLen = 9;
if (isDistortionCoeffsMat(mat)) dataLen = 5;
// truncate Mat data to correct number data points.
return Arrays.copyOfRange(data, 0, dataLen);
}
public static JsonMat fromMat(Mat mat) {
if (!isCalibrationMat(mat)) return null;
return new JsonMat(mat.rows(), mat.cols(), getDataFromMat(mat));
}
@JsonIgnore
public Mat getAsMat() {
if (this.type != CvType.CV_64FC1) return null;
if (wrappedMat == null) {
this.wrappedMat = new Mat(this.rows, this.cols, this.type);
this.wrappedMat.put(0, 0, this.data);
}
return this.wrappedMat;
}
@JsonIgnore
public MatOfDouble getAsMatOfDouble() {
if (this.wrappedMatOfDouble == null) {
this.wrappedMatOfDouble = new MatOfDouble();
getAsMat().convertTo(wrappedMatOfDouble, CvType.CV_64F);
}
return this.wrappedMatOfDouble;
}
@Override
public void release() {
getAsMat().release();
}
}

View File

@@ -1,18 +1,17 @@
package org.photonvision.common.configuration;
import org.photonvision.common.calibration.CameraCalibrationCoefficients;
import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
import org.photonvision.common.vision.camera.CameraType;
import org.photonvision.common.vision.pipeline.CVPipelineSettings;
import org.photonvision.common.vision.pipeline.DriverModePipelineSettings;
import org.photonvision.common.vision.processes.PipelineManager;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.ArrayList;
import java.util.List;
import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
import org.photonvision.vision.calibration.CameraCalibrationCoefficients;
import org.photonvision.vision.camera.CameraType;
import org.photonvision.vision.pipeline.CVPipelineSettings;
import org.photonvision.vision.pipeline.DriverModePipelineSettings;
import org.photonvision.vision.processes.PipelineManager;
public class CameraConfiguration {
private static final Logger logger = new Logger(CameraConfiguration.class, LogGroup.Camera);

View File

@@ -1,12 +1,6 @@
package org.photonvision.common.configuration;
import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
import org.photonvision.common.util.file.JacksonUtils;
import org.photonvision.common.vision.pipeline.CVPipelineSettings;
import org.photonvision.common.vision.pipeline.DriverModePipelineSettings;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
@@ -16,6 +10,11 @@ import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
import org.photonvision.common.util.file.JacksonUtils;
import org.photonvision.vision.pipeline.CVPipelineSettings;
import org.photonvision.vision.pipeline.DriverModePipelineSettings;
public class ConfigManager {
private static final Logger logger = new Logger(ConfigManager.class, LogGroup.General);

View File

@@ -1,7 +1,6 @@
package org.photonvision.common.datatransfer;
import org.photonvision.common.vision.processes.Data;
import java.util.function.Consumer;
import org.photonvision.vision.processes.Data;
public interface DataConsumer extends Consumer<Data> {}

View File

@@ -1,14 +1,13 @@
package org.photonvision.common.datatransfer.networktables;
import edu.wpi.first.networktables.LogMessage;
import edu.wpi.first.networktables.NetworkTable;
import edu.wpi.first.networktables.NetworkTableInstance;
import java.util.function.Consumer;
import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
import org.photonvision.common.scripting.ScriptEventType;
import org.photonvision.common.scripting.ScriptManager;
import edu.wpi.first.networktables.LogMessage;
import edu.wpi.first.networktables.NetworkTable;
import edu.wpi.first.networktables.NetworkTableInstance;
import java.util.function.Consumer;
public class NetworkTablesManager {

View File

@@ -1,7 +1,5 @@
package org.photonvision.common.networking;
import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
@@ -11,6 +9,8 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
public class LinuxNetworking extends SysNetworking {
private static final String PATH = "/etc/dhcpcd.conf";

View File

@@ -1,11 +1,10 @@
package org.photonvision.common.networking;
import org.photonvision.common.util.ShellExec;
import java.io.IOException;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.List;
import org.photonvision.common.util.ShellExec;
public abstract class SysNetworking {
NetworkInterface networkInterface;

View File

@@ -1,11 +1,10 @@
package org.photonvision.common.scripting;
import java.io.IOException;
import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
import org.photonvision.common.util.ShellExec;
import java.io.IOException;
public class ScriptEvent {
private static final ShellExec executor = new ShellExec(true, true);

View File

@@ -1,11 +1,5 @@
package org.photonvision.common.scripting;
import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
import org.photonvision.common.util.LoopingRunnable;
import org.photonvision.common.util.Platform;
import org.photonvision.common.util.file.JacksonUtils;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -13,6 +7,11 @@ import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingDeque;
import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
import org.photonvision.common.util.LoopingRunnable;
import org.photonvision.common.util.Platform;
import org.photonvision.common.util.file.JacksonUtils;
public class ScriptManager {

View File

@@ -1,9 +1,5 @@
package org.photonvision.common.util.file;
import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
import org.photonvision.common.util.Platform;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
@@ -13,6 +9,9 @@ import java.nio.file.attribute.PosixFilePermission;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
import org.photonvision.common.util.Platform;
public class FileUtils {

View File

@@ -1,5 +0,0 @@
package org.photonvision.common.vision.camera;
public enum CameraQuirks {
Gain
}

View File

@@ -1,11 +0,0 @@
package org.photonvision.common.vision.frame.consumer;
import org.photonvision.common.vision.frame.Frame;
import org.photonvision.common.vision.frame.FrameConsumer;
public class DummyFrameConsumer implements FrameConsumer {
@Override
public void accept(Frame frame) {
frame.release(); // lol ez
}
}

View File

@@ -1,14 +0,0 @@
package org.photonvision.common.vision.pipe;
public enum ImageFlipMode {
NONE(Integer.MIN_VALUE),
VERTICAL(1),
HORIZONTAL(0),
BOTH(-1);
public final int value;
ImageFlipMode(int value) {
this.value = value;
}
}

View File

@@ -1,18 +0,0 @@
package org.photonvision.common.vision.pipe;
public enum ImageRotationMode {
DEG_0(-1),
DEG_90(0),
DEG_180(1),
DEG_270(2);
public final int value;
ImageRotationMode(int value) {
this.value = value;
}
public boolean isRotated() {
return this.value == DEG_90.value || this.value == DEG_270.value;
}
}

View File

@@ -1,8 +0,0 @@
package org.photonvision.common.vision.processes;
import org.photonvision.common.vision.pipeline.CVPipelineResult;
// TODO replace with CTT's data class
public class Data {
public CVPipelineResult result;
}

View File

@@ -1,6 +0,0 @@
package org.photonvision.common.vision.target;
public enum TargetOrientation {
Portrait,
Landscape
}

Some files were not shown because too many files have changed in this diff Show More