//----------------------------------------------------------------------------// // // // V e r t i c a l s B u i l d e r // // // //----------------------------------------------------------------------------// // <editor-fold defaultstate="collapsed" desc="hdr"> // // Copyright © Hervé Bitteur and others 2000-2013. All rights reserved. // // This software is released under the GNU General Public License. // // Goto http://kenai.com/projects/audiveris to report bugs or suggestions. // //----------------------------------------------------------------------------// // </editor-fold> package omr.sheet; import omr.check.Check; import omr.check.CheckSuite; import omr.check.FailureResult; import omr.check.SuccessResult; import omr.constant.Constant; import omr.constant.ConstantSet; import omr.glyph.Glyphs; import omr.glyph.Shape; import omr.glyph.ShapeChecker; import omr.glyph.facets.Glyph; import omr.lag.Lag; import omr.lag.Section; import omr.lag.Sections; import omr.run.Orientation; import static omr.run.Orientation.*; import omr.selection.GlyphEvent; import omr.selection.UserEvent; import omr.step.StepException; import omr.stick.SectionsSource; import omr.stick.SticksBuilder; import omr.util.Predicate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.Rectangle; import java.util.ArrayList; import java.util.Collection; /** * Class {@code VerticalsBuilder} is in charge of retrieving all the * vertical sticks of a dedicated system. * * Bars are assumed to have been already recognized, so this accounts for stems, * vertical edges of endings, and potentially parts of alterations * (sharp, natural, flat). * * @author Hervé Bitteur */ public class VerticalsBuilder { //~ Static fields/initializers --------------------------------------------- /** Specific application parameters */ private static final Constants constants = new Constants(); /** Usual logger utility */ private static final Logger logger = LoggerFactory.getLogger( VerticalsBuilder.class); /** Events this entity is interested in */ private static final Collection<Class<? extends UserEvent>> eventClasses = new ArrayList<>(); static { eventClasses.add(GlyphEvent.class); } // Success codes private static final SuccessResult STEM = new SuccessResult("Stem"); // Failure codes private static final FailureResult TOO_LIMITED = new FailureResult( "Stem-TooLimited"); private static final FailureResult TOO_SHORT = new FailureResult( "Stem-TooShort"); private static final FailureResult TOO_FAT = new FailureResult( "Stem-TooFat"); private static final FailureResult TOO_HIGH_ADJACENCY = new FailureResult( "Stem-TooHighAdjacency"); private static final FailureResult OUTSIDE_SYSTEM = new FailureResult( "Stem-OutsideSystem"); private static final FailureResult TOO_HOLLOW = new FailureResult( "Stem-TooHollow"); //~ Instance fields -------------------------------------------------------- /** Related sheet */ private final Sheet sheet; /** Dedicated system */ private final SystemInfo system; /** Related lag */ private final Lag lag; /** Global sheet scale */ private final Scale scale; //~ Constructors ----------------------------------------------------------- //------------------// // VerticalsBuilder // //------------------// /** * Creates a new VerticalsBuilder object. * * @param system the related system */ public VerticalsBuilder (SystemInfo system) { // We work with the sheet vertical lag this.system = system; this.sheet = system.getSheet(); this.lag = sheet.getVerticalLag(); scale = system.getSheet().getScale(); } //~ Methods ---------------------------------------------------------------- //----------------------// // createStemCheckSuite // //----------------------// /** * Create a brand new check suite for stem glyph candidates * * @param isShort should we look for short (vs standard) stems? * @return the check suite ready for use */ public CheckSuite<Glyph> createStemCheckSuite (boolean isShort) { return new StemCheckSuite(isShort); } //-------------------// // retrieveVerticals // //-------------------// /** * Actually build the new verticals glyphs out of the dedicated system * * @return the number of stems found * @throws omr.step.StepException */ public int retrieveVerticals () throws StepException { // Get rid of former symbols system.removeInactiveGlyphs(); // We cannot reuse the sticks, since thick sticks are allowed for bars // but not for stems. SticksBuilder verticalsBuilder = new SticksBuilder( Orientation.VERTICAL, scale, sheet.getNest(), new SectionsSource( system.getVerticalSections(), new MySectionPredicate()), false); verticalsBuilder.setMaxThickness(constants.maxStemThickness); return retrieveVerticals(verticalsBuilder.retrieveSticks(), false); } //---------------------// // segmentGlyphOnStems // //---------------------// /** * Decompose the provided glyph into stems + leaves * * @param glyph the glyph to decompose * @param isShort are we looking for short (vs standard) stems? */ public void segmentGlyphOnStems (Glyph glyph, boolean isShort) { // Gather all sections to be browsed Collection<Section> sections = new ArrayList<>( glyph.getMembers()); logger.debug("Sections browsed: {}", Sections.toString(sections)); // Retrieve vertical sticks as stem candidates try { SticksBuilder verticalsArea = new SticksBuilder( Orientation.VERTICAL, scale, sheet.getNest(), new SectionsSource(sections, new MySectionPredicate()), false); verticalsArea.setMaxThickness(constants.maxStemThickness); // Retrieve stems int nb = retrieveVerticals(verticalsArea.retrieveSticks(), isShort); if (nb > 0) { logger.debug("{} stem{}", nb, (nb > 1) ? "s" : ""); } else { logger.debug("No stem found"); } } catch (StepException ex) { logger.warn("stemSegment. Error in retrieving verticals"); } } //-------------------// // retrieveVerticals // //-------------------// /** * This method retrieve compliant vertical entities (stems) within a * collection of vertical sticks, and in the context of a system * * @param sticks the provided collection of vertical sticks * @param isShort true for short stems * @return the number of stems found * @throws StepException */ private int retrieveVerticals (Collection<Glyph> sticks, boolean isShort) throws StepException { /** Suite of checks for a stem glyph */ StemCheckSuite suite = new StemCheckSuite(isShort); double minResult = constants.minCheckResult.getValue(); int stemNb = 0; logger.debug("Searching verticals among {} sticks from {}", sticks.size(), Glyphs.toString(sticks)); for (Glyph stick : sticks) { stick = system.addGlyph(stick); if (stick.isKnown()) { continue; } // Check stem is not too far from nearest staff if (!ShapeChecker.getInstance().checkStem(system, stick)) { logger.debug("Too distant stem {}", stick.idString()); continue; } if (!stick.isShapeForbidden(Shape.STEM)) { // Run the various Checks double res = suite.pass(stick); logger.debug("suite=> {} for {}", res, stick); if (res >= minResult) { stick.setResult(STEM); stick.setShape(Shape.STEM); stemNb++; } else { stick.setResult(TOO_LIMITED); } } } logger.debug("Found {} stems", stemNb); return stemNb; } //~ Inner Classes ---------------------------------------------------------- //----------------// // StemCheckSuite // //----------------// /** * A suite of checks meant for stem candidates */ public class StemCheckSuite extends CheckSuite<Glyph> { //~ Constructors ------------------------------------------------------- /** * Create a new instance * * @param isShort for short stems */ public StemCheckSuite (boolean isShort) { super("Stem", constants.minCheckResult.getValue()); add(1, new MinLengthCheck(isShort)); add(1, new MinAspectCheck()); add(1, new FirstAdjacencyCheck()); add(1, new LastAdjacencyCheck()); add(0, new LeftCheck(system)); add(0, new RightCheck(system)); add(2, new MinDensityCheck()); if (logger.isDebugEnabled()) { dump(); } } //~ Methods ------------------------------------------------------------ @Override protected void dumpSpecific (StringBuilder sb) { sb.append(String.format("%s%n", system)); } } //-----------// // Constants // //-----------// private static final class Constants extends ConstantSet { //~ Instance fields ---------------------------------------------------- Constant.Ratio maxStemAdjacencyHigh = new Constant.Ratio( 0.75, "High Maximum adjacency ratio for a stem stick"); Constant.Ratio maxStemAdjacencyLow = new Constant.Ratio( 0.60, "Low Maximum adjacency ratio for a stem stick"); Check.Grade minCheckResult = new Check.Grade( 0.4, "Minimum result for suite of check"); Constant.Ratio minDensityHigh = new Constant.Ratio( 0.75, "High Minimum density for a stem"); Constant.Ratio minDensityLow = new Constant.Ratio( 0.35, "Low Minimum density for a stem"); Constant.Ratio minStemAspectHigh = new Constant.Ratio( 8.33, "High Minimum aspect ratio for a stem stick"); Constant.Ratio minStemAspectLow = new Constant.Ratio( 6.67, "Low Minimum aspect ratio for a stem stick"); Scale.Fraction minStemLengthHigh = new Scale.Fraction( 2.5, "High Minimum length for a stem"); Scale.Fraction minStemLengthLow = new Scale.Fraction( 2.0, "Low Minimum length for a stem"); Scale.Fraction minShortStemLengthHigh = new Scale.Fraction( 2.5, "Low Minimum length for a short stem"); Scale.Fraction minShortStemLengthLow = new Scale.Fraction( 2.0, "Low Minimum length for a short stem"); Scale.Fraction maxStemThickness = new Scale.Fraction( 0.3, "Maximum thickness of an interesting vertical stick"); Scale.Fraction minStaffDxHigh = new Scale.Fraction( 0, "High Minimum horizontal distance between a stem and a staff edge"); Scale.Fraction minStaffDxLow = new Scale.Fraction( 0, "Low Minimum horizontal distance between a stem and a staff edge"); } //---------------------// // FirstAdjacencyCheck // //---------------------// private static class FirstAdjacencyCheck extends Check<Glyph> { //~ Constructors ------------------------------------------------------- protected FirstAdjacencyCheck () { super( "LeftAdj", "Check that stick is open on left side", constants.maxStemAdjacencyLow, constants.maxStemAdjacencyHigh, false, TOO_HIGH_ADJACENCY); } //~ Methods ------------------------------------------------------------ // Retrieve the adjacency value @Override protected double getValue (Glyph stick) { return (double) stick.getFirstStuck() / stick.getLength(VERTICAL); } } //--------------------// // LastAdjacencyCheck // //--------------------// private static class LastAdjacencyCheck extends Check<Glyph> { //~ Constructors ------------------------------------------------------- protected LastAdjacencyCheck () { super( "RightAdj", "Check that stick is open on right side", constants.maxStemAdjacencyLow, constants.maxStemAdjacencyHigh, false, TOO_HIGH_ADJACENCY); } //~ Methods ------------------------------------------------------------ // Retrieve the adjacency value @Override protected double getValue (Glyph stick) { return (double) stick.getLastStuck() / stick.getLength(VERTICAL); } } //--------------------// // MySectionPredicate // //--------------------// private static class MySectionPredicate implements Predicate<Section> { //~ Methods ------------------------------------------------------------ @Override public boolean check (Section section) { // We process section for which glyph is null // or GLYPH_PART, NO_LEGAL_TIME, NOISE, STRUCTURE boolean result = (section.getGlyph() == null) || !section.getGlyph().isWellKnown(); return result; } } //-----------// // LeftCheck // //-----------// private class LeftCheck extends Check<Glyph> { //~ Instance fields ---------------------------------------------------- private final SystemInfo system; //~ Constructors ------------------------------------------------------- protected LeftCheck (SystemInfo system) { super( "LeftLimit", "Check stick is on right of the system beginning bar", constants.minStaffDxLow, constants.minStaffDxHigh, true, OUTSIDE_SYSTEM); this.system = system; } //~ Methods ------------------------------------------------------------ // Retrieve the stick abscissa @Override protected double getValue (Glyph stick) { int x = stick.getMidPos(Orientation.VERTICAL); return scale.pixelsToFrac(x - system.getLeft()); } } //----------------// // MinAspectCheck // //----------------// private static class MinAspectCheck extends Check<Glyph> { //~ Constructors ------------------------------------------------------- protected MinAspectCheck () { super( "MinAspect", "Check that stick aspect (length/thickness) is high enough", constants.minStemAspectLow, constants.minStemAspectHigh, true, TOO_FAT); } //~ Methods ------------------------------------------------------------ // Retrieve the ratio length / thickness @Override protected double getValue (Glyph stick) { return stick.getAspect(VERTICAL); } } //-----------------// // MinDensityCheck // //-----------------// private static class MinDensityCheck extends Check<Glyph> { //~ Constructors ------------------------------------------------------- protected MinDensityCheck () { super( "MinDensity", "Check that stick fills its bounding rectangle", constants.minDensityLow, constants.minDensityHigh, true, TOO_HOLLOW); } //~ Methods ------------------------------------------------------------ // Retrieve the density @Override protected double getValue (Glyph stick) { Rectangle rect = stick.getBounds(); double area = rect.width * rect.height; return (double) stick.getWeight() / area; } } //----------------// // MinLengthCheck // //----------------// private class MinLengthCheck extends Check<Glyph> { //~ Constructors ------------------------------------------------------- protected MinLengthCheck (boolean isShort) { super( "MinLength", "Check that stick is long enough", isShort ? constants.minShortStemLengthLow : constants.minStemLengthLow, isShort ? constants.minShortStemLengthHigh : constants.minStemLengthHigh, true, TOO_SHORT); } //~ Methods ------------------------------------------------------------ // Retrieve the length data @Override protected double getValue (Glyph stick) { return scale.pixelsToFrac(stick.getLength(VERTICAL)); } } //------------// // RightCheck // //------------// private class RightCheck extends Check<Glyph> { //~ Instance fields ---------------------------------------------------- private final SystemInfo system; //~ Constructors ------------------------------------------------------- protected RightCheck (SystemInfo system) { super( "RightLimit", "Check stick is on left of the system ending bar", constants.minStaffDxLow, constants.minStaffDxHigh, true, OUTSIDE_SYSTEM); this.system = system; } //~ Methods ------------------------------------------------------------ // Retrieve the stick abscissa @Override protected double getValue (Glyph stick) { return scale.pixelsToFrac( (system.getLeft() + system.getWidth()) - stick.getMidPos(Orientation.VERTICAL)); } } }