uk.ac.horizon.artcodes.detect.marker.MarkerEmbeddedChecksumDetector.java Source code

Java tutorial

Introduction

Here is the source code for uk.ac.horizon.artcodes.detect.marker.MarkerEmbeddedChecksumDetector.java

Source

/*
 * Artcodes recognises a different marker scheme that allows the
 * creation of aesthetically pleasing, even beautiful, codes.
 * Copyright (C) 2013-2016  The University of Nottingham
 *
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Affero General Public License as published
 *     by the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Affero General Public License for more details.
 *
 *     You should have received a copy of the GNU Affero General Public License
 *     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package uk.ac.horizon.artcodes.detect.marker;

import android.content.Context;

import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import uk.ac.horizon.artcodes.detect.handler.MarkerDetectionHandler;
import uk.ac.horizon.artcodes.model.Experience;
import uk.ac.horizon.artcodes.process.ImageProcessor;
import uk.ac.horizon.artcodes.process.ImageProcessorFactory;

/**
 * <p>This class detects Artcodes with an embedded checksum region.</p>
 */
public class MarkerEmbeddedChecksumDetector extends MarkerDetector {
    public static class Factory implements ImageProcessorFactory {
        public String getName() {
            return "detectEmbedded";
        }

        public ImageProcessor create(Context context, Experience experience, MarkerDetectionHandler handler,
                Map<String, String> args) {
            return new MarkerEmbeddedChecksumDetector(experience, handler,
                    args != null && args.containsKey("embeddedOnly"), args != null && args.containsKey("relaxed"));
        }
    }

    private boolean embeddedChecksumRequired;
    private boolean relaxedEmbeddedChecksumIgnoreNonHollowDots;
    private boolean relaxedEmbeddedChecksumIgnoreMultipleHollowSegments;

    public MarkerEmbeddedChecksumDetector(Experience experience, MarkerDetectionHandler handler,
            boolean embeddedChecksumRequired, boolean relaxed) {
        super(experience, handler);
        this.embeddedChecksumRequired = embeddedChecksumRequired;
        this.relaxedEmbeddedChecksumIgnoreNonHollowDots = this.relaxedEmbeddedChecksumIgnoreMultipleHollowSegments = relaxed;
    }

    protected Marker createMarkerForNode(int nodeIndex, List<MatOfPoint> contours, Mat hierarchy) {
        List<MarkerRegion> regions = null;
        MarkerRegion checksumRegion = null;
        for (int currentNodeIndex = (int) hierarchy.get(0,
                nodeIndex)[FIRST_NODE]; currentNodeIndex >= 0; currentNodeIndex = (int) hierarchy.get(0,
                        currentNodeIndex)[NEXT_NODE]) {
            final MarkerRegion region = createRegionForNode(currentNodeIndex, contours, hierarchy);
            if (region != null) {
                if (this.ignoreEmptyRegions && region.value == 0) {
                    continue;
                } else if (regions == null) {
                    regions = new ArrayList<>();
                } else if (regions.size() >= maxRegions) {
                    return null;
                }

                regions.add(region);
            } else if (checksumRegion == null) {
                checksumRegion = getChecksumRegionAtNode(currentNodeIndex, hierarchy);
                if (checksumRegion == null) {
                    return null;
                }
            } else {
                return null;
            }
        }

        if (regions != null) {
            Marker marker = new MarkerWithEmbeddedChecksum(nodeIndex, regions, checksumRegion);
            sortCode(marker);
            if (isValidRegionList(marker)) {
                return marker;
            }
        }

        return null;
    }

    protected MarkerRegion getChecksumRegionAtNode(int regionIndex, Mat hierarchy) {
        // Find the first dot index:
        double[] nodes = hierarchy.get(0, regionIndex);
        int currentDotIndex = (int) nodes[FIRST_NODE];
        if (currentDotIndex < 0) {
            return null; // There are no dots in this region.
        }

        // Count all the dots and check if they are leaf nodes in the hierarchy:
        int dotCount = 0;
        while (currentDotIndex >= 0) {
            if (isValidHollowDot(currentDotIndex, hierarchy)) {
                dotCount++;
            } else if (!(this.relaxedEmbeddedChecksumIgnoreNonHollowDots
                    && this.isValidDot(currentDotIndex, hierarchy))) {
                return null; // Dot is not a leaf in the hierarchy.
            }

            // Get next dot node:
            nodes = hierarchy.get(0, currentDotIndex);
            currentDotIndex = (int) nodes[NEXT_NODE];
        }

        return new MarkerRegion(regionIndex, dotCount);
    }

    private boolean isValidHollowDot(int nodeIndex, Mat hierarchy) {
        double[] nodes = hierarchy.get(0, nodeIndex);
        return nodes[FIRST_NODE] >= 0 && // has a child node, and
                (hierarchy.get(0, (int) nodes[FIRST_NODE])[NEXT_NODE] < 0
                        || this.relaxedEmbeddedChecksumIgnoreMultipleHollowSegments)
                && //the child has no siblings, and
                isValidDot((int) nodes[FIRST_NODE], hierarchy);// the child is a leaf
    }

    @Override
    protected boolean hasValidChecksum(Marker marker) {
        if (marker instanceof MarkerWithEmbeddedChecksum) {
            // If the detected Artcodes has an embedded checksum validate it
            MarkerWithEmbeddedChecksum markerEc = (MarkerWithEmbeddedChecksum) marker;
            if (markerEc.checksumRegion != null) {
                // Find weighted sum of code, e.g. 1:1:2:4:4 -> 1*1 + 1*2 + 2*3 + 4*4 + 4*5 = 45
                // Although do not use weights/values divisible by 7
                // e.g. transform values 1,2,3,4,5,6,7,8, 9,10,11,12,13,14,15... to
                //                       1,2,3,4,5,6,8,9,10,11,12,13,15,16,17
                int embeddedChecksumModValue = 7;
                int weightedSum = 0;
                int weight = 1;
                for (int i = 0; i < markerEc.regions.size(); ++i) {
                    int value = markerEc.regions.get(i).value;
                    value += (value + value / embeddedChecksumModValue) / embeddedChecksumModValue;
                    if (weight % embeddedChecksumModValue == 0) {
                        ++weight;
                    }
                    weightedSum += value * weight++;
                }
                return markerEc.checksumRegion.value == (weightedSum - 1) % 7 + 1;
            }
        }

        if (!this.embeddedChecksumRequired) {
            // If the detected Artcode does not have an embedded checksum and embedded checksum is
            // not required, delegate to super class
            return super.hasValidChecksum(marker);
        } else {
            // If the detected Artcode does not have an embedded checksum and embedded checksum is
            // required this is not a valid Artcode.
            return false;
        }
    }
}