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.pipe.impl; 019 020import java.util.ArrayList; 021import java.util.Collections; 022import java.util.List; 023import org.photonvision.vision.opencv.Contour; 024import org.photonvision.vision.opencv.ContourGroupingMode; 025import org.photonvision.vision.opencv.ContourIntersectionDirection; 026import org.photonvision.vision.pipe.CVPipe; 027import org.photonvision.vision.target.PotentialTarget; 028 029public class GroupContoursPipe 030 extends CVPipe<List<Contour>, List<PotentialTarget>, GroupContoursPipe.GroupContoursParams> { 031 private final List<PotentialTarget> m_targets = new ArrayList<>(); 032 033 @Override 034 protected List<PotentialTarget> process(List<Contour> input) { 035 for (var target : m_targets) { 036 target.release(); 037 } 038 039 m_targets.clear(); 040 041 if (params.getGroup() == ContourGroupingMode.Single) { 042 for (var contour : input) { 043 m_targets.add(new PotentialTarget(contour)); 044 } 045 } 046 // Check if we have at least 2 targets for 2 or more 047 // This will only ever return 1 contour! 048 else if (params.getGroup() == ContourGroupingMode.TwoOrMore 049 && input.size() >= ContourGroupingMode.TwoOrMore.count) { 050 // Just blob everything together 051 Contour groupedContour = Contour.combineContourList(input); 052 if (groupedContour != null) { 053 m_targets.add(new PotentialTarget(groupedContour, input)); 054 } 055 } else { 056 int groupingCount = params.getGroup().count; 057 058 if (input.size() >= groupingCount) { 059 input.sort(Contour.SortByMomentsX); 060 // also why reverse? shouldn't the sort comparator just get reversed? 061 // TODO: Matt, see this 062 Collections.reverse(input); 063 064 for (int i = 0; i < input.size() - 1; i++) { 065 // make a list of the desired count of contours to group 066 // (Just make sure we don't get an index out of bounds exception 067 if (i < 0 || i + groupingCount > input.size()) continue; 068 069 // If we're in two or more mode, just try to group everything 070 List<Contour> groupingSet = input.subList(i, i + groupingCount); 071 072 try { 073 // FYI: This method only takes 2 contours! 074 Contour groupedContour = 075 Contour.groupContoursByIntersection( 076 groupingSet.get(0), groupingSet.get(1), params.getIntersection()); 077 078 if (groupedContour != null) { 079 m_targets.add(new PotentialTarget(groupedContour, groupingSet)); 080 i += (groupingCount - 1); 081 } 082 } catch (Exception ex) { 083 ex.printStackTrace(); 084 } 085 } 086 } 087 } 088 return m_targets; 089 } 090 091 public static class GroupContoursParams { 092 private final ContourGroupingMode m_group; 093 private final ContourIntersectionDirection m_intersection; 094 095 public GroupContoursParams( 096 ContourGroupingMode group, ContourIntersectionDirection intersectionDirection) { 097 m_group = group; 098 m_intersection = intersectionDirection; 099 } 100 101 public ContourGroupingMode getGroup() { 102 return m_group; 103 } 104 105 public ContourIntersectionDirection getIntersection() { 106 return m_intersection; 107 } 108 } 109}