//----------------------------------------------------------------------------//
// //
// R u n s R e t r i e v e 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.run;
import omr.step.ProcessingCancellationException;
import omr.util.Concurrency;
import omr.util.OmrExecutors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
/**
* Class {@code RunsRetriever} is in charge of reading a source of
* pixels and retrieving foreground runs and background runs from it.
* What is done with the retrieved runs is essentially the purpose of the
* provided adapter.
*
* @author Hervé Bitteur
*/
public class RunsRetriever
{
//~ Static fields/initializers ---------------------------------------------
/** Usual logger utility */
private static final Logger logger = LoggerFactory.getLogger(RunsRetriever.class);
//~ Instance fields --------------------------------------------------------
//
/** The orientation of desired runs */
private final Orientation orientation;
/** The adapter for pixel access and call-backs at run level */
private final Adapter adapter;
//~ Constructors -----------------------------------------------------------
//
//---------------//
// RunsRetriever //
//---------------//
/**
* Creates a new RunsRetriever object.
*
* @param orientation the desired orientation
* @param adapter an adapter to provide pixel access as well as specific
* call-back actions when a run (either foreground or
* background) has just been read.
*/
public RunsRetriever (Orientation orientation,
Adapter adapter)
{
this.orientation = orientation;
this.adapter = adapter;
}
//~ Methods ----------------------------------------------------------------
//
//--------------//
// retrieveRuns //
//--------------//
/**
* The {@code retrieveRuns} method can be used to build the runs on
* the fly, by providing a given absolute rectangle.
*
* @param area the ABSOLUTE rectangular area to explore
*/
public void retrieveRuns (Rectangle area)
{
Rectangle rect = orientation.oriented(area);
final int cMin = rect.x;
final int cMax = (rect.x + rect.width) - 1;
final int pMin = rect.y;
final int pMax = (rect.y + rect.height) - 1;
rowBasedRetrieval(pMin, pMax, cMin, cMax);
adapter.terminate();
}
//-----------------//
// processPosition //
//-----------------//
/**
* Process the pixels in position 'p' between coordinates 'cMin'
* and 'cMax'
*
* @param p the position in the pixels array (x for vertical)
* @param cMin the starting coordinate (y for vertical)
* @param cMax the ending coordinate
*/
private void processPosition (int p,
int cMin,
int cMax)
{
// Current run is FOREGROUND or BACKGROUND
boolean isFore = false;
// Current length of the run in progress
int length = 0;
// Current cumulated gray level for the run in progress
int cumul = 0;
// Browse other dimension
for (int c = cMin; c <= cMax; c++) {
final int level = adapter.getLevel(c, p);
///logger.info("p:" + p + " c:" + c + " level:" + level);
if (adapter.isFore(c, p)) {
// We are on a foreground pixel
if (isFore) {
// Append to the foreground run in progress
length++;
cumul += level;
} else {
// End the previous background run if any
if (length > 0) {
adapter.backRun(c, p, length);
}
// Initialize values for the starting foreground run
isFore = true;
length = 1;
cumul = level;
}
} else {
// We are on a background pixel
if (isFore) {
// End the previous foreground run
adapter.foreRun(c, p, length, cumul);
// Initialize values for the starting background run
isFore = false;
length = 1;
} else {
// Append to the background run in progress
length++;
}
}
}
// Process end of last run in this position
if (isFore) {
adapter.foreRun(cMax + 1, p, length, cumul);
} else {
adapter.backRun(cMax + 1, p, length);
}
}
//-------------------//
// rowBasedRetrieval //
//-------------------//
/**
* Retrieve runs row by row.
* This method handles the pixels run either in a parallel or a serial way,
* according to the possibilities of the high OMR executor.
*/
private void rowBasedRetrieval (int pMin,
int pMax,
final int cMin,
final int cMax)
{
if (OmrExecutors.defaultParallelism.getSpecific() == false
|| !adapter.isThreadSafe()) {
// Sequential
for (int p = pMin; p <= pMax; p++) {
processPosition(p, cMin, cMax);
}
} else {
// Parallel (TODO: should use Java 7 fork/join someday...)
try {
// Browse one dimension
List<Callable<Void>> tasks = new ArrayList<>(
pMax - pMin + 1);
for (int p = pMin; p <= pMax; p++) {
final int pp = p;
tasks.add(
new Callable<Void>()
{
@Override
public Void call ()
throws Exception
{
processPosition(pp, cMin, cMax);
return null;
}
});
}
// Launch the tasks and wait for their completion
OmrExecutors.getHighExecutor()
.invokeAll(tasks);
} catch (InterruptedException ex) {
logger.warn("ParallelRuns got interrupted");
throw new ProcessingCancellationException(ex);
} catch (ProcessingCancellationException pce) {
throw pce;
} catch (Throwable ex) {
logger.warn("Exception raised in ParallelRuns", ex);
throw new RuntimeException(ex);
}
}
}
//~ Inner Interfaces -------------------------------------------------------
//
//---------//
// Adapter //
//---------//
/**
* Interface {@code Adapter} is used to plug call-backs to a run
* retrieval process.
*/
public static interface Adapter
extends Concurrency
{
//---------//
// backRun //
//---------//
/**
* Called at end of a background run, with the related coordinates
*
* @param coord location of the point past the end of the run
* @param pos constant position of the run
* @param length length of the run just found
*/
void backRun (int coord,
int pos,
int length);
//---------//
// foreRun //
//---------//
/**
* Same as background, but for a foreground run. We also provide the
* measure of accumulated gray level in that case.
*
* @param coord location of the point past the end of the run
* @param pos constant position of the run
* @param length length of the run just found
* @param cumul cumulated gray levels along the run
*/
void foreRun (int coord,
int pos,
int length,
int cumul);
//----------//
// getLevel //
//----------//
/**
* This method is used to report the gray level of the pixel
* read at location (coord, pos).
*
* @param coord x for horizontal runs, y for vertical runs
* @param pos y for horizontal runs, x for vertical runs
*
* @return the pixel gray value (from 0 for black up to 255 for white)
*/
int getLevel (int coord,
int pos);
//--------//
// isFore //
//--------//
/**
* This method is used to check if the pixel at location
* (coord, pos) is a foreground pixel.
*
* @param coord x for horizontal runs, y for vertical runs
* @param pos y for horizontal runs, x for vertical runs
*
* @return true if pixel is foreground, false otherwise
*/
boolean isFore (int coord,
int pos);
//-----------//
// terminate //
//-----------//
/**
* Called at the very end of run retrieval.
*/
void terminate ();
}
}