Save input image before resize or draw (#214)

This commit is contained in:
Matt
2021-01-01 15:16:45 -08:00
committed by GitHub
parent f676023a5d
commit 8e190ce5f7
5 changed files with 65 additions and 11 deletions

View File

@@ -0,0 +1,22 @@
/*
* Copyright (C) Photon Vision.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.photonvision.common.util.java;
public interface TriConsumer<T, U, V> {
void accept(T t, U u, V v);
}

View File

@@ -24,6 +24,7 @@ import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import org.opencv.imgcodecs.Imgcodecs;
import org.photonvision.common.configuration.ConfigManager;
import org.photonvision.common.dataflow.networktables.NetworkTablesManager;
@@ -32,7 +33,7 @@ import org.photonvision.common.logging.Logger;
import org.photonvision.common.util.TimedTaskManager;
import org.photonvision.vision.frame.Frame;
public class FileSaveFrameConsumer {
public class FileSaveFrameConsumer implements Consumer<Frame> {
// Formatters to generate unique, timestamped file names
private static String FILE_PATH = ConfigManager.getInstance().getImageSavePath().toString();
@@ -59,7 +60,7 @@ public class FileSaveFrameConsumer {
this.rootTable = NetworkTablesManager.getInstance().kRootTable;
updateCameraNickname(camNickname);
entry = subTable.getEntry(ntEntryName);
entry.setBoolean(false);
entry.forceSetBoolean(false);
this.logger = new Logger(FileSaveFrameConsumer.class, this.camNickname, LogGroup.General);
}
@@ -68,8 +69,7 @@ public class FileSaveFrameConsumer {
if (lock.tryLock()) {
boolean curCommand = entry.getBoolean(false);
if (curCommand == true && prevCommand == false) {
if (curCommand && !prevCommand) {
Date now = new Date();
String savefile =
FILE_PATH
@@ -81,12 +81,16 @@ public class FileSaveFrameConsumer {
+ tf.format(now)
+ FILE_EXTENSION;
Imgcodecs.imwrite(savefile.toString(), frame.image.getMat());
Imgcodecs.imwrite(savefile, frame.image.getMat());
// Help the user a bit - set the NT entry back to false after 500ms
TimedTaskManager.getInstance().addOneShotTask(() -> resetCommand(), CMD_RESET_TIME_MS);
TimedTaskManager.getInstance().addOneShotTask(this::resetCommand, CMD_RESET_TIME_MS);
logger.info("Saved new image at " + savefile);
} else if (!curCommand) {
// If the entry is currently false, set it again. This will make sure it shows up on the
// dashboard.
entry.forceSetBoolean(false);
}
prevCommand = curCommand;

View File

@@ -69,9 +69,14 @@ public class CVMat implements Releasable {
@Override
public void release() {
int matNo = allMats.get(mat);
allMats.remove(mat);
// If this mat is empty, all we can do is return
if (mat.empty()) return;
// If the mat isn't in the hashmap, we can't remove it
Integer matNo = allMats.get(mat);
if (matNo != null) allMats.remove(mat);
mat.release();
if (shouldPrint) {
logger.trace(() -> "CVMat" + matNo + " de-alloc - new count: " + allMats.size());
logger.trace(getStackTraceBuilder()::toString);
@@ -82,6 +87,11 @@ public class CVMat implements Releasable {
return mat;
}
@Override
public String toString() {
return "CVMat{" + mat.toString() + '}';
}
public static int getMatCount() {
return allMats.size();
}

View File

@@ -158,9 +158,15 @@ public class ReflectivePipeline extends CVPipeline<CVPipelineResult, ReflectiveP
sumPipeNanosElapsed += hsvPipeResult.nanosElapsed;
pipeProfileNanos[1] = pipeProfileNanos[1] = hsvPipeResult.nanosElapsed;
} else {
// Try to copy the color frame.
long inputMatPtr = PicamJNI.grabFrame(true);
if (inputMatPtr != 0) rawInputMat = new Mat(inputMatPtr);
else rawInputMat = frame.image.getMat();
if (inputMatPtr != 0) {
// If we grabbed it (in color copy mode), make a new Mat of it
rawInputMat = new Mat(inputMatPtr);
} else {
// Otherwise, the input mat is frame we got from the camera
rawInputMat = frame.image.getMat();
}
// We can skip a few steps if the image is single channel because we've already done them on
// the GPU

View File

@@ -33,6 +33,7 @@ import org.photonvision.common.hardware.HardwareManager;
import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
import org.photonvision.common.util.SerializationUtils;
import org.photonvision.common.util.java.TriConsumer;
import org.photonvision.vision.calibration.CameraCalibrationCoefficients;
import org.photonvision.vision.camera.CameraQuirk;
import org.photonvision.vision.camera.QuirkyCamera;
@@ -66,6 +67,9 @@ public class VisionModule {
private final StreamRunnable streamRunnable;
private final LinkedList<CVPipelineResultConsumer> resultConsumers = new LinkedList<>();
private final LinkedList<CVPipelineResultConsumer> fpsLimitedResultConsumers = new LinkedList<>();
// Raw result consumers run before any drawing has been done by the OutputStreamPipeline
private final LinkedList<TriConsumer<Frame, Frame, List<TrackedTarget>>> rawResultConsumers =
new LinkedList<>();
private final NTDataPublisher ntConsumer;
private final UIDataPublisher uiDataConsumer;
protected final int moduleIndex;
@@ -175,7 +179,7 @@ public class VisionModule {
private void recreateFpsLimitedResultConsumers() {
// Important! These must come before the stream result consumers because the stream result
// consumers release the frame
fpsLimitedResultConsumers.add(result -> inputFrameSaver.accept(result.inputFrame));
rawResultConsumers.add((in, out, tgts) -> inputFrameSaver.accept(in));
fpsLimitedResultConsumers.add(result -> outputFrameSaver.accept(result.outputFrame));
fpsLimitedResultConsumers.add(
@@ -249,6 +253,7 @@ public class VisionModule {
this.shouldRun = false;
}
if (shouldRun) {
consumeRawResults(inputFrame, outputFrame, targets);
try {
var osr = outputStreamPipeline.process(inputFrame, outputFrame, settings, targets);
consumeFpsLimitedResult(osr);
@@ -522,6 +527,13 @@ public class VisionModule {
}
}
/** Consume results prior to drawing on them. */
private void consumeRawResults(Frame inputFrame, Frame outputFrame, List<TrackedTarget> targets) {
for (var c : rawResultConsumers) {
c.accept(inputFrame, outputFrame, targets);
}
}
public void setTargetModel(TargetModel targetModel) {
var settings = pipelineManager.getCurrentUserPipeline().getSettings();
if (settings instanceof ReflectivePipelineSettings) {