diff --git a/README.md b/README.md
index 749d68f..613870e 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,4 @@
-# Hatch 3.1.2
-
+# Hatch 3.2.0
This tool converts the largest image in a VSI, SVS, or TIF image into a new TIFF image with a freshly created image pyramid with each scaling 1/2 dimensions each scale.
diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml
index 8b96ffb..db9ca07 100644
--- a/dependency-reduced-pom.xml
+++ b/dependency-reduced-pom.xml
@@ -3,7 +3,7 @@
4.0.0
edu.stonybrook.bmi
hatch
- 3.1.2
+ 3.2.0
@@ -45,7 +45,7 @@
maven-shade-plugin
- 3.4.1
+ 3.5.1
package
@@ -87,7 +87,7 @@
org.graalvm.buildtools
native-maven-plugin
- 0.9.27
+ 0.10.0
true
@@ -130,6 +130,7 @@
21
+ 3.10.1
21
UTF-8
7.0.0
diff --git a/pom.xml b/pom.xml
index 9f35775..dba3479 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,13 +3,14 @@
4.0.0
edu.stonybrook.bmi
hatch
- 3.1.2
+ 3.2.0
jar
UTF-8
21
21
7.0.0
+ 3.10.1
@@ -29,6 +30,11 @@
jcommander
1.82
+
+ com.twelvemonkeys.imageio
+ imageio-tiff
+ ${twelvemonkeys.version}
+
@@ -87,7 +93,7 @@
org.apache.maven.plugins
maven-shade-plugin
- 3.4.1
+ 3.5.1
false
@@ -129,7 +135,7 @@
org.graalvm.buildtools
native-maven-plugin
- 0.9.27
+ 0.10.0
true
diff --git a/src/main/java/edu/stonybrook/bmi/hatch/Hatch.java b/src/main/java/edu/stonybrook/bmi/hatch/Hatch.java
index 91cfd09..c9419a7 100644
--- a/src/main/java/edu/stonybrook/bmi/hatch/Hatch.java
+++ b/src/main/java/edu/stonybrook/bmi/hatch/Hatch.java
@@ -12,6 +12,7 @@
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
+import java.util.logging.LogManager;
import java.util.logging.Logger;
import java.util.stream.Stream;
@@ -20,14 +21,29 @@
* @author erich
*/
public class Hatch {
- public static String software = "hatch 3.1.2 by Wing-n-Beak";
+ public static String software = "hatch 3.2.0 by Wing-n-Beak";
private static final String[] ext = new String[] {".vsi", ".svs", ".tif"};
+ private static final String errorlog = "error.log";
+ private static Logger LOGGER;
public static final String HELP = Hatch.software+"\n"+
"""
usage: hatch
-v : verbose
""";
+ public Hatch() {
+ LOGGER = Logger.getLogger(Hatch.class.getName());
+ }
+
+ static {
+ try {
+ LogManager.getLogManager().readConfiguration(Hatch.class.getResourceAsStream("/logging.properties"));
+ } catch (IOException | SecurityException | ExceptionInInitializerError ex) {
+ Logger.getLogger(Hatch.class.getName()).log(Level.SEVERE, "Failed to read logging.properties file", ex);
+ }
+ LOGGER = Logger.getLogger(Hatch.class.getName());
+ }
+
private static void Traverse(HatchParameters params) {
Path s = params.src.toPath();
Path d = params.dest.toPath();
@@ -48,28 +64,23 @@ private static void Traverse(HatchParameters params) {
String frag = s.relativize(f).toString();
frag = frag.substring(0,frag.length()-4)+".tif";
Path t = Path.of(d.toString(), frag);
- if (!t.toFile().exists()) {
- System.out.println("PROCESSING FILE --> "+f);
- engine.submit(new FileProcessor(params, f, t));
- } else if (t.toFile().length()==0) {
- System.out.println("ZERO LENGTH FILE --> "+f);
+ if (t.toFile().exists()&¶ms.overwrite) {
t.toFile().delete();
- System.out.println("RE-PROCESSING FILE --> "+f);
- engine.submit(new FileProcessor(params, f, t));
}
+ engine.submit(new FileProcessor(params, f, t));
});
- } catch (IOException ex) {
- Logger.getLogger(Hatch.class.getName()).log(Level.SEVERE, null, ex);
+ } catch (IOException ex) {
+ LOGGER.severe("FILE PROCESSOR ERROR --> "+params.src.toString()+" "+params.dest.toString()+" "+ex.toString());
}
int cc = engine.getActiveCount()+engine.getQueue().size();
while ((engine.getActiveCount()+engine.getQueue().size())>0) {
int curr = engine.getActiveCount()+engine.getQueue().size();
if (cc!=curr) {
cc=curr;
- if (params.verbose) System.out.println("All jobs submitted...waiting for "+curr);
+ if (params.verbose) LOGGER.log(Level.INFO,"All jobs submitted...waiting for "+curr);
}
}
- System.out.println("Engine shutdown");
+ LOGGER.info("Engine shutdown");
engine.shutdown();
}
@@ -79,13 +90,17 @@ private static String getFileNameBase(File file) {
}
public static void main(String[] args) {
+ LOGGER.setLevel(Level.SEVERE);
loci.common.DebugTools.setRootLevel("WARN");
HatchParameters params = new HatchParameters();
JCommander jc = JCommander.newBuilder().addObject(params).build();
jc.setProgramName(Hatch.software+"\nhatch");
try {
jc.parse(args);
- System.out.println(params);
+ LOGGER.log(Level.INFO,params.toString());
+ if (params.verbose) {
+ LOGGER.setLevel(Level.INFO);
+ }
if (params.isHelp()) {
jc.usage();
System.exit(0);
@@ -118,7 +133,7 @@ public static void main(String[] args) {
try (X2TIF v2t = new X2TIF(params, params.src.toString(), params.dest.toString(), series)){
v2t.Execute();
} catch (Exception ex) {
- Logger.getLogger(Hatch.class.getName()).log(Level.SEVERE, null, ex);
+ LOGGER.severe("FILE PROCESSOR ERROR --> "+params.src.toString()+" "+params.dest.toString()+" "+ex.toString());
}
}
} else {
@@ -128,7 +143,7 @@ public static void main(String[] args) {
try (X2TIF v2t = new X2TIF(params, params.src.toString(), dest.toString(), null);) {
v2t.Execute();
} catch (Exception ex) {
- Logger.getLogger(Hatch.class.getName()).log(Level.SEVERE, null, ex);
+ LOGGER.severe("FILE PROCESSOR ERROR --> "+params.src.toString()+" "+params.dest.toString()+" "+ex.toString());
}
} else {
params.series.forEach(s->{
@@ -136,7 +151,7 @@ public static void main(String[] args) {
try (X2TIF v2t = new X2TIF(params, params.src.toString(), dest.toString(), Integer.valueOf(s));) {
v2t.Execute();
} catch (Exception ex) {
- Logger.getLogger(Hatch.class.getName()).log(Level.SEVERE, null, ex);
+ LOGGER.severe("FILE PROCESSOR ERROR --> "+params.src.toString()+" "+params.dest.toString()+" "+ex.toString());
}
});
}
@@ -147,15 +162,15 @@ public static void main(String[] args) {
try (X2TIF v2t = new X2TIF(params, params.src.toString(), params.dest.toString(), null);) {
v2t.Execute();
} catch (Exception ex) {
- Logger.getLogger(Hatch.class.getName()).log(Level.SEVERE, null, ex);
+ LOGGER.severe("FILE PROCESSOR ERROR --> "+params.src.toString()+" "+params.dest.toString()+" "+ex.toString());
}
}
}
} else {
- System.out.println(params.src.toString()+" does not exist!");
+ LOGGER.severe(params.src.toString()+" does not exist!");
}
} catch (ParameterException ex) {
- System.out.println(ex.getMessage());
+ LOGGER.severe("FILE PROCESSOR ERROR --> "+params.src.toString()+" "+params.dest.toString()+" "+ex.toString());
}
}
}
@@ -164,25 +179,40 @@ class FileProcessor implements Callable {
private final HatchParameters params;
private final File src;
private final File dest;
+ private static Logger LOGGER;
public FileProcessor(HatchParameters params, Path src, Path dest) {
this.params = params;
this.src = src.toFile();
this.dest = dest.toFile();
+ LOGGER = Logger.getLogger(Hatch.class.getName());
}
@Override
public String call() {
if (dest.exists()) {
- dest.delete();
- } else {
- dest.getParentFile().mkdirs();
+ if (params.overwrite) {
+ dest.delete();
+ } else {
+ if (params.retry) {
+ if (!Validate2.file(dest.toPath())) {
+ dest.delete();
+ }
+ } else {
+ if (params.validate) {
+ Validate2.file(dest.toPath());
+ }
+ return null;
+ }
+ }
}
+ dest.getParentFile().mkdirs();
try (X2TIF v2t = new X2TIF(params, src.toString(), dest.toString(), null)) {
v2t.Execute();
} catch (Exception ex) {
- System.out.println("FILE PROCESSOR ERROR --> "+src+" "+dest+" "+ex.toString());
- }
+ LOGGER.severe("FILE PROCESSOR ERROR --> "+src+" "+dest+" "+ex.toString());
+ }
+ Validate2.file(dest.toPath());
return null;
}
}
diff --git a/src/main/java/edu/stonybrook/bmi/hatch/HatchParameters.java b/src/main/java/edu/stonybrook/bmi/hatch/HatchParameters.java
index e54d9cf..2b21ce1 100644
--- a/src/main/java/edu/stonybrook/bmi/hatch/HatchParameters.java
+++ b/src/main/java/edu/stonybrook/bmi/hatch/HatchParameters.java
@@ -29,6 +29,15 @@ public boolean isHelp() {
@Parameter(names = {"-v","-verbose"})
public boolean verbose = false;
+ @Parameter(names = {"-o","-overwrite"})
+ public boolean overwrite = false;
+
+ @Parameter(names = {"-r","-retry"})
+ public boolean retry = false;
+
+ @Parameter(names = {"-validate"})
+ public boolean validate = false;
+
@Parameter(names = "-jp2", hidden = true)
public boolean jp2 = false;
diff --git a/src/main/java/edu/stonybrook/bmi/hatch/SingleLineFormatter.java b/src/main/java/edu/stonybrook/bmi/hatch/SingleLineFormatter.java
new file mode 100644
index 0000000..77ddb82
--- /dev/null
+++ b/src/main/java/edu/stonybrook/bmi/hatch/SingleLineFormatter.java
@@ -0,0 +1,17 @@
+package edu.stonybrook.bmi.hatch;
+
+import java.util.logging.Formatter;
+import java.util.logging.LogRecord;
+
+public class SingleLineFormatter extends Formatter {
+
+ @Override
+ public String format(LogRecord record) {
+ return String.format("%1$tF %1$tT %2$s %3$s: %4$s %n",
+ record.getMillis(),
+ record.getLevel().getLocalizedName(),
+ record.getSourceClassName() + "." + record.getSourceMethodName(),
+ formatMessage(record));
+ }
+}
+
diff --git a/src/main/java/edu/stonybrook/bmi/hatch/Validate2.java b/src/main/java/edu/stonybrook/bmi/hatch/Validate2.java
new file mode 100644
index 0000000..aaba115
--- /dev/null
+++ b/src/main/java/edu/stonybrook/bmi/hatch/Validate2.java
@@ -0,0 +1,74 @@
+package edu.stonybrook.bmi.hatch;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Iterator;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.imageio.ImageIO;
+import javax.imageio.stream.ImageInputStream;
+
+/**
+ *
+ * @author erich
+ */
+public class Validate2 {
+
+ public static boolean file(Path path) {
+ Logger LOGGER = Logger.getLogger(Validate2.class.getName());
+ javax.imageio.ImageReader reader = null;
+ ImageInputStream input;
+ try {
+ input = ImageIO.createImageInputStream(path.toFile());
+ Iterator readers = ImageIO.getImageReadersByFormatName("tif");
+ javax.imageio.ImageReader ir = null;
+ while (readers.hasNext()) {
+ ir = readers.next();
+ //System.out.println("IMAGE CLASS --> "+ir.getClass().getCanonicalName());
+ if ("com.twelvemonkeys.imageio.plugins.tiff.TIFFImageReader".equals(ir.getClass().getCanonicalName())) {
+ //System.out.println("YES! : "+ir.getClass().getCanonicalName());
+ reader = ir;
+ } else {
+ //System.out.println("NOPE : "+ir.getClass().getCanonicalName());
+ }
+ }
+ if (ir==null) {
+ throw new IllegalArgumentException("No reader for: " + path);
+ }
+ //System.out.println("READER IS ---> "+reader.getClass().getCanonicalName());
+ reader.setInput(input);
+ try {
+ if (reader.getWidth(0)==0) {
+ LOGGER.severe(path.toString()+" X dimension is ZERO");
+ return false;
+ }
+ } catch (IOException ex) {
+ Logger.getLogger(Validate2.class.getName()).log(Level.SEVERE, null, ex);
+ return false;
+ }
+ try {
+ if (reader.getHeight(0)==0) {
+ LOGGER.severe(path.toString()+" Y dimension is ZERO");
+ return false;
+ }
+ } catch (IOException ex) {
+ LOGGER.severe(ex.getMessage());
+ return false;
+ }
+ try {
+ int last = reader.getNumImages(true)-1;
+ if ((reader.getWidth(last)>1024)||(reader.getHeight(last)>1024)) {
+ LOGGER.severe(path.toString()+" smallest scaled image ("+reader.getNumImages(true)+" - "+reader.getWidth(last)+"x"+reader.getHeight(last)+") must be less than 1024x1024");
+ return false;
+ }
+ } catch (IOException ex) {
+ LOGGER.severe(ex.getMessage());
+ return false;
+ }
+ } catch (IOException ex) {
+ LOGGER.severe(ex.getMessage());
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/edu/stonybrook/bmi/hatch/X2TIF.java b/src/main/java/edu/stonybrook/bmi/hatch/X2TIF.java
index 35c4725..85ffb26 100644
--- a/src/main/java/edu/stonybrook/bmi/hatch/X2TIF.java
+++ b/src/main/java/edu/stonybrook/bmi/hatch/X2TIF.java
@@ -1,7 +1,6 @@
package edu.stonybrook.bmi.hatch;
import java.awt.image.BufferedImage;
-import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
@@ -27,8 +26,6 @@
import loci.formats.tiff.IFD;
import loci.formats.tiff.PhotoInterp;
import loci.formats.tiff.TiffRational;
-import ome.codecs.CodecException;
-import ome.codecs.JPEG2000Codec;
import ome.units.UNITS;
import ome.units.quantity.Length;
import ome.xml.model.enums.DimensionOrder;
@@ -60,14 +57,16 @@ public class X2TIF implements AutoCloseable {
private HatchWriter writer;
private IMetadata meta;
private byte compression;
+ private static Logger LOGGER;
public X2TIF(HatchParameters params, String src, String dest, Integer series) {
+ LOGGER = Logger.getLogger(X2TIF.class.getName());
time = new StopWatch();
inputFile = src;
outputFile = dest;
this.params = params;
if (params.verbose) {
- System.out.println("initializing...");
+ LOGGER.log(Level.INFO,"initializing...");
}
try {
ServiceFactory factory = new ServiceFactory();
@@ -95,13 +94,13 @@ public X2TIF(HatchParameters params, String src, String dest, Integer series) {
maximage = series;
}
if ((series!=null)&&((series<0)||(series>reader.getSeriesCount()))) {
- System.out.println("Series doesn't exist : "+src+" --> "+series);
+ LOGGER.log(Level.INFO,"Series doesn't exist : "+src+" --> "+series);
System.exit(0);
}
try {
writer = new HatchWriter(dest);
} catch (IOException ex) {
- Logger.getLogger(X2TIF.class.getName()).log(Level.SEVERE, null, ex);
+ LOGGER.log(Level.SEVERE, "FILE PROCESSOR ERROR --> "+params.src+" "+params.dest+" "+ex.toString());
}
reader.setSeries(maximage);
tileSizeX = reader.getOptimalTileWidth();
@@ -113,16 +112,16 @@ public X2TIF(HatchParameters params, String src, String dest, Integer series) {
ppy = retrieve.getPixelsPhysicalSizeY(maximage);
SetPPS();
if (params.verbose) {
- System.out.println("Image Size : "+width+"x"+height);
- System.out.println("Tile size : "+tileSizeX+"x"+tileSizeY);
- System.out.println("Compression : "+reader.metadata.get("Compression"));
+ LOGGER.log(Level.INFO,"Image Size : "+width+"x"+height);
+ LOGGER.log(Level.INFO,"Tile size : "+tileSizeX+"x"+tileSizeY);
+ LOGGER.log(Level.INFO,"Compression : "+reader.metadata.get("Compression"));
}
//String xml = service.getOMEXML(omexml);
- //System.out.println(xml);
+ //Systsm.out.println(xml);
try {
if (reader.metadata.get("Compression")==null) {
if (params.verbose) {
- System.out.println("NULL compression specified...trying JPEG...no promises...");
+ LOGGER.log(Level.INFO,"NULL compression specified...trying JPEG...no promises...");
}
} else if ((reader.metadata.get("Compression")=="JPEG-2000")&¶ms.jp2) {
@@ -130,7 +129,7 @@ public X2TIF(HatchParameters params, String src, String dest, Integer series) {
throw new Error("Hatch can only convert images that have JPEG compression.");
}
} catch (Error e){
- System.out.println(X2TIF.class.getName()+" : "+e.getLocalizedMessage());
+ LOGGER.log(Level.SEVERE, e.getLocalizedMessage()+" : "+src+" "+dest);
System.exit(0);
}
int size = Math.max(width, height);
@@ -139,7 +138,7 @@ public X2TIF(HatchParameters params, String src, String dest, Integer series) {
depth = ss-tiless+2;
TileSize = reader.getOptimalTileHeight() * reader.getOptimalTileWidth() * 24;
if (params.verbose) {
- System.out.println("# of scales to be generated : "+depth);
+ LOGGER.log(Level.INFO,"# of scales to be generated : "+depth);
}
meta = service.createOMEXMLMetadata();
meta.setImageID("Image:0", 0);
@@ -157,8 +156,14 @@ public X2TIF(HatchParameters params, String src, String dest, Integer series) {
meta.setPixelsSizeZ(new PositiveInteger(1), 0);
meta.setPixelsSizeC(new PositiveInteger(3), 0);
meta.setPixelsSizeT(new PositiveInteger(1), 0);
- } catch (DependencyException | ServiceException | FormatException | IOException ex) {
- Logger.getLogger(X2TIF.class.getName()).log(Level.SEVERE, null, ex);
+ } catch (DependencyException ex) {
+ LOGGER.log(Level.SEVERE, "DependencyException : "+src+" "+dest);
+ } catch (ServiceException ex) {
+ LOGGER.log(Level.SEVERE, "ServiceException : "+src+" "+dest);
+ } catch (FormatException ex) {
+ LOGGER.log(Level.SEVERE, "FormatException : "+src+" "+dest);
+ } catch (IOException ex) {
+ LOGGER.log(Level.SEVERE, "IOException : "+src+" "+dest);
}
}
@@ -210,9 +215,9 @@ public void Dump2File3(byte[] buffer, int a, int b) {
fos.flush();
}
} catch (FileNotFoundException ex) {
- Logger.getLogger(Pyramid.class.getName()).log(Level.SEVERE, null, ex);
+ LOGGER.log(Level.SEVERE, "FILE PROCESSOR ERROR --> "+params.src+" "+params.dest+" "+ex.toString());
} catch (IOException ex) {
- Logger.getLogger(Pyramid.class.getName()).log(Level.SEVERE, null, ex);
+ LOGGER.log(Level.SEVERE, "FILE PROCESSOR ERROR --> "+params.src+" "+params.dest+" "+ex.toString());
}
}
@@ -234,14 +239,14 @@ public void DumpBI2File3(BufferedImage bi, int a, int b) {
try {
jpgWriter.write(null, outputImage, param);
} catch (IOException ex) {
- Logger.getLogger(NeoJPEGCodec.class.getName()).log(Level.SEVERE, null, ex);
+ LOGGER.log(Level.SEVERE, "FILE PROCESSOR ERROR --> "+params.src+" "+params.dest+" "+ex.toString());
}
jpgWriter.dispose();
Files.write(f.toPath(), baos.toByteArray());
} catch (FileNotFoundException ex) {
- Logger.getLogger(Pyramid.class.getName()).log(Level.SEVERE, null, ex);
+ LOGGER.log(Level.SEVERE, "FILE PROCESSOR ERROR --> "+params.src+" "+params.dest+" "+ex.toString());
} catch (IOException ex) {
- Logger.getLogger(Pyramid.class.getName()).log(Level.SEVERE, null, ex);
+ LOGGER.log(Level.SEVERE, "FILE PROCESSOR ERROR --> "+params.src+" "+params.dest+" "+ex.toString());
}
}
@@ -255,7 +260,7 @@ public static void Display(byte[] buffer, int from, int to) {
public void readWriteTiles() throws FormatException, IOException {
if (params.verbose) {
- System.out.println("transferring image data...");
+ LOGGER.log(Level.INFO,"transferring image data...");
}
reader.setSeries(maximage);
int nXTiles = width / tileSizeX;
@@ -319,7 +324,7 @@ public void readWriteTiles() throws FormatException, IOException {
for (int y=0; y "+params.src+" "+params.dest+" "+ex.toString());
}
return bb;
}
- public void Execute() {
- try {
- readWriteTiles();
- time.Cumulative();
- } catch(IOException | FormatException e) {
- System.err.println(e.toString());
- }
+ public void Execute() throws FormatException, IOException {
+ readWriteTiles();
+ time.Cumulative();
}
@Override
diff --git a/src/main/resources/logging.properties b/src/main/resources/logging.properties
new file mode 100644
index 0000000..b8c2054
--- /dev/null
+++ b/src/main/resources/logging.properties
@@ -0,0 +1,15 @@
+# Default global logging level
+.level=INFO
+
+# ConsoleHandler
+handlers=java.util.logging.ConsoleHandler, java.util.logging.FileHandler
+java.util.logging.ConsoleHandler.level=FINE
+java.util.logging.ConsoleHandler.formatter = edu.stonybrook.bmi.hatch.SingleLineFormatter
+
+edu.stonybrook.bmi.hatch.Hatch.level=FINE
+
+java.util.logging.FileHandler.pattern=./error.log
+java.util.logging.FileHandler.limit=0
+java.util.logging.FileHandler.count=1
+java.util.logging.FileHandler.formatter=edu.stonybrook.bmi.hatch.SingleLineFormatter
+java.util.logging.FileHandler.level=SEVERE