/** * Creates a new ClefPattern object. * * @param system the containing system */ public ClefPattern(SystemInfo system) { super("Clef", system); nest = system.getSheet().getNest(); clefWidth = scale.toPixels(constants.clefWidth); xOffset = scale.toPixels(constants.xOffset); yOffset = scale.toPixels(constants.yOffset); xMargin = scale.toPixels(constants.xMargin); yMargin = scale.toPixels(constants.yMargin); }
/** * Try to infer the role of this textual item. For the time being, this is a simple algorithm * based on sentence location within the page, augmented by valid chord name, etc. * * @param line the sentence * @param systemInfo the containing system * @return the role information inferred for the provided sentence glyph */ public static TextRoleInfo guessRole(TextLine line, SystemInfo systemInfo) { if (line == null) { return null; } if (line.isVip()) { logger.info("TextRoleInfo. guessRole for {}", line.getValue()); } int chordCount = 0; for (TextWord word : line.getWords()) { // At least one word/glyph with a role manually assigned Glyph glyph = word.getGlyph(); if (glyph != null) { if (glyph.getManualRole() != null) { return glyph.getManualRole(); } } // Word that could be a chord symbol? if (word.guessChordInfo() != null) { chordCount++; } } // Is line made entirely of potential chord symbols? boolean isAllChord = chordCount == line.getWords().size(); Rectangle box = line.getBounds(); if (box == null) { return null; } // Is line mainly in italic? boolean isMainlyItalic = systemInfo.getTextBuilder().isMainlyItalic(line); Sheet sheet = systemInfo.getSheet(); ScoreSystem system = systemInfo.getScoreSystem(); Scale scale = system.getScale(); Point left = new Point(box.x, box.y + (box.height / 2)); Point right = new Point(box.x + box.width, box.y + (box.height / 2)); // First system in page? boolean firstSystem = system.getId() == 1; // Last system in page? boolean lastSystem = sheet.getSystems().size() == system.getId(); // Vertical position wrt (system) staves StaffPosition systemPosition = system.getStaffPosition(left); // Vertical position wrt (part) staves SystemPart part = system.getPartAbove(left); StaffPosition partPosition = part.getStaffPosition(left); // Vertical distance from staff? Staff staff = system.getStaffAt(left); int staffDy = Math.abs(staff.getTopLeft().y - box.y); boolean closeToStaff = staffDy <= scale.toPixels(constants.maxStaffDy); // Begins on left side of the part? boolean leftOfStaves = system.isLeftOfStaves(left); // At the center of page width? int maxCenterDx = scale.toPixels(constants.maxCenterDx); int pageCenter = sheet.getWidth() / 2; boolean pageCentered = Math.abs((box.x + (box.width / 2)) - pageCenter) <= maxCenterDx; // Right aligned with staves? int maxRightDx = scale.toPixels(constants.maxRightDx); boolean rightAligned = Math.abs(right.x - system.getTopLeft().x - system.getDimension().width) <= maxRightDx; // Short Sentence? int maxShortLength = scale.toPixels(constants.maxShortLength); boolean shortSentence = box.width <= maxShortLength; // Tiny Sentence? int maxTinyLength = scale.toPixels(constants.maxTinyLength); boolean tinySentence = box.width <= maxTinyLength; // High text? int minTitleHeight = scale.toPixels(constants.minTitleHeight); boolean highText = box.height >= minTitleHeight; logger.debug( "{} firstSystem={} lastSystem={} systemPosition={}" + " partPosition={} closeToStaff={} leftOfStaves={}" + " pageCentered={} rightAligned={} shortSentence={}" + " highText={10}", box, firstSystem, lastSystem, systemPosition, partPosition, closeToStaff, leftOfStaves, pageCentered, rightAligned, shortSentence, highText); // Decisions ... switch (systemPosition) { case ABOVE_STAVES: // Title, Number, Creator, Direction, Chord if (tinySentence) { if (isAllChord) { return new TextRoleInfo(TextRole.Chord); } else { return new TextRoleInfo(TextRole.UnknownRole); } } if (firstSystem) { if (leftOfStaves) { return new TextRoleInfo(TextRole.Creator, Text.CreatorText.CreatorType.lyricist); } else if (rightAligned) { return new TextRoleInfo(TextRole.Creator, Text.CreatorText.CreatorType.composer); } else if (closeToStaff) { if (isAllChord) { return new TextRoleInfo(TextRole.Chord); } else { return new TextRoleInfo(TextRole.Direction); } } else if (pageCentered) { // Title, Number if (highText) { return new TextRoleInfo(TextRole.Title); } else { return new TextRoleInfo(TextRole.Number); } } } else { if (isAllChord) { return new TextRoleInfo(TextRole.Chord); } else { return new TextRoleInfo(TextRole.Direction); } } break; case WITHIN_STAVES: // Name, Lyrics, Direction if (leftOfStaves) { return new TextRoleInfo(TextRole.Name); } else if ((partPosition == StaffPosition.BELOW_STAVES) && !isMainlyItalic) { return new TextRoleInfo(TextRole.Lyrics); } else { return new TextRoleInfo(TextRole.Direction); } case BELOW_STAVES: // Copyright, Lyrics for single-staff part if (tinySentence) { return new TextRoleInfo(TextRole.UnknownRole); } if (pageCentered && shortSentence && lastSystem) { return new TextRoleInfo(TextRole.Rights); } if (part.getStaves().size() == 1) { if ((partPosition == StaffPosition.BELOW_STAVES) && !isMainlyItalic) { return new TextRoleInfo(TextRole.Lyrics); } } } // Default return new TextRoleInfo(TextRole.UnknownRole); }
/** * 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; }