mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-21 01:01:41 +00:00
Compare commits
30 Commits
v2021.1.2-
...
v2021.1.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba8d2691fc | ||
|
|
f3d3a59ca0 | ||
|
|
b653fc7db1 | ||
|
|
71ee03a531 | ||
|
|
4a2493ff2e | ||
|
|
0b20111824 | ||
|
|
449977e63b | ||
|
|
b0504cbef5 | ||
|
|
fccb395564 | ||
|
|
b510417541 | ||
|
|
0330467874 | ||
|
|
4c15a46cda | ||
|
|
d59f8d1227 | ||
|
|
bbc8a3137b | ||
|
|
951c038f19 | ||
|
|
1b0c5af86e | ||
|
|
a113bd4192 | ||
|
|
d3c23345da | ||
|
|
2e1b3d0f83 | ||
|
|
58b39f47aa | ||
|
|
dc0f8cf296 | ||
|
|
3d34d1ca40 | ||
|
|
5298de0f64 | ||
|
|
2330b72451 | ||
|
|
69d2499e1a | ||
|
|
0a4dcd17e0 | ||
|
|
15c687655a | ||
|
|
839f959681 | ||
|
|
0b2de7d9f1 | ||
|
|
5d139e0a4e |
157
.github/workflows/main.yml
vendored
157
.github/workflows/main.yml
vendored
@@ -7,12 +7,14 @@ name: CI
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
# This job builds the client (web view).
|
||||
build-client:
|
||||
photonclient-build:
|
||||
|
||||
# Let all steps run within the photon-client dir.
|
||||
defaults:
|
||||
@@ -47,12 +49,7 @@ jobs:
|
||||
name: built-client
|
||||
path: photon-client/dist/
|
||||
|
||||
build-server:
|
||||
# Let all steps run within the photon-server dir.
|
||||
defaults:
|
||||
run:
|
||||
working-directory: photon-server
|
||||
|
||||
photon-build-all:
|
||||
# The type of runner that the job will run on.
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -60,6 +57,10 @@ jobs:
|
||||
# Checkout code.
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v1
|
||||
|
||||
# Fetch tags.
|
||||
- name: Fetch tags
|
||||
run: git fetch --tags --force
|
||||
|
||||
# Install Java 11.
|
||||
- name: Install Java 11
|
||||
@@ -78,12 +79,17 @@ jobs:
|
||||
run: ./gradlew jacocoTestReport
|
||||
|
||||
# Publish Coverage Report.
|
||||
- name: Publish Coverage Report
|
||||
- name: Publish Server Coverage Report
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
file: ./photon-server/build/reports/jacoco/test/jacocoTestReport.xml
|
||||
|
||||
build-offline-docs:
|
||||
- name: Publish Core Coverage Report
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
file: ./photon-core/build/reports/jacoco/test/jacocoTestReport.xml
|
||||
|
||||
photonserver-build-offline-docs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@@ -104,11 +110,6 @@ jobs:
|
||||
pip install sphinx sphinx_rtd_theme sphinx-tabs sphinxext-opengraph doc8
|
||||
pip install -r requirements.txt
|
||||
|
||||
- name: Install LaTeX and other system dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y texlive-latex-recommended texlive-fonts-recommended texlive-latex-extra latexmk texlive-lang-greek texlive-luatex texlive-xetex texlive-fonts-extra dvipng graphviz
|
||||
|
||||
- name: Check the docs
|
||||
run: |
|
||||
make linkcheck
|
||||
@@ -123,15 +124,9 @@ jobs:
|
||||
with:
|
||||
name: built-docs
|
||||
path: build/html
|
||||
|
||||
|
||||
build-package:
|
||||
needs: [build-client, build-server, build-offline-docs]
|
||||
|
||||
# Let all steps run within the photon-server dir.
|
||||
defaults:
|
||||
run:
|
||||
working-directory: photon-server
|
||||
photon-build-package:
|
||||
needs: [photonclient-build, photon-build-all, photonserver-build-offline-docs]
|
||||
|
||||
# The type of runner that the job will run on.
|
||||
runs-on: ubuntu-latest
|
||||
@@ -147,8 +142,8 @@ jobs:
|
||||
|
||||
# Clear any existing web resources.
|
||||
- run: |
|
||||
rm -rf src/main/resources/web/*
|
||||
mkdir -p src/main/resources/web/docs
|
||||
rm -rf photon-server/src/main/resources/web/*
|
||||
mkdir -p photon-server/src/main/resources/web/docs
|
||||
|
||||
# Download client artifact to resources folder.
|
||||
- uses: actions/download-artifact@v2
|
||||
@@ -162,16 +157,10 @@ jobs:
|
||||
name: built-docs
|
||||
path: photon-server/src/main/resources/web/docs
|
||||
|
||||
|
||||
# Print folder contents.
|
||||
- run: ls
|
||||
working-directory: photon-server/src/main/resources/web/
|
||||
|
||||
# Build fat jar.
|
||||
- run: |
|
||||
chmod +x gradlew
|
||||
./gradlew shadowJar
|
||||
working-directory: photon-server
|
||||
./gradlew photon-server:shadowJar
|
||||
|
||||
# Upload final fat jar as artifact.
|
||||
- uses: actions/upload-artifact@master
|
||||
@@ -188,12 +177,7 @@ jobs:
|
||||
photon-server/build/libs/*.jar
|
||||
if: github.event_name == 'push'
|
||||
|
||||
check-lint:
|
||||
# Let all steps run within the photon-server dir.
|
||||
defaults:
|
||||
run:
|
||||
working-directory: photon-server
|
||||
|
||||
photonserver-check-lint:
|
||||
# The type of runner that the job will run on.
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -210,4 +194,101 @@ jobs:
|
||||
- run: |
|
||||
chmod +x gradlew
|
||||
./gradlew spotlessCheck
|
||||
|
||||
|
||||
photon-release:
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
needs: [photon-build-package]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: jar
|
||||
- uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: '**/*'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Building photonlib
|
||||
photonlib-build-host:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-latest
|
||||
artifact-name: Win64
|
||||
- os: macos-latest
|
||||
artifact-name: macOS
|
||||
- os: ubuntu-latest
|
||||
artifact-name: Linux
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: "Photonlib - Build - ${{ matrix.artifact-name }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- run: |
|
||||
chmod +x gradlew
|
||||
./gradlew photon-lib:build
|
||||
- run: ./gradlew photon-lib:publish
|
||||
name: Publish
|
||||
env:
|
||||
ARTIFACTORY_API_KEY: ${{ secrets.ARTIFACTORY_API_KEY }}
|
||||
if: github.event_name == 'push'
|
||||
|
||||
photonlib-build-docker:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- container: wpilib/roborio-cross-ubuntu:2020-18.04
|
||||
artifact-name: Athena
|
||||
- container: wpilib/raspbian-cross-ubuntu:10-18.04
|
||||
artifact-name: Raspbian
|
||||
- container: wpilib/aarch64-cross-ubuntu:bionic-18.04
|
||||
artifact-name: Aarch64
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
container: ${{ matrix.container }}
|
||||
name: "Photonlib - Build - ${{ matrix.artifact-name }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- run: |
|
||||
chmod +x gradlew
|
||||
./gradlew photon-lib:build
|
||||
- run: |
|
||||
chmod +x gradlew
|
||||
./gradlew photon-lib:publish
|
||||
env:
|
||||
ARTIFACTORY_API_KEY: ${{ secrets.ARTIFACTORY_API_KEY }}
|
||||
if: github.event_name == 'push'
|
||||
|
||||
photonlib-wpiformat:
|
||||
name: "wpiformat"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Fetch all history and metadata
|
||||
run: |
|
||||
git fetch --prune --unshallow
|
||||
git checkout -b pr
|
||||
git branch -f master origin/master
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.8
|
||||
- name: Install clang-format
|
||||
run: sudo apt-get update -q && sudo apt-get install clang-format-10
|
||||
- name: Install wpiformat
|
||||
run: pip3 install wpiformat
|
||||
- name: Run
|
||||
run: |
|
||||
ls -la
|
||||
wpiformat -clang 10 -f photon-lib
|
||||
- name: Check Output
|
||||
run: git --no-pager diff --exit-code HEAD
|
||||
|
||||
27
.gitignore
vendored
27
.gitignore
vendored
@@ -104,15 +104,15 @@ fabric.properties
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
|
||||
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
|
||||
# Temporary build files
|
||||
**/.gradle
|
||||
**/target
|
||||
**/src/main/java/META-INF
|
||||
**/.settings
|
||||
**/.classpath
|
||||
**/.project
|
||||
**/settings
|
||||
**/dependency-reduced-pom.xml
|
||||
# photon-server/photon-vision.iml
|
||||
|
||||
New client/photon-client/*
|
||||
@@ -126,3 +126,12 @@ photon-server/photon-vision
|
||||
photon-server/src/main/resources/web
|
||||
photon-server/src/main/java/org/photonvision/PhotonVersion.java
|
||||
photon-server/src/main/generated/native/include/org_photonvision_raspi_PicamJNI.h
|
||||
*.bin
|
||||
.gradle
|
||||
.gradle/*
|
||||
photonvision_config
|
||||
build/spotlessJava
|
||||
build/*
|
||||
build
|
||||
photon-lib/src/main/java/org/photonvision/PhotonVersion.java
|
||||
/photonlib-java-examples/bin/
|
||||
|
||||
16
README.md
16
README.md
@@ -1,16 +1,12 @@
|
||||
# Photon Vision
|
||||
|
||||
[](https://github.com/PhotonVision/photonvision/actions?query=workflow%3ACI) [](https://codecov.io/gh/PhotonVision/photonvision)
|
||||
[](https://github.com/PhotonVision/photonvision/actions?query=workflow%3ACI) [](https://codecov.io/gh/PhotonVision/photonvision) [](https://discord.gg/wYxTwym)
|
||||
|
||||
A copy of the latest development release is available [here](https://github.com/PhotonVision/photonvision/releases/tag/Dev).
|
||||
PhotonVision is the free, fast, and easy-to-use computer vision solution for the *FIRST* Robotics Competition. You can read an overview of our features [on our website](https://photonvision.org). You can find our comprehensive documentation [here](https://docs.photonvision.org).
|
||||
|
||||
PhotonVision is a fork of [Chameleon Vision](https://github.com/Chameleon-Vision/chameleon-vision/), a free open-source software for FRC teams to use for vision processing on their robots. Thank you to everyone who worked on the original project.
|
||||
A copy of the latest Raspberry Pi image is available [here](https://github.com/PhotonVision/photon-pi-gen/releases). A copy of the latest standalone JAR is available [here](https://github.com/PhotonVision/photonvision/releases). If you are a Gloworm user you can find the latest Gloworm image [here](https://github.com/gloworm-vision/pi-gen/releases).
|
||||
|
||||
For information on contributing or running PhotonVision, please read our documentation on ReadTheDocs.
|
||||
|
||||
# Roadmap
|
||||
|
||||
Our roadmap is publicly available on [Trello](https://trello.com/photonvision).
|
||||
If you are interested in contributing code or documentation to the project, please [read our getting started page for contributors](https://docs.photonvision.org/en/latest/docs/other/contributing/index.html) and **[join the Discord](https://discord.gg/wYxTwym) to introduce yourself!** We hope to provide a welcoming community to anyone who is interested in helping.
|
||||
|
||||
## Authors
|
||||
|
||||
@@ -18,6 +14,8 @@ A list of contributors is available in our documentation on ReadTheDocs.
|
||||
|
||||
|
||||
## Acknowledgments
|
||||
PhotonVision was forked from [Chameleon Vision](https://github.com/Chameleon-Vision/chameleon-vision/). Thank you to everyone who worked on the original project.
|
||||
|
||||
|
||||
* [WPILib](https://github.com/wpilibsuite) - Specifically [cscore](https://github.com/wpilibsuite/allwpilib/tree/master/cscore), [CameraServer](https://github.com/wpilibsuite/allwpilib/tree/master/cameraserver), [NTCore](https://github.com/wpilibsuite/allwpilib/tree/master/ntcore), and [OpenCV](https://github.com/wpilibsuite/thirdparty-opencv).
|
||||
|
||||
@@ -30,4 +28,4 @@ A list of contributors is available in our documentation on ReadTheDocs.
|
||||
* [FasterXML](https://github.com/FasterXML) - Specifically [jackson](https://github.com/FasterXML/jackson)
|
||||
|
||||
## License
|
||||
Usage of PhotonVision must fall under all terms of [GNU General Public License](https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
PhotonVision is licensed under the [GNU General Public License](https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
45
build.gradle
Normal file
45
build.gradle
Normal file
@@ -0,0 +1,45 @@
|
||||
plugins {
|
||||
id "com.diffplug.gradle.spotless" version "3.28.0"
|
||||
id "com.github.johnrengelman.shadow" version "5.2.0"
|
||||
id "com.github.node-gradle.node" version "2.2.4" apply false
|
||||
id "edu.wpi.first.GradleJni" version "0.10.1"
|
||||
id "edu.wpi.first.GradleVsCode" version "0.12.0"
|
||||
id "edu.wpi.first.NativeUtils" version "2021.1.1" apply false
|
||||
id "edu.wpi.first.wpilib.repositories.WPILibRepositoriesPlugin" version "2020.2"
|
||||
id "org.hidetake.ssh" version "2.10.1"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
jcenter()
|
||||
maven { url = "https://maven.photonvision.org/repository/internal/" }
|
||||
}
|
||||
wpilibRepositories.addAllReleaseRepositories(it)
|
||||
wpilibRepositories.addAllDevelopmentRepositories(it)
|
||||
}
|
||||
|
||||
// Configure the version number.
|
||||
apply from: "versioningHelper.gradle"
|
||||
|
||||
ext {
|
||||
wpilibVersion = "2021.2.1"
|
||||
opencvVersion = "3.4.7-5"
|
||||
joglVersion = "2.4.0-rc-20200307"
|
||||
pubVersion = versionString
|
||||
isDev = pubVersion.startsWith("dev")
|
||||
}
|
||||
|
||||
spotless {
|
||||
java {
|
||||
googleJavaFormat()
|
||||
paddedCell()
|
||||
indentWithTabs(2)
|
||||
indentWithSpaces(4)
|
||||
removeUnusedImports()
|
||||
}
|
||||
java {
|
||||
target "**/*.java"
|
||||
licenseHeaderFile "$rootDir/LicenseHeader.txt"
|
||||
targetExclude("photon-core/src/main/java/org/photonvision/PhotonVersion.java")
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0-bin.zip
|
||||
distributionSha256Sum=5a3578b9f0bb162f5e08cf119f447dfb8fa950cedebb4d2a977e912a11a74b91
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
|
||||
distributionSha256Sum=3239b5ed86c3838a37d983ac100573f64c1f3fd8e1eb6c89fa5f9529b5ec091d
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
0
photon-server/gradlew → gradlew
vendored
0
photon-server/gradlew → gradlew
vendored
0
photon-server/gradlew.bat → gradlew.bat
vendored
0
photon-server/gradlew.bat → gradlew.bat
vendored
@@ -145,7 +145,7 @@ import Logs from "./views/LogsView"
|
||||
},
|
||||
data: () => ({
|
||||
// Used so that we can switch back to the previously selected pipeline after camera calibration
|
||||
previouslySelectedIndex: undefined,
|
||||
previouslySelectedIndices: [],
|
||||
timer: undefined,
|
||||
}),
|
||||
computed: {
|
||||
@@ -213,6 +213,8 @@ import Logs from "./views/LogsView"
|
||||
handleMessage(key, value) {
|
||||
if (key === "logMessage") {
|
||||
this.logMessage(value["logMessage"], value["logLevel"]);
|
||||
} else if(key === "log"){
|
||||
this.logMessage(value["logMessage"]["logMessage"], value["logMessage"]["logLevel"]);
|
||||
} else if (key === "updatePipelineResult") {
|
||||
this.$store.commit('mutatePipelineResults', value)
|
||||
} else if (this.$store.state.hasOwnProperty(key)) {
|
||||
@@ -240,15 +242,23 @@ import Logs from "./views/LogsView"
|
||||
})
|
||||
},
|
||||
switchToDriverMode() {
|
||||
this.previouslySelectedIndex = this.$store.getters.currentPipelineIndex;
|
||||
this.handleInputWithIndex('currentPipeline', -1)
|
||||
},
|
||||
rollbackPipelineIndex() {
|
||||
if (this.previouslySelectedIndex !== null) {
|
||||
this.handleInputWithIndex('currentPipeline', this.previouslySelectedIndex || 0);
|
||||
if (!this.previouslySelectedIndices) this.previouslySelectedIndices = [];
|
||||
|
||||
for (const [i, cameraSettings] of this.$store.state.cameraSettings.entries()) {
|
||||
this.previouslySelectedIndices[i] = cameraSettings.currentPipelineIndex;
|
||||
this.handleInputWithIndex('currentPipeline', -1, i);
|
||||
}
|
||||
this.previouslySelectedIndex = null;
|
||||
},
|
||||
rollbackPipelineIndex()
|
||||
{
|
||||
if (this.previouslySelectedIndices !== null) {
|
||||
for (const [i] of this.$store.state.cameraSettings.entries()) {
|
||||
this.handleInputWithIndex('currentPipeline', this.previouslySelectedIndices[i] || 0, i);
|
||||
}
|
||||
}
|
||||
this.previouslySelectedIndices = null;
|
||||
}
|
||||
,
|
||||
switchToSettingsTab() {
|
||||
this.axios.post('http://' + this.$address + '/api/sendMetrics', {})
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ export default {
|
||||
computed: {
|
||||
localValue: {
|
||||
get() {
|
||||
return Object.values(this.value);
|
||||
return Object.values(this.value || [0, 0]);
|
||||
},
|
||||
set(value) {
|
||||
this.$emit("input", value);
|
||||
|
||||
@@ -4,10 +4,10 @@ export const dataHandleMixin = {
|
||||
let msg = this.$msgPack.encode({[key]: value});
|
||||
this.$socket.send(msg);
|
||||
},
|
||||
handleInputWithIndex(key, value) {
|
||||
handleInputWithIndex(key, value, cameraIndex = this.$store.getters.currentCameraIndex) {
|
||||
let msg = this.$msgPack.encode({
|
||||
[key]: value,
|
||||
["cameraIndex"]: this.$store.getters.currentCameraIndex
|
||||
["cameraIndex"]: cameraIndex,
|
||||
});
|
||||
this.$socket.send(msg);
|
||||
},
|
||||
|
||||
@@ -260,7 +260,7 @@
|
||||
<v-dialog
|
||||
v-model="snack"
|
||||
width="500px"
|
||||
persistent="true"
|
||||
:persistent="true"
|
||||
>
|
||||
<v-card
|
||||
color="primary"
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
:text-color="fpsTooLow ? 'white' : 'grey'"
|
||||
>
|
||||
<span class="pr-1">{{ Math.round($store.state.pipelineResults.fps) }} FPS –</span>
|
||||
<span v-if="!fpsTooLow">{{ Math.round($store.state.pipelineResults.latency) }} ms latency</span>
|
||||
<span v-if="!fpsTooLow">{{ Math.min(Math.round($store.state.pipelineResults.latency), 100) }} ms latency</span>
|
||||
<span v-else-if="!$store.getters.currentPipelineSettings.inputShouldShow">HSV thresholds are too broad; narrow them for better performance</span>
|
||||
<span v-else>stop viewing the color stream for better performance</span>
|
||||
</v-chip>
|
||||
|
||||
13
photon-core/.gitignore
vendored
Normal file
13
photon-core/.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
bin/*
|
||||
.settings/*
|
||||
.project
|
||||
.classpath
|
||||
*.prefs
|
||||
.gradle
|
||||
.gradle/*
|
||||
build
|
||||
build/*
|
||||
photonvision/*
|
||||
photonvision_config/*
|
||||
|
||||
src/main/java/org/photonvision/PhotonVersion.java
|
||||
52
photon-core/build.gradle
Normal file
52
photon-core/build.gradle
Normal file
@@ -0,0 +1,52 @@
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
apply from: "${rootDir}/shared/common.gradle"
|
||||
|
||||
dependencies {
|
||||
implementation project(':photon-targeting')
|
||||
|
||||
implementation 'io.javalin:javalin:3.7.0'
|
||||
|
||||
implementation 'org.msgpack:msgpack-core:0.8.20'
|
||||
implementation 'org.msgpack:jackson-dataformat-msgpack:0.8.20'
|
||||
|
||||
// wpiutil
|
||||
compile "edu.wpi.first.wpiutil:wpiutil-jni:$wpilibVersion:linuxaarch64bionic"
|
||||
compile "edu.wpi.first.wpiutil:wpiutil-jni:$wpilibVersion:linuxraspbian"
|
||||
compile "edu.wpi.first.wpiutil:wpiutil-jni:$wpilibVersion:linuxx86-64"
|
||||
compile "edu.wpi.first.wpiutil:wpiutil-jni:$wpilibVersion:osxx86-64"
|
||||
compile "edu.wpi.first.wpiutil:wpiutil-jni:$wpilibVersion:windowsx86-64"
|
||||
|
||||
// JOGL stuff (currently we only distribute for aarch64, which is Pi 4)
|
||||
implementation "org.jogamp.gluegen:gluegen-rt:$joglVersion"
|
||||
implementation "org.jogamp.jogl:jogl-all:$joglVersion"
|
||||
|
||||
implementation "org.jogamp.gluegen:gluegen-rt:$joglVersion:natives-linux-aarch64"
|
||||
implementation "org.jogamp.jogl:jogl-all:$joglVersion:natives-linux-aarch64"
|
||||
|
||||
// Zip
|
||||
compile 'org.zeroturnaround:zt-zip:1.14'
|
||||
}
|
||||
|
||||
task writeCurrentVersionJava {
|
||||
String date = DateTimeFormatter.ofPattern("yyyy-M-d hh:mm:ss").format(LocalDateTime.now())
|
||||
File versionFile = new File(java.nio.file.Path.of("$projectDir", "src", "main", "java", "org", "photonvision", "PhotonVersion.java")
|
||||
.toAbsolutePath().toString())
|
||||
versionFile.delete()
|
||||
versionFile << "package org.photonvision;\n" +
|
||||
"\n" +
|
||||
"/*\n" +
|
||||
" * Autogenerated file! Do not manually edit this file. This version is regenerated\n" +
|
||||
" * any time the publish task is run, or when this file is deleted.\n" +
|
||||
" */\n" +
|
||||
"\n" +
|
||||
"@SuppressWarnings(\"ALL\")\n" +
|
||||
"public final class PhotonVersion {\n" +
|
||||
" public static final String versionString = \"${versionString}\";\n" +
|
||||
" public static final String buildDate = \"${date}\";\n" +
|
||||
" public static final boolean isRelease = !versionString.startsWith(\"dev\");\n" +
|
||||
"}"
|
||||
}
|
||||
|
||||
build.dependsOn writeCurrentVersionJava
|
||||
2
photon-core/settings.gradle
Normal file
2
photon-core/settings.gradle
Normal file
@@ -0,0 +1,2 @@
|
||||
rootProject.name = 'photon-core'
|
||||
|
||||
@@ -332,8 +332,8 @@ public class ConfigManager {
|
||||
return loadedConfigurations;
|
||||
}
|
||||
|
||||
public void addCameraConfigurations(HashMap<VisionSource, CameraConfiguration> sources) {
|
||||
getConfig().addCameraConfigs(sources.values());
|
||||
public void addCameraConfigurations(List<VisionSource> sources) {
|
||||
getConfig().addCameraConfigs(sources);
|
||||
requestSave();
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.photonvision.common.util.SerializationUtils;
|
||||
import org.photonvision.raspi.PicamJNI;
|
||||
import org.photonvision.vision.processes.VisionModule;
|
||||
import org.photonvision.vision.processes.VisionModuleManager;
|
||||
import org.photonvision.vision.processes.VisionSource;
|
||||
|
||||
// TODO rename this class
|
||||
public class PhotonConfiguration {
|
||||
@@ -75,9 +76,9 @@ public class PhotonConfiguration {
|
||||
return cameraConfigurations;
|
||||
}
|
||||
|
||||
public void addCameraConfigs(Collection<CameraConfiguration> config) {
|
||||
for (var c : config) {
|
||||
addCameraConfig(c);
|
||||
public void addCameraConfigs(Collection<VisionSource> sources) {
|
||||
for (var s : sources) {
|
||||
addCameraConfig(s.getCameraConfiguration());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,13 @@ public class OutgoingUIEvent<T> extends DataChangeEvent<T> {
|
||||
this.originContext = originContext;
|
||||
}
|
||||
|
||||
public static OutgoingUIEvent<HashMap<String, Object>> wrappedOf(
|
||||
String commandName, Object value) {
|
||||
HashMap<String, Object> data = new HashMap<>();
|
||||
data.put(commandName, value);
|
||||
return new OutgoingUIEvent<>(commandName, data);
|
||||
}
|
||||
|
||||
public static OutgoingUIEvent<HashMap<String, Object>> wrappedOf(
|
||||
String commandName, String propertyName, Object value, WsContext originContext) {
|
||||
HashMap<String, Object> data = new HashMap<>();
|
||||
@@ -20,13 +20,17 @@ package org.photonvision.common.dataflow.networktables;
|
||||
import edu.wpi.first.networktables.EntryNotification;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import org.photonvision.common.dataflow.CVPipelineResultConsumer;
|
||||
import org.photonvision.common.dataflow.structures.Packet;
|
||||
import org.photonvision.targeting.PhotonPipelineResult;
|
||||
import org.photonvision.targeting.PhotonTrackedTarget;
|
||||
import org.photonvision.vision.pipeline.result.CVPipelineResult;
|
||||
import org.photonvision.vision.pipeline.result.SimplePipelineResult;
|
||||
import org.photonvision.vision.target.TrackedTarget;
|
||||
|
||||
public class NTDataPublisher implements CVPipelineResultConsumer {
|
||||
|
||||
@@ -163,7 +167,9 @@ public class NTDataPublisher implements CVPipelineResultConsumer {
|
||||
|
||||
@Override
|
||||
public void accept(CVPipelineResult result) {
|
||||
var simplified = new SimplePipelineResult(result);
|
||||
var simplified =
|
||||
new PhotonPipelineResult(
|
||||
result.getLatencyMillis(), simpleFromTrackedTargets(result.targets));
|
||||
Packet packet = new Packet(simplified.getPacketSize());
|
||||
simplified.populatePacket(packet);
|
||||
|
||||
@@ -201,4 +207,14 @@ public class NTDataPublisher implements CVPipelineResultConsumer {
|
||||
}
|
||||
rootTable.getInstance().flush();
|
||||
}
|
||||
|
||||
public static List<PhotonTrackedTarget> simpleFromTrackedTargets(List<TrackedTarget> targets) {
|
||||
var ret = new ArrayList<PhotonTrackedTarget>();
|
||||
for (var t : targets) {
|
||||
ret.add(
|
||||
new PhotonTrackedTarget(
|
||||
t.getYaw(), t.getPitch(), t.getArea(), t.getSkew(), t.getCameraToTarget()));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -79,6 +79,7 @@ public class NetworkTablesManager {
|
||||
ntInstance.stopServer();
|
||||
|
||||
ntInstance.startClientTeam(teamNumber);
|
||||
ntInstance.startDSClient();
|
||||
if (ntInstance.isConnected()) {
|
||||
logger.info("[NetworkTablesManager] Connected to the robot!");
|
||||
} else {
|
||||
@@ -17,14 +17,13 @@
|
||||
|
||||
package org.photonvision.common.dataflow.websocket;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import org.photonvision.common.dataflow.CVPipelineResultConsumer;
|
||||
import org.photonvision.common.dataflow.DataChangeService;
|
||||
import org.photonvision.common.dataflow.events.OutgoingUIEvent;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
import org.photonvision.server.SocketHandler;
|
||||
import org.photonvision.vision.pipeline.result.CVPipelineResult;
|
||||
|
||||
public class UIDataPublisher implements CVPipelineResultConsumer {
|
||||
@@ -39,16 +38,17 @@ public class UIDataPublisher implements CVPipelineResultConsumer {
|
||||
|
||||
@Override
|
||||
public void accept(CVPipelineResult result) {
|
||||
var now = System.currentTimeMillis();
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
var dataMap = new HashMap<String, Object>();
|
||||
dataMap.put("latency", result.getLatencyMillis());
|
||||
|
||||
// only update the UI at 15hz
|
||||
if (lastUIResultUpdateTime + 1000.0 / 10.0 > now) return;
|
||||
|
||||
var uiMap = new HashMap<Integer, HashMap<String, Object>>();
|
||||
var dataMap = new HashMap<String, Object>();
|
||||
|
||||
dataMap.put("fps", result.fps);
|
||||
dataMap.put("latency", result.getLatencyMillis());
|
||||
|
||||
var targets = result.targets;
|
||||
|
||||
@@ -57,18 +57,10 @@ public class UIDataPublisher implements CVPipelineResultConsumer {
|
||||
uiTargets.add(t.toHashMap());
|
||||
}
|
||||
dataMap.put("targets", uiTargets);
|
||||
|
||||
uiMap.put(index, dataMap);
|
||||
var retMap = new HashMap<String, Object>();
|
||||
retMap.put("updatePipelineResult", uiMap);
|
||||
|
||||
try {
|
||||
SocketHandler.getInstance().broadcastMessage(retMap, null);
|
||||
} catch (JsonProcessingException e) {
|
||||
logger.error(e.getMessage());
|
||||
logger.error(Arrays.toString(e.getStackTrace()));
|
||||
}
|
||||
|
||||
DataChangeService.getInstance()
|
||||
.publishEvent(OutgoingUIEvent.wrappedOf("updatePipelineResult", uiMap));
|
||||
lastUIResultUpdateTime = now;
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@ import org.photonvision.common.dataflow.networktables.NTDataChangeListener;
|
||||
import org.photonvision.common.dataflow.networktables.NetworkTablesManager;
|
||||
import org.photonvision.common.hardware.GPIO.CustomGPIO;
|
||||
import org.photonvision.common.hardware.GPIO.pi.PigpioSocket;
|
||||
import org.photonvision.common.hardware.VisionLED.VisionLEDMode;
|
||||
import org.photonvision.common.hardware.metrics.MetricsBase;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
@@ -91,7 +90,7 @@ public class HardwareManager {
|
||||
pigpioSocket);
|
||||
|
||||
ledModeEntry = NetworkTablesManager.getInstance().kRootTable.getEntry("ledMode");
|
||||
ledModeEntry.setNumber(VisionLEDMode.VLM_DEFAULT.value);
|
||||
ledModeEntry.setNumber(VisionLEDMode.kDefault.value);
|
||||
ledModeListener =
|
||||
visionLED == null
|
||||
? null
|
||||
@@ -40,7 +40,7 @@ public class VisionLED {
|
||||
private final int brightnessMax;
|
||||
private final PigpioSocket pigpioSocket;
|
||||
|
||||
private VisionLEDMode currentLedMode = VisionLEDMode.VLM_DEFAULT;
|
||||
private VisionLEDMode currentLedMode = VisionLEDMode.kDefault;
|
||||
private BooleanSupplier pipelineModeSupplier;
|
||||
|
||||
private int mappedBrightnessPercentage;
|
||||
@@ -111,7 +111,7 @@ public class VisionLED {
|
||||
}
|
||||
|
||||
public void setState(boolean on) {
|
||||
setInternal(on ? VisionLEDMode.VLM_ON : VisionLEDMode.VLM_OFF, false);
|
||||
setInternal(on ? VisionLEDMode.kOn : VisionLEDMode.kOff, false);
|
||||
}
|
||||
|
||||
void onLedModeChange(EntryNotification entryNotification) {
|
||||
@@ -120,20 +120,20 @@ public class VisionLED {
|
||||
VisionLEDMode newLedMode;
|
||||
switch (newLedModeRaw) {
|
||||
case -1:
|
||||
newLedMode = VisionLEDMode.VLM_DEFAULT;
|
||||
newLedMode = VisionLEDMode.kDefault;
|
||||
break;
|
||||
case 0:
|
||||
newLedMode = VisionLEDMode.VLM_OFF;
|
||||
newLedMode = VisionLEDMode.kOff;
|
||||
break;
|
||||
case 1:
|
||||
newLedMode = VisionLEDMode.VLM_ON;
|
||||
newLedMode = VisionLEDMode.kOn;
|
||||
break;
|
||||
case 2:
|
||||
newLedMode = VisionLEDMode.VLM_BLINK;
|
||||
newLedMode = VisionLEDMode.kBlink;
|
||||
break;
|
||||
default:
|
||||
logger.warn("User supplied invalid LED mode, falling back to Default");
|
||||
newLedMode = VisionLEDMode.VLM_DEFAULT;
|
||||
newLedMode = VisionLEDMode.kDefault;
|
||||
break;
|
||||
}
|
||||
setInternal(newLedMode, true);
|
||||
@@ -145,16 +145,16 @@ public class VisionLED {
|
||||
|
||||
if (fromNT) {
|
||||
switch (newLedMode) {
|
||||
case VLM_DEFAULT:
|
||||
case kDefault:
|
||||
setStateImpl(pipelineModeSupplier.getAsBoolean());
|
||||
break;
|
||||
case VLM_OFF:
|
||||
case kOff:
|
||||
setStateImpl(false);
|
||||
break;
|
||||
case VLM_ON:
|
||||
case kOn:
|
||||
setStateImpl(true);
|
||||
break;
|
||||
case VLM_BLINK:
|
||||
case kBlink:
|
||||
blinkImpl(85, -1);
|
||||
break;
|
||||
}
|
||||
@@ -166,15 +166,15 @@ public class VisionLED {
|
||||
+ newLedMode.toString()
|
||||
+ "\"");
|
||||
} else {
|
||||
if (currentLedMode == VisionLEDMode.VLM_DEFAULT) {
|
||||
if (currentLedMode == VisionLEDMode.kDefault) {
|
||||
switch (newLedMode) {
|
||||
case VLM_DEFAULT:
|
||||
case kDefault:
|
||||
setStateImpl(pipelineModeSupplier.getAsBoolean());
|
||||
break;
|
||||
case VLM_OFF:
|
||||
case kOff:
|
||||
setStateImpl(false);
|
||||
break;
|
||||
case VLM_ON:
|
||||
case kOn:
|
||||
setStateImpl(true);
|
||||
break;
|
||||
}
|
||||
@@ -182,32 +182,4 @@ public class VisionLED {
|
||||
logger.info("Changing LED internal state to " + newLedMode.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public enum VisionLEDMode {
|
||||
VLM_DEFAULT(-1),
|
||||
VLM_OFF(0),
|
||||
VLM_ON(1),
|
||||
VLM_BLINK(2);
|
||||
|
||||
public final int value;
|
||||
|
||||
VisionLEDMode(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
switch (this) {
|
||||
case VLM_DEFAULT:
|
||||
return "Default";
|
||||
case VLM_OFF:
|
||||
return "Off";
|
||||
case VLM_ON:
|
||||
return "On";
|
||||
case VLM_BLINK:
|
||||
return "Blink";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,13 +17,13 @@
|
||||
|
||||
package org.photonvision.common.hardware.metrics;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import java.util.HashMap;
|
||||
import org.photonvision.common.dataflow.DataChangeService;
|
||||
import org.photonvision.common.dataflow.events.OutgoingUIEvent;
|
||||
import org.photonvision.common.hardware.Platform;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
import org.photonvision.common.util.TimedTaskManager;
|
||||
import org.photonvision.server.SocketHandler;
|
||||
|
||||
public class MetricsPublisher {
|
||||
private static final Logger logger = new Logger(MetricsPublisher.class, LogGroup.General);
|
||||
@@ -65,14 +65,7 @@ public class MetricsPublisher {
|
||||
metrics.put("gpuMemUtil", gpuMetrics.getMallocedMemory());
|
||||
metrics.put("diskUtilPct", diskMetrics.getUsedDiskPct());
|
||||
|
||||
var retMap = new HashMap<String, Object>();
|
||||
retMap.put("metrics", metrics);
|
||||
|
||||
try {
|
||||
SocketHandler.getInstance().broadcastMessage(retMap, null);
|
||||
} catch (JsonProcessingException e) {
|
||||
logger.error("Exception while sending metrics!", e);
|
||||
}
|
||||
DataChangeService.getInstance().publishEvent(OutgoingUIEvent.wrappedOf("metrics", metrics));
|
||||
}
|
||||
|
||||
private static class Singleton {
|
||||
@@ -33,7 +33,6 @@ import org.photonvision.common.configuration.ConfigManager;
|
||||
import org.photonvision.common.dataflow.DataChangeService;
|
||||
import org.photonvision.common.dataflow.events.OutgoingUIEvent;
|
||||
import org.photonvision.common.util.TimedTaskManager;
|
||||
import org.photonvision.server.SocketHandler;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class Logger {
|
||||
@@ -302,12 +301,12 @@ public class Logger {
|
||||
private static class UILogAppender implements LogAppender {
|
||||
@Override
|
||||
public void log(String message, LogLevel level) {
|
||||
var messageMap = new SocketHandler.UIMap();
|
||||
var messageMap = new HashMap<String, Object>();
|
||||
messageMap.put("logMessage", message);
|
||||
messageMap.put("logLevel", level.code);
|
||||
var superMap = new SocketHandler.UIMap();
|
||||
var superMap = new HashMap<String, Object>();
|
||||
superMap.put("logMessage", messageMap);
|
||||
DataChangeService.getInstance().publishEvent(new OutgoingUIEvent<>("log", superMap));
|
||||
DataChangeService.getInstance().publishEvent(OutgoingUIEvent.wrappedOf("log", superMap));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
|
||||
package org.photonvision.common.util.math;
|
||||
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
import edu.wpi.first.wpiutil.WPIUtilJNI;
|
||||
|
||||
public class MathUtils {
|
||||
MathUtils() {}
|
||||
|
||||
public static double toSlope(Number angle) {
|
||||
return FastMath.atan(FastMath.toRadians(angle.doubleValue() - 90));
|
||||
return Math.atan(Math.toRadians(angle.doubleValue() - 90));
|
||||
}
|
||||
|
||||
public static int safeDivide(int quotient, int divisor) {
|
||||
@@ -43,6 +43,10 @@ public class MathUtils {
|
||||
return nanos / 1000000.0;
|
||||
}
|
||||
|
||||
public static double nanosToMillis(double nanos) {
|
||||
return nanos / 1000000.0;
|
||||
}
|
||||
|
||||
public static long millisToNanos(long millis) {
|
||||
return millis * 1000000;
|
||||
}
|
||||
@@ -59,4 +63,21 @@ public class MathUtils {
|
||||
public static int map(int value, int inMin, int inMax, int outMin, int outMax) {
|
||||
return (int) Math.floor(map((double) value, inMin, inMax, outMin, outMax) + 0.5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Linearly interpolates between two values.
|
||||
*
|
||||
* @param startValue The start value.
|
||||
* @param endValue The end value.
|
||||
* @param t The fraction for interpolation.
|
||||
* @return The interpolated value.
|
||||
*/
|
||||
@SuppressWarnings("ParameterName")
|
||||
public static double lerp(double startValue, double endValue, double t) {
|
||||
return startValue + (endValue - startValue) * t;
|
||||
}
|
||||
|
||||
public static long wpiNanoTime() {
|
||||
return microsToNanos(WPIUtilJNI.now());
|
||||
}
|
||||
}
|
||||
@@ -85,7 +85,7 @@ public class PicamJNI {
|
||||
}
|
||||
|
||||
public static boolean isSupported() {
|
||||
return libraryLoaded && getSensorModel() != SensorModel.Disconnected;
|
||||
return libraryLoaded && isVCSMSupported() && getSensorModel() != SensorModel.Disconnected;
|
||||
}
|
||||
|
||||
public static SensorModel getSensorModel() {
|
||||
@@ -105,6 +105,9 @@ public class PicamJNI {
|
||||
|
||||
private static native String getSensorModelRaw();
|
||||
|
||||
// This is the main thing we need that isn't supported on Pi 4s, which makes it a good check
|
||||
private static native boolean isVCSMSupported();
|
||||
|
||||
// Everything here is static because multiple picams are unsupported at the hardware level
|
||||
|
||||
/**
|
||||
@@ -28,14 +28,13 @@ import org.photonvision.vision.frame.provider.FileFrameProvider;
|
||||
import org.photonvision.vision.processes.VisionSource;
|
||||
import org.photonvision.vision.processes.VisionSourceSettables;
|
||||
|
||||
public class FileVisionSource implements VisionSource {
|
||||
public class FileVisionSource extends VisionSource {
|
||||
|
||||
private final CameraConfiguration cameraConfiguration;
|
||||
private final FileFrameProvider frameProvider;
|
||||
private final FileSourceSettables settables;
|
||||
|
||||
public FileVisionSource(CameraConfiguration cameraConfiguration) {
|
||||
this.cameraConfiguration = cameraConfiguration;
|
||||
super(cameraConfiguration);
|
||||
frameProvider =
|
||||
new FileFrameProvider(
|
||||
Path.of(cameraConfiguration.path),
|
||||
@@ -48,7 +47,7 @@ public class FileVisionSource implements VisionSource {
|
||||
}
|
||||
|
||||
public FileVisionSource(String name, String imagePath, double fov) {
|
||||
cameraConfiguration = new CameraConfiguration(name, imagePath);
|
||||
super(new CameraConfiguration(name, imagePath));
|
||||
frameProvider = new FileFrameProvider(imagePath, fov);
|
||||
settables =
|
||||
new FileSourceSettables(cameraConfiguration, frameProvider.get().frameStaticProperties);
|
||||
@@ -33,19 +33,18 @@ import org.photonvision.vision.frame.provider.USBFrameProvider;
|
||||
import org.photonvision.vision.processes.VisionSource;
|
||||
import org.photonvision.vision.processes.VisionSourceSettables;
|
||||
|
||||
public class USBCameraSource implements VisionSource {
|
||||
public class USBCameraSource extends VisionSource {
|
||||
private final Logger logger;
|
||||
private final UsbCamera camera;
|
||||
private final USBCameraSettables usbCameraSettables;
|
||||
private final USBFrameProvider usbFrameProvider;
|
||||
public final CameraConfiguration configuration;
|
||||
private final CvSink cvSink;
|
||||
|
||||
public final QuirkyCamera cameraQuirks;
|
||||
|
||||
public USBCameraSource(CameraConfiguration config) {
|
||||
super(config);
|
||||
logger = new Logger(USBCameraSource.class, config.nickname, LogGroup.Camera);
|
||||
configuration = config;
|
||||
camera = new UsbCamera(config.nickname, config.path);
|
||||
cvSink = CameraServer.getInstance().getVideo(this.camera);
|
||||
|
||||
@@ -88,7 +87,6 @@ public class USBCameraSource implements VisionSource {
|
||||
super(configuration);
|
||||
getAllVideoModes();
|
||||
setVideoMode(videoModes.get(0));
|
||||
calculateFrameStaticProps();
|
||||
}
|
||||
|
||||
private int timeToPiCamV2RawExposure(double time_us) {
|
||||
@@ -281,6 +279,6 @@ public class USBCameraSource implements VisionSource {
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(
|
||||
camera, usbCameraSettables, usbFrameProvider, configuration, cvSink, cameraQuirks);
|
||||
camera, usbCameraSettables, usbFrameProvider, cameraConfiguration, cvSink, cameraQuirks);
|
||||
}
|
||||
}
|
||||
@@ -29,13 +29,14 @@ import org.photonvision.vision.frame.provider.AcceleratedPicamFrameProvider;
|
||||
import org.photonvision.vision.processes.VisionSource;
|
||||
import org.photonvision.vision.processes.VisionSourceSettables;
|
||||
|
||||
public class ZeroCopyPicamSource implements VisionSource {
|
||||
public class ZeroCopyPicamSource extends VisionSource {
|
||||
private static final Logger logger = new Logger(ZeroCopyPicamSource.class, LogGroup.Camera);
|
||||
|
||||
private final VisionSourceSettables settables;
|
||||
private final AcceleratedPicamFrameProvider frameProvider;
|
||||
|
||||
public ZeroCopyPicamSource(CameraConfiguration configuration) {
|
||||
super(configuration);
|
||||
if (configuration.cameraType != CameraType.ZeroCopyPicam) {
|
||||
throw new IllegalArgumentException(
|
||||
"GPUAcceleratedPicamSource only accepts CameraConfigurations with type Picam");
|
||||
@@ -99,8 +100,7 @@ public class ZeroCopyPicamSource implements VisionSource {
|
||||
0, new FPSRatedVideoMode(VideoMode.PixelFormat.kUnknown, 320, 240, 120, 120, .39));
|
||||
videoModes.put(
|
||||
1, new FPSRatedVideoMode(VideoMode.PixelFormat.kUnknown, 640, 480, 65, 90, .39));
|
||||
videoModes.put(
|
||||
2, new FPSRatedVideoMode(VideoMode.PixelFormat.kUnknown, 1280, 720, 40, 90, .72));
|
||||
// TODO: fix 1280x720 in the native code and re-add it
|
||||
videoModes.put(
|
||||
3, new FPSRatedVideoMode(VideoMode.PixelFormat.kUnknown, 1920, 1080, 15, 20, .53));
|
||||
} else {
|
||||
@@ -136,19 +136,22 @@ public class ZeroCopyPicamSource implements VisionSource {
|
||||
@Override
|
||||
public void setExposure(double exposure) {
|
||||
lastExposure = exposure;
|
||||
PicamJNI.setExposure((int) Math.round(exposure));
|
||||
var success = PicamJNI.setExposure((int) Math.round(exposure));
|
||||
if (!success) logger.warn("Couldn't set Pi camera exposure");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBrightness(int brightness) {
|
||||
lastBrightness = brightness;
|
||||
PicamJNI.setBrightness(brightness);
|
||||
var success = PicamJNI.setBrightness(brightness);
|
||||
if (!success) logger.warn("Couldn't set Pi camera brightness");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGain(int gain) {
|
||||
lastGain = gain;
|
||||
PicamJNI.setGain(gain);
|
||||
var success = PicamJNI.setGain(gain);
|
||||
if (!success) logger.warn("Couldn't set Pi camera gain");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -159,8 +162,14 @@ public class ZeroCopyPicamSource implements VisionSource {
|
||||
@Override
|
||||
protected void setVideoModeInternal(VideoMode videoMode) {
|
||||
var mode = (FPSRatedVideoMode) videoMode;
|
||||
PicamJNI.destroyCamera();
|
||||
PicamJNI.createCamera(mode.width, mode.height, mode.fpsActual);
|
||||
var success = PicamJNI.destroyCamera();
|
||||
if (!success)
|
||||
throw new RuntimeException(
|
||||
"Couldn't destroy a zero copy Pi camera while switching video modes");
|
||||
success = PicamJNI.createCamera(mode.width, mode.height, mode.fpsActual);
|
||||
if (!success)
|
||||
throw new RuntimeException(
|
||||
"Couldn't create a zero copy Pi camera while switching video modes");
|
||||
|
||||
// We don't store last settings on the native side, and when you change video mode these get
|
||||
// reset on MMAL's end
|
||||
@@ -18,6 +18,7 @@
|
||||
package org.photonvision.vision.frame;
|
||||
|
||||
import edu.wpi.first.wpilibj.geometry.Rotation2d;
|
||||
import org.photonvision.common.util.math.MathUtils;
|
||||
import org.photonvision.vision.opencv.CVMat;
|
||||
import org.photonvision.vision.opencv.Releasable;
|
||||
|
||||
@@ -33,13 +34,14 @@ public class Frame implements Releasable {
|
||||
}
|
||||
|
||||
public Frame(CVMat image, FrameStaticProperties frameStaticProperties) {
|
||||
this(image, System.nanoTime(), frameStaticProperties);
|
||||
this(image, MathUtils.wpiNanoTime(), frameStaticProperties);
|
||||
}
|
||||
|
||||
public Frame() {
|
||||
timestampNanos = 0;
|
||||
image = new CVMat();
|
||||
frameStaticProperties = new FrameStaticProperties(0, 0, 0, new Rotation2d(), null);
|
||||
this(
|
||||
new CVMat(),
|
||||
MathUtils.wpiNanoTime(),
|
||||
new FrameStaticProperties(0, 0, 0, new Rotation2d(), null));
|
||||
}
|
||||
|
||||
public void copyTo(Frame destFrame) {
|
||||
@@ -19,8 +19,6 @@ package org.photonvision.vision.frame;
|
||||
|
||||
import edu.wpi.cscore.VideoMode;
|
||||
import edu.wpi.first.wpilibj.geometry.Rotation2d;
|
||||
import org.apache.commons.math3.fraction.Fraction;
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
import org.opencv.core.Point;
|
||||
import org.photonvision.common.util.numbers.DoubleCouple;
|
||||
import org.photonvision.vision.calibration.CameraCalibrationCoefficients;
|
||||
@@ -79,23 +77,20 @@ public class FrameStaticProperties {
|
||||
DoubleCouple horizVertViews =
|
||||
calculateHorizontalVerticalFoV(this.fov, this.imageWidth, this.imageHeight);
|
||||
|
||||
horizontalFocalLength = this.imageWidth / (2 * FastMath.tan(horizVertViews.getFirst() / 2));
|
||||
verticalFocalLength = this.imageHeight / (2 * FastMath.tan(horizVertViews.getSecond() / 2));
|
||||
horizontalFocalLength = this.imageWidth / (2 * Math.tan(horizVertViews.getFirst() / 2));
|
||||
verticalFocalLength = this.imageHeight / (2 * Math.tan(horizVertViews.getSecond() / 2));
|
||||
}
|
||||
|
||||
public static DoubleCouple calculateHorizontalVerticalFoV(
|
||||
double diagonalFoV, int imageWidth, int imageHeight) {
|
||||
double diagonalView = FastMath.toRadians(diagonalFoV);
|
||||
Fraction aspectFraction = new Fraction(imageWidth, imageHeight);
|
||||
|
||||
int horizontalRatio = aspectFraction.getNumerator();
|
||||
int verticalRatio = aspectFraction.getDenominator();
|
||||
double diagonalView = Math.toRadians(diagonalFoV);
|
||||
double diagonalAspect = Math.hypot(imageWidth, imageHeight);
|
||||
|
||||
double diagonalAspect = FastMath.hypot(horizontalRatio, verticalRatio);
|
||||
double horizontalView =
|
||||
FastMath.atan(FastMath.tan(diagonalView / 2) * (horizontalRatio / diagonalAspect)) * 2;
|
||||
Math.atan(Math.tan(diagonalView / 2) * (imageWidth / diagonalAspect)) * 2;
|
||||
double verticalView =
|
||||
FastMath.atan(FastMath.tan(diagonalView / 2) * (verticalRatio / diagonalAspect)) * 2;
|
||||
Math.atan(Math.tan(diagonalView / 2) * (imageHeight / diagonalAspect)) * 2;
|
||||
|
||||
return new DoubleCouple(horizontalView, verticalView);
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
package org.photonvision.vision.frame.provider;
|
||||
|
||||
import org.opencv.core.Mat;
|
||||
import org.photonvision.common.util.math.MathUtils;
|
||||
import org.photonvision.raspi.PicamJNI;
|
||||
import org.photonvision.vision.frame.Frame;
|
||||
import org.photonvision.vision.frame.FrameProvider;
|
||||
@@ -34,7 +35,13 @@ public class AcceleratedPicamFrameProvider implements FrameProvider {
|
||||
this.settables = visionSettables;
|
||||
|
||||
var vidMode = settables.getCurrentVideoMode();
|
||||
PicamJNI.createCamera(vidMode.width, vidMode.height, vidMode.fps);
|
||||
var success = PicamJNI.createCamera(vidMode.width, vidMode.height, vidMode.fps);
|
||||
if (!success) {
|
||||
success = PicamJNI.destroyCamera();
|
||||
if (!success) throw new RuntimeException("Couldn't destroy Pi camera after init failure!");
|
||||
throw new RuntimeException(
|
||||
"Couldn't initialize zero copy Pi camera; check stdout for native code logs");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -47,6 +54,8 @@ public class AcceleratedPicamFrameProvider implements FrameProvider {
|
||||
long matHandle = PicamJNI.grabFrame(false);
|
||||
mat = new CVMat(new Mat(matHandle));
|
||||
return new Frame(
|
||||
mat, System.nanoTime() - PicamJNI.getFrameLatency(), settables.getFrameStaticProperties());
|
||||
mat,
|
||||
MathUtils.wpiNanoTime() - PicamJNI.getFrameLatency(),
|
||||
settables.getFrameStaticProperties());
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user