//----------------------------------------------------------------------------// // // // S h e e t // // // //----------------------------------------------------------------------------// // <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.Main; import omr.constant.Constant; import omr.constant.ConstantSet; import omr.glyph.BasicNest; import omr.glyph.Glyphs; import omr.glyph.Nest; import omr.glyph.Shape; import omr.glyph.SymbolsModel; import omr.glyph.facets.Glyph; import omr.glyph.ui.SymbolsController; import omr.glyph.ui.SymbolsEditor; import omr.grid.GridBuilder; import omr.grid.StaffManager; import omr.grid.TargetBuilder; import omr.lag.Lag; import omr.lag.Section; import omr.lag.Sections; import omr.run.RunsTable; import omr.score.Score; import omr.score.ScoresManager; import omr.score.entity.Page; import omr.score.entity.SystemNode; import omr.selection.LocationEvent; import omr.selection.PixelLevelEvent; import omr.selection.SelectionService; import omr.sheet.picture.ImageFormatException; import omr.sheet.picture.Picture; import omr.sheet.picture.PictureView; import omr.sheet.ui.BinarizationBoard; import omr.sheet.ui.BoundaryEditor; import omr.sheet.ui.PixelBoard; import omr.sheet.ui.SheetAssembly; import omr.sheet.ui.SheetsController; import omr.step.Step; import omr.step.StepException; import omr.step.Stepping; import omr.step.Steps; import omr.ui.BoardsPane; import omr.ui.ErrorsEditor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.Dimension; import java.awt.Point; import java.awt.image.RenderedImage; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.concurrent.ConcurrentSkipListSet; /** * Class {@code Sheet} is the corner stone for Sheet processing, * keeping pointers to all processings related to the image, and to * their results. * * @author Hervé Bitteur */ public class Sheet { //~ Static fields/initializers --------------------------------------------- /** Specific application parameters */ private static final Constants constants = new Constants(); /** Usual logger utility */ private static final Logger logger = LoggerFactory.getLogger(Sheet.class); /** Events that can be published on a sheet service */ public static final Class<?>[] allowedEvents = new Class<?>[]{ LocationEvent.class, PixelLevelEvent.class }; //~ Instance fields -------------------------------------------------------- // /** Corresponding page. */ private final Page page; /** Containing score. */ private final Score score; /** Selections for this sheet. (SheetLocation and PixelLevel) */ private final SelectionService locationService; /** The recording of key processing data. */ private final SheetBench bench; /** Related assembly instance, if any. */ private final SheetAssembly assembly; /** Related errors editor, if any. */ private final ErrorsEditor errorsEditor; /** Retrieved systems. */ private final List<SystemInfo> systems = new ArrayList<>(); //-- resettable members ---------------------------------------------------- // /** The related picture */ private Picture picture; /** All steps already done on this sheet */ private Set<Step> doneSteps = new HashSet<>(); /** The step being done on this sheet */ private Step currentStep; /** Global scale for this sheet */ private Scale scale; /** Table of all vertical (foreground) runs */ private RunsTable wholeVerticalTable; /** Table of all horizontal (foreground) runs */ private RunsTable wholeHorizontalTable; /** Initial skew value */ private Skew skew; /** Horizontal entities */ private Horizontals horizontals; /** Horizontal lag */ private Lag hLag; /** Vertical lag */ private Lag vLag; /** Global glyph nest */ private Nest nest; // Companion processors // /** Scale */ private ScaleBuilder scaleBuilder; /** Staves */ private final StaffManager staffManager; /** Grid */ private GridBuilder gridBuilder; /** Bars checker */ private volatile BarsChecker barsChecker; /** A bar line extractor for this sheet */ private volatile SystemsBuilder systemsBuilder; /** Specific builder dealing with glyphs */ private volatile SymbolsController symbolsController; /** Related verticals model */ private volatile VerticalsController verticalsController; /** Related target builder */ private volatile TargetBuilder targetBuilder; /** Related symbols editor */ private SymbolsEditor symbolsEditor; /** Related boundary editor */ private BoundaryEditor boundaryEditor; // ?????????????? /** Id of last long horizontal section */ private int lastLongHSectionId = -1; /** Have systems their boundaries? */ private boolean hasSystemBoundaries = false; //~ Constructors ----------------------------------------------------------- // //-------// // Sheet // //-------// /** * Create a new {@code Sheet} instance, based on a couple made of * an image (the original pixel input) and a page (the score entities * output). * * @param page the related score page * @param image the already loaded image */ public Sheet (Page page, RenderedImage image) throws StepException { this.page = page; this.score = page.getScore(); locationService = new SelectionService("sheet", allowedEvents); staffManager = new StaffManager(this); bench = new SheetBench(this); // Update UI information if so needed if (Main.getGui() != null) { errorsEditor = new ErrorsEditor(this); assembly = Main.getGui().sheetsController.createAssembly(this); } else { errorsEditor = null; assembly = null; } setImage(image); logger.debug("Created {}", this); } //~ Methods ---------------------------------------------------------------- //------------// // getSystems // //------------// /** * Report an unmodifiable view on current systems. * * @return a view on systems list */ public List<SystemInfo> getSystems () { return Collections.unmodifiableList(systems); } //------------// // setSystems // //------------// /** * Assign the whole sequence of systems * * @param systems the (new) systems */ public void setSystems (Collection<SystemInfo> systems) { if (this.systems != systems) { this.systems.clear(); this.systems.addAll(systems); } } //------// // done // //------// /** * Remember that the provided step has been completed on the sheet. * * @param step the provided step */ public final void done (Step step) { if (step.isMandatory()) { doneSteps.add(step); } } //----------// // addError // //----------// /** * Register an error in the sheet ErrorsWindow * * @param container the immediate container for the error location * @param glyph the related glyph if any * @param text the error message */ public void addError (SystemNode container, Glyph glyph, String text) { if (Main.getGui() != null) { getErrorsEditor().addError(container, glyph, text); } } // //----------// // // colorize // // //----------// // /** // * Set proper colors for sections of all recognized items so far, using the // * provided color // * // * @param lag the lag to be colorized // * @param viewIndex the provided lag view index // * @param color the color to use // */ // public void colorize (Color color) // { // if (score != null) { // // Colorization of all known score items // score.accept(new ScoreColorizer(color)); // } else { // // Nothing to colorize ? TODO // } // } //----------------------------------// // createSymbolsControllerAndEditor // //----------------------------------// public void createSymbolsControllerAndEditor () { SymbolsModel model = new SymbolsModel(this); symbolsController = new SymbolsController(model); if (Main.getGui() != null) { symbolsEditor = new SymbolsEditor(this, symbolsController); } } //----------------------// // createSystemsBuilder // //----------------------// public void createSystemsBuilder () { page.resetSystems(); systemsBuilder = new SystemsBuilder(this); } //---------------------------// // createVerticalsController // //---------------------------// public void createVerticalsController () { verticalsController = new VerticalsController(this); } //-----------------// // dumpSystemInfos // //-----------------// /** * Utility method, to dump all sheet systems */ public void dumpSystemInfos () { System.out.println("--- SystemInfos ---"); int i = 0; for (SystemInfo system : getSystems()) { Main.dumping.dump(system, "#" + i++); } System.out.println("--- SystemInfos end ---"); } //-----------------// // getActiveGlyphs // //-----------------// /** * Export the active glyphs of the vertical lag. * * @return the collection of glyphs for which at least a section is assigned */ public Collection<Glyph> getActiveGlyphs () { return getNest().getActiveGlyphs(); } //-------------// // getAssembly // //-------------// /** * Report the related SheetAssembly for GUI * * @return the assembly, or null otherwise */ public SheetAssembly getAssembly () { return assembly; } //----------------// // getBarsChecker // //----------------// /** * @return the barsChecker */ public BarsChecker getBarsChecker () { return barsChecker; } //----------// // getBench // //----------// /** * Report the related sheet bench * * @return the related bench */ public SheetBench getBench () { return bench; } //-------------------// // getBoundaryEditor // //-------------------// /** * @return the boundaryEditor */ public BoundaryEditor getBoundaryEditor () { return boundaryEditor; } //----------------// // getCurrentStep // //----------------// /** * Retrieve the step being processed "as we speak" * * @return the current step */ public Step getCurrentStep () { return currentStep; } //-------------------------// // getDefaultMaxForeground // //-------------------------// public static int getDefaultMaxForeground () { return constants.maxForegroundGrayLevel.getValue(); } //--------------// // getDimension // //--------------// /** * Report the dimension of the sheet/page * * @return the page/sheet dimension in pixels */ public Dimension getDimension () { return picture.getDimension(); } //-----------------// // getErrorsEditor // //-----------------// public ErrorsEditor getErrorsEditor () { return errorsEditor; } //-----------// // getHeight // //-----------// /** * Report the picture height in pixels * * @return the picture height */ public int getHeight () { return picture.getHeight(); } //------------------// // getHorizontalLag // //------------------// /** * Report the current horizontal lag for this sheet * * @return the current horizontal lag */ public Lag getHorizontalLag () { return hLag; } //----------------// // getHorizontals // //----------------// /** * Retrieve horizontals system by system * * @return the horizontals found */ public Horizontals getHorizontals () { return horizontals; } //-------// // getId // //-------// public String getId () { return page.getId(); } //--------------// // getInterline // //--------------// /** * Convenient method to report the scaling information of the sheet * * @return the scale interline */ public int getInterline () { return scale.getInterline(); } //--------------------// // getLocationService // //--------------------// /** * Report the sheet selection service (for LocationEvent & PixelLevelEvent) * * @return the sheet dedicated event service */ public SelectionService getLocationService () { return locationService; } //--------------// // getLogPrefix // //--------------// /** * Report the proper prefix to use when logging a message * * @return the proper prefix */ public String getLogPrefix () { if (ScoresManager.isMultiScore()) { return "[" + getId() + "] "; } else { if (score.isMultiPage()) { return "[#" + page.getIndex() + "] "; } else { return ""; } } } //---------------------// // getLongSectionMaxId // //---------------------// /** * Report the id of the last long horizontal section * * @return the id of the last long horizontal section */ public int getLongSectionMaxId () { return lastLongHSectionId; } //---------// // getNest // //---------// /** * Report the global nest for glyphs of this sheet * * @return the nest for glyphs */ public Nest getNest () { if (nest == null) { // Beware: Glyph nest must subscribe to location before any lag, // to allow cleaning up of glyph data, before publication by a lag nest = new BasicNest("gNest", this); nest.setServices(locationService); } return nest; } //---------// // getPage // //---------// /** * @return the page */ public Page getPage () { return page; } //------------// // getPicture // //------------// /** * Report the picture of this sheet, that is the image to be processed. * * @return the related picture */ public Picture getPicture () { return picture; } //----------// // getScale // //----------// /** * Report the computed scale of this sheet. This drives several processing * thresholds. * * @return the sheet scale */ public Scale getScale () { return scale; } //-----------------// // getScaleBuilder // //-----------------// /** * @return the scaleBuilder */ public ScaleBuilder getScaleBuilder () { if (scaleBuilder == null) { scaleBuilder = new ScaleBuilder(this); } return scaleBuilder; } //----------------// // getGridBuilder // //----------------// /** * @return the gridBuilder */ public GridBuilder getGridBuilder () { if (gridBuilder == null) { gridBuilder = new GridBuilder(this); } return gridBuilder; } //----------// // getScore // //----------// /** * Return the eventual Score that gathers in a score the information * retrieved from this sheet. * * @return the related score, or null if not available */ public Score getScore () { return score; } //-----------------// // getShapedGlyphs // //-----------------// /** * Report the collection of glyphs whose shape is identical to the provided * shape * * @param shape the imposed shape * @return the (perhaps empty) collection of active glyphs with right shape */ public Collection<Glyph> getShapedGlyphs (Shape shape) { List<Glyph> found = new ArrayList<>(); for (Glyph glyph : getActiveGlyphs()) { if (glyph.getShape() == shape) { found.add(glyph); } } return found; } //---------// // getSkew // //---------// /** * Report the skew information for this sheet. If not yet available, * processing is launched to compute the average skew in the sheet image. * * @return the skew information */ public Skew getSkew () { return skew; } //-----------------// // getStaffManager // //-----------------// /** * @return the staffManager */ public StaffManager getStaffManager () { return staffManager; } //----------------------// // getSymbolsController // //----------------------// /** * Give access to the module dealing with symbol management * * @return the symbols model */ public SymbolsController getSymbolsController () { if (symbolsController == null) { createSymbolsControllerAndEditor(); } return symbolsController; } //------------------// // getSymbolsEditor // //------------------// /** * Give access to the UI dealing with symbol recognition * * @return the symbols symbolsEditor */ public SymbolsEditor getSymbolsEditor () { return symbolsEditor; } //---------------// // getSystemById // //---------------// /** * Report the system info for which id is provided * * @param id id of desired system * @return the desired system info */ public SystemInfo getSystemById (int id) { return systems.get(id - 1); } //-------------// // getSystemOf // //-------------// /** * Report the system info that contains the provided point * * @param point the provided pixel point * @return the containing system info * (or null if there is no enclosing system) */ public SystemInfo getSystemOf (Point point) { for (SystemInfo info : getSystems()) { SystemBoundary boundary = info.getBoundary(); if ((boundary != null) && boundary.contains(point)) { return info; } } return null; } //-------------// // getSystemOf // //-------------// /** * Report the system, if any, which contains the provided glyph * (as determined by the first section of the glyph) * * @param glyph the provided glyph * @return the containing system, or null */ public SystemInfo getSystemOf (Glyph glyph) { if (glyph.isVirtual() || glyph.getMembers().isEmpty()) { return getSystemOf(glyph.getAreaCenter()); } else { return glyph.getMembers().first().getSystem(); } } //-------------// // getSystemOf // //-------------// /** * Report the system, if any, which contains the provided vertical section * * @param section the provided section * @return the containing system, or null */ public SystemInfo getSystemOf (Section section) { return section.getSystem(); } //-------------// // getSystemOf // //-------------// /** * Report the system that contains ALL glyphs provided. * If all glyphs do not belong to the same system, exception is thrown * * @param glyphs the collection of glyphs * @return the containing system * @exception IllegalArgumentException raised if glyphs collection is not OK */ public SystemInfo getSystemOf (Collection<Glyph> glyphs) { if ((glyphs == null) || glyphs.isEmpty()) { throw new IllegalArgumentException( "getSystemOf. Glyphs collection is null or empty"); } SystemInfo system = null; Collection<Glyph> toRemove = new ArrayList<>(); for (Glyph glyph : glyphs) { SystemInfo glyphSystem = glyph.isVirtual() ? getSystemOf(glyph.getAreaCenter()) : getSystemOf(glyph); if (glyphSystem == null) { toRemove.add(glyph); } else { if (system == null) { system = glyphSystem; } else { // Make sure we are still in the same system if (glyphSystem != system) { throw new IllegalArgumentException( "getSystemOf. Glyphs from different systems (" + getSystemOf(glyph) + " and " + system + ") " + Glyphs.toString(glyphs)); } } } } if (!toRemove.isEmpty()) { logger.warn("No system for {}", Glyphs.toString(toRemove)); glyphs.removeAll(toRemove); } return system; } //---------------------// // getSystemOfSections // //---------------------// /** * Report the system that contains ALL sections provided. * If all sections do not belong to the same system, exception is thrown * * @param sections the collection of sections * @return the containing system * @exception IllegalArgumentException raised if section collection is not * OK */ public SystemInfo getSystemOfSections (Collection<Section> sections) { if ((sections == null) || sections.isEmpty()) { throw new IllegalArgumentException( "getSystemOfSections. Sections collection is null or empty"); } SystemInfo system = null; Collection<Section> toRemove = new ArrayList<>(); for (Section section : sections) { SystemInfo sectionSystem = section.getSystem(); if (sectionSystem == null) { toRemove.add(section); } else { if (system == null) { system = sectionSystem; } else { // Make sure we are still in the same system if (sectionSystem != system) { throw new IllegalArgumentException( "getSystemOfSections. Sections from different systems (" + section.getSystem() + " and " + system + ") " + Sections.toString(sections)); } } } } if (!toRemove.isEmpty()) { logger.warn("No system for {}", Sections.toString(toRemove)); sections.removeAll(toRemove); } return system; } //-------------------// // getSystemsBuilder // //-------------------// /** * Give access to the builder in charge of bars & systems computation * * @return the builder instance */ public SystemsBuilder getSystemsBuilder () { return systemsBuilder; } //----------------// // getSystemsNear // //----------------// /** * Report the ordered list of systems containing or close to the * provided point. * * @param point the provided point * @return a collection of systems ordered by increasing distance from the * provided point */ public List<SystemInfo> getSystemsNear (final Point point) { List<SystemInfo> neighbors = new ArrayList<>(systems); Collections.sort( neighbors, new Comparator<SystemInfo>() { @Override public int compare (SystemInfo s1, SystemInfo s2) { int y1 = (s1.getTop() + s1.getBottom()) / 2; int d1 = Math.abs(point.y - y1); int y2 = (s2.getTop() + s2.getBottom()) / 2; int d2 = Math.abs(point.y - y2); return Integer.signum(d1 - d2); } }); return neighbors; } //------------------// // getTargetBuilder // //------------------// /** * @return the targetBuilder */ public TargetBuilder getTargetBuilder () { return targetBuilder; } //----------------// // getVerticalLag // //----------------// /** * Report the current vertical lag of the sheet * * @return the current vertical lag */ public Lag getVerticalLag () { return vLag; } //------------------------// // getVerticalsController // //------------------------// public VerticalsController getVerticalsController () { return verticalsController; } //----------// // getWidth // //----------// /** * Report the picture width in pixels * * @return the picture width */ public int getWidth () { return picture.getWidth(); } //---------------------// // hasSystemBoundaries // //---------------------// /** * Report whether the systems have their boundaries defined yet. * * @return true if already defined */ public boolean hasSystemBoundaries () { return hasSystemBoundaries; } //--------// // isDone // //--------// /** * Report whether the specified step has been performed onn this sheet * * @param step the step to check * @return true if already performed */ public boolean isDone (Step step) { return doneSteps.contains(step); } //--------------// // isOnPatterns // //--------------// /** * Check whether current step is SYMBOLS. * * @return true if on SYMBOLS */ public boolean isOnPatterns () { return Stepping.getLatestStep(this) == Steps.valueOf(Steps.SYMBOLS); } //--------// // remove // //--------// /** * Remove this sheet from the containing score */ public void remove (boolean closing) { logger.debug("remove sheet {} closing:{}", this, closing); // Close the related page getScore().remove(page); // Close related UI assembly if any if (assembly != null) { SheetsController.getInstance().removeAssembly(this); assembly.close(); } picture.close(); // If no sheet is left, force score closing if (!closing) { if (!score.getPages().isEmpty()) { logger.info("{}Removed page #{}", page.getScore().getLogPrefix(), page.getIndex()); } else { score.close(); } } } //----------------// // setBarsChecker // //----------------// /** * @param barsChecker the barsChecker */ public void setBarsChecker (BarsChecker barsChecker) { this.barsChecker = barsChecker; } //-------------------// // setBoundaryEditor // //-------------------// /** * @param boundaryEditor the boundaryEditor to set */ public void setBoundaryEditor (BoundaryEditor boundaryEditor) { this.boundaryEditor = boundaryEditor; } //----------------// // setCurrentStep // //----------------// /** * This records the starting of a step. * * @param step the starting step */ public void setCurrentStep (Step step) { currentStep = step; } //-------------------------// // setDefaultMaxForeground // //-------------------------// public static void setDefaultMaxForeground (int level) { constants.maxForegroundGrayLevel.setValue(level); } //------------------// // setHorizontalLag // //------------------// /** * Assign the current horizontal lag for the sheet * * @param hLag the horizontal lag at hand */ public void setHorizontalLag (Lag hLag) { if (this.hLag != null) { this.hLag.cutServices(); } this.hLag = hLag; hLag.setServices(locationService, getNest().getGlyphService()); } //----------------// // setHorizontals // //----------------// /** * Set horizontals system by system * * @param horizontals the horizontals found */ public void setHorizontals (Horizontals horizontals) { this.horizontals = horizontals; } //----------// // setImage // //----------// public final void setImage (RenderedImage image) throws StepException { // Reset most of members reset(Steps.LOAD); try { picture = new Picture(image, locationService); setPicture(picture); getBench().recordImageDimension(picture.getWidth(), picture. getHeight()); done(Steps.valueOf(Steps.LOAD)); } catch (ImageFormatException ex) { String msg = "Unsupported image format in file " + getScore().getImagePath() + "\n" + ex.getMessage(); if (Main.getGui() != null) { Main.getGui().displayWarning(msg); } else { logger.warn(msg); } throw new StepException(ex); } } //---------------------// // setLongSectionMaxId // //---------------------// /** * Remember the id of the last long horizontal section * * @param id the id of the last long horizontal section */ public void setLongSectionMaxId (int id) { lastLongHSectionId = id; } //----------// // setScale // //----------// /** * Link scale information to this sheet * * @param scale the computed (or read from score file) scale * @throws StepException */ public void setScale (Scale scale) throws StepException { this.scale = scale; page.setScale(scale); } //---------// // setSkew // //---------// /** * Link skew information to this sheet * * @param skew the skew information */ public void setSkew (Skew skew) { this.skew = skew; } //---------------------// // setSystemBoundaries // //---------------------// /** * Set the flag about systems boundaries. */ public void setSystemBoundaries () { hasSystemBoundaries = true; } //------------------// // setTargetBuilder // //------------------// /** * @param targetBuilder the targetBuilder to set */ public void setTargetBuilder (TargetBuilder targetBuilder) { this.targetBuilder = targetBuilder; } //----------------// // setVerticalLag // //----------------// /** * Assign the current vertical lag for the sheet * * @param vLag the current vertical lag */ public void setVerticalLag (Lag vLag) { this.vLag = vLag; vLag.setServices(locationService, getNest().getGlyphService()); } //-------------// // splitGlyphs // //-------------// /** * Split the sheet glyphs among systems * * @return the set of modified systems */ public Set<SystemInfo> splitGlyphs () { Map<SystemInfo, SortedSet<Glyph>> glyphsMap = new HashMap<>(); for (SystemInfo system : systems) { glyphsMap.put( system, new ConcurrentSkipListSet<>(system.getGlyphs())); system.clearGlyphs(); } // Assign the glyphs to the proper system glyphs collection for (Glyph glyph : nest.getAllGlyphs()) { if (glyph.isActive()) { SystemInfo system = getSystemOf(glyph); if (system != null) { system.addGlyph(glyph); } else { glyph.setShape(null); } } } Set<SystemInfo> modified = new LinkedHashSet<>(); for (SystemInfo system : systems) { if (!(system.getGlyphs().equals(glyphsMap.get(system)))) { modified.add(system); } } return modified; } //-------------------------// // splitHorizontalSections // //-------------------------// /** * Split the various horizontal sections among systems * * @return the set of modified systems */ public Set<SystemInfo> splitHorizontalSections () { // Take a snapshot of sections collection per system and clear it Map<SystemInfo, Collection<Section>> sections = new HashMap<>(); for (SystemInfo system : systems) { Collection<Section> systemSections = system. getMutableHorizontalSections(); sections.put(system, new ArrayList<>(systemSections)); systemSections.clear(); } // Now dispatch the lag sections among the systems for (Section section : getHorizontalLag().getSections()) { SystemInfo system = getSystemOf(section.getCentroid()); // Link section -> system section.setSystem(system); if (system != null) { // Link system <>-> section system.getMutableHorizontalSections().add(section); } } // Detect precisely which systems have been modified Set<SystemInfo> modifiedSystems = new LinkedHashSet<>(); for (SystemInfo system : systems) { if (!(system.getMutableHorizontalSections().equals( sections.get(system)))) { modifiedSystems.add(system); } } return modifiedSystems; } //-----------------------// // splitVerticalSections // //-----------------------// /** * Split the various vertical sections among systems * * @return the set of modified systems */ public Set<SystemInfo> splitVerticalSections () { // Take a snapshot of sections collection per system and clear it Map<SystemInfo, Collection<Section>> sections = new HashMap<>(); for (SystemInfo system : systems) { Collection<Section> systemSections = system. getMutableVerticalSections(); sections.put(system, new ArrayList<>(systemSections)); systemSections.clear(); } // Now dispatch the lag sections among the systems for (Section section : getVerticalLag().getSections()) { SystemInfo system = getSystemOf(section.getCentroid()); // Link section -> system section.setSystem(system); if (system != null) { // Link system <>-> section system.getMutableVerticalSections().add(section); } } // Detect precisely which systems have been modified Set<SystemInfo> modifiedSystems = new LinkedHashSet<>(); for (SystemInfo system : systems) { if (!(system.getMutableVerticalSections().equals( sections.get(system)))) { modifiedSystems.add(system); } } return modifiedSystems; } //----------// // toString // //----------// @Override public String toString () { return "{Sheet " + page.getId() + "}"; } //-------// // reset // //-------// /** * Reinitialize the sheet members, according to step needs. */ public void reset (String stepName) { switch (stepName) { case Steps.LOAD: picture = null; doneSteps = new HashSet<>(); currentStep = null; case Steps.SCALE: scaleBuilder = null; scale = null; wholeVerticalTable = null; case Steps.GRID: if (nest != null) { nest.cutServices(locationService); nest = null; } skew = null; horizontals = null; if (hLag != null) { hLag.cutServices(); hLag = null; } if (vLag != null) { vLag.cutServices(); vLag = null; } systems.clear(); gridBuilder = null; staffManager.reset(); barsChecker = null; systemsBuilder = null; symbolsController = null; verticalsController = null; targetBuilder = null; symbolsEditor = null; } } //------------// // setPicture // //------------// /** * Set the picture of this sheet, that is the image to be processed. * * @param picture the related picture */ private void setPicture (Picture picture) { this.picture = picture; locationService.subscribeStrongly(LocationEvent.class, picture); // Display sheet picture if not batch mode if (Main.getGui() != null) { PictureView pictureView = new PictureView(Sheet.this); assembly.addViewTab( Step.PICTURE_TAB, pictureView, new BoardsPane( new PixelBoard(this), new BinarizationBoard(this))); } } //-----------------------// // getWholeVerticalTable // //-----------------------// /** * Get access to the whole table of vertical runs. * * @return the wholeVerticalTable */ public RunsTable getWholeVerticalTable () { return wholeVerticalTable; } //-----------------------// // setWholeVerticalTable // //-----------------------// /** * Remember the whole table of vertical runs. * * @param wholeVerticalTable the wholeVerticalTable to set */ public void setWholeVerticalTable (RunsTable wholeVerticalTable) { this.wholeVerticalTable = wholeVerticalTable; } //-------- JLPOOLE added ---------------// //-----------------------// // getWholeHorizontalTable // //-----------------------// /** * Get access to the whole table of horizontal runs. * * @return the wholeHorizontalTable */ public RunsTable getWholeHorizontalTable () { return wholeHorizontalTable; } // setWholeHorizontalTable // //-----------------------// /** * Remember the whole table of horizontal runs. * * @param wholeVerticalTable the wholeVerticalTable to set */ public void setWholeHorizontalTable (RunsTable wholeHorizontalTable) { this.wholeHorizontalTable = wholeHorizontalTable; } // ------------ end JLP ----------------- //~ Inner Classes ---------------------------------------------------------- //-----------// // Constants // //-----------// private static final class Constants extends ConstantSet { //~ Instance fields ---------------------------------------------------- Constant.Integer maxForegroundGrayLevel = new Constant.Integer( "ByteLevel", 140, "Maximum gray level for a pixel to be considered as foreground (black)"); } }