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.opencv;
019
020import edu.wpi.first.util.RawFrame;
021import java.util.HashMap;
022import org.opencv.core.Mat;
023import org.photonvision.common.logging.LogGroup;
024import org.photonvision.common.logging.Logger;
025
026public class CVMat implements Releasable {
027    private static final Logger logger = new Logger(CVMat.class, LogGroup.General);
028
029    private static int allMatCounter = 0;
030    private static final HashMap<Mat, Integer> allMats = new HashMap<>();
031
032    private static boolean shouldPrint;
033
034    private final Mat mat;
035    private final RawFrame backingFrame;
036
037    public CVMat() {
038        this(new Mat());
039    }
040
041    public CVMat(Mat mat) {
042        this(mat, null);
043    }
044
045    public void copyFrom(CVMat srcMat) {
046        copyFrom(srcMat.getMat());
047    }
048
049    public void copyFrom(Mat srcMat) {
050        srcMat.copyTo(mat);
051    }
052
053    private StringBuilder getStackTraceBuilder() {
054        var trace = Thread.currentThread().getStackTrace();
055
056        final int STACK_FRAMES_TO_SKIP = 3;
057        final var traceStr = new StringBuilder();
058        for (int idx = STACK_FRAMES_TO_SKIP; idx < trace.length; idx++) {
059            traceStr.append("\t\n").append(trace[idx]);
060        }
061        traceStr.append("\n");
062        return traceStr;
063    }
064
065    public CVMat(Mat mat, RawFrame frame) {
066        this.mat = mat;
067        this.backingFrame = frame;
068
069        allMatCounter++;
070        allMats.put(mat, allMatCounter);
071
072        if (shouldPrint) {
073            logger.trace(() -> "CVMat" + allMatCounter + " alloc - new count: " + allMats.size());
074            logger.trace(getStackTraceBuilder()::toString);
075        }
076    }
077
078    @Override
079    public void release() {
080        if (this.backingFrame != null) this.backingFrame.close();
081
082        // If this mat is empty, all we can do is return
083        if (mat.empty()) return;
084
085        // If the mat isn't in the hashmap, we can't remove it
086        Integer matNo = allMats.get(mat);
087        if (matNo != null) allMats.remove(mat);
088        mat.release();
089
090        if (shouldPrint) {
091            logger.trace(() -> "CVMat" + matNo + " de-alloc - new count: " + allMats.size());
092            logger.trace(getStackTraceBuilder()::toString);
093        }
094    }
095
096    public Mat getMat() {
097        return mat;
098    }
099
100    @Override
101    public String toString() {
102        return "CVMat{" + mat.toString() + '}';
103    }
104
105    public static int getMatCount() {
106        return allMats.size();
107    }
108
109    public static void enablePrint(boolean enabled) {
110        shouldPrint = enabled;
111    }
112}