//----------------------------------------------------------------------------//
// //
// G l y p h I n s p e c t o 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.glyph;
import omr.constant.ConstantSet;
import omr.glyph.facets.Glyph;
import omr.sheet.Scale;
import omr.sheet.SystemInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
/**
* Class {@code GlyphInspector} is at a system level, dedicated to the
* inspection of retrieved glyphs, their recognition being usually
* based on features used by a shape evaluator.
*
* @author Hervé Bitteur
*/
public class GlyphInspector
{
//~ Static fields/initializers ---------------------------------------------
/** Specific application parameters */
private static final Constants constants = new Constants();
/** Usual logger utility */
private static final Logger logger = LoggerFactory.getLogger(GlyphInspector.class);
/** Shapes acceptable for a part candidate */
private static final EnumSet<Shape> partShapes = EnumSet.of(
Shape.DOT_set,
Shape.NOISE,
Shape.CLUTTER,
Shape.STACCATISSIMO,
Shape.NOTEHEAD_VOID,
Shape.FLAG_1,
Shape.FLAG_1_UP);
//~ Instance fields --------------------------------------------------------
/** Dedicated system */
private final SystemInfo system;
//~ Constructors -----------------------------------------------------------
//----------------//
// GlyphInspector //
//----------------//
/**
* Create an GlyphInspector instance.
*
* @param system the dedicated system
*/
public GlyphInspector (SystemInfo system)
{
this.system = system;
}
//~ Methods ----------------------------------------------------------------
//----------------//
// evaluateGlyphs //
//----------------//
/**
* All unassigned symbol glyphs of a given system, for which we can
* get a positive vote from the evaluator, are assigned the voted
* shape.
*
* @param minGrade the lower limit on grade to accept an evaluation
*/
public void evaluateGlyphs (double minGrade)
{
ShapeEvaluator evaluator = GlyphNetwork.getInstance();
for (Glyph glyph : system.getGlyphs()) {
if (glyph.getShape() == null) {
// Get vote
Evaluation vote = evaluator.vote(glyph, system, minGrade);
if (vote != null) {
glyph.setEvaluation(vote);
}
}
}
}
//---------------//
// inspectGlyphs //
//---------------//
/**
* Process the given system, by retrieving unassigned glyphs,
* evaluating and assigning them if OK, or trying compounds
* otherwise.
*
* @param minGrade the minimum acceptable grade for this processing
* @param wide flag for extra wide box
*/
public void inspectGlyphs (double minGrade,
boolean wide)
{
logger.debug("S#{} inspectGlyphs start", system.getId());
// For Symbols & Leaves
system.retrieveGlyphs();
system.removeInactiveGlyphs();
evaluateGlyphs(minGrade);
system.removeInactiveGlyphs();
// For Compounds
retrieveCompounds(minGrade, wide);
system.removeInactiveGlyphs();
evaluateGlyphs(minGrade);
system.removeInactiveGlyphs();
}
//-------------------//
// retrieveCompounds //
//-------------------//
/**
* In the specified system, look for glyphs portions that should be
* considered as parts of compound glyphs.
*
* @param minGrade minimum acceptable grade
* @param wide flag for extra wide box
*/
private void retrieveCompounds (double minGrade,
boolean wide)
{
// Use a copy to avoid concurrent modifications
List<Glyph> glyphs = new ArrayList<>(system.getGlyphs());
for (Glyph seed : glyphs) {
// Now process this seed, by looking at neighbors
BasicAdapter adapter = new BasicAdapter(system, minGrade, seed, wide);
if (adapter.isCandidateSuitable(seed)) {
system.buildCompound(seed, true, glyphs, adapter);
}
}
}
//~ Inner Classes ----------------------------------------------------------
//-----------//
// Constants //
//-----------//
private static final class Constants
extends ConstantSet
{
//~ Instance fields ----------------------------------------------------
Scale.Fraction boxMargin = new Scale.Fraction(
0.25,
"Box margin to check intersection with compound");
Scale.Fraction boxWiden = new Scale.Fraction(
0.5,
"Box special abscissa margin to check intersection with compound");
}
//--------------//
// BasicAdapter //
//--------------//
/**
* Class {@code BasicAdapter} is a CompoundAdapter meant to
* retrieve all compounds (in a system).
*/
private static class BasicAdapter
extends CompoundBuilder.AbstractAdapter
{
//~ Instance fields ----------------------------------------------------
private Glyph stem = null;
private int stemX;
private int stemToSeed;
private final boolean wide;
//~ Constructors -------------------------------------------------------
/**
* Construct a BasicAdapter around a given seed
*
* @param system the containing system
* @param minGrade minimum acceptable grade
*/
public BasicAdapter (SystemInfo system,
double minGrade,
Glyph seed,
boolean wide)
{
super(system, minGrade);
this.wide = wide;
stem = seed.getFirstStem();
if (stem != null) {
// Remember this stem as a border
stemX = stem.getCentroid().x;
stemToSeed = seed.getCentroid().x - stemX;
}
}
//~ Methods ------------------------------------------------------------
@Override
public Rectangle computeReferenceBox ()
{
if (seed == null) {
throw new NullPointerException(
"Compound seed has not been set");
}
Rectangle newBox = seed.getBounds();
Scale scale = system.getScoreSystem().getScale();
int boxMargin = scale.toPixels(GlyphInspector.constants.boxMargin);
int boxWiden = scale.toPixels(GlyphInspector.constants.boxWiden);
if (wide) {
newBox.grow(boxWiden, boxMargin);
} else {
newBox.grow(boxMargin, boxMargin);
}
return newBox;
}
@Override
public boolean isCandidateSuitable (Glyph glyph)
{
Shape shape = glyph.getShape();
if (!glyph.isActive() || (shape == Shape.LEDGER)) {
return false;
}
if (glyph.isKnown()
&& (glyph.isManualShape()
|| (!partShapes.contains(shape)
&& (glyph.getGrade() > Grades.compoundPartMaxGrade)))) {
return false;
}
// Stay on same side of the stem if any
if ((stem != null)) {
return ((glyph.getCentroid().x - stemX) * stemToSeed) > 0;
} else {
return true;
}
}
@Override
public boolean isCompoundValid (Glyph compound)
{
Evaluation eval = GlyphNetwork.getInstance().vote(compound, system,
minGrade);
if ((eval != null)
&& eval.shape.isWellKnown()
&& (eval.shape != Shape.CLUTTER)
&& (!seed.isKnown() || (eval.grade > seed.getGrade()))) {
chosenEvaluation = eval;
return true;
} else {
return false;
}
}
}
}