001/*
002 * Copyright (C) Photon Vision.
003 *
004 * This program is free software: you can redistribute it and/or modify
005 * it under the terms of the GNU General Public License as published by
006 * the Free Software Foundation, either version 3 of the License, or
007 * (at your option) any later version.
008 *
009 * This program is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012 * GNU General Public License for more details.
013 *
014 * You should have received a copy of the GNU General Public License
015 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
016 */
017
018package org.photonvision.vision.pipeline;
019
020import org.photonvision.vision.camera.QuirkyCamera;
021import org.photonvision.vision.frame.Frame;
022import org.photonvision.vision.frame.FrameStaticProperties;
023import org.photonvision.vision.frame.FrameThresholdType;
024import org.photonvision.vision.opencv.Releasable;
025import org.photonvision.vision.pipeline.result.CVPipelineResult;
026
027public abstract class CVPipeline<R extends CVPipelineResult, S extends CVPipelineSettings>
028        implements Releasable {
029    static final int MAX_MULTI_TARGET_RESULTS = 10;
030
031    protected S settings;
032    protected FrameStaticProperties frameStaticProperties;
033    protected QuirkyCamera cameraQuirks;
034
035    private final FrameThresholdType thresholdType;
036
037    // So releaseable doesn't keep track of if we double-free something. so (ew) remember that here
038    protected volatile boolean released = false;
039
040    public CVPipeline(FrameThresholdType thresholdType) {
041        this.thresholdType = thresholdType;
042    }
043
044    public FrameThresholdType getThresholdType() {
045        return thresholdType;
046    }
047
048    protected void setPipeParams(
049            FrameStaticProperties frameStaticProperties, S settings, QuirkyCamera cameraQuirks) {
050        this.settings = settings;
051        this.frameStaticProperties = frameStaticProperties;
052        this.cameraQuirks = cameraQuirks;
053
054        setPipeParamsImpl();
055    }
056
057    protected abstract void setPipeParamsImpl();
058
059    protected abstract R process(Frame frame, S settings);
060
061    public S getSettings() {
062        return settings;
063    }
064
065    public void setSettings(S s) {
066        this.settings = s;
067    }
068
069    public R run(Frame frame, QuirkyCamera cameraQuirks) {
070        if (released) {
071            throw new RuntimeException("Pipeline use-after-free!");
072        }
073        if (settings == null) {
074            throw new RuntimeException("No settings provided for pipeline!");
075        }
076        setPipeParams(frame.frameStaticProperties, settings, cameraQuirks);
077
078        // if (frame.image.getMat().empty()) {
079        //     //noinspection unchecked
080        //     return (R) new CVPipelineResult(0, 0, List.of(), frame);
081        // }
082        R result = process(frame, settings);
083
084        result.setImageCaptureTimestampNanos(frame.timestampNanos);
085
086        return result;
087    }
088
089    /**
090     * Release any native memory associated with this pipeline. Called by pipelinemanager at pipeline
091     * switch. Stubbed out, but override if needed.
092     */
093    @Override
094    public void release() {
095        released = true;
096    }
097}