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}