//----------------------------------------------------------------------------//
// //
// S t e m P a t t e r n //
// //
//----------------------------------------------------------------------------//
// <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.glyph.pattern;
import omr.glyph.Evaluation;
import omr.glyph.ShapeEvaluator;
import omr.glyph.GlyphNetwork;
import omr.glyph.Grades;
import omr.glyph.Shape;
import omr.glyph.ShapeSet;
import omr.glyph.facets.Glyph;
import omr.lag.Section;
import omr.sheet.SystemInfo;
import omr.util.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Class {@code StemPattern} is a GlyphInspector dedicated to the
* inspection of Stems at System level
*
* @author Hervé Bitteur
*/
public class StemPattern
extends GlyphPattern
{
//~ Static fields/initializers ---------------------------------------------
/** Usual logger utility */
private static final Logger logger = LoggerFactory.getLogger(StemPattern.class);
/** Predicate to filter reliable symbols attached to a stem. */
public static final Predicate<Glyph> reliableStemSymbols = new Predicate<Glyph>()
{
@Override
public boolean check (Glyph glyph)
{
Shape shape = glyph.getShape();
boolean res = glyph.isWellKnown()
&& ShapeSet.StemSymbols.contains(shape)
&& (shape != Shape.BEAM_HOOK);
return res;
}
};
//~ Constructors -----------------------------------------------------------
/**
* Creates a new StemPattern object.
*
* @param system the dedicated system
*/
public StemPattern (SystemInfo system)
{
super("Stem", system);
}
//~ Methods ----------------------------------------------------------------
//------------//
// runPattern //
//------------//
/**
* 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
*/
@Override
public int runPattern ()
{
int nb = 0;
Map<Glyph, Set<Glyph>> badsMap = new HashMap<>();
// Collect all undue stems
List<Glyph> SuspectedStems = new ArrayList<>();
for (Glyph glyph : system.getGlyphs()) {
if (glyph.isStem() && !glyph.isManualShape() && glyph.isActive()) {
Set<Glyph> goods = new HashSet<>();
Set<Glyph> bads = new HashSet<>();
glyph.getSymbolsBefore(reliableStemSymbols, goods, bads);
glyph.getSymbolsAfter(reliableStemSymbols, goods, bads);
if (goods.isEmpty()) {
logger.debug("Suspected Stem {}", glyph);
SuspectedStems.add(glyph);
// Discard "bad" ones
badsMap.put(glyph, bads);
for (Glyph g : bads) {
logger.debug("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<>();
final ShapeEvaluator evaluator = GlyphNetwork.getInstance();
for (Glyph glyph : system.getGlyphs()) {
if (glyph.getShape() == null) {
Evaluation vote = evaluator.vote(
glyph,
system,
Grades.patternsMinGrade);
if (vote != null) {
glyph.setEvaluation(vote);
if (glyph.isWellKnown()) {
logger.debug("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 (Section 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);
// Deassign the nearby glyphs that cannot accept a stem
Set<Glyph> bads = badsMap.get(stem);
if (bads != null) {
for (Glyph g : bads) {
Shape shape = g.getShape();
if ((shape != null)
&& !g.isManualShape()
&& !ShapeSet.StemSymbols.contains(shape)) {
g.setShape(null);
}
}
}
} else if (stem.isVip()) {
logger.info("StemPattern deassigned stem#{}", stem.getId());
}
}
return nb;
}
}