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.frame.provider;
019
020import org.photonvision.common.util.numbers.IntegerCouple;
021import org.photonvision.vision.frame.Frame;
022import org.photonvision.vision.frame.FrameProvider;
023import org.photonvision.vision.frame.FrameStaticProperties;
024import org.photonvision.vision.frame.FrameThresholdType;
025import org.photonvision.vision.opencv.CVMat;
026import org.photonvision.vision.opencv.ImageRotationMode;
027import org.photonvision.vision.pipe.CVPipe.CVPipeResult;
028import org.photonvision.vision.pipe.impl.GrayscalePipe;
029import org.photonvision.vision.pipe.impl.HSVPipe;
030import org.photonvision.vision.pipe.impl.RotateImagePipe;
031
032public abstract class CpuImageProcessor extends FrameProvider {
033    protected static class CapturedFrame {
034        CVMat colorImage;
035        FrameStaticProperties staticProps;
036        long captureTimestamp;
037
038        public CapturedFrame(
039                CVMat colorImage, FrameStaticProperties staticProps, long captureTimestampNanos) {
040            this.colorImage = colorImage;
041            this.staticProps = staticProps;
042            this.captureTimestamp = captureTimestampNanos;
043        }
044    }
045
046    private final HSVPipe m_hsvPipe = new HSVPipe();
047    private final RotateImagePipe m_rImagePipe = new RotateImagePipe();
048    private final GrayscalePipe m_grayPipe = new GrayscalePipe();
049    FrameThresholdType m_processType;
050
051    private final Object m_mutex = new Object();
052
053    abstract CapturedFrame getInputMat();
054
055    public CpuImageProcessor() {
056        m_hsvPipe.setParams(
057                new HSVPipe.HSVParams(
058                        new IntegerCouple(0, 180),
059                        new IntegerCouple(0, 255),
060                        new IntegerCouple(0, 255),
061                        false));
062    }
063
064    @Override
065    public final Frame get() {
066        // TODO Auto-generated method stub
067        var input = getInputMat();
068
069        CVMat outputMat = null;
070        long sumNanos = 0;
071
072        {
073            CVPipeResult<Void> out = m_rImagePipe.run(input.colorImage.getMat());
074            sumNanos += out.nanosElapsed;
075        }
076
077        if (!input.colorImage.getMat().empty()) {
078            if (m_processType == FrameThresholdType.HSV) {
079                var hsvResult = m_hsvPipe.run(input.colorImage.getMat());
080                outputMat = new CVMat(hsvResult.output);
081                sumNanos += hsvResult.nanosElapsed;
082            } else if (m_processType == FrameThresholdType.GREYSCALE) {
083                var result = m_grayPipe.run(input.colorImage.getMat());
084                outputMat = new CVMat(result.output);
085                sumNanos += result.nanosElapsed;
086            } else {
087                outputMat = new CVMat();
088            }
089
090            ++sequenceID;
091        } else {
092            System.out.println("Input was empty!");
093            outputMat = new CVMat();
094        }
095
096        return new Frame(
097                sequenceID,
098                input.colorImage,
099                outputMat,
100                m_processType,
101                input.captureTimestamp,
102                input.staticProps != null
103                        ? input.staticProps.rotate(m_rImagePipe.getParams().rotation)
104                        : input.staticProps);
105    }
106
107    @Override
108    public void requestFrameThresholdType(FrameThresholdType type) {
109        synchronized (m_mutex) {
110            this.m_processType = type;
111        }
112    }
113
114    @Override
115    public void requestFrameRotation(ImageRotationMode rotationMode) {
116        synchronized (m_mutex) {
117            m_rImagePipe.setParams(new RotateImagePipe.RotateImageParams(rotationMode));
118        }
119    }
120
121    /** Ask the camera to rotate frames it outputs */
122    public void requestHsvSettings(HSVPipe.HSVParams params) {
123        synchronized (m_mutex) {
124            m_hsvPipe.setParams(params);
125        }
126    }
127
128    @Override
129    public void requestFrameCopies(boolean copyInput, boolean copyOutput) {
130        // We don't actually do zero-copy, so this method is a no-op
131    }
132}