diff --git a/config/jni-config.json b/config/jni-config.json index 7a4af15..8b4e417 100644 --- a/config/jni-config.json +++ b/config/jni-config.json @@ -1,8 +1,4 @@ [ -{ - "name":"java.io.IOException", - "methods":[{"name":"","parameterTypes":["java.lang.String"] }] -}, { "name":"java.lang.Boolean", "methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }] diff --git a/config/reflect-config.json b/config/reflect-config.json index 7da836f..de3f44b 100644 --- a/config/reflect-config.json +++ b/config/reflect-config.json @@ -1,13 +1,4 @@ [ -{ - "name":"[[I" -}, -{ - "name":"[[J" -}, -{ - "name":"[[Lloci.formats.tiff.TiffRational;" -}, { "name":"boolean", "allDeclaredFields":true @@ -22,6 +13,11 @@ "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"com.beust.jcommander.converters.IntegerConverter", + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":["java.lang.String"] }] +}, { "name":"com.beust.jcommander.validators.NoValidator", "methods":[{"name":"","parameterTypes":[] }] @@ -30,15 +26,34 @@ "name":"com.beust.jcommander.validators.NoValueValidator", "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"com.sun.xml.internal.stream.XMLInputFactoryImpl", + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"edu.stonybrook.bmi.hatch.HatchParameters", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, +{ + "name" : "java.util.logging.FileHandler", + "methods" : [{ "name" : "", "parameterTypes" : [] }] +}, { "name":"java.io.File", "allDeclaredFields":true }, +{ + "name":"java.lang.Integer", + "allDeclaredFields":true +}, +{ + "name":"java.security.SecureRandomParameters" +}, +{ + "name":"java.util.concurrent.atomic.AtomicReference", + "fields":[{"name":"value"}] +}, { "name":"loci.common.Log4jTools", "methods":[{"name":"setRootLevel","parameterTypes":["java.lang.String"] }] @@ -82,12 +97,6 @@ { "name":"loci.formats.services.JPEGXRServiceImpl" }, -{ - "name":"loci.formats.services.LuraWaveService" -}, -{ - "name":"loci.formats.services.LuraWaveServiceImpl" -}, { "name":"loci.formats.services.MDBService" }, @@ -120,21 +129,53 @@ "name":"loci.formats.services.POIServiceImpl" }, { - "name":"loci.formats.services.WlzService" + "name":"ome.codecs.services.JAIIIOService" }, { - "name":"loci.formats.services.WlzServiceImpl" + "name":"ome.codecs.services.JAIIIOServiceImpl" }, { - "name":"ome.codecs.services.JAIIIOService" + "name":"org.apache.jena.rdfpatch.system.InitPatch" }, { - "name":"ome.codecs.services.JAIIIOServiceImpl" + "name":"org.apache.jena.rdfs.sys.InitRDFS" }, { - "name":"ome.codecs.services.LuraWaveService" + "name":"org.apache.jena.riot.system.InitRIOT" }, { - "name":"ome.codecs.services.LuraWaveServiceImpl" + "name":"org.apache.jena.shacl.sys.InitShacl" +}, +{ + "name":"org.apache.jena.shex.sys.InitShex" +}, +{ + "name":"org.apache.jena.sparql.system.InitARQ" +}, +{ + "name":"org.apache.jena.sys.InitJenaCore" +}, +{ + "name":"org.apache.jena.tdb1.sys.InitTDB" +}, +{ + "name":"org.apache.jena.tdb2.sys.InitTDB2" +}, +{ + "name":"org.apache.log4j.Level" +}, +{ + "name":"org.apache.xerces.jaxp.SAXParserFactoryImpl" +}, +{ + "name":"org.slf4j.simple.SimpleServiceProvider" +}, +{ + "name":"sun.security.provider.NativePRNG", + "methods":[{"name":"","parameterTypes":[] }, {"name":"","parameterTypes":["java.security.SecureRandomParameters"] }] +}, +{ + "name":"sun.security.provider.SHA", + "methods":[{"name":"","parameterTypes":[] }] } ] diff --git a/config/resource-config.json b/config/resource-config.json index fc92eb2..350b09f 100644 --- a/config/resource-config.json +++ b/config/resource-config.json @@ -1,27 +1,58 @@ { "resources":{ - "includes":[ - { - "pattern":"\\Qloci/common/services/services.properties\\E" - }, - { - "pattern":"\\Qloci/formats/FormatTools.class\\E" - }, - { - "pattern":"\\Qorg/joda/time/tz/data/America/Chicago\\E" - }, - { - "pattern":"\\Qorg/joda/time/tz/data/America/Denver\\E" - }, - { - "pattern":"\\Qorg/joda/time/tz/data/America/Los_Angeles\\E" - }, - { - "pattern":"\\Qorg/joda/time/tz/data/America/New_York\\E" - }, - { - "pattern":"\\Qorg/joda/time/tz/data/ZoneInfoMap\\E" - } - ]}, - "bundles":[] + "includes":[{ + "pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E" + }, { + "pattern":"\\QMETA-INF/services/java.util.spi.ResourceBundleControlProvider\\E" + }, { + "pattern":"\\QMETA-INF/services/javax.xml.parsers.SAXParserFactory\\E" + }, { + "pattern":"\\QMETA-INF/services/javax.xml.stream.XMLInputFactory\\E" + }, { + "pattern":"\\QMETA-INF/services/org.apache.jena.sys.JenaSubsystemLifecycle\\E" + }, { + "pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E" + }, { + "pattern":"\\Qetc/location-mapping.rdf\\E" + }, { + "pattern":"\\Qetc/location-mapping.ttl\\E" + }, { + "pattern":"\\Qlocation-mapping.rdf\\E" + }, { + "pattern":"\\Qlocation-mapping.ttl\\E" + }, { + "pattern":"\\Qloci/common/services/services.properties\\E" + }, { + "pattern":"\\Qloci/formats/FormatTools.class\\E" + }, { + "pattern":"\\Qlogging.properties\\E" + }, { + "pattern":"\\Qorg/apache/jena/ext/xerces/impl/xpath/regex/message.properties\\E" + }, { + "pattern":"\\Qorg/apache/jena/ext/xerces/impl/xpath/regex/message_en.properties\\E" + }, { + "pattern":"\\Qorg/apache/jena/ext/xerces/impl/xpath/regex/message_en_US.properties\\E" + }, { + "pattern":"\\Qorg/joda/time/tz/data/America/New_York\\E" + }, { + "pattern":"\\Qorg/joda/time/tz/data/ZoneInfoMap\\E" + }, { + "pattern":"\\Qsimplelogger.properties\\E" + }, { + "pattern":"java.logging:\\Qsun/util/logging/resources/logging_en.properties\\E" + }, { + "pattern":"java.logging:\\Qsun/util/logging/resources/logging_en_US.properties\\E" + }, { + "pattern":"java.xml:\\Qjdk/xml/internal/jdkcatalog/JDKCatalog.xml\\E" + }]}, + "bundles":[{ + "name":"org.apache.jena.ext.xerces.impl.xpath.regex.message", + "locales":["en-US"] + }, { + "name":"sun.text.resources.cldr.FormatData", + "locales":["en", "en-US", "und"] + }, { + "name":"sun.util.resources.cldr.CalendarData", + "locales":["und"] + }] } diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml index 4dddc3f..85f03b5 100644 --- a/dependency-reduced-pom.xml +++ b/dependency-reduced-pom.xml @@ -3,7 +3,7 @@ 4.0.0 edu.stonybrook.bmi hatch - 3.3.0 + 4.0.0 @@ -45,7 +45,7 @@ maven-shade-plugin - 3.5.1 + 3.5.2 package @@ -87,7 +87,7 @@ org.graalvm.buildtools native-maven-plugin - 0.10.0 + 0.10.1 true @@ -127,12 +127,16 @@ OME Artifactory https://artifacts.openmicroscopy.org/artifactory/maven/ + + halcyon + https://cursus.bmi.stonybrookmedicine.edu/releases + org.apache.jena apache-jena-libs - 5.0.0-rc1 + 5.0.0 pom compile @@ -142,6 +146,6 @@ 3.10.1 21 UTF-8 - 7.0.0 + 7.3.0 diff --git a/error.log b/error.log index e69de29..7c4d7ba 100644 --- a/error.log +++ b/error.log @@ -0,0 +1,2 @@ +2024-05-15 12:34:25 SEVERE edu.stonybrook.bmi.hatch.Validate.file: D:\bad\dest\Image_01_M200_40X_A36.tif ==> index >= numImages (0 >= 0) +2024-05-15 12:34:25 SEVERE edu.stonybrook.bmi.hatch.Validate.file: D:\bad\dest\ugh.tif ==> Invalid TIFF byte order mark 'sd', expected: 'II' or 'MM' diff --git a/nbactions-hatchjar.xml b/nbactions-hatchjar.xml index 23b9bd7..8c1adf6 100644 --- a/nbactions-hatchjar.xml +++ b/nbactions-hatchjar.xml @@ -12,7 +12,7 @@ ${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs} - -v -src D:\HalcyonStorage\tcga\brca -dest D:\HalcyonStorage\tcga\brca\tif + -v -src D:\bad\src -dest D:\bad\dest -validate edu.stonybrook.bmi.hatch.Hatch java @@ -29,7 +29,7 @@ -agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address} ${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs} - -v -src D:\HalcyonStorage\tcga\brca -dest D:\HalcyonStorage\tcga\brca\tif + -v -src D:\bad\src -dest D:\bad\dest -validate edu.stonybrook.bmi.hatch.Hatch java true @@ -49,7 +49,7 @@ ${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs} edu.stonybrook.bmi.hatch.Hatch java - -v -src D:\HalcyonStorage\tcga\brca -dest D:\HalcyonStorage\tcga\brca\tif + -v -src D:\bad\src -dest D:\bad\dest -validate diff --git a/pom.xml b/pom.xml index 52671ad..4bce4ec 100644 --- a/pom.xml +++ b/pom.xml @@ -3,13 +3,13 @@ 4.0.0 edu.stonybrook.bmi hatch - 3.3.0 + 4.0.0 jar UTF-8 21 21 - 7.0.0 + 7.3.0 3.10.1 @@ -26,9 +26,9 @@ - com.beust + org.jcommander jcommander - 1.82 + 1.83 com.twelvemonkeys.imageio @@ -38,9 +38,19 @@ org.apache.jena apache-jena-libs - 5.0.0-rc1 + 5.0.0 pom + + org.slf4j + slf4j-simple + 2.0.12 + @@ -71,10 +81,17 @@ OME Artifactory https://artifacts.openmicroscopy.org/artifactory/maven/ + + halcyon + https://cursus.bmi.stonybrookmedicine.edu/releases + hatchjar + + true + hatch-${project.version} @@ -99,7 +116,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.5.1 + 3.5.2 false @@ -141,7 +158,7 @@ org.graalvm.buildtools native-maven-plugin - 0.10.0 + 0.10.1 true diff --git a/src/main/java/edu/stonybrook/bmi/hatch/BaseTiffReader.java b/src/main/java/edu/stonybrook/bmi/hatch/BaseTiffReader.java index 5585f92..8dcec30 100644 --- a/src/main/java/edu/stonybrook/bmi/hatch/BaseTiffReader.java +++ b/src/main/java/edu/stonybrook/bmi/hatch/BaseTiffReader.java @@ -43,8 +43,6 @@ import loci.formats.MetadataTools; import loci.formats.in.MetadataLevel; import loci.formats.meta.MetadataStore; -import loci.formats.tiff.IFD; -import loci.formats.tiff.IFDList; import loci.formats.tiff.PhotoInterp; import loci.formats.tiff.TiffRational; import ome.units.UNITS; @@ -95,11 +93,6 @@ protected void initMetadata() throws FormatException, IOException { initStandardMetadata(); initMetadataStore(); } - - @Override - public IFDList getIFDs() { - return ifds; - } /** * Parses standard metadata. @@ -136,7 +129,7 @@ protected void initStandardMetadata() throws FormatException, IOException { } } - loci.formats.tiff.TiffCompression comp = firstIFD.getCompression(); + TiffCompression comp = firstIFD.getCompression(); put("Compression", comp.getCodecName()); PhotoInterp photo = firstIFD.getPhotometricInterpretation(); diff --git a/src/main/java/edu/stonybrook/bmi/hatch/CellSensReader.java b/src/main/java/edu/stonybrook/bmi/hatch/CellSensReader.java index b9abc72..dfbf047 100644 --- a/src/main/java/edu/stonybrook/bmi/hatch/CellSensReader.java +++ b/src/main/java/edu/stonybrook/bmi/hatch/CellSensReader.java @@ -34,10 +34,7 @@ import loci.formats.in.DynamicMetadataOptions; import loci.formats.in.MetadataOptions; import loci.formats.meta.MetadataStore; -import loci.formats.tiff.IFD; -import loci.formats.tiff.IFDList; import loci.formats.tiff.PhotoInterp; -import loci.formats.tiff.TiffParser; import ome.units.UNITS; import ome.xml.model.primitives.Timestamp; diff --git a/src/main/java/edu/stonybrook/bmi/hatch/DicomRDFProvider.java b/src/main/java/edu/stonybrook/bmi/hatch/DicomRDFProvider.java new file mode 100644 index 0000000..d714367 --- /dev/null +++ b/src/main/java/edu/stonybrook/bmi/hatch/DicomRDFProvider.java @@ -0,0 +1,16 @@ +package edu.stonybrook.bmi.hatch; + +import loci.formats.dicom.DicomJSONProvider; +import loci.formats.out.DicomWriter; + +/** + * + * @author erich + */ +public class DicomRDFProvider extends DicomJSONProvider { + + + public static void main(String args[]) { + DicomWriter ha; + } +} diff --git a/src/main/java/edu/stonybrook/bmi/hatch/ExtendedDicomWriter.java b/src/main/java/edu/stonybrook/bmi/hatch/ExtendedDicomWriter.java new file mode 100644 index 0000000..81c4f96 --- /dev/null +++ b/src/main/java/edu/stonybrook/bmi/hatch/ExtendedDicomWriter.java @@ -0,0 +1,46 @@ +package edu.stonybrook.bmi.hatch; + +import java.io.IOException; +import java.util.ArrayList; +import static loci.formats.FormatHandler.checkSuffix; +import loci.formats.FormatTools; +import loci.formats.dicom.DicomJSONProvider; +import loci.formats.dicom.ITagProvider; +import loci.formats.out.DicomWriter; + +/** + * + * @author erich + */ +public class ExtendedDicomWriter extends DicomWriter { + private final ArrayList tagProviders = new ArrayList<>(); + + @Override + public void setExtraMetadata(String tagSource) { + FormatTools.assertId(currentId, false, 1); + + // get the provider (parser) from the source name + // uses the file extension, this might need improvement + + if (tagSource != null) { + ITagProvider provider = null; + if (checkSuffix(tagSource, "json")) { + provider = new DicomJSONProvider(); + } else { + throw new IllegalArgumentException("Unknown tag format: " + tagSource); + } + try { + provider.readTagSource(tagSource); + tagProviders.add(provider); + } catch (IOException e) { + LOGGER.error("Could not parse extra metadata: " + tagSource, e); + } + } + } + + public static void main(String args[]) { + // TODO code application logic here + ExtendedDicomWriter dw = new ExtendedDicomWriter(); + + } +} diff --git a/src/main/java/edu/stonybrook/bmi/hatch/FormatReader.java b/src/main/java/edu/stonybrook/bmi/hatch/FormatReader.java index 1e754b4..f734892 100644 --- a/src/main/java/edu/stonybrook/bmi/hatch/FormatReader.java +++ b/src/main/java/edu/stonybrook/bmi/hatch/FormatReader.java @@ -65,8 +65,6 @@ import loci.formats.meta.MetadataStore; import loci.formats.ome.OMEXMLMetadata; import loci.formats.services.OMEXMLService; -import loci.formats.tiff.IFD; -import loci.formats.tiff.IFDList; import ome.xml.model.AffineTransform; import ome.xml.model.enums.AcquisitionMode; import ome.xml.model.enums.ArcType; diff --git a/src/main/java/edu/stonybrook/bmi/hatch/Hatch.java b/src/main/java/edu/stonybrook/bmi/hatch/Hatch.java index f1c16d0..37cfe72 100644 --- a/src/main/java/edu/stonybrook/bmi/hatch/Hatch.java +++ b/src/main/java/edu/stonybrook/bmi/hatch/Hatch.java @@ -21,10 +21,10 @@ * @author erich */ public class Hatch { - public static String software = "hatch 3.3.0 by Wing-n-Beak"; + public static String software = "hatch 4.0.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; + private static final Logger LOGGER; public static final String HELP = Hatch.software+"\n"+ """ usage: hatch @@ -32,7 +32,7 @@ public class Hatch { """; public Hatch() { - LOGGER = Logger.getLogger(Hatch.class.getName()); + //LOGGER = Logger.getLogger(Hatch.class.getName()); } static { @@ -52,6 +52,12 @@ private static void Traverse(HatchParameters params) { try (Stream X = Files.walk(params.src.toPath())) { X .filter(Objects::nonNull) + .filter(path->{ + if (params.filter==null) { + return true; + } + return path.toString().contains(params.filter); + }) .filter(fff -> { for (String ext1 : ext) { if (fff.toFile().toString().toLowerCase().endsWith(ext1)) { @@ -70,14 +76,14 @@ private static void Traverse(HatchParameters params) { engine.submit(new FileProcessor(params, f, t)); }); } catch (IOException ex) { - LOGGER.severe("FILE PROCESSOR ERROR --> "+params.src.toString()+" "+params.dest.toString()+" "+ex.toString()); + LOGGER.log(Level.SEVERE, "FILE PROCESSOR ERROR --> {0} {1} {2}", new Object[]{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) LOGGER.log(Level.INFO,"All jobs submitted...waiting for "+curr); + if (params.verbose) LOGGER.log(Level.INFO, "All jobs submitted...waiting for {0}", curr); } } LOGGER.info("Engine shutdown"); @@ -92,6 +98,10 @@ private static String getFileNameBase(File file) { public static void main(String[] args) { LOGGER.setLevel(Level.SEVERE); loci.common.DebugTools.setRootLevel("WARN"); + if (args.length==0) { + System.out.println("please specify parameters. Try 'hatch -help' for help! :-)"); + System.exit(0); + } HatchParameters params = new HatchParameters(); JCommander jc = JCommander.newBuilder().addObject(params).build(); jc.setProgramName(Hatch.software+"\nhatch"); @@ -133,7 +143,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.severe("FILE PROCESSOR ERROR --> "+params.src.toString()+" "+params.dest.toString()+" "+ex.toString()); + LOGGER.log(Level.SEVERE, "FILE PROCESSOR ERROR --> {0} {1} {2}", new Object[]{params.src.toString(), params.dest.toString(), ex.toString()}); } } } else { @@ -143,7 +153,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.severe("FILE PROCESSOR ERROR --> "+params.src.toString()+" "+params.dest.toString()+" "+ex.toString()); + LOGGER.log(Level.SEVERE, "FILE PROCESSOR ERROR --> {0} {1} {2}", new Object[]{params.src.toString(), params.dest.toString(), ex.toString()}); } } else { params.series.forEach(s->{ @@ -151,7 +161,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.severe("FILE PROCESSOR ERROR --> "+params.src.toString()+" "+params.dest.toString()+" "+ex.toString()); + LOGGER.log(Level.SEVERE, "FILE PROCESSOR ERROR --> {0} {1} {2}", new Object[]{params.src.toString(), params.dest.toString(), ex.toString()}); } }); } @@ -162,15 +172,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.severe("FILE PROCESSOR ERROR --> "+params.src.toString()+" "+params.dest.toString()+" "+ex.toString()); + LOGGER.log(Level.SEVERE, "FILE PROCESSOR ERROR --> {0} {1} {2}", new Object[]{params.src.toString(), params.dest.toString(), ex.toString()}); } } } } else { - LOGGER.severe(params.src.toString()+" does not exist!"); + LOGGER.log(Level.SEVERE, "{0} does not exist!", params.src.toString()); } } catch (ParameterException ex) { - LOGGER.severe("FILE PROCESSOR ERROR --> "+params.src.toString()+" "+params.dest.toString()+" "+ex.toString()); + LOGGER.severe(ex.getMessage()); } } } @@ -195,24 +205,28 @@ public String call() { dest.delete(); } else { if (params.retry) { - if (!Validate2.file(dest.toPath())) { + if (!Validate.file(dest.toPath())) { dest.delete(); } } else { if (params.validate) { - Validate2.file(dest.toPath()); + Validate.file(dest.toPath()); } return null; } } } - dest.getParentFile().mkdirs(); - try (X2TIF v2t = new X2TIF(params, src.toString(), dest.toString(), null)) { - v2t.Execute(); - } catch (Exception ex) { - LOGGER.severe("FILE PROCESSOR ERROR --> "+src+" "+dest+" "+ex.toString()); - } - Validate2.file(dest.toPath()); + if ((!dest.exists())&&(!params.validateonly)) { + dest.getParentFile().mkdirs(); + try (X2TIF v2t = new X2TIF(params, src.toString(), dest.toString(), null)) { + v2t.Execute(); + } catch (Exception ex) { + LOGGER.log(Level.SEVERE, "FILE PROCESSOR ERROR --> {0} {1} {2}", new Object[]{src, dest, ex.toString()}); + } + } + if (dest.exists()&&((params.validate)||params.validateonly)) { + Validate.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 2b21ce1..7725c55 100644 --- a/src/main/java/edu/stonybrook/bmi/hatch/HatchParameters.java +++ b/src/main/java/edu/stonybrook/bmi/hatch/HatchParameters.java @@ -1,6 +1,7 @@ package edu.stonybrook.bmi.hatch; import com.beust.jcommander.Parameter; +import com.beust.jcommander.converters.IntegerConverter; import com.beust.jcommander.internal.Lists; import java.io.File; import java.util.List; @@ -23,9 +24,12 @@ public boolean isHelp() { @Parameter(names = "-dest", description = "Destination Folder or File", required = true) public File dest; - @Parameter(names = "-fp", description = "# of file processors") - public int fp = 1; + @Parameter(names = "-fp", description = "# of file processors", converter = IntegerConverter.class) + public Integer fp = 1; + @Parameter(names = {"-filter", "-f"}, description = "String that each path must contain") + public String filter = null; + @Parameter(names = {"-v","-verbose"}) public boolean verbose = false; @@ -37,6 +41,9 @@ public boolean isHelp() { @Parameter(names = {"-validate"}) public boolean validate = false; + + @Parameter(names = {"-validateonly"}) + public boolean validateonly = false; @Parameter(names = "-jp2", hidden = true) public boolean jp2 = false; @@ -45,6 +52,5 @@ public boolean isHelp() { public float quality = 1.0f; @Parameter(names = {"-s","-series"}, description = "specify source series separated by commas") - public List series = Lists.newArrayList(); - -} \ No newline at end of file + public List series = Lists.newArrayList(); +} diff --git a/src/main/java/edu/stonybrook/bmi/hatch/IFD.java b/src/main/java/edu/stonybrook/bmi/hatch/IFD.java new file mode 100644 index 0000000..690060a --- /dev/null +++ b/src/main/java/edu/stonybrook/bmi/hatch/IFD.java @@ -0,0 +1,1021 @@ +/* + * #%L + * BSD implementations of Bio-Formats readers and writers + * %% + * Copyright (C) 2005 - 2017 Open Microscopy Environment: + * - Board of Regents of the University of Wisconsin-Madison + * - Glencoe Software, Inc. + * - University of Dundee + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package edu.stonybrook.bmi.hatch; + +import java.io.IOException; +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.HashMap; + +import loci.common.DebugTools; +import loci.formats.FormatException; +import loci.formats.FormatTools; +import loci.formats.tiff.OnDemandLongArray; +import loci.formats.tiff.PhotoInterp; +import loci.formats.tiff.TiffIFDEntry; +import loci.formats.tiff.TiffRational; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Data structure for working with TIFF Image File Directories (IFDs). + * + * @author Curtis Rueden ctrueden at wisc.edu + * @author Eric Kjellman egkjellman at wisc.edu + * @author Melissa Linkert melissa at glencoesoftware.com + * @author Chris Allan callan at blackcat.ca + */ +public class IFD extends HashMap { + + // -- Constants -- + + private static final Logger LOGGER = LoggerFactory.getLogger(IFD.class); + + // non-IFD tags (for internal use) + public static final int LITTLE_ENDIAN = 0; + public static final int BIG_TIFF = 1; + public static final int REUSE = 3; + + // IFD tags + public static final int NEW_SUBFILE_TYPE = 254; + public static final int SUBFILE_TYPE = 255; + public static final int IMAGE_WIDTH = 256; + public static final int IMAGE_LENGTH = 257; + public static final int BITS_PER_SAMPLE = 258; + public static final int COMPRESSION = 259; + public static final int PHOTOMETRIC_INTERPRETATION = 262; + public static final int THRESHHOLDING = 263; + public static final int CELL_WIDTH = 264; + public static final int CELL_LENGTH = 265; + public static final int FILL_ORDER = 266; + public static final int DOCUMENT_NAME = 269; + public static final int IMAGE_DESCRIPTION = 270; + public static final int MAKE = 271; + public static final int MODEL = 272; + public static final int STRIP_OFFSETS = 273; + public static final int ORIENTATION = 274; + public static final int SAMPLES_PER_PIXEL = 277; + public static final int ROWS_PER_STRIP = 278; + public static final int STRIP_BYTE_COUNTS = 279; + public static final int MIN_SAMPLE_VALUE = 280; + public static final int MAX_SAMPLE_VALUE = 281; + public static final int X_RESOLUTION = 282; + public static final int Y_RESOLUTION = 283; + public static final int PLANAR_CONFIGURATION = 284; + public static final int PAGE_NAME = 285; + public static final int X_POSITION = 286; + public static final int Y_POSITION = 287; + public static final int FREE_OFFSETS = 288; + public static final int FREE_BYTE_COUNTS = 289; + public static final int GRAY_RESPONSE_UNIT = 290; + public static final int GRAY_RESPONSE_CURVE = 291; + public static final int T4_OPTIONS = 292; + public static final int T6_OPTIONS = 293; + public static final int RESOLUTION_UNIT = 296; + public static final int PAGE_NUMBER = 297; + public static final int TRANSFER_FUNCTION = 301; + public static final int SOFTWARE = 305; + public static final int DATE_TIME = 306; + public static final int ARTIST = 315; + public static final int HOST_COMPUTER = 316; + public static final int PREDICTOR = 317; + public static final int WHITE_POINT = 318; + public static final int PRIMARY_CHROMATICITIES = 319; + public static final int COLOR_MAP = 320; + public static final int HALFTONE_HINTS = 321; + public static final int TILE_WIDTH = 322; + public static final int TILE_LENGTH = 323; + public static final int TILE_OFFSETS = 324; + public static final int TILE_BYTE_COUNTS = 325; + public static final int SUB_IFD = 330; + public static final int INK_SET = 332; + public static final int INK_NAMES = 333; + public static final int NUMBER_OF_INKS = 334; + public static final int DOT_RANGE = 336; + public static final int TARGET_PRINTER = 337; + public static final int EXTRA_SAMPLES = 338; + public static final int SAMPLE_FORMAT = 339; + public static final int S_MIN_SAMPLE_VALUE = 340; + public static final int S_MAX_SAMPLE_VALUE = 341; + public static final int TRANSFER_RANGE = 342; + public static final int JPEG_TABLES = 347; + public static final int JPEG_PROC = 512; + public static final int JPEG_INTERCHANGE_FORMAT = 513; + public static final int JPEG_INTERCHANGE_FORMAT_LENGTH = 514; + public static final int JPEG_RESTART_INTERVAL = 515; + public static final int JPEG_LOSSLESS_PREDICTORS = 517; + public static final int JPEG_POINT_TRANSFORMS = 518; + public static final int JPEG_Q_TABLES = 519; + public static final int JPEG_DC_TABLES = 520; + public static final int JPEG_AC_TABLES = 521; + public static final int Y_CB_CR_COEFFICIENTS = 529; + public static final int Y_CB_CR_SUB_SAMPLING = 530; + public static final int Y_CB_CR_POSITIONING = 531; + public static final int REFERENCE_BLACK_WHITE = 532; + public static final int COPYRIGHT = 33432; + public static final int EXIF = 34665; + + /** EXIF tags. */ + public static final int EXPOSURE_TIME = 33434; + public static final int F_NUMBER = 33437; + public static final int EXPOSURE_PROGRAM = 34850; + public static final int SPECTRAL_SENSITIVITY = 34852; + public static final int ISO_SPEED_RATINGS = 34855; + public static final int OECF = 34856; + public static final int EXIF_VERSION = 36864; + public static final int DATE_TIME_ORIGINAL = 36867; + public static final int DATE_TIME_DIGITIZED = 36868; + public static final int COMPONENTS_CONFIGURATION = 37121; + public static final int COMPRESSED_BITS_PER_PIXEL = 37122; + public static final int SHUTTER_SPEED_VALUE = 37377; + public static final int APERTURE_VALUE = 37378; + public static final int BRIGHTNESS_VALUE = 37379; + public static final int EXPOSURE_BIAS_VALUE = 37380; + public static final int MAX_APERTURE_VALUE = 37381; + public static final int SUBJECT_DISTANCE = 37382; + public static final int METERING_MODE = 37383; + public static final int LIGHT_SOURCE = 37384; + public static final int FLASH = 37385; + public static final int FOCAL_LENGTH = 37386; + public static final int MAKER_NOTE = 37500; + public static final int USER_COMMENT = 37510; + public static final int SUB_SEC_TIME = 37520; + public static final int SUB_SEC_TIME_ORIGINAL = 37521; + public static final int SUB_SEC_TIME_DIGITIZED = 37522; + public static final int FLASH_PIX_VERSION = 40960; + public static final int COLOR_SPACE = 40961; + public static final int PIXEL_X_DIMENSION = 40962; + public static final int PIXEL_Y_DIMENSION = 40963; + public static final int RELATED_SOUND_FILE = 40964; + public static final int FLASH_ENERGY = 41483; + public static final int SPATIAL_FREQUENCY_RESPONSE = 41484; + public static final int FOCAL_PLANE_X_RESOLUTION = 41486; + public static final int FOCAL_PLANE_Y_RESOLUTION = 41487; + public static final int FOCAL_PLANE_RESOLUTION_UNIT = 41488; + public static final int SUBJECT_LOCATION = 41492; + public static final int EXPOSURE_INDEX = 41493; + public static final int SENSING_METHOD = 41495; + public static final int FILE_SOURCE = 41728; + public static final int SCENE_TYPE = 41729; + public static final int CFA_PATTERN = 41730; + public static final int CUSTOM_RENDERED = 41985; + public static final int EXPOSURE_MODE = 41986; + public static final int WHITE_BALANCE = 41987; + public static final int DIGITAL_ZOOM_RATIO = 41988; + public static final int FOCAL_LENGTH_35MM_FILM = 41989; + public static final int SCENE_CAPTURE_TYPE = 41990; + public static final int GAIN_CONTROL = 41991; + public static final int CONTRAST = 41992; + public static final int SATURATION = 41993; + public static final int SHARPNESS = 41994; + public static final int SUBJECT_DISTANCE_RANGE = 41996; + + // -- Constructors -- + + public IFD() { + super(); + } + + public IFD(IFD ifd) { + super(ifd); + } + + // -- Tag retrieval methods -- + + /** Gets whether this is a BigTIFF IFD. */ + public boolean isBigTiff() throws FormatException { + return ((Boolean) getIFDValue(BIG_TIFF, Boolean.class)).booleanValue(); + } + + /** Gets whether the TIFF information in this IFD is little-endian. */ + public boolean isLittleEndian() throws FormatException { + return ((Boolean) getIFDValue(LITTLE_ENDIAN, Boolean.class)).booleanValue(); + } + + /** Gets the given directory entry value from this IFD. */ + public Object getIFDValue(int tag) { + return get(Integer.valueOf(tag)); + } + + /** + * Gets the given directory entry value from this IFD, + * performing some error checking. + */ + public Object getIFDValue(int tag, Class checkClass) throws FormatException { + Object value = get(Integer.valueOf(tag)); + if (checkClass != null && value != null && !checkClass.isInstance(value)) { + // wrap object in array of length 1, if appropriate + Class cType = checkClass.getComponentType(); + Object array = + Array.newInstance(cType == null ? value.getClass() : cType, 1); + if (cType == value.getClass()) { + Array.set(array, 0, value); + } + else if (cType == boolean.class && value instanceof Boolean) { + Array.setBoolean(array, 0, ((Boolean) value).booleanValue()); + } + else if (cType == byte.class && value instanceof Byte) { + Array.setByte(array, 0, ((Byte) value).byteValue()); + } + else if (cType == char.class && value instanceof Character) { + Array.setChar(array, 0, ((Character) value).charValue()); + } + else if (cType == double.class && value instanceof Double) { + Array.setDouble(array, 0, ((Double) value).doubleValue()); + } + else if (cType == float.class && value instanceof Float) { + Array.setFloat(array, 0, ((Float) value).floatValue()); + } + else if (cType == int.class && value instanceof Integer) { + Array.setInt(array, 0, ((Integer) value).intValue()); + } + else if (cType == long.class && value instanceof Long) { + Array.setLong(array, 0, ((Long) value).longValue()); + } + else if (cType == short.class && value instanceof Short) { + Array.setShort(array, 0, ((Short) value).shortValue()); + } + else { + try { + if (checkClass.equals(String.class)) { + StringBuilder sb = new StringBuilder(); + int l = Array.getLength(value); + for (int i = 0; i < l; i++) { + sb.append(Array.get(value, i)); + if (i < l - 1) + sb.append(" "); + } + return sb.toString(); + } else { + value = Array.get(value, 0); + if (checkClass.isInstance(value)) + return value; + } + } + catch (IllegalArgumentException exc) { } + catch (ArrayIndexOutOfBoundsException exc) { + // some files misbehave and report an array size of 1 + // when it is actually 0 + return null; + } + + throw new FormatException(getIFDTagName(tag) + + " directory entry is the wrong type (got " + + value.getClass().getName() + ", expected " + checkClass.getName()); + } + + return array; + } + return value; + } + + /** + * Gets the given directory entry value in long format from this IFD, + * performing some error checking. + */ + public long getIFDLongValue(int tag, long defaultValue) throws FormatException + { + long value = defaultValue; + Number number = (Number) getIFDValue(tag, Number.class); + if (number != null) value = number.longValue(); + return value; + } + + /** + * Gets the given directory entry value in int format from this IFD, + * or -1 if the given directory does not exist. + */ + public int getIFDIntValue(int tag) { + int value = -1; + try { + value = getIFDIntValue(tag, -1); + } + catch (FormatException exc) { } + return value; + } + + /** + * Gets the given directory entry value in int format from this IFD, + * performing some error checking. + */ + public int getIFDIntValue(int tag, int defaultValue) throws FormatException { + int value = defaultValue; + Number number = (Number) getIFDValue(tag, Number.class); + if (number != null) value = number.intValue(); + return value; + } + + /** + * Gets the given directory entry value in rational format from this IFD, + * performing some error checking. + */ + public TiffRational getIFDRationalValue(int tag) throws FormatException { + return (TiffRational) getIFDValue(tag, TiffRational.class); + } + + /** + * Gets the given directory entry value as a string from this IFD, + * performing some error checking. + */ + public String getIFDStringValue(int tag) throws FormatException { + return (String) getIFDValue(tag, String.class); + } + + /** Gets the given directory entry value as a string (regardless of type). */ + public String getIFDTextValue(int tag) { + String value = null; + Object o = getIFDValue(tag); + if (o instanceof String[]) { + StringBuilder sb = new StringBuilder(); + String[] s = (String[]) o; + for (int i=0; i Integer.MAX_VALUE) { + throw new FormatException("Sorry, ImageWidth > " + Integer.MAX_VALUE + + " is not supported."); + } + return width; + } + + /** + * Retrieves the image's length (TIFF tag ImageLength) from a given TIFF IFD. + * @return the image's length. + * @throws FormatException if there is a problem parsing the IFD metadata. + */ + public long getImageLength() throws FormatException { + long length = getIFDLongValue(IMAGE_LENGTH, 0); + if (length > Integer.MAX_VALUE) { + throw new FormatException("Sorry, ImageLength > " + Integer.MAX_VALUE + + " is not supported."); + } + return length; + } + + /** + * Retrieves the image's bits per sample (TIFF tag BitsPerSample) from a given + * TIFF IFD. + * @return the image's bits per sample. The length of the array is equal to + * the number of samples per pixel. + * @throws FormatException if there is a problem parsing the IFD metadata. + * @see #getSamplesPerPixel() + */ + public int[] getBitsPerSample() throws FormatException { + int[] bitsPerSample = getIFDIntArray(BITS_PER_SAMPLE); + if (bitsPerSample == null) bitsPerSample = new int[] {1}; + + int samplesPerPixel = getSamplesPerPixel(); + if (bitsPerSample.length < samplesPerPixel) { + LOGGER.debug("BitsPerSample length ({}) does not match " + + "SamplesPerPixel ({})", bitsPerSample.length, samplesPerPixel); + int bits = bitsPerSample[0]; + bitsPerSample = new int[samplesPerPixel]; + Arrays.fill(bitsPerSample, bits); + } + int nSamples = (int) Math.min(bitsPerSample.length, samplesPerPixel); + for (int i=0; iFormatTools.INT8 + *
  • FormatTools.UINT8
  • + *
  • FormatTools.INT16
  • + *
  • FormatTools.UINT16
  • + *
  • FormatTools.INT32
  • + *
  • FormatTools.UINT32
  • + *
  • FormatTools.FLOAT
  • + *
  • FormatTools.DOUBLE
  • + * @throws FormatException if there is a problem parsing the IFD metadata. + * @see #getBitsPerSample() + */ + public int getPixelType() throws FormatException { + int bps = getBitsPerSample()[0]; + int bitFormat = getIFDIntValue(SAMPLE_FORMAT); + + while (bps % 8 != 0) bps++; + if (bps == 24 && bitFormat != 3) bps = 32; + + switch (bps) { + case 16: + if (bitFormat == 3) return FormatTools.FLOAT; + return bitFormat == 2 ? FormatTools.INT16 : FormatTools.UINT16; + case 24: + return FormatTools.FLOAT; + case 64: + if (bitFormat != 3) { + throw new FormatException("64-bit int data not supported"); + } + return FormatTools.DOUBLE; + case 32: + if (bitFormat == 3) return FormatTools.FLOAT; + return bitFormat == 2 ? FormatTools.INT32 : FormatTools.UINT32; + default: + return bitFormat == 2 ? FormatTools.INT8 : FormatTools.UINT8; + } + } + + /** + * Retrieves the image's bytes per sample (derived from tag BitsPerSample) + * from this IFD. + * @return the image's bytes per sample. The length of the array is equal to + * the number of samples per pixel. + * @throws FormatException if there is a problem parsing the IFD metadata. + * @see #getSamplesPerPixel() + * @see #getBitsPerSample() + */ + public int[] getBytesPerSample() throws FormatException { + int[] bitsPerSample = getBitsPerSample(); + int[] bps = new int[bitsPerSample.length]; + for (int i=0; i + *
  • Uncompressed (1)
  • + *
  • CCITT 1D (2)
  • + *
  • Group 3 Fax (3)
  • + *
  • Group 4 Fax (4)
  • + *
  • LZW (5)
  • + *
  • JPEG (6)
  • + *
  • PackBits (32773)
  • + * + * Other proprietary compression types are also possible; + * see {@link TiffCompression} for more details. + * + * @throws FormatException if there is a problem parsing the IFD metadata. + */ + public TiffCompression getCompression() throws FormatException { + return TiffCompression.get(getIFDIntValue( + COMPRESSION, TiffCompression.UNCOMPRESSED.getCode())); + } + + /** + * Retrieves the image's photometric interpretation (TIFF tag + * PhotometricInterpretation) from this IFD. + * @return the image's photometric interpretation. As of TIFF 6.0 this is one + * of: + *
      + *
    • WhiteIsZero (0)
    • + *
    • BlackIsZero (1)
    • + *
    • RGB (2)
    • + *
    • RGB Palette (3)
    • + *
    • Transparency mask (4)
    • + *
    • CMYK (5)
    • + *
    • YbCbCr (6)
    • + *
    • CIELab (8)
    • + *
    + * + * @throws FormatException if there is a problem parsing the IFD metadata. + */ + public PhotoInterp getPhotometricInterpretation() throws FormatException { + Object photo = getIFDValue(PHOTOMETRIC_INTERPRETATION); + if (photo instanceof PhotoInterp) return (PhotoInterp) photo; + if (photo == null && getCompression() == TiffCompression.OLD_JPEG) { + return PhotoInterp.RGB; + } + int pi = photo instanceof Number ? ((Number) photo).intValue() : + ((int[]) photo)[0]; + return PhotoInterp.get(pi); + } + + /** + * Retrieves the image's planar configuration (TIFF tag PlanarConfiguration) + * from this IFD. + * @return the image's planar configuration. As of TIFF 6.0 this is one of: + *
      + *
    • Chunky (1)
    • + *
    • Planar (2)
    • + *
    + * + * @throws FormatException if there is a problem parsing the IFD metadata. + */ + public int getPlanarConfiguration() throws FormatException { + int planarConfig = getIFDIntValue(PLANAR_CONFIGURATION, 1); + if (planarConfig != 1 && planarConfig != 2) { + throw new FormatException("Sorry, PlanarConfiguration (" + planarConfig + + ") not supported."); + } + return planarConfig; + } + + /** + * Retrieves the strip offsets for the image (TIFF tag StripOffsets) from + * this IFD. + * @return the strip offsets for the image. The length of the array is equal + * to the number of strips per image. StripsPerImage = + * floor ((ImageLength + RowsPerStrip - 1) / RowsPerStrip). + * @throws FormatException if there is a problem parsing the IFD metadata. + * @see #getStripByteCounts() + * @see #getRowsPerStrip() + */ + public long[] getStripOffsets() throws FormatException { + int tag = isTiled() ? TILE_OFFSETS : STRIP_OFFSETS; + long[] offsets = null; + OnDemandLongArray compressedOffsets = getOnDemandStripOffsets(); + if (compressedOffsets != null) { + offsets = new long[(int) compressedOffsets.size()]; + try { + for (int q=0; qStripsPerImage = + * floor((ImageLength + RowsPerStrip - 1) / RowsPerStrip). + * @throws FormatException if there is a problem parsing the IFD metadata. + * @see #getStripOffsets() + */ + public long[] getStripByteCounts() throws FormatException { + int tag = isTiled() ? TILE_BYTE_COUNTS : STRIP_BYTE_COUNTS; + long[] byteCounts = getIFDLongArray(tag); + if (isTiled() && byteCounts == null) { + byteCounts = getIFDLongArray(STRIP_BYTE_COUNTS); + } + long imageLength = getImageLength(); + if (byteCounts == null) { + // technically speaking, this shouldn't happen (since TIFF writers are + // required to write the StripByteCounts tag), but we'll support it + // anyway + + // don't rely on RowsPerStrip, since it's likely that if the file doesn't + // have the StripByteCounts tag, it also won't have the RowsPerStrip tag + long[] offsets = getStripOffsets(); + if (offsets == null) return null; + int bytesPerSample = getBytesPerSample()[0]; + long imageWidth = getImageWidth(); + byteCounts = new long[offsets.length]; + int samples = getSamplesPerPixel(); + long imageSize = imageWidth * imageLength * bytesPerSample * + (getPlanarConfiguration() == 2 ? 1 : samples); + long count = imageSize / byteCounts.length; + Arrays.fill(byteCounts, count); + } + + long[] counts = new long[byteCounts.length]; + + if (getCompression() == TiffCompression.LZW && + (!containsKey(ROWS_PER_STRIP) || + ((imageLength % getRowsPerStrip()[0])) != 0)) + { + for (int i=0; i { } diff --git a/src/main/java/edu/stonybrook/bmi/hatch/MinimalTiffReader.java b/src/main/java/edu/stonybrook/bmi/hatch/MinimalTiffReader.java index 4e9dbc3..5ee7678 100644 --- a/src/main/java/edu/stonybrook/bmi/hatch/MinimalTiffReader.java +++ b/src/main/java/edu/stonybrook/bmi/hatch/MinimalTiffReader.java @@ -46,8 +46,6 @@ import loci.formats.codec.JPEG2000CodecOptions; import loci.formats.in.JPEG2000MetadataParser; import loci.formats.meta.MetadataStore; -import loci.formats.tiff.IFD; -import loci.formats.tiff.IFDList; import loci.formats.tiff.PhotoInterp; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -128,6 +126,7 @@ public MinimalTiffReader(String name, String[] suffixes) { // -- MinimalTiffReader API methods -- /** Gets the list of IFDs associated with the current TIFF's image planes. */ + @Override public IFDList getIFDs() { return ifds; } @@ -287,7 +286,7 @@ public byte[] getDecodedTile(byte[] rawbuffer, int no, int row, int col) { ifd = ifds.get(no); } try { - if ((firstIFD.getCompression() == loci.formats.tiff.TiffCompression.JPEG_2000 || firstIFD.getCompression() == loci.formats.tiff.TiffCompression.JPEG_2000_LOSSY) && resolutionLevels != null) { + if ((firstIFD.getCompression() == TiffCompression.JPEG_2000 || firstIFD.getCompression() == TiffCompression.JPEG_2000_LOSSY) && resolutionLevels != null) { if (getCoreIndex() > 0) { ifd = subResolutionIFDs.get(no).get(getCoreIndex() - 1); } @@ -336,7 +335,7 @@ public byte[] getRawBytes(byte[] rawbuffer, int no, int row, int col) { ifd = ifds.get(no); } try { - if ((firstIFD.getCompression() == loci.formats.tiff.TiffCompression.JPEG_2000 || firstIFD.getCompression() == loci.formats.tiff.TiffCompression.JPEG_2000_LOSSY) && resolutionLevels != null) { + if ((firstIFD.getCompression() == TiffCompression.JPEG_2000 || firstIFD.getCompression() == TiffCompression.JPEG_2000_LOSSY) && resolutionLevels != null) { if (getCoreIndex() > 0) { ifd = subResolutionIFDs.get(no).get(getCoreIndex() - 1); } @@ -378,8 +377,8 @@ public byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h) throws F } else { ifd = ifds.get(no); } - if ((firstIFD.getCompression() == loci.formats.tiff.TiffCompression.JPEG_2000 - || firstIFD.getCompression() == loci.formats.tiff.TiffCompression.JPEG_2000_LOSSY) + if ((firstIFD.getCompression() == TiffCompression.JPEG_2000 + || firstIFD.getCompression() == TiffCompression.JPEG_2000_LOSSY) && resolutionLevels != null) { if (getCoreIndex() > 0) { ifd = subResolutionIFDs.get(no).get(getCoreIndex() - 1); @@ -607,8 +606,8 @@ else if (subfileType == 1) { tiffParser.setAssumeEqualStrips(equalStrips); for (IFD ifd : ifds) { - if ((ifd.getCompression() == loci.formats.tiff.TiffCompression.JPEG_2000 - || ifd.getCompression() == loci.formats.tiff.TiffCompression.JPEG_2000_LOSSY) && + if ((ifd.getCompression() == TiffCompression.JPEG_2000 + || ifd.getCompression() == TiffCompression.JPEG_2000_LOSSY) && ifd.getImageWidth() == ifds.get(0).getImageWidth()) { LOGGER.debug("Found IFD with JPEG 2000 compression"); long[] stripOffsets = ifd.getStripOffsets(); diff --git a/src/main/java/edu/stonybrook/bmi/hatch/PositiveInteger.java b/src/main/java/edu/stonybrook/bmi/hatch/PositiveInteger.java new file mode 100644 index 0000000..3ff6bdc --- /dev/null +++ b/src/main/java/edu/stonybrook/bmi/hatch/PositiveInteger.java @@ -0,0 +1,15 @@ +package edu.stonybrook.bmi.hatch; + +import com.beust.jcommander.IParameterValidator; +import com.beust.jcommander.ParameterException; + +public class PositiveInteger implements IParameterValidator { + + @Override + public void validate(String name, String value) throws ParameterException { + int n = Integer.parseInt(value); + if (n < 0) { + throw new ParameterException("Parameter " + name + " should be positive (found " + value +")"); + } + } +} diff --git a/src/main/java/edu/stonybrook/bmi/hatch/SVSReader.java b/src/main/java/edu/stonybrook/bmi/hatch/SVSReader.java index c853984..8b8bf59 100644 --- a/src/main/java/edu/stonybrook/bmi/hatch/SVSReader.java +++ b/src/main/java/edu/stonybrook/bmi/hatch/SVSReader.java @@ -16,7 +16,6 @@ import loci.formats.MetadataTools; import loci.formats.in.MetadataLevel; import loci.formats.meta.MetadataStore; -import loci.formats.tiff.IFD; import loci.formats.tiff.PhotoInterp; import loci.formats.tiff.TiffIFDEntry; import ome.xml.model.primitives.Color; diff --git a/src/main/java/edu/stonybrook/bmi/hatch/TiffCompression.java b/src/main/java/edu/stonybrook/bmi/hatch/TiffCompression.java index 5aaafcb..1d8c5bf 100644 --- a/src/main/java/edu/stonybrook/bmi/hatch/TiffCompression.java +++ b/src/main/java/edu/stonybrook/bmi/hatch/TiffCompression.java @@ -22,7 +22,6 @@ import loci.formats.codec.PackbitsCodec; import loci.formats.codec.PassthroughCodec; import loci.formats.codec.ZlibCodec; -import loci.formats.tiff.IFD; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/edu/stonybrook/bmi/hatch/TiffParser.java b/src/main/java/edu/stonybrook/bmi/hatch/TiffParser.java index 7081cd6..7746f6a 100644 --- a/src/main/java/edu/stonybrook/bmi/hatch/TiffParser.java +++ b/src/main/java/edu/stonybrook/bmi/hatch/TiffParser.java @@ -15,8 +15,6 @@ import loci.formats.FormatException; import loci.formats.ImageTools; import loci.formats.codec.CodecOptions; -import loci.formats.tiff.IFD; -import loci.formats.tiff.IFDList; import loci.formats.tiff.IFDType; import loci.formats.tiff.OnDemandLongArray; import loci.formats.tiff.PhotoInterp; @@ -792,7 +790,7 @@ else if (stripByteCounts[countIndex] < 0 && countIndex > 0) { codecOptions.maxBytes = (int) Math.max(size, tile.length); codecOptions.ycbcr = ifd.getPhotometricInterpretation() == PhotoInterp.Y_CB_CR && ifd.getIFDIntValue(IFD.Y_CB_CR_SUB_SAMPLING) == 1 && ycbcrCorrection; tile = compression.decompress(tile, codecOptions); - loci.formats.tiff.TiffCompression.undifference(tile, ifd); + TiffCompression.undifference(tile, ifd); unpackBytes(buf, 0, tile, ifd); if (planarConfig == 2 && !ifd.isTiled() && ifd.getSamplesPerPixel() > 1) { @@ -879,9 +877,9 @@ public byte[] getSamples(IFD ifd, byte[] buf, int x, int y, long width, long hei LOGGER.trace("reading image data (samplesPerPixel={}; numSamples={})", samplesPerPixel, numSamples); - loci.formats.tiff.TiffCompression compression = ifd.getCompression(); + TiffCompression compression = ifd.getCompression(); - if (compression == loci.formats.tiff.TiffCompression.JPEG_2000 || compression == loci.formats.tiff.TiffCompression.JPEG_2000_LOSSY) { + if (compression == TiffCompression.JPEG_2000 || compression == TiffCompression.JPEG_2000_LOSSY) { codecOptions = compression.getCompressionCodecOptions(ifd, codecOptions); } else codecOptions = compression.getCompressionCodecOptions(ifd); codecOptions.interleaved = true; @@ -935,7 +933,7 @@ public byte[] getSamples(IFD ifd, byte[] buf, int x, int y, long width, long hei if ((effectiveChannels == 1 || planarConfig == 1) && (ifd.getBitsPerSample()[0] % 8) == 0 && photoInterp != PhotoInterp.WHITE_IS_ZERO && photoInterp != PhotoInterp.CMYK && photoInterp != PhotoInterp.Y_CB_CR && - compression == loci.formats.tiff.TiffCompression.UNCOMPRESSED && + compression == TiffCompression.UNCOMPRESSED && ifd.getIFDIntValue(IFD.FILL_ORDER) != 2 && stripOffsets != null && stripByteCounts != null && in.length() >= stripOffsets[0] + stripByteCounts[0] && @@ -1127,10 +1125,10 @@ public static void unpackBytes(byte[] samples, int startIndex, byte[] bytes, { boolean planar = ifd.getPlanarConfiguration() == 2; - loci.formats.tiff.TiffCompression compression = ifd.getCompression(); + TiffCompression compression = ifd.getCompression(); PhotoInterp photoInterp = ifd.getPhotometricInterpretation(); - if (compression == loci.formats.tiff.TiffCompression.JPEG || - compression == loci.formats.tiff.TiffCompression.JPEGXR) + if (compression == TiffCompression.JPEG || + compression == TiffCompression.JPEGXR) { photoInterp = PhotoInterp.RGB; } diff --git a/src/main/java/edu/stonybrook/bmi/hatch/TiffReader.java b/src/main/java/edu/stonybrook/bmi/hatch/TiffReader.java index a13465b..f133bda 100644 --- a/src/main/java/edu/stonybrook/bmi/hatch/TiffReader.java +++ b/src/main/java/edu/stonybrook/bmi/hatch/TiffReader.java @@ -16,9 +16,6 @@ import loci.formats.FormatTools; import loci.formats.in.MetadataLevel; import loci.formats.meta.MetadataStore; -import loci.formats.tiff.IFD; -import loci.formats.tiff.IFDList; -import loci.formats.tiff.TiffCompression; import ome.units.quantity.Time; import ome.units.quantity.Length; @@ -211,6 +208,15 @@ protected void initMetadataStore() throws FormatException { private boolean checkCommentImageJ(String comment) { return comment != null && comment.startsWith("ImageJ="); } + + /** + * + * @return + */ + @Override + public IFDList getIFDs() { + return ifds; + } private boolean checkCommentMetamorph(String comment) { String software = ifds.get(0).getIFDTextValue(IFD.SOFTWARE); diff --git a/src/main/java/edu/stonybrook/bmi/hatch/Validate.java b/src/main/java/edu/stonybrook/bmi/hatch/Validate.java new file mode 100644 index 0000000..8b9266c --- /dev/null +++ b/src/main/java/edu/stonybrook/bmi/hatch/Validate.java @@ -0,0 +1,76 @@ +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 Validate { + + public static javax.imageio.ImageReader getReader(Path path) { + Iterator readers = ImageIO.getImageReadersByFormatName("tif"); + while (readers.hasNext()) { + javax.imageio.ImageReader ir = readers.next(); + if ("com.twelvemonkeys.imageio.plugins.tiff.TIFFImageReader".equals(ir.getClass().getCanonicalName())) { + return ir; + } else { + throw new Error("No Twelve Monkeys."); + } + } + return null; + } + + public static boolean file(Path path) { + Logger LOGGER = Logger.getLogger(Validate.class.getName()); + javax.imageio.ImageReader reader = getReader(path); + if (reader==null) { + throw new IllegalArgumentException("No reader for: " + path); + } + try { + ImageInputStream input = ImageIO.createImageInputStream(path.toFile()); + reader.setInput(input); + } catch (Exception ex) { + LOGGER.log(Level.SEVERE, "{0} ==> {1}", new Object[]{path.toString(), ex.getMessage() }); + return false; + } + try { + if (reader.getWidth(0)==0) { + LOGGER.log(Level.SEVERE, "{0} X dimension is ZERO", path.toString()); + return false; + } + } catch (Exception ex) { + LOGGER.log(Level.SEVERE, "{0} ==> {1}", new Object[]{path.toString(), ex.getMessage() }); + return false; + } + try { + if (reader.getHeight(0)==0) { + LOGGER.log(Level.SEVERE, "{0} Y dimension is ZERO", path.toString()); + return false; + } + } catch (Exception ex) { + LOGGER.log(Level.SEVERE, "{0} ==> {1}", new Object[]{path.toString(), ex.getMessage() }); + return false; + } + try { + int last = reader.getNumImages(true)-1; + if ((reader.getWidth(last)>1024)||(reader.getHeight(last)>1024)) { + LOGGER.log(Level.SEVERE, "{0} smallest scaled image ({1} - {2}x{3}) must be less than 1024x1024", new Object[]{path.toString(), reader.getNumImages(true), reader.getWidth(last), reader.getHeight(last)}); + return false; + } + } catch (IOException ex) { + LOGGER.log(Level.SEVERE, "{0} ==> {1}", new Object[]{path.toString(), ex.getMessage() }); + return false; + } catch (Exception ex) { + LOGGER.log(Level.SEVERE, "{0} ==> {1}", new Object[]{path.toString(), ex.getMessage() }); + return false; + } + return true; + } +} diff --git a/src/main/java/edu/stonybrook/bmi/hatch/Validate2.java b/src/main/java/edu/stonybrook/bmi/hatch/Validate2.java deleted file mode 100644 index aaba115..0000000 --- a/src/main/java/edu/stonybrook/bmi/hatch/Validate2.java +++ /dev/null @@ -1,74 +0,0 @@ -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 28cde46..4e07924 100644 --- a/src/main/java/edu/stonybrook/bmi/hatch/X2TIF.java +++ b/src/main/java/edu/stonybrook/bmi/hatch/X2TIF.java @@ -25,7 +25,6 @@ import loci.formats.meta.MetadataRetrieve; import loci.formats.ome.OMEPyramidStore; import loci.formats.services.OMEXMLService; -import loci.formats.tiff.IFD; import loci.formats.tiff.PhotoInterp; import loci.formats.tiff.TiffRational; import ome.units.UNITS; @@ -33,6 +32,9 @@ import ome.xml.model.enums.DimensionOrder; import ome.xml.model.enums.PixelType; import ome.xml.model.primitives.PositiveInteger; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Map; /** * @@ -41,7 +43,6 @@ public class X2TIF implements AutoCloseable { private FormatReader reader; private final String inputFile; - private final String outputFile; private int tileSizeX; private int tileSizeY; private int height; @@ -61,12 +62,12 @@ public class X2TIF implements AutoCloseable { private byte compression; private static Logger LOGGER; private XMP xmp = null; + private String xcompression = null; 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) { LOGGER.log(Level.INFO,"initializing..."); @@ -91,6 +92,16 @@ public X2TIF(HatchParameters params, String src, String dest, Integer series) { } reader.setMetadataStore(omexml); reader.setId(inputFile); + //reader.getGlobalMetadata().forEach((k,v)->{ + // System.out.println("META : "+k+" "+v); + //}); + //DicomWriter ha; + if (reader instanceof SVSReader rrr) { + if (reader.getGlobalMetadata().containsKey("Compression")) { + xcompression = (String) rrr.getGlobalMetadata().get("Compression"); + xcompression = xcompression.trim(); + } + } if (series==null) { maximage = MaxImage(reader); } else { @@ -117,19 +128,19 @@ public X2TIF(HatchParameters params, String src, String dest, Integer series) { if (params.verbose) { LOGGER.log(Level.INFO, "Image Size : {0}x{1}", new Object[]{width, height}); LOGGER.log(Level.INFO, "Tile size : {0}x{1}", new Object[]{tileSizeX, tileSizeY}); - LOGGER.log(Level.INFO, "Compression : {0}", reader.metadata.get("Compression")); + LOGGER.log(Level.INFO, "Compression : {0}", xcompression); } //String xml = service.getOMEXML(omexml); //Systsm.out.println(xml); try { - if (reader.metadata.get("Compression")==null) { + if (xcompression==null) { if (params.verbose) { LOGGER.log(Level.INFO,"NULL compression specified...trying JPEG...no promises..."); } - } else if ((reader.metadata.get("Compression")=="JPEG-2000")&¶ms.jp2) { + } else if (("JPEG-2000".equals(xcompression))&¶ms.jp2) { - } else if (reader.metadata.get("Compression")!="JPEG") { - throw new Error("Hatch can only convert images that have JPEG compression."); + } else if (!"JPEG".equals(xcompression)) { + throw new Error("Hatch can only convert images that have JPEG compression."); } } catch (Error e){ LOGGER.log(Level.SEVERE, "{0} : {1} {2}", new Object[]{e.getLocalizedMessage(), src, dest}); @@ -168,33 +179,77 @@ public X2TIF(HatchParameters params, String src, String dest, Integer series) { } catch (IOException ex) { LOGGER.log(Level.SEVERE, "IOException : {0} {1}", new Object[]{src, dest}); } - xmp = new XMP(); - xmp.setMagnification(FindMagnification()); - xmp.setSizePerPixelXinMM((1d/((px.doubleValue()/10d)/1000d))/1000d); - xmp.setSizePerPixelYinMM((1d/((py.doubleValue()/10d)/1000d))/1000d); - //writer.setXMP(xmp); + xmp = new XMP(); + FindMeta(xmp); } - private Double FindMagnification() { - var mx = (OMEPyramidStore) reader.getMetadataStore(); - try { - String objectiveID = mx.getObjectiveSettingsID(0); - int instrument = -1; - int objective = -1; - int numberOfInstruments = mx.getInstrumentCount(); - for (int ii = 0; ii < numberOfInstruments; ii++) { - int numObjectives = mx.getObjectiveCount(ii); - for (int oi = 0; 0 < numObjectives; oi++) { - if (objectiveID.equals(mx.getObjectiveID(ii, oi))) { - instrument = ii; - objective = oi; - break; + private void FindMeta(XMP xmp) { + switch (reader) { + case CellSensReader r -> { + OMEPyramidStore mx = (OMEPyramidStore) reader.getMetadataStore(); + try { + String objectiveID = mx.getObjectiveSettingsID(maximage); + int instrument = -1; + int objective = -1; + int numberOfInstruments = mx.getInstrumentCount(); + for (int ii = 0; ii < numberOfInstruments; ii++) { + int numObjectives = mx.getObjectiveCount(ii); + for (int oi = 0; 0 < numObjectives; oi++) { + if (objectiveID.equals(mx.getObjectiveID(ii, oi))) { + instrument = ii; + objective = oi; + break; + } + } + } + BigDecimal t = BigDecimal.valueOf(mx.getPlaneExposureTime(maximage, 0).value(UNITS.MILLISECOND).doubleValue()); + xmp.setExposureTime(t); + if (instrument >= 0 ) { + xmp.setMagnification(BigDecimal.valueOf(mx.getObjectiveNominalMagnification(instrument, objective))); + String manu = mx.getDetectorManufacturer(instrument, objective); + if (manu!=null) { + xmp.setManufacturer(manu); + } + String model = mx.getDetectorModel(instrument, objective); + if (model!=null) { + xmp.setManufacturerDeviceName(model); + } + } + } catch (NullPointerException ex) {} + BigDecimal xpp = BigDecimal.valueOf(px.doubleValue()).divide(BigDecimal.TEN); + BigDecimal ypp = BigDecimal.valueOf(py.doubleValue()).divide(BigDecimal.TEN); + xpp = xpp.divide(BigDecimal.valueOf(1000d)); + ypp = ypp.divide(BigDecimal.valueOf(1000d)); + xpp = BigDecimal.ONE.divide(xpp, 5, RoundingMode.HALF_UP); + ypp = BigDecimal.ONE.divide(ypp, 5, RoundingMode.HALF_UP); + xpp = xpp.multiply(BigDecimal.valueOf(1000d)); + ypp = ypp.multiply(BigDecimal.valueOf(1000d)); + xmp.setSizePerPixelXinMM(xpp); + xmp.setSizePerPixelYinMM(ypp); + } + case SVSReader r -> { + System.out.println("YAY -> "+r.getIFDs().get(0).getIFDValue(IFD.Y_CB_CR_SUB_SAMPLING)); + Map list = r.getSeriesMetadata(); + xmp.setMagnification(BigDecimal.valueOf(Double.parseDouble((String) list.get("AppMag")))); + xmp.setManufacturer((String) list.get("Image Description")); + xmp.setManufacturerDeviceName((String) list.get("ScanScope ID")); + if (list.containsKey("Exposure Time")) { + Double exposuretime = Double.valueOf((String) list.get("Exposure Time")); + if (list.containsKey("Exposure Scale")) { + Double exposurescale = Double.valueOf((String) list.get("Exposure Scale")); + xmp.setExposureTime( BigDecimal.valueOf(exposuretime).multiply(BigDecimal.valueOf( exposurescale ).multiply(BigDecimal.valueOf(1000d)))); } - } + } + BigDecimal mpp = BigDecimal.valueOf(Double.parseDouble((String) list.get("MPP"))).multiply(BigDecimal.valueOf(1000000)); + xmp.setSizePerPixelXinMM(mpp); + xmp.setSizePerPixelYinMM(mpp); + //IFD ifd = r.getCurrentIFD(); + //byte[] iccprofile = (byte[]) ifd.getIFDValue(34675); + //xmp.setICCColorProfile(iccprofile); + //xmp.setICCColorProfile((String) list.get("ICC Profile")); } - return (instrument < 0) ? null : mx.getObjectiveNominalMagnification(instrument, objective); - } catch (NullPointerException ex) {} - return null; + default -> {} + } } private int MaxImage(FormatReader reader) { @@ -288,11 +343,10 @@ public static void Display(byte[] buffer, int from, int to) { } public short[] byte2short(byte[] byteArray) { - short[] shortArray = new short[byteArray.length+1]; - for (int i = 0; i < shortArray.length-1; i++) { + short[] shortArray = new short[byteArray.length]; + for (int i = 0; i < shortArray.length; i++) { shortArray[i] = (short) byteArray[i]; } - shortArray[shortArray.length-1] = 0x00; return shortArray; } @@ -308,7 +362,7 @@ public void readWriteTiles() throws FormatException, IOException { int numtiles = nXTiles*nYTiles; pyramid = new Pyramid(params,nXTiles,nYTiles,tileSizeX,tileSizeY,width,height); byte[] rawbuffer = new byte[TileSize+20]; - IFD ifd = new IFD(); + loci.formats.tiff.IFD ifd = new loci.formats.tiff.IFD(); ifd.put(IFD.RESOLUTION_UNIT, 3); ifd.put(IFD.X_RESOLUTION, px); ifd.put(IFD.Y_RESOLUTION, py); @@ -318,14 +372,16 @@ public void readWriteTiles() throws FormatException, IOException { ifd.put(IFD.IMAGE_LENGTH, (long) height); ifd.put(IFD.TILE_OFFSETS, new long[numtiles]); ifd.put(IFD.TILE_BYTE_COUNTS, new long[numtiles]); + //DicomWriter ha; + //DicomJSONProvider meta = new DicomJSONProvider(); + //meta.readTagSource(inputFile); if (xmp!=null) { ifd.putIFDValue(700, byte2short(xmp.getXMPString().getBytes(StandardCharsets.UTF_8))); } - String comp = (String) reader.metadata.get("Compression"); - if (comp==null) { - comp = "UNKNOWN"; + if (xcompression==null) { + xcompression = "UNKNOWN"; } - switch (comp) { + switch (xcompression) { //case "JPEG-2000": // compression = 2; //ifd.put(IFD.COMPRESSION, 34712); @@ -354,14 +410,32 @@ public void readWriteTiles() throws FormatException, IOException { //ifd.put(IFD.Y_CB_CR_SUB_SAMPLING, new int[] {2, 1}); ifd.put(IFD.Y_CB_CR_SUB_SAMPLING, new int[] {1, 1}); ifd.putIFDValue(IFD.PHOTOMETRIC_INTERPRETATION, PhotoInterp.Y_CB_CR.getCode()); - } else if (inputFile.toLowerCase().endsWith(".svs")) { - IFD x = reader.getIFDs().get(maximage); - ifd.putIFDValue(IFD.PHOTOMETRIC_INTERPRETATION, (int) x.get(IFD.PHOTOMETRIC_INTERPRETATION)); - //ifd.putIFDValue(IFD.PHOTOMETRIC_INTERPRETATION, PhotoInterp.RGB.getCode()); + } else if (reader instanceof SVSReader rrr) { + IFDList list = rrr.getIFDs(); + IFD rah = list.get(0); + if (reader.getIFDs().get(0).containsKey(IFD.Y_CB_CR_SUB_SAMPLING)) { + short[] samp = reader.getIFDs().get(0).getIFDShortArray(IFD.Y_CB_CR_SUB_SAMPLING); + if ((samp[0]==2)&&(samp[1]==2)) { + ifd.putIFDValue(IFD.PHOTOMETRIC_INTERPRETATION, (int) rah.get(IFD.PHOTOMETRIC_INTERPRETATION)); + } else { + ifd.putIFDValue(IFD.Y_CB_CR_SUB_SAMPLING, samp); + } + } else { + ifd.putIFDValue(IFD.PHOTOMETRIC_INTERPRETATION, PhotoInterp.Y_CB_CR.getCode()); + ifd.put(IFD.Y_CB_CR_SUB_SAMPLING, new int[] {1, 1}); + } } else { throw new Error("IFD.PHOTOMETRIC_INTERPRETATION ERROR!!!"); } // JPEG2000Codec codec = new JPEG2000Codec(); + byte method = 0; + if (reader instanceof CellSensReader) { + method = 1; + } else if (reader instanceof SVSReader) { + method = 2; + } else if (reader instanceof TiffReader) { + method = 3; + } for (int y=0; y list = new ArrayList<>(); - DecimalFormat formatter = new DecimalFormat("0.000000000"); - list.add(m.createLiteral(formatter.format(ppsy))); - list.add(m.createLiteral(formatter.format(ppsx))); - root.addProperty(m.createProperty("http://ns.adobe.com/DICOM/PixelSpacing"), m.createList(list.iterator())); + DecimalFormat f = new DecimalFormat("#.##############################"); + f.setDecimalSeparatorAlwaysShown(false); + root.addProperty(m.createProperty("http://ns.adobe.com/DICOM/PixelSpacing"), m.createList(m.createLiteral(f.format(ppsy)), m.createLiteral(f.format(ppsx)))); } m.setNsPrefix("DICOM", "http://ns.adobe.com/DICOM/"); m.setNsPrefix("rdf", RDF.uri); m.setNsPrefix("xmpMM", "http://ns.adobe.com/xap/1.0/mm/"); m.setNsPrefix("xmp", "http://ns.adobe.com/xap/1.0/"); + m.setNsPrefix("xsd", XSD.NS); ByteArrayOutputStream os = new ByteArrayOutputStream(); RDFWriterBuilder builder = RDFWriterBuilder.create(); builder @@ -89,27 +138,65 @@ public byte[] getXMP() { .base(uuid) .output(os); builder.build(); - builder = RDFWriterBuilder.create(); - builder - .source(m) - .lang(Lang.RDFXML) - .base(uuid) - .output(System.out); - builder.build(); return os.toByteArray(); } public String getXMPString() { String packet = new String(getXMP(),StandardCharsets.UTF_8); packet = "\n\n"+packet; - packet = packet+"\n"+(new String(new char[2424]).replace('\0', ' '))+"\n\n"; + packet = packet+"\n"+(new String(new char[2424]).replace('\0', ' '))+"\n"; return packet; } + public static Model getXMP(String base, String xml) { + InputStream xmlis = new ByteArrayInputStream(xml.getBytes()); + Model xmp = ModelFactory.createDefaultModel(); + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(xmlis); + XPathFactory xpathFactory = XPathFactory.newInstance(); + XPath xpath = xpathFactory.newXPath(); + xpath.setNamespaceContext(new NamespaceContext() { + @Override + public String getNamespaceURI(String prefix) { + if (prefix.equals("rdf")) { + return RDF.getURI(); + } + return null; + } + @Override + public Iterator getPrefixes(String val) { return null; } + @Override + public String getPrefix(String uri) { return null; } + }); + String expression = "//rdf:RDF"; // XPath expression to find the rdf:RDF element + XPathExpression expr = xpath.compile(expression); + Node node = (Node) expr.evaluate(doc, XPathConstants.NODE); + if (node != null) { + //System.out.println("Found node: " + node.getNodeName()); + StringWriter writer = new StringWriter(); + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + transformer.transform(new DOMSource(node), new StreamResult(writer)); + //System.out.println(writer.toString()); + byte[] byteArray = writer.toString().getBytes(StandardCharsets.UTF_8); + RDFParserBuilder.create() + .base(base) + .source(new ByteArrayInputStream(byteArray)) + .lang(Lang.RDFXML) + .parse(xmp); + } + } catch (Exception e) { + e.printStackTrace(); + } + return xmp; + } + public static InputStream grab() { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); // Enable namespace awareness + factory.setNamespaceAware(true); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.parse("xmp.xml"); XPathFactory xpathFactory = XPathFactory.newInstance(); @@ -148,34 +235,13 @@ public String getNamespaceURI(String prefix) { return null; } - public static void main(String args[]) throws FileNotFoundException { - Model xml = ModelFactory.createDefaultModel(); - xml.setNsPrefix("DICOM", "http://ns.adobe.com/DICOM/"); - xml.setNsPrefix("rdf", RDF.uri); - xml.setNsPrefix("x", "adobe:ns:meta/"); - xml.setNsPrefix("photoshop", "http://ns.adobe.com/photoshop/1.0/"); - xml.setNsPrefix("xmp", "http://ns.adobe.com/xap/1.0/"); - System.out.println("XXXXXXXXXXXXXXXXXXXXXXXXXXXX========================================================================================="); - RDFDataMgr.read(xml, grab(), Lang.RDFXML); - System.out.println("YYYYYYYYYYYYYYYYYYYYYYYYYY========================================================================================="); - RDFDataMgr.write(System.out, xml, Lang.TURTLE); - System.out.println("========================================================================================="); - RDFWriterBuilder builder = RDFWriterBuilder.create(); - builder - .source(xml) - .lang(Lang.RDFXML) - .base("http://njh.me/") - .output(System.out); - builder.build(); - RDFDataMgr.write(System.out, xml, RDFFormat.TURTLE_PRETTY); - - + public static void main(String args[]) throws FileNotFoundException { System.out.println("YAY !!!!========================================================================================="); XMP xmp = new XMP(); - xmp.setMagnification(40.4); - xmp.setSizePerPixelXinMM(0.43); - xmp.setSizePerPixelYinMM(0.41); - System.out.println(xmp.getXMPString()); - + xmp.setMagnification(BigDecimal.valueOf(40.4)); + xmp.setSizePerPixelXinMM(BigDecimal.valueOf(0.2468d).divide(BigDecimal.valueOf(1000000))); + xmp.setSizePerPixelYinMM(BigDecimal.valueOf(0.2468d).divide(BigDecimal.valueOf(1000000))); + xmp.setExposureTime(BigDecimal.valueOf(0.0041234d)); + System.out.println(xmp.getXMPString()); } } diff --git a/src/main/resources/services.properties b/src/main/resources/services.properties index 8b13789..e69de29 100644 --- a/src/main/resources/services.properties +++ b/src/main/resources/services.properties @@ -1 +0,0 @@ -