Skip to content

Commit

Permalink
Merge pull request #172 from melissalinkert/progress-api
Browse files Browse the repository at this point in the history
Add progress API
  • Loading branch information
melissalinkert authored Apr 24, 2023
2 parents 380a161 + 01ff9b2 commit 8537f73
Show file tree
Hide file tree
Showing 6 changed files with 310 additions and 29 deletions.
65 changes: 36 additions & 29 deletions src/main/java/com/glencoesoftware/bioformats2raw/Converter.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,6 @@
import com.univocity.parsers.csv.CsvParserSettings;

import ch.qos.logback.classic.Level;
import me.tongfei.progressbar.DelegatingProgressBarConsumer;
import me.tongfei.progressbar.ProgressBar;
import me.tongfei.progressbar.ProgressBarBuilder;
import picocli.CommandLine;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
Expand Down Expand Up @@ -211,6 +208,8 @@ public class Converter implements Callable<Integer> {
/** Calculated from outputLocation. */
private volatile Path outputPath;

private IProgressListener progressListener;

// Option setters

/**
Expand Down Expand Up @@ -1084,6 +1083,10 @@ public Integer call() throws Exception {
LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
root.setLevel(Level.toLevel(logLevel));

if (progressBars) {
setProgressListener(new ProgressBarListener(logLevel));
}

if (outputLocation.contains("://")) {

LOGGER.info("*** experimental remote filesystem support ***");
Expand Down Expand Up @@ -1782,6 +1785,8 @@ private void processChunk(int series, int resolution, int plane,
finally {
readers.put(reader);
}
getProgressListener().notifyChunkStart(
plane, offset[4], offset[3], zOffset);
Slf4JStopWatch t0 = new Slf4JStopWatch("getChunk");
try {
for (int z = zOffset; z < zOffset + zShape; z++) {
Expand Down Expand Up @@ -1813,6 +1818,7 @@ private void processChunk(int series, int resolution, int plane,
littleEndian ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
}
writeBytes(zarr, shape, offset, tileBuffer);
getProgressListener().notifyChunkEnd(plane, offset[4], offset[3], zOffset);
}

/**
Expand All @@ -1829,6 +1835,7 @@ public void saveResolutions(int series)
throws FormatException, IOException, InterruptedException,
EnumerationException
{
getProgressListener().notifySeriesStart(series);
IFormatReader workingReader = readers.take();
int resolutions = 1;
int sizeX;
Expand Down Expand Up @@ -1950,24 +1957,7 @@ public void saveResolutions(int series)
List<CompletableFuture<Void>> futures =
new ArrayList<CompletableFuture<Void>>();

final ProgressBar pb;
if (progressBars) {
ProgressBarBuilder builder = new ProgressBarBuilder()
.setInitialMax(tileCount)
.setTaskName(String.format("[%d/%d]", series, resolution));

if (!(logLevel.equals("OFF") ||
logLevel.equals("ERROR") ||
logLevel.equals("WARN")))
{
builder.setConsumer(new DelegatingProgressBarConsumer(LOGGER::trace));
}

pb = builder.build();
}
else {
pb = null;
}
getProgressListener().notifyResolutionStart(resolution, tileCount);

try {
for (int j=0; j<scaledHeight; j+=tileHeight) {
Expand Down Expand Up @@ -2016,11 +2006,6 @@ public void saveResolutions(int series)
"xx={} yy={} zz={} width={} height={} depth={}",
resolution, plane, xx, yy, zz, width, height, depth, t);
}
finally {
if (pb != null) {
pb.step();
}
}
});
}
}
Expand All @@ -2038,16 +2023,15 @@ public void saveResolutions(int series)

}
finally {
if (pb != null) {
pb.close();
}
getProgressListener().notifyResolutionEnd(resolution);
}
}

// series level metadata
// do this after pixels are written, otherwise
// min/max calculation won't have been performed
setSeriesLevelMetadata(series, resolutions);
getProgressListener().notifySeriesEnd(series);
}

private void saveHCSMetadata(IMetadata meta) throws IOException {
Expand Down Expand Up @@ -2802,6 +2786,29 @@ private static Slf4JStopWatch stopWatch() {
return new Slf4JStopWatch(LOGGER, Slf4JStopWatch.DEBUG_LEVEL);
}

/**
* Set a listener for tile processing events.
* Intended to be used to show a status bar.
*
* @param listener a progress event listener
*/
public void setProgressListener(IProgressListener listener) {
progressListener = listener;
}

/**
* Get the currrent listener for tile processing events.
* If no listener was set, a no-op listener is returned.
*
* @return the current progress listener
*/
public IProgressListener getProgressListener() {
if (progressListener == null) {
setProgressListener(new NoOpProgressListener());
}
return progressListener;
}

/**
* Perform file conversion as specified by command line arguments.
* @param args command line arguments
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* Copyright (c) 2022 Glencoe Software, Inc. All rights reserved.
*
* This software is distributed under the terms described by the LICENSE.txt
* file you can find at the root of the distribution bundle. If the file is
* missing please request a copy by contacting info@glencoesoftware.com
*/
package com.glencoesoftware.bioformats2raw;

import java.util.EventListener;

public interface IProgressListener extends EventListener {

/**
* Indicates the beginning of processing a particular series.
*
* @param series the series index being processed
*/
void notifySeriesStart(int series);

/**
* Indicates the end of processing a particular series.
*
* @param series the series index being processed
*/
void notifySeriesEnd(int series);

/**
* Indicates the beginning of processing a particular resolution.
*
* @param resolution the resolution index being processed
* @param tileCount the total number of tiles in this resolution
*/
void notifyResolutionStart(int resolution, int tileCount);

/**
* Indicates the end of processing a particular resolution.
*
* @param resolution the resolution index being processed
*/
void notifyResolutionEnd(int resolution);

/**
* Indicates that the given chunk is about to be processed.
*
* @param plane plane index
* @param xx X coordinate
* @param yy Y coordinate
* @param zz Z coordinate
*/
void notifyChunkStart(int plane, int xx, int yy, int zz);

/**
* Indicates that the given chunk has been processed.
*
* @param plane plane index
* @param xx X coordinate
* @param yy Y coordinate
* @param zz Z coordinate
*/
void notifyChunkEnd(int plane, int xx, int yy, int zz);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Copyright (c) 2022 Glencoe Software, Inc. All rights reserved.
*
* This software is distributed under the terms described by the LICENSE.txt
* file you can find at the root of the distribution bundle. If the file is
* missing please request a copy by contacting info@glencoesoftware.com
*/
package com.glencoesoftware.bioformats2raw;

public class NoOpProgressListener implements IProgressListener {

@Override
public void notifySeriesStart(int series) {
}

@Override
public void notifySeriesEnd(int series) {
}

@Override
public void notifyResolutionStart(int resolution, int tileCount) {
}

@Override
public void notifyResolutionEnd(int resolution) {
}

@Override
public void notifyChunkStart(int plane, int xx, int yy, int zz) {
}

@Override
public void notifyChunkEnd(int plane, int xx, int yy, int zz) {
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* Copyright (c) 2022 Glencoe Software, Inc. All rights reserved.
*
* This software is distributed under the terms described by the LICENSE.txt
* file you can find at the root of the distribution bundle. If the file is
* missing please request a copy by contacting info@glencoesoftware.com
*/
package com.glencoesoftware.bioformats2raw;

import me.tongfei.progressbar.DelegatingProgressBarConsumer;
import me.tongfei.progressbar.ProgressBar;
import me.tongfei.progressbar.ProgressBarBuilder;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ProgressBarListener implements IProgressListener {

// not a typo - the progress bar consumes Converter's logging output
private static final Logger LOGGER = LoggerFactory.getLogger(Converter.class);

private String logLevel;
private ProgressBar pb;
private int currentSeries = -1;

/**
* Create a new progress listener that displays a progress bar.
*
* @param level logging level
*/
public ProgressBarListener(String level) {
logLevel = level;
}


@Override
public void notifySeriesStart(int series) {
currentSeries = series;
}

@Override
public void notifySeriesEnd(int series) {
}

@Override
public void notifyResolutionStart(int resolution, int tileCount) {
ProgressBarBuilder builder = new ProgressBarBuilder()
.setInitialMax(tileCount)
.setTaskName(String.format("[%d/%d]", currentSeries, resolution));

if (!(logLevel.equals("OFF") ||
logLevel.equals("ERROR") ||
logLevel.equals("WARN")))
{
builder.setConsumer(new DelegatingProgressBarConsumer(LOGGER::trace));
}
pb = builder.build();
}

@Override
public void notifyChunkStart(int plane, int xx, int yy, int zz) {
// intentional no-op
}

@Override
public void notifyChunkEnd(int plane, int xx, int yy, int zz) {
if (pb != null) {
pb.step();
}
}

@Override
public void notifyResolutionEnd(int resolution) {
if (pb != null) {
pb.close();
pb = null;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* Copyright (c) 2022 Glencoe Software, Inc. All rights reserved.
*
* This software is distributed under the terms described by the LICENSE.txt
* file you can find at the root of the distribution bundle. If the file is
* missing please request a copy by contacting info@glencoesoftware.com
*/
package com.glencoesoftware.bioformats2raw.test;

import com.glencoesoftware.bioformats2raw.IProgressListener;

import java.util.ArrayList;
import java.util.List;

public class TestProgressListener implements IProgressListener {

private List<Integer> finishedResolutions = new ArrayList<Integer>();
private int startedTiles = 0;
private int completedTiles = 0;
private int expectedTileCount = 0;

@Override
public void notifySeriesStart(int series) {
}

@Override
public void notifySeriesEnd(int series) {
}

@Override
public void notifyResolutionStart(int resolution, int tileCount) {
expectedTileCount = tileCount;
}

@Override
public void notifyChunkStart(int plane, int xx, int yy, int zz) {
synchronized (this) {
startedTiles++;
}
}

@Override
public void notifyChunkEnd(int plane, int xx, int yy, int zz) {
synchronized (this) {
completedTiles++;
}
}

@Override
public void notifyResolutionEnd(int resolution) {
if (startedTiles == completedTiles && completedTiles == expectedTileCount) {
finishedResolutions.add(completedTiles);
}
completedTiles = 0;
startedTiles = 0;
}

/**
* Get the recorded tile counts for each completed resolution.
*
* @return an array with one element per resolution
*/
public Integer[] getTileCounts() {
return finishedResolutions.toArray(new Integer[finishedResolutions.size()]);
}

}
Loading

0 comments on commit 8537f73

Please sign in to comment.