/** * Try to recognize a clef in the compound of the provided glyphs. * * @param glyphs the parts of a clef candidate * @param staff the containing staff * @return true if successful */ private boolean checkClef(Collection<Glyph> glyphs, StaffInfo staff) { if (glyphs.isEmpty()) { return false; } // Check if we already have a clef among the intersected glyphs Set<Glyph> clefs = Glyphs.lookupGlyphs(glyphs, clefGlyphPredicate); Glyph orgClef = null; if (!clefs.isEmpty()) { if (Glyphs.containsManual(clefs)) { return false; // Respect user decision } else { // Remember grade of the best existing clef for (Glyph glyph : clefs) { if ((orgClef == null) || (glyph.getGrade() > orgClef.getGrade())) { orgClef = glyph; } } } } // Remove potential aliens Glyphs.purgeManuals(glyphs); Glyph compound = system.buildTransientCompound(glyphs); // Check if a clef appears in the top evaluations Evaluation vote = GlyphNetwork.getInstance().vote(compound, system, Grades.clefMinGrade, clefShapePredicate); if ((vote != null) && ((orgClef == null) || (vote.grade > orgClef.getGrade()))) { // We now have a clef! // Look around for an even better result... logger.debug("{} built from {}", vote.shape, Glyphs.toString(glyphs)); // Look for larger stuff Rectangle outer = compound.getBounds(); outer.grow(xMargin, yMargin); // Remember the box, for visual debug staff.addAttachment("co", outer); List<Glyph> outerGlyphs = system.lookupIntersectedGlyphs(outer); outerGlyphs.removeAll(glyphs); Collections.sort(outerGlyphs, Glyph.byReverseWeight); final double minWeight = constants.minWeight.getValue(); for (Glyph g : outerGlyphs) { // Consider only glyphs with a minimum weight if (g.getNormalizedWeight() < minWeight) { break; } logger.debug("Considering {}", g); Glyph newCompound = system.buildTransientCompound(Arrays.asList(compound, g)); final Evaluation newVote = GlyphNetwork.getInstance() .vote(newCompound, system, Grades.clefMinGrade, clefShapePredicate); if ((newVote != null) && (newVote.grade > vote.grade)) { logger.debug("{} better built with {}", vote, g.idString()); compound = newCompound; vote = newVote; } } // Register the last definition of the clef compound = system.addGlyph(compound); compound.setShape(vote.shape, Evaluation.ALGORITHM); logger.debug("{} rebuilt as {}", vote.shape, compound.idString()); return true; } else { return false; } }
/** * From the list of vertical sticks, this method uses several tests to provide the initial * collection of good barlines candidates. * * @param sticks the collection of candidate sticks */ public void checkCandidates(Collection<? extends Glyph> sticks) { // // Sort candidates according to their abscissa // List<Glyph> sortedSticks = new ArrayList<Glyph>(sticks); // Collections.sort(sortedSticks, Glyph.midPosComparator); double minResult = constants.minCheckResult.getValue(); // Check each candidate stick in turn for (Glyph stick : sticks) { // Allocate the candidate context, and pass the whole check suite GlyphContext context = new GlyphContext(stick); double res = suite.pass(context); if (logger.isDebugEnabled() || stick.isVip()) { logger.info( "suite => {}{} for {}", (float) res, (stick.getResult() != null) ? (" " + stick.getResult()) : "", stick); } if ((stick.isBar() && stick.isManualShape()) || res >= minResult) { // OK, we flag this candidate with proper barline shape contexts.put(stick, context); if ((!stick.isBar() || !stick.isManualShape())) { stick.setShape(isThickBar(stick) ? Shape.THICK_BARLINE : Shape.THIN_BARLINE); } // Additional processing for Bars that define a system or a part // (they start AND end with precise staves horizontal limits) if ((context.topStaff != -1) && (context.botStaff != -1)) { // Here, we have both part & system defining bars // System bars occur first // (since glyphs are sorted by increasing abscissa) stick.setResult(BAR_PART_DEFINING); logger.debug( "Part-defining Barline from staff {} to staff {} {}", context.topStaff, context.botStaff, stick); } else { if (logger.isDebugEnabled()) { logger.debug( "Non-Part-defining Bar line {}{}", (context.topStaff != -1) ? (" topIdx=" + context.topStaff) : "", (context.botStaff != -1) ? (" botIdx=" + context.botStaff) : ""); } stick.setResult(BAR_NOT_PART_DEFINING); } } else { if (stick.isBar()) { if (logger.isDebugEnabled() || stick.isVip()) { logger.info("Purged {} {}", stick.idString(), stick.getShape()); } stick.setShape(null); } } } }
/** * In a specified system, look for all stems that should not be kept, rebuild surrounding glyphs * and try to recognize them. If this action does not lead to some recognized symbol, then we * restore the stems. * * @return the number of symbols recognized */ public int runStemPattern() { int nb = 0; // Collect all undue stems List<Glyph> SuspectedStems = new ArrayList<Glyph>(); for (Glyph glyph : system.getGlyphs()) { if (glyph.isStem() && glyph.isActive()) { Set<Glyph> goods = new HashSet<Glyph>(); Set<Glyph> bads = new HashSet<Glyph>(); glyph.getSymbolsBefore(reliableStemSymbols, goods, bads); glyph.getSymbolsAfter(reliableStemSymbols, goods, bads); if (goods.isEmpty()) { if (logger.isFineEnabled()) { logger.finest("Suspected Stem " + glyph); } SuspectedStems.add(glyph); // Discard "bad" ones for (Glyph g : bads) { if (logger.isFineEnabled()) { logger.finest("Deassigning bad glyph " + g); } g.setShape((Shape) null); } } } } // Remove these stem glyphs since nearby stems are used for recognition for (Glyph glyph : SuspectedStems) { system.removeGlyph(glyph); } // Extract brand new glyphs (removeInactiveGlyphs + retrieveGlyphs) system.extractNewGlyphs(); // Try to recognize each glyph in turn List<Glyph> symbols = new ArrayList<Glyph>(); final GlyphEvaluator evaluator = GlyphNetwork.getInstance(); final double maxDoubt = GlyphInspector.getPatternsMaxDoubt(); for (Glyph glyph : system.getGlyphs()) { if (glyph.getShape() == null) { Evaluation vote = evaluator.vote(glyph, maxDoubt); if (vote != null) { glyph.setShape(vote.shape, vote.doubt); if (glyph.isWellKnown()) { if (logger.isFineEnabled()) { logger.finest("New symbol " + glyph); } symbols.add(glyph); nb++; } } } } // Keep stems that have not been replaced by symbols, definitively // remove the others for (Glyph stem : SuspectedStems) { // Check if one of its section is now part of a symbol boolean known = false; Glyph glyph = null; for (GlyphSection section : stem.getMembers()) { glyph = section.getGlyph(); if ((glyph != null) && glyph.isWellKnown()) { known = true; break; } } if (!known) { // Remove the newly created glyph if (glyph != null) { system.removeGlyph(glyph); } // Restore the stem system.addGlyph(stem); } } return nb; }