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}