Commit 2397e946 authored by Andrey Filippov's avatar Andrey Filippov

More on JP4+Tiff reading with bioformatrs.

parent fb75351e
...@@ -8,4 +8,5 @@ attic ...@@ -8,4 +8,5 @@ attic
*.log *.log
FOCUS-PSF* FOCUS-PSF*
src/main/resources/trained_model src/main/resources/trained_model
bioformats bioformats*.*
\ No newline at end of file metadata*.*
...@@ -88,7 +88,8 @@ ...@@ -88,7 +88,8 @@
<dependency> <dependency>
<groupId>ome</groupId> <groupId>ome</groupId>
<artifactId>loci_tools</artifactId> <artifactId>loci_tools</artifactId>
<version>5.9.0</version> <version>6.1.0-SNAPSHOT</version>
<type>java-source</type>
</dependency> </dependency>
</dependencies> </dependencies>
......
...@@ -31,21 +31,32 @@ import java.awt.image.ColorModel; ...@@ -31,21 +31,32 @@ import java.awt.image.ColorModel;
import java.awt.image.DataBufferInt; import java.awt.image.DataBufferInt;
import java.awt.image.Raster; import java.awt.image.Raster;
import java.awt.image.WritableRaster; import java.awt.image.WritableRaster;
import java.io.DataInputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.Arrays; import java.util.Arrays;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import com.elphel.imagej.readers.ElphelTiffReader;
//import org.apache.log4j.Logger; //import org.apache.log4j.Logger;
...@@ -55,20 +66,15 @@ import ij.WindowManager; ...@@ -55,20 +66,15 @@ import ij.WindowManager;
import ij.io.FileInfo; import ij.io.FileInfo;
import ij.process.FloatProcessor; import ij.process.FloatProcessor;
import ij.process.ImageProcessor; import ij.process.ImageProcessor;
import loci.common.ByteArrayHandle;
import loci.common.Location;
import loci.common.RandomAccessInputStream; import loci.common.RandomAccessInputStream;
import loci.common.services.DependencyException; import loci.common.services.DependencyException;
import loci.common.services.ServiceException; import loci.common.services.ServiceException;
import loci.common.services.ServiceFactory; import loci.common.services.ServiceFactory;
import loci.formats.ClassList; import loci.formats.ClassList;
import loci.formats.CoreMetadata;
import loci.formats.FormatException; import loci.formats.FormatException;
import loci.formats.IFormatReader; import loci.formats.IFormatReader;
import loci.formats.ImageReader; import loci.formats.ImageReader;
//import loci.formats.in.TiffReader;
import loci.formats.meta.IMetadata; import loci.formats.meta.IMetadata;
import loci.formats.meta.MetadataStore;
import loci.formats.services.OMEXMLService; import loci.formats.services.OMEXMLService;
import loci.formats.tiff.IFD; import loci.formats.tiff.IFD;
import loci.formats.tiff.IFDList; import loci.formats.tiff.IFDList;
...@@ -86,6 +92,7 @@ public class EyesisTiff { ...@@ -86,6 +92,7 @@ public class EyesisTiff {
defaultClasses = defaultClasses =
new ClassList<IFormatReader>( new ClassList<IFormatReader>(
EyesisTiff.class.getClassLoader().getResource("readers.txt").getFile(), // @param file Configuration file containing the list of classes. EyesisTiff.class.getClassLoader().getResource("readers.txt").getFile(), // @param file Configuration file containing the list of classes.
// EyesisTiff.class.getClassLoader().getResource("readers1.txt").getFile(), // @param file Configuration file containing the list of classes.
IFormatReader.class, // @param base Base class to which all classes are assignable. IFormatReader.class, // @param base Base class to which all classes are assignable.
null); // @param location Class indicating which package to search for the file. null); // @param location Class indicating which package to search for the file.
} }
...@@ -109,43 +116,14 @@ public class EyesisTiff { ...@@ -109,43 +116,14 @@ public class EyesisTiff {
} }
public ImagePlus readTiff(String path) { public ImagePlus readTiff(String path) {
// TiffReader tiffReader = new TiffReader(); return readTiff(path, "STD_"); // null);
// tiffReader.initFile(path); }
// read in entire file
// TODO: add option to get URL
//https://docs.openmicroscopy.org/bio-formats/5.9.2/developers/in-memory.html
System.out.println("Reading file into memory from disk: "+path);
File inputFile = new File(path);
int fileSize = (int) inputFile.length();
DataInputStream in = null;
try {
in = new DataInputStream(new FileInputStream(inputFile));
} catch (FileNotFoundException e) {
System.out.println("File not found: "+path);
}
byte[] inBytes = new byte[fileSize];
try {
in.readFully(inBytes);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(fileSize + " bytes read.");
// determine input file suffix
String fileName = inputFile.getName();
int dot = fileName.lastIndexOf(".");
String suffix = dot < 0 ? "" : fileName.substring(dot);
public ImagePlus readTiff(String path, String std ) { // std - include non-elphel properties with prefix std
// map input id string to input byte array String inId = null;
String inId = "inBytes" + suffix; inId = path;
Location.mapFile(inId, new ByteArrayHandle(inBytes));
// read data from byte array using ImageReader
System.out.println();
System.out.println("Reading image data from memory...");
//
ServiceFactory factory = null; ServiceFactory factory = null;
try { try {
factory = new ServiceFactory(); factory = new ServiceFactory();
...@@ -153,13 +131,15 @@ public class EyesisTiff { ...@@ -153,13 +131,15 @@ public class EyesisTiff {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();
} }
OMEXMLService service = null;
OMEXMLService service = null;
try { try {
service = factory.getInstance(OMEXMLService.class); service = factory.getInstance(OMEXMLService.class);
} catch (DependencyException e) { } catch (DependencyException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();
} }
IMetadata omeMeta = null; IMetadata omeMeta = null;
try { try {
omeMeta = service.createOMEXMLMetadata(); omeMeta = service.createOMEXMLMetadata();
...@@ -174,8 +154,13 @@ public class EyesisTiff { ...@@ -174,8 +154,13 @@ public class EyesisTiff {
//https://www.javatips.net/api/libbio-formats-java-master/components/scifio/src/loci/formats/in/BaseTiffReader.java //https://www.javatips.net/api/libbio-formats-java-master/components/scifio/src/loci/formats/in/BaseTiffReader.java
//https://docs.openmicroscopy.org/bio-formats/5.7.2/developers/reader-guide.html //https://docs.openmicroscopy.org/bio-formats/5.7.2/developers/reader-guide.html
//https://docs.openmicroscopy.org/bio-formats/5.9.2/developers/java-library.html#file-reading-and-performance //https://docs.openmicroscopy.org/bio-formats/5.9.2/developers/java-library.html#file-reading-and-performance
//defaultClasses = new ClassList<IFormatReader>(IFormatReader.class);
// ClassList<IFormatReader> cl0 = getCustomReaderClasses(); // new ClassList<IFormatReader>(IFormatReader.class);
ClassList<IFormatReader> classList = new ClassList<IFormatReader>(IFormatReader.class);
classList.addClass(com.elphel.imagej.readers.ElphelTiffReader.class);
ImageReader reader = new ImageReader(getCustomReaderClasses()); ImageReader reader = new ImageReader(getCustomReaderClasses());
// BaseTiffReader reader = new BaseTiffReader("Base_tiff_reader","tiff"); // ImageReader reader = new ImageReader(classList);
reader.setMetadataStore(omeMeta); reader.setMetadataStore(omeMeta);
try { try {
reader.setId(inId); reader.setId(inId);
...@@ -187,32 +172,10 @@ public class EyesisTiff { ...@@ -187,32 +172,10 @@ public class EyesisTiff {
//e.printStackTrace(); //e.printStackTrace();
} }
/* read-end */ /* read-end */
int seriesCount = reader.getSeriesCount();
int imageCount = reader.getImageCount();
int sizeX = reader.getSizeX();
int sizeY = reader.getSizeY();
int sizeZ = reader.getSizeZ();
int sizeC = reader.getSizeC();
int sizeT = reader.getSizeT();
int bpp = reader.getBitsPerPixel(); int bpp = reader.getBitsPerPixel();
int pixelType = reader.getPixelType();
java.util.List<CoreMetadata> cmd = reader.getCoreMetadataList();
java.util.Hashtable<java.lang.String,java.lang.Object> gmd = reader.getGlobalMetadata();
MetadataStore mtds = reader.getMetadataStore();
IFormatReader ifr = reader.getReader();
IFormatReader[] ifrs = reader.getReaders(); // all available readers?
// output some details
System.out.println("Series count: " + seriesCount);
System.out.println("First series:");
System.out.println("\tImage count = " + imageCount);
System.out.println("\tSizeX = " + sizeX);
System.out.println("\tSizeY = " + sizeY);
System.out.println("\tSizeZ = " + sizeZ);
System.out.println("\tSizeC = " + sizeC);
System.out.println("\tSizeT = " + sizeT);
System.out.println("\tbppT = " + bpp);
System.out.println("\treader = " + ifr.toString());
System.out.println("\tpixelType = " + pixelType); // 3
byte [] bytes = null; byte [] bytes = null;
ImagePlus imp= null; ImagePlus imp= null;
try { try {
...@@ -245,11 +208,26 @@ public class EyesisTiff { ...@@ -245,11 +208,26 @@ public class EyesisTiff {
ImageProcessor ip=new FloatProcessor(reader.getSizeX(), reader.getSizeY()); ImageProcessor ip=new FloatProcessor(reader.getSizeX(), reader.getSizeY());
ip.setPixels(pixels); ip.setPixels(pixels);
ip.resetMinAndMax(); ip.resetMinAndMax();
// imp = new ImagePlus(fileName, ip); // original jp46 reader had full path as title Hashtable<String, Object> meta_hash = reader.getGlobalMetadata();
imp = new ImagePlus(path, ip); // original jp46 reader had full path as title String prefix = ElphelTiffReader.ELPHEL_PROPERTY_PREFIX;
String imageName = path;
String imageNameKey = prefix+ElphelTiffReader.CONTENT_FILENAME;
if (meta_hash.containsKey(imageNameKey)) {
imageName = meta_hash.get(imageNameKey).toString();
}
imp = new ImagePlus(imageName, ip); // original jp46 reader had full path as title
// first - save all as properties, later - only ELPHEL_*
for (String key:meta_hash.keySet()) {
if (key.startsWith(prefix)) {
imp.setProperty(key.substring(prefix.length()), meta_hash.get(key).toString());
} else if (std != null) {
imp.setProperty(std+(key.replace(" ","_")), meta_hash.get(key).toString());
}
}
encodeProperiesToInfo(imp);
} }
// ImagePlus imp= makeArrays(pixels, width, height, title);
try { try {
reader.close(); reader.close();
} catch (IOException e) { } catch (IOException e) {
...@@ -662,4 +640,58 @@ the type of pixel data in this file getPixelType() ...@@ -662,4 +640,58 @@ the type of pixel data in this file getPixelType()
return sb.toString(); return sb.toString();
} }
// copied from JP46_Reader_camera.java
public ImagePlus encodeProperiesToInfo(ImagePlus imp){
String info="<?xml version=\"1.0\" encoding=\"UTF-8\"?><properties>";
Set<Object> jp4_set;
Properties jp4_prop;
Iterator<Object> itr;
String str;
jp4_prop=imp.getProperties();
if (jp4_prop!=null) {
jp4_set=jp4_prop.keySet();
itr=jp4_set.iterator();
while(itr.hasNext()) {
str = (String) itr.next();
// if (!str.equals("Info")) info+="<"+str+">\""+jp4_prop.getProperty(str)+"\"</"+str+">";
if (!str.equals("Info")) info+="<"+str+">"+jp4_prop.getProperty(str)+"</"+str+">";
}
}
info+="</properties>\n";
imp.setProperty("Info", info);
return imp;
}
public boolean decodeProperiesFromInfo(ImagePlus imp){
if (imp.getProperty("Info")==null) return false;
String xml= (String) imp.getProperty("Info");
DocumentBuilder db=null;
try {
db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
} catch (ParserConfigurationException e) {
return false;
}
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(xml));
Document doc = null;
try {
doc = db.parse(is);
} catch (SAXException e) {
return false;
} catch (IOException e) {
return false;
}
NodeList allNodes=doc.getDocumentElement().getElementsByTagName("*");
for (int i=0;i<allNodes.getLength();i++) {
String name= allNodes.item(i).getNodeName();
String value=allNodes.item(i).getFirstChild().getNodeValue();
imp.setProperty(name, value);
}
return true;
}
} }
...@@ -59,6 +59,8 @@ import org.w3c.dom.NodeList; ...@@ -59,6 +59,8 @@ import org.w3c.dom.NodeList;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import com.elphel.imagej.readers.ImagejJp4Tiff;
import ij.IJ; import ij.IJ;
import ij.ImageJ; import ij.ImageJ;
import ij.ImagePlus; import ij.ImagePlus;
...@@ -75,6 +77,7 @@ import ij.process.ImageConverter; ...@@ -75,6 +77,7 @@ import ij.process.ImageConverter;
import ij.process.ImageProcessor; import ij.process.ImageProcessor;
import ij.text.TextWindow; import ij.text.TextWindow;
import loci.common.RandomAccessInputStream; import loci.common.RandomAccessInputStream;
import loci.formats.FormatException;
import loci.formats.tiff.IFD; import loci.formats.tiff.IFD;
import loci.formats.tiff.IFDList; import loci.formats.tiff.IFDList;
import loci.formats.tiff.TiffParser; import loci.formats.tiff.TiffParser;
...@@ -96,7 +99,7 @@ public class JP46_Reader_camera extends PlugInFrame implements ActionListener { ...@@ -96,7 +99,7 @@ public class JP46_Reader_camera extends PlugInFrame implements ActionListener {
static File dir; static File dir;
public String camera_url = "http://192.168.0.236:8081/"; public String camera_url = "http://192.168.0.36:2323/";
public String camera_img = "bimg"; public String camera_img = "bimg";
public String camera_img_new = "towp/wait/bimg"; // will always wait for the next image (repetitive acquisitions get new images) public String camera_img_new = "towp/wait/bimg"; // will always wait for the next image (repetitive acquisitions get new images)
public String camera_jp46settings = ""; public String camera_jp46settings = "";
...@@ -322,6 +325,10 @@ public class JP46_Reader_camera extends PlugInFrame implements ActionListener { ...@@ -322,6 +325,10 @@ public class JP46_Reader_camera extends PlugInFrame implements ActionListener {
if (demux) showImage=false; if (demux) showImage=false;
double [] xtraExif=new double[1]; // ExposureTime double [] xtraExif=new double[1]; // ExposureTime
double [] lla = null; double [] lla = null;
imp = openJpegOrGif(directory, fileName);
imp.show();
if (imp != null) return imp;
try { try {
imp = openJpegOrGif(directory, fileName); imp = openJpegOrGif(directory, fileName);
if (imp == null) { if (imp == null) {
...@@ -390,7 +397,35 @@ public class JP46_Reader_camera extends PlugInFrame implements ActionListener { ...@@ -390,7 +397,35 @@ public class JP46_Reader_camera extends PlugInFrame implements ActionListener {
String arg, String arg,
boolean scale, boolean scale,
ImagePlus imp_src, ImagePlus imp_src,
boolean showImage) { boolean showImage)
{
// ImagePlus imptiff = (new EyesisTiff()).readTiff(url);
ImagePlus imptiff = null;
try {
imptiff = (new ImagejJp4Tiff()).readTiffJp4(url);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (FormatException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
if (imptiff!=null) {
imptiff.show();
if (imptiff.getType()==ImagePlus.COLOR_RGB) {
checkGrayJpegTo32Bits(imptiff);
}
IJ.showStatus("Converting to 32-bits");
new ImageConverter(imptiff).convertToGray32();
FileInfo fi = imptiff.getFileInfo();
// fi.fileName = name;
// fi.directory = dir;
fi.fileFormat = FileInfo.TIFF;
imptiff.setFileInfo(fi);
return imptiff;
}
long[] ElphelMakerNote=null; long[] ElphelMakerNote=null;
ImagePlus imp = null; ImagePlus imp = null;
boolean reuse_imp=false; boolean reuse_imp=false;
...@@ -399,14 +434,14 @@ public class JP46_Reader_camera extends PlugInFrame implements ActionListener { ...@@ -399,14 +434,14 @@ public class JP46_Reader_camera extends PlugInFrame implements ActionListener {
double [] xtraExif=new double[1]; // ExposureTime double [] xtraExif=new double[1]; // ExposureTime
// System.out.println("imp_src is "+((imp_src!=null)?"not ":"")+"null"); // System.out.println("imp_src is "+((imp_src!=null)?"not ":"")+"null");
try { try {
imp = openJpegOrGifUsingURL(url); imp = openJpegOrGifUsingURL(url);
if (imp == null) { if (imp == null) {
IJ.showMessage("JP46 Reader Error", "Could not open the URL: " + url + " as JPEG/JP46"); IJ.showMessage("JP46 Reader Error", "Could not open the URL: " + url + " as JPEG/JP46");
} else { } else {
if ((imp_src==null) && showImage) { if ((imp_src==null) && showImage) {
// System.out.println("show() 1"); // System.out.println("show() 1");
imp.show(); /* Shows before re-ordering*/ imp.show(); /* Shows before re-ordering*/
} }
/// get rid of the "/towp/wait" if any - there is a chance to re-read the same image /// get rid of the "/towp/wait" if any - there is a chance to re-read the same image
...@@ -429,7 +464,7 @@ public class JP46_Reader_camera extends PlugInFrame implements ActionListener { ...@@ -429,7 +464,7 @@ public class JP46_Reader_camera extends PlugInFrame implements ActionListener {
if (reuse_imp) { if (reuse_imp) {
imp=imp_src; imp=imp_src;
} else if ((imp_src!=null) && showImage) { /* tried to reuse, but wrong size */ } else if ((imp_src!=null) && showImage) { /* tried to reuse, but wrong size */
// System.out.println("show() 2"); // System.out.println("show() 2");
imp.show(); /* never did that before */ imp.show(); /* never did that before */
} }
if ((xtraExif!=null) && !Double.isNaN(xtraExif[0])){ if ((xtraExif!=null) && !Double.isNaN(xtraExif[0])){
...@@ -1042,6 +1077,35 @@ public class JP46_Reader_camera extends PlugInFrame implements ActionListener { ...@@ -1042,6 +1077,35 @@ public class JP46_Reader_camera extends PlugInFrame implements ActionListener {
/* Modified from Opener.java */ /* Modified from Opener.java */
ImagePlus openJpegOrGif(String dir, String name) { ImagePlus openJpegOrGif(String dir, String name) {
// Testing new readers
ImagePlus imptiff = null;
// imptiff = (new EyesisTiff()).readTiff(dir+name);
try {
imptiff = (new ImagejJp4Tiff()).readTiffJp4(dir+name);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (FormatException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
if (imptiff!=null) {
imptiff.show();
if (imptiff.getType()==ImagePlus.COLOR_RGB) {
checkGrayJpegTo32Bits(imptiff);
}
IJ.showStatus("Converting to 32-bits");
new ImageConverter(imptiff).convertToGray32();
FileInfo fi = imptiff.getFileInfo();
fi.fileName = name;
fi.directory = dir;
fi.fileFormat = FileInfo.TIFF;
imptiff.setFileInfo(fi);
return imptiff;
}
ImagePlus imp = null; ImagePlus imp = null;
boolean isTiff = false; boolean isTiff = false;
Image img = Toolkit.getDefaultToolkit().createImage(dir+name); Image img = Toolkit.getDefaultToolkit().createImage(dir+name);
...@@ -1073,8 +1137,8 @@ public class JP46_Reader_camera extends PlugInFrame implements ActionListener { ...@@ -1073,8 +1137,8 @@ public class JP46_Reader_camera extends PlugInFrame implements ActionListener {
fi = imp.getFileInfo(); fi = imp.getFileInfo();
// testing // testing
/*
if ((ofi!=null) && (ofi.directory!=null) && (ofi.fileFormat ==FileInfo.TIFF)) { if ((ofi!=null) && (ofi.directory!=null) && (ofi.fileFormat == FileInfo.TIFF)) {
String path = ofi.directory + ofi.fileName; String path = ofi.directory + ofi.fileName;
EyesisTiff ET = new EyesisTiff(); EyesisTiff ET = new EyesisTiff();
ImagePlus imptiff = ET.readTiff(path); ImagePlus imptiff = ET.readTiff(path);
...@@ -1093,6 +1157,7 @@ public class JP46_Reader_camera extends PlugInFrame implements ActionListener { ...@@ -1093,6 +1157,7 @@ public class JP46_Reader_camera extends PlugInFrame implements ActionListener {
Frame log = WindowManager.getFrame("Log"); Frame log = WindowManager.getFrame("Log");
if (log!=null) log.toFront(); if (log!=null) log.toFront();
} }
*/
} }
......
/**
** -----------------------------------------------------------------------------**
** ElphelJp4Reader.java
**
** loci.format compatible reader for Elphel JP4 files
**
**
** Copyright (C) 2019 Elphel, Inc.
**
** -----------------------------------------------------------------------------**
**
** ElphelTiffReader.java is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
** -----------------------------------------------------------------------------**
**
*/
package com.elphel.imagej.readers;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Date;
import java.util.HashMap;
import java.util.Hashtable;
import org.apache.commons.compress.utils.IOUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import loci.common.ByteArrayHandle;
import loci.common.Location;
import loci.common.RandomAccessInputStream;
import loci.common.services.DependencyException;
import loci.common.services.ServiceException;
import loci.common.services.ServiceFactory;
import loci.formats.FormatException;
import loci.formats.FormatTools;
import loci.formats.in.ImageIOReader;
import loci.formats.in.MetadataLevel;
import loci.formats.services.EXIFService;
//import loci.formats.services.EXIFService;
import ome.xml.meta.MetadataStore;
import ome.xml.model.primitives.Timestamp;
//ElphelTiffReader
public class ElphelJp4Reader extends ImageIOReader{
// -- Constants --
public static final String MAKER_NOTE = "Makernote";
public static final String SUB_SEC_TIME_ORIGINAL = "Sub-Sec Time Original";
public static final String EXPOSURE_TIME = "Exposure Time";
public static final String DATE_TIME_ORIGINAL = "Date/Time Original";
public static final String ELPHEL_PROPERTY_PREFIX = "ELPHEL_";
public static final String CONTENT_FILENAME = "CONTENT_FILENAME";
/** Logger for this class. */
private static final Logger LOGGER =
LoggerFactory.getLogger(ElphelTiffReader.class);
// -- Fields --
private URL url = null; // save here actual URL when reading file to memory
private String content_fileName = null; // from Content-disposition
private boolean file_initialized = false;
private ElphelTiffReader elphelTiffReader = null;
// private ImageReader helperReader;
// -- Constructor --
/** Constructs a new Tiff reader. */
public ElphelJp4Reader() {
super("JP4", new String[] {"jp4"});
// mergeSubIFDs = true; // false;
suffixNecessary = true; // false
suffixSufficient = true; // false;
LOGGER.info("ElphelTiffReader(), after super()");
elphelTiffReader = new ElphelTiffReader();
}
// -- IFormatReader API methods --
/* @see loci.formats.IFormatReader#isThisType(String, boolean) */
@Override
public boolean isThisType(String name, boolean open) {
if (open) {
return super.isThisType(name, open);
}
return checkSuffix(name, getSuffixes());
}
/* @see loci.formats.IFormatReader#isThisType(RandomAccessInputStream) */
@Override
public boolean isThisType(RandomAccessInputStream stream) throws IOException
{
final int blockLen = 4;
if (!FormatTools.validStream(stream, blockLen, false)) return false;
byte[] signature = new byte[blockLen];
stream.read(signature);
if (signature[0] != (byte) 0xff || signature[1] != (byte) 0xd8 ||
signature[2] != (byte) 0xff || (signature[3] & 0xf0) == 0)
{
return false;
}
return true;
}
@Override
public void setId(String id) throws FormatException, IOException { // same as for tiff?
LOGGER.debug("setId("+id+"). before super" );
file_initialized = false;
if (Location.getIdMap().containsKey(id)) {
LOGGER.debug("id '"+id+"' is already mapped" );
content_fileName = null; // id; // maybe set to null to handle externally?
LOGGER.info("Starting initFile() method, read file directly");
super.setId(id);
} else {
// If URL, then read to memory, if normal file - use direct access
url = null;
// String mime = null; // use to select jp4/tiff later? Or to check it is correct
content_fileName = null;
try {
url = new URL(id);
} catch (MalformedURLException e) {
LOGGER.warn("Bad URL: " + id);
}
if (url != null) {
LOGGER.info("Starting initFile() method, read "+ id +" to memory first");
//https://www.rgagnon.com/javadetails/java-0487.html
URLConnection connection = url.openConnection();
String content_disposition = connection.getHeaderField("Content-Disposition");
// raw = "attachment; filename=abc.jpg"
if(content_disposition != null && content_disposition.indexOf("=") != -1) {
content_fileName = content_disposition.split("=")[1]; //getting value after '='
// trim quotes
content_fileName= content_fileName.substring(1, content_fileName.length()-1);
} else {
String mime = connection.getContentType();
int slash = mime.lastIndexOf("/");
String suffix = slash < 0 ? "" : mime.substring(slash+1);
content_fileName = "unknown." + suffix;
}
// currentId = fileName; //???
// LOGGER.info("Mime type = "+mime);
// https://stackoverflow.com/questions/2793150/how-to-use-java-net-urlconnection-to-fire-and-handle-http-requests
//https://stackoverflow.com/questions/2295221/java-net-url-read-stream-to-byte
InputStream is = url.openStream (); //
byte[] inBytes = IOUtils.toByteArray(is);
if (is != null) is.close();
LOGGER.info("Bytes read: "+ inBytes.length);
Location.mapFile(content_fileName, new ByteArrayHandle(inBytes));
HashMap<String,Object> dbg_loc = Location.getIdMap();
super.setId(content_fileName);
} else { // read file normally
content_fileName = id;
LOGGER.info("read file directly");
super.setId(id);
}
}
//getReader
// super.setId(id);
LOGGER.debug("setId("+id+"). after super" );
file_initialized = true;
}
/* @see loci.formats.FormatReader#initFile(String) */
@Override
protected void initFile(String id) throws FormatException, IOException {
LOGGER.debug("initFile("+id+"), currentId="+currentId+", before super" );
try {
super.initFile(id); // fails class_not_found
}
catch (IllegalArgumentException e) {
throw new FormatException(e);
}
LOGGER.debug("initFile("+id+"), currentId="+currentId+", after super" );
// elphelTiffReader.setId(id); //error here - invalid tiff file
// Below needs to be modified - EXIFService does not work with mapFile
MetadataStore store = makeFilterMetadata();
LOGGER.info("Parsing JPEG EXIF data");
HashMap<String, String> tags = null;
try {
EXIFService exif = new ServiceFactory().getInstance(EXIFService.class);
if (exif == null) {
return;
}
exif.initialize(id);
// Set the acquisition date
Date date = exif.getCreationDate();
if (date != null) {
Timestamp timestamp = new Timestamp(new DateTime(date));
store.setImageAcquisitionDate(timestamp, 0);
}
tags = exif.getTags();
for (String tagName : tags.keySet()) {
addGlobalMeta(tagName, tags.get(tagName));
} //{Makernote=105455 131072 127570 300581 171508736 171508736 171508736 171508736 169869312 124780556 1118544 0 0 327779 648 1296, Sub-Sec Time Original=560439, Exposure Time=11167/500000 sec, Date/Time Original=2019:05:13 04:30:26}
}
catch (ServiceException e) {
LOGGER.debug("Could not parse EXIF data", e);
}
catch (DependencyException e) {
LOGGER.debug("Could not parse EXIF data", e);
}
long [] maker_note = null;
double exposure = Double.NaN;
String date_time = null;
if (tags.containsKey(MAKER_NOTE)){
String [] smn = tags.get(MAKER_NOTE).split(" ");
maker_note = new long[smn.length];
for (int i = 0; i < maker_note.length; i++) {
maker_note[i] = Integer.parseInt(smn[i]);
}
}
if (tags.containsKey(EXPOSURE_TIME)){
String [] s = tags.get(EXPOSURE_TIME).split("/");
exposure = 1.0 * Integer.parseInt(s[0]) / Integer.parseInt(s[1].split(" ")[0]);
}
if (tags.containsKey(DATE_TIME_ORIGINAL)){
date_time = tags.get(DATE_TIME_ORIGINAL);
if (tags.containsKey(SUB_SEC_TIME_ORIGINAL)){
date_time += tags.get(SUB_SEC_TIME_ORIGINAL);
}
}
Hashtable<String, String> property_table = ElphelMeta.getMeta(
null, maker_note, exposure, date_time, true );
LOGGER.info("Created elphelMeta table, size="+property_table.size());
for (String key:property_table.keySet()) {
addGlobalMeta(ELPHEL_PROPERTY_PREFIX+key,property_table.get(key));
}
MetadataLevel level = getMetadataOptions().getMetadataLevel();
if (level != MetadataLevel.MINIMUM) {
// Integer[] tags = ifds.get(0).keySet().toArray(new Integer[0]);
// LOGGER.info("initStandardMetadata() - got "+tags.length+" tags");
}
addGlobalMeta(ELPHEL_PROPERTY_PREFIX+CONTENT_FILENAME,content_fileName);
}
/* @see loci.formats.IFormatReader#close(boolean) */
@Override
public void close(boolean fileOnly) throws IOException {
HashMap<String,Object> dbg_loc = Location.getIdMap();
String saveCurrentId = currentId;
currentId = null;
LOGGER.info("close("+fileOnly+") before super");
super.close(fileOnly); // curerent_id == null only during actual close?
LOGGER.info("close("+fileOnly+") after super");
currentId = saveCurrentId;
if ((content_fileName != null) && file_initialized){
Location.mapFile(content_fileName, null);
file_initialized = false;
}
dbg_loc = Location.getIdMap();
if (!fileOnly) {
// companionFile = null;
// description = null;
// calibrationUnit = null;
// physicalSizeZ = null;
// timeIncrement = null;
// xOrigin = null;
// yOrigin = null;
}
}
}
...@@ -29,74 +29,59 @@ import java.util.Hashtable; ...@@ -29,74 +29,59 @@ import java.util.Hashtable;
//import ij.IJ; //import ij.IJ;
import loci.formats.FormatException; import loci.formats.FormatException;
import loci.formats.tiff.IFD;
import loci.formats.tiff.IFDList;
import loci.formats.tiff.TiffParser;
import loci.formats.tiff.TiffRational;
public class ElphelMeta { public class ElphelMeta {
private Hashtable<String, String> property_table = null; // private Hashtable<String, String> property_table = null;
public ElphelMeta (TiffParser tiffParser, boolean scale) throws FormatException, IOException {
IFDList exifIFDs = tiffParser.getExifIFDs(); public static Hashtable<String, String> getMeta (Hashtable<String, String> property_table,
property_table = new Hashtable<String, String> (); long[] maker_note,
long[] maker_note = null; double exposure,
if (exifIFDs.size() > 0) { String date_time,
IFD exifIFD = exifIFDs.get(0); boolean scale) throws FormatException, IOException {
tiffParser.fillInIFD(exifIFD); if (property_table == null) {
if (exifIFD.containsKey(IFD.MAKER_NOTE)) { property_table = new Hashtable<String, String> ();
maker_note = (long[]) exifIFD.get(IFD.MAKER_NOTE);
}
if (exifIFD.containsKey(IFD.EXPOSURE_TIME)) {
Object exp = exifIFD.get(IFD.EXPOSURE_TIME);
if (exp instanceof TiffRational) {
TiffRational texp = (TiffRational) exp;
double d = 1.0*texp.getNumerator()/texp.getDenominator();
property_table.put("EXPOSURE", String.format("%f",d));
}
}
if (exifIFD.containsKey(IFD.DATE_TIME_ORIGINAL)) {
String dt = exifIFD.get(IFD.DATE_TIME_ORIGINAL).toString();
if (exifIFD.containsKey(IFD.SUB_SEC_TIME_ORIGINAL)) {
dt += "."+exifIFD.get(IFD.SUB_SEC_TIME_ORIGINAL).toString();
}
property_table.put("DATE_TIME", dt);
}
} }
// copied from JP4_Reader_cam if (!Double.isNaN(exposure)) {
// Add GPS tags when there will be images to experiment (or while reimplementing JP4 reader) property_table.put("EXPOSURE", String.format("%f",exposure));
double[] gains= new double[4]; }
double[] blacks= new double[4]; if (date_time == null) {
double[] blacks256= new double[4]; property_table.put("DATE_TIME", date_time);
double[] gammas= new double[4]; }
long [] gamma_scales= new long[4]; /* now not used, was scale _after_ gamma is applied, 0x400(default) corresponds to 1.0 */ if (maker_note != null) {
int i; // copied from JP4_Reader_cam
double[][] rgammas=new double[4][]; // Add GPS tags when there will be images to experiment (or while reimplementing JP4 reader)
double min_gain; double[] gains= new double[4];
long WOI_LEFT,WOI_WIDTH,WOI_TOP,WOI_HEIGHT,BAYER_MODE,DCM_HOR,DCM_VERT,BIN_HOR,BIN_VERT; double[] blacks= new double[4];
long COLOR_MODE=0; double[] blacks256= new double[4];
long FLIPH=0; double[] gammas= new double[4];
long FLIPV=0; long [] gamma_scales= new long[4]; /* now not used, was scale _after_ gamma is applied, 0x400(default) corresponds to 1.0 */
long HEIGHT1=0; int i;
long HEIGHT2=0; double[][] rgammas=new double[4][];
long HEIGHT3=0; double min_gain;
long BLANK1=0; long WOI_LEFT,WOI_WIDTH,WOI_TOP,WOI_HEIGHT,BAYER_MODE,DCM_HOR,DCM_VERT,BIN_HOR,BIN_VERT;
long BLANK2=0; long COLOR_MODE=0;
boolean FLIPH1=false; long FLIPH=0;
boolean FLIPV1=false; long FLIPV=0;
boolean FLIPH2=false; long HEIGHT1=0;
boolean FLIPV2=false; long HEIGHT2=0;
boolean FLIPH3=false; long HEIGHT3=0;
boolean FLIPV3=false; long BLANK1=0;
boolean COMPOSITE=false; long BLANK2=0;
boolean PORTRAIT=false; boolean FLIPH1=false;
boolean YTABLEFORC=false; boolean FLIPV1=false;
long QUALITY=0; boolean FLIPH2=false;
long CQUALITY=0; boolean FLIPV2=false;
long CORING_INDEX_Y=0; boolean FLIPH3=false;
long CORING_INDEX_C=0; boolean FLIPV3=false;
double [] satValue={255.0, 255.0, 255.0, 255.0}; boolean COMPOSITE=false;
if (maker_note !=null) { boolean PORTRAIT=false;
boolean YTABLEFORC=false;
long QUALITY=0;
long CQUALITY=0;
long CORING_INDEX_Y=0;
long CORING_INDEX_C=0;
double [] satValue={255.0, 255.0, 255.0, 255.0};
for (i=0;i<4;i++) { /* r,g,gb,b */ for (i=0;i<4;i++) { /* r,g,gb,b */
gains[i]= maker_note[i]/65536.0; gains[i]= maker_note[i]/65536.0;
blacks[i]=(maker_note[i+4]>>24)/256.0; blacks[i]=(maker_note[i+4]>>24)/256.0;
...@@ -193,7 +178,7 @@ public class ElphelMeta { ...@@ -193,7 +178,7 @@ public class ElphelMeta {
property_table.put("FLIPV2",FLIPV2?"1":"0"); property_table.put("FLIPV2",FLIPV2?"1":"0");
property_table.put("FLIPV3",FLIPV3?"1":"0"); property_table.put("FLIPV3",FLIPV3?"1":"0");
} }
// If there are FLIPH, FLIPV - swap gains, gammas, blacks accordingly. later the images will be also flipped // If there are FLIPH, FLIPV - swap gains, gammas, blacks accordingly. later the images will be also flipped
if (FLIPV!=0) { if (FLIPV!=0) {
swapArrayElements (gains, 1, 3); swapArrayElements (gains, 1, 3);
swapArrayElements (gains, 0, 2); swapArrayElements (gains, 0, 2);
...@@ -215,54 +200,57 @@ public class ElphelMeta { ...@@ -215,54 +200,57 @@ public class ElphelMeta {
swapArrayElements (gamma_scales,3, 2); swapArrayElements (gamma_scales,3, 2);
} }
for (i=0;i<4;i++) rgammas[i]=elphel_gamma_calc (gammas[i], blacks[i], gamma_scales[i]); for (i=0;i<4;i++) rgammas[i]=elphel_gamma_calc (gammas[i], blacks[i], gamma_scales[i]);
} else { /**adjusting gains to have the result picture in the range 0..256 */
return; // No MakerNote, return with nothing done min_gain=2.0*gains[0];
} for (i=0;i<4;i++) {
/**adjusting gains to have the result picture in the range 0..256 */ if (min_gain > gains[i]*(1.0-blacks[i])) min_gain = gains[i]*(1.0-blacks[i]);
min_gain=2.0*gains[0]; }
for (i=0;i<4;i++) { property_table.put("GAIN",String.format("%f",min_gain)); // common gain
if (min_gain > gains[i]*(1.0-blacks[i])) min_gain = gains[i]*(1.0-blacks[i]);
}
property_table.put("GAIN",String.format("%f",min_gain)); // common gain
for (i=0;i<4;i++) gains[i]/=min_gain; for (i=0;i<4;i++) gains[i]/=min_gain;
for (i=0;i<4;i++) blacks256[i]=256.0*blacks[i]; for (i=0;i<4;i++) blacks256[i]=256.0*blacks[i];
for (i=0;i<4;i++) { for (i=0;i<4;i++) {
if (maker_note !=null) { if (maker_note !=null) {
if (scale) satValue[i]=((rgammas[i][255])-blacks256[i])/gains[i]; if (scale) satValue[i]=((rgammas[i][255])-blacks256[i])/gains[i];
else satValue[i]=((rgammas[i][255])-blacks256[i]); else satValue[i]=((rgammas[i][255])-blacks256[i]);
} else satValue[i]=255.0; } else satValue[i]=255.0;
property_table.put("saturation_"+i,String.format("%f",satValue[i])); property_table.put("saturation_"+i,String.format("%f",satValue[i]));
}
// swap satValue to match FLIPH,FLIPV again
if (FLIPV!=0) {
swapArrayElements (satValue, 1, 3);
swapArrayElements (satValue, 0, 2);
}
if (FLIPH!=0) {
swapArrayElements (satValue, 1, 0);
swapArrayElements (satValue, 3, 2);
}
for (i=0;i<4;i++) {
property_table.put("saturation_"+i,String.format("%f",satValue[i]));
}
} }
// swap satValue to match FLIPH,FLIPV again
if (FLIPV!=0) {
swapArrayElements (satValue, 1, 3);
swapArrayElements (satValue, 0, 2);
}
if (FLIPH!=0) {
swapArrayElements (satValue, 1, 0);
swapArrayElements (satValue, 3, 2);
}
for (i=0;i<4;i++) {
property_table.put("saturation_"+i,String.format("%f",satValue[i]));
}
}
public Hashtable<String, String> getPropertyTable(){
return property_table; return property_table;
} }
// -- Helper methods --
void swapArrayElements (double[]arr,int i, int j) {
// public Hashtable<String, String> getPropertyTable(){
// return property_table;
// }
// -- Helper methods --
static void swapArrayElements (double[]arr,int i, int j) {
double tmp=arr[i]; double tmp=arr[i];
arr[i]=arr[j]; arr[i]=arr[j];
arr[j]=tmp; arr[j]=tmp;
} }
void swapArrayElements (long[]arr,int i, int j) { static void swapArrayElements (long[]arr,int i, int j) {
long tmp=arr[i]; long tmp=arr[i];
arr[i]=arr[j]; arr[i]=arr[j];
arr[j]=tmp; arr[j]=tmp;
...@@ -270,7 +258,7 @@ public class ElphelMeta { ...@@ -270,7 +258,7 @@ public class ElphelMeta {
/* reverses gamma calculations in the camera /* reverses gamma calculations in the camera
returns double[] table , in the range 0.0..255.996 returns double[] table , in the range 0.0..255.996
*/ */
double [] elphel_gamma_calc (double gamma, double black, long gamma_scale) { static double [] elphel_gamma_calc (double gamma, double black, long gamma_scale) {
int i; int i;
double x, black256 ,k; double x, black256 ,k;
int[] gtable = new int[257]; int[] gtable = new int[257];
......
...@@ -27,15 +27,25 @@ ...@@ -27,15 +27,25 @@
*/ */
package com.elphel.imagej.readers; package com.elphel.imagej.readers;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Hashtable; import java.util.Hashtable;
import org.apache.commons.compress.utils.IOUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import loci.common.ByteArrayHandle;
import loci.common.Location;
import loci.formats.FormatException; import loci.formats.FormatException;
import loci.formats.in.MetadataLevel; import loci.formats.in.MetadataLevel;
import loci.formats.in.TiffReader; import loci.formats.in.TiffReader;
import loci.formats.meta.MetadataStore; import loci.formats.meta.MetadataStore;
import loci.formats.tiff.IFD;
import loci.formats.tiff.IFDList;
import loci.formats.tiff.TiffRational;
/* /*
// non-IFD tags (for internal use) // non-IFD tags (for internal use)
...@@ -72,7 +82,9 @@ IFD.java public static final int MAKER_NOTE = 37500; ...@@ -72,7 +82,9 @@ IFD.java public static final int MAKER_NOTE = 37500;
public class ElphelTiffReader extends TiffReader{ // BaseTiffReader { public class ElphelTiffReader extends TiffReader{ // BaseTiffReader {
// -- Constants -- // -- Constants --
public static final String ELPHEL_PROPERTY_PREFIX = "ELPHEL_"; public static final String ELPHEL_PROPERTY_PREFIX = "ELPHEL_";
public static final String CONTENT_FILENAME = "CONTENT_FILENAME";
/** Merge SubIFDs into the main IFD list. */ /** Merge SubIFDs into the main IFD list. */
// protected transient boolean mergeSubIFDs = true; // false; // protected transient boolean mergeSubIFDs = true; // false;
...@@ -89,7 +101,10 @@ public class ElphelTiffReader extends TiffReader{ // BaseTiffReader { ...@@ -89,7 +101,10 @@ public class ElphelTiffReader extends TiffReader{ // BaseTiffReader {
// public static final int IMAGEJ_TAG = 50839; // public static final int IMAGEJ_TAG = 50839;
// -- Fields -- // -- Fields --
// private String inId = null; // to close Location.mapFile
private URL url = null; // save here actual URL when reading file to memory
private String content_fileName = null; // from Content-disposition
private boolean file_initialized = false;
// private String companionFile; // private String companionFile;
// private String description; // private String description;
// private String calibrationUnit; // private String calibrationUnit;
...@@ -102,12 +117,75 @@ public class ElphelTiffReader extends TiffReader{ // BaseTiffReader { ...@@ -102,12 +117,75 @@ public class ElphelTiffReader extends TiffReader{ // BaseTiffReader {
/** Constructs a new Tiff reader. */ /** Constructs a new Tiff reader. */
public ElphelTiffReader() { public ElphelTiffReader() {
super(); // "Tagged Image File Format", ELPHEL_TIFF_SUFFIXES); // See if we can use TiffReader without its parent super(); // "Tagged Image File Format", ELPHEL_TIFF_SUFFIXES); // See if we can use TiffReader without its parent
mergeSubIFDs = true; // false; /// mergeSubIFDs = true; // false;
LOGGER.info("ElphelTiffReader(), after supper(), mergeSubIFDs = true;"); LOGGER.info("ElphelTiffReader(), after supper(), mergeSubIFDs = true;");
} }
// -- IFormatReader API methods -- // -- IFormatReader API methods --
@Override
public void setId(String id) throws FormatException, IOException {
LOGGER.debug("setId("+id+"). before super" );
file_initialized = false;
if (Location.getIdMap().containsKey(id)) {
LOGGER.debug("id '"+id+"' is already mapped" );
content_fileName = null; // id; // maybe set to null to handle externally?
LOGGER.info("Starting initFile() method, read file directly");
super.setId(id);
} else {
// If URL, then read to memory, if normal file - use direct access
// String cameraURL = "http://192.168.0.36:2323/img"; // for testing
// just testing - ignore file name and use camera URL
// id = cameraURL;
url = null;
// String mime = null; // use to select jp4/tiff later? Or to check it is correct
content_fileName = null;
try {
url = new URL(id);
} catch (MalformedURLException e) {
LOGGER.warn("Bad URL: " + id);
}
if (url != null) {
LOGGER.info("Starting initFile() method, read "+ id +" to memory first");
//https://www.rgagnon.com/javadetails/java-0487.html
URLConnection connection = url.openConnection();
String content_disposition = connection.getHeaderField("Content-Disposition");
// raw = "attachment; filename=abc.jpg"
if(content_disposition != null && content_disposition.indexOf("=") != -1) {
content_fileName = content_disposition.split("=")[1]; //getting value after '='
// trim quotes
content_fileName= content_fileName.substring(1, content_fileName.length()-1);
} else {
String mime = connection.getContentType();
int slash = mime.lastIndexOf("/");
String suffix = slash < 0 ? "" : mime.substring(slash+1);
content_fileName = "unknown." + suffix;
}
// currentId = fileName; //???
// LOGGER.info("Mime type = "+mime);
// https://stackoverflow.com/questions/2793150/how-to-use-java-net-urlconnection-to-fire-and-handle-http-requests
//https://stackoverflow.com/questions/2295221/java-net-url-read-stream-to-byte
InputStream is = url.openStream (); //
byte[] inBytes = IOUtils.toByteArray(is);
if (is != null) is.close();
LOGGER.info("Bytes read: "+ inBytes.length);
Location.mapFile(content_fileName, new ByteArrayHandle(inBytes));
// HashMap<String,Object> dbg_loc = Location.getIdMap();
super.setId(content_fileName);
} else { // read file normally
content_fileName = id;
LOGGER.info("read file directly");
super.setId(id);
}
}
//getReader
// super.setId(id);
LOGGER.debug("setId("+id+"). after super" );
file_initialized = true;
}
@Override @Override
protected void initFile(java.lang.String id) protected void initFile(java.lang.String id)
...@@ -117,6 +195,8 @@ public class ElphelTiffReader extends TiffReader{ // BaseTiffReader { ...@@ -117,6 +195,8 @@ public class ElphelTiffReader extends TiffReader{ // BaseTiffReader {
LOGGER.info("Starting initFile() method"); LOGGER.info("Starting initFile() method");
super.initFile(id); super.initFile(id);
LOGGER.info("Ending initFile() method"); LOGGER.info("Ending initFile() method");
} }
/* @see loci.formats.IFormatReader#getSeriesUsedFiles(boolean) */ /* @see loci.formats.IFormatReader#getSeriesUsedFiles(boolean) */
...@@ -134,8 +214,14 @@ public class ElphelTiffReader extends TiffReader{ // BaseTiffReader { ...@@ -134,8 +214,14 @@ public class ElphelTiffReader extends TiffReader{ // BaseTiffReader {
@Override @Override
public void close(boolean fileOnly) throws IOException { public void close(boolean fileOnly) throws IOException {
LOGGER.info("close("+fileOnly+") before super"); LOGGER.info("close("+fileOnly+") before super");
super.close(fileOnly); super.close(fileOnly); // curerent_id == null only during actual close?
LOGGER.info("close("+fileOnly+") after super"); LOGGER.info("close("+fileOnly+") after super");
if ((content_fileName != null) && file_initialized){
Location.mapFile(content_fileName, null);
file_initialized = false;
}
// HashMap<String,Object> dbg_loc = Location.getIdMap();
if (!fileOnly) { if (!fileOnly) {
// companionFile = null; // companionFile = null;
// description = null; // description = null;
...@@ -156,8 +242,35 @@ public class ElphelTiffReader extends TiffReader{ // BaseTiffReader { ...@@ -156,8 +242,35 @@ public class ElphelTiffReader extends TiffReader{ // BaseTiffReader {
super.initStandardMetadata(); super.initStandardMetadata();
String comment = ifds.get(0).getComment(); // IMAGE_DESCRIPTION String comment = ifds.get(0).getComment(); // IMAGE_DESCRIPTION
LOGGER.info("initStandardMetadata() - after super()"); LOGGER.info("initStandardMetadata() - after super()");
ElphelMeta elphelMeta = new ElphelMeta(tiffParser, true); long[] maker_note = null;
Hashtable<String, String> property_table = elphelMeta.getPropertyTable(); double exposure = Double.NaN;
String date_time = null;
IFDList exifIFDs = tiffParser.getExifIFDs();
if (exifIFDs.size() > 0) {
IFD exifIFD = exifIFDs.get(0);
tiffParser.fillInIFD(exifIFD);
if (exifIFD.containsKey(IFD.MAKER_NOTE)) {
maker_note = (long[]) exifIFD.get(IFD.MAKER_NOTE);
}
if (exifIFD.containsKey(IFD.EXPOSURE_TIME)) {
Object exp = exifIFD.get(IFD.EXPOSURE_TIME);
if (exp instanceof TiffRational) {
TiffRational texp = (TiffRational) exp;
exposure = 1.0*texp.getNumerator()/texp.getDenominator();
}
}
if (exifIFD.containsKey(IFD.DATE_TIME_ORIGINAL)) {
date_time = exifIFD.get(IFD.DATE_TIME_ORIGINAL).toString();
if (exifIFD.containsKey(IFD.SUB_SEC_TIME_ORIGINAL)) {
date_time += "."+exifIFD.get(IFD.SUB_SEC_TIME_ORIGINAL).toString();
}
}
}
Hashtable<String, String> property_table = ElphelMeta.getMeta(
null, maker_note, exposure, date_time, true );
LOGGER.info("Created elphelMeta table, size="+property_table.size()); LOGGER.info("Created elphelMeta table, size="+property_table.size());
for (String key:property_table.keySet()) { for (String key:property_table.keySet()) {
addGlobalMeta(ELPHEL_PROPERTY_PREFIX+key,property_table.get(key)); addGlobalMeta(ELPHEL_PROPERTY_PREFIX+key,property_table.get(key));
...@@ -167,6 +280,7 @@ public class ElphelTiffReader extends TiffReader{ // BaseTiffReader { ...@@ -167,6 +280,7 @@ public class ElphelTiffReader extends TiffReader{ // BaseTiffReader {
Integer[] tags = ifds.get(0).keySet().toArray(new Integer[0]); Integer[] tags = ifds.get(0).keySet().toArray(new Integer[0]);
LOGGER.info("initStandardMetadata() - got "+tags.length+" tags"); LOGGER.info("initStandardMetadata() - got "+tags.length+" tags");
} }
addGlobalMeta(ELPHEL_PROPERTY_PREFIX+CONTENT_FILENAME,content_fileName);
// check for ImageJ-style TIFF comment // check for ImageJ-style TIFF comment
boolean ij = checkCommentImageJ(comment); boolean ij = checkCommentImageJ(comment);
// if (ij) parseCommentImageJ(comment); // if (ij) parseCommentImageJ(comment);
...@@ -220,6 +334,22 @@ public class ElphelTiffReader extends TiffReader{ // BaseTiffReader { ...@@ -220,6 +334,22 @@ public class ElphelTiffReader extends TiffReader{ // BaseTiffReader {
populateMetadataStoreImageJ(store); populateMetadataStoreImageJ(store);
} }
/**
* @see loci.formats.IFormatReader#openBytes(int, byte[], int, int, int, int)
*/
@Override
public byte[] openBytes(int no, byte[] buf, int x, int y, int w, int h)
throws FormatException, IOException
{
LOGGER.info("openBytes() - before super()");
super.openBytes(no, buf, x, y, w, h);
LOGGER.info("openBytes() - after super()");
return buf;
}
// -- Helper methods -- // -- Helper methods --
// Convert to Elphel-specific parameters // Convert to Elphel-specific parameters
......
/**
** -----------------------------------------------------------------------------**
** ImagejJp4Tiff.java
**
** Uses loci.format compatible readers for Elphel 8/16 bpp monochrome Tiff and
** JP4 files to read/parse camera files in a single url read operation by
** buffering camera data with Location.mapFile()
**
**
** Copyright (C) 2019 Elphel, Inc.
**
** -----------------------------------------------------------------------------**
**
** ImagejJp4Tiff.java is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
** -----------------------------------------------------------------------------**
**
*/
package com.elphel.imagej.readers;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import org.apache.commons.compress.utils.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ij.ImagePlus;
import ij.process.FloatProcessor;
import ij.process.ImageProcessor;
import loci.common.ByteArrayHandle;
import loci.common.Location;
import loci.common.services.DependencyException;
import loci.common.services.ServiceException;
import loci.common.services.ServiceFactory;
import loci.formats.ClassList;
import loci.formats.FormatException;
import loci.formats.IFormatReader;
import loci.formats.ImageReader;
import loci.formats.meta.IMetadata;
import loci.formats.services.OMEXMLService;
public class ImagejJp4Tiff {
// -- Constants --
private static final Logger LOGGER = LoggerFactory.getLogger(ClassList.class);
private static final boolean BYPASS_SERVICES = true;
// -- Fields --
private ImageReader reader = null;
private String content_fileName = "undefined"; // from Content-disposition
private URL url = null; // save here actual URL when reading file to memory
IMetadata omeMeta = null;
// -- Constructor --
public ImagejJp4Tiff() {
// Bypass readers.txt
ClassList<IFormatReader> classList = new ClassList<IFormatReader>(IFormatReader.class);
// classList.addClass(com.elphel.imagej.readers.ElphelTiffJp4Reader.class);
classList.addClass(com.elphel.imagej.readers.ElphelJp4Reader.class);
classList.addClass(com.elphel.imagej.readers.ElphelTiffReader.class);
if (!BYPASS_SERVICES) {
ServiceFactory factory = null;
try {
factory = new ServiceFactory();
} catch (DependencyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
OMEXMLService service = null;
try {
service = factory.getInstance(OMEXMLService.class);
} catch (DependencyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
omeMeta = service.createOMEXMLMetadata();
} catch (ServiceException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
reader = new ImageReader(classList);
if (!BYPASS_SERVICES) {
reader.setMetadataStore(omeMeta);
}
}
// -- API methods --
public ImagePlus readTiffJp4(String path) throws IOException, FormatException {
return readTiffJp4(path, "STD_"); // null);
}
public ImagePlus readTiffJp4(String path_url, String std ) throws IOException, FormatException { // std - include non-elphel properties with prefix std
// determine if it is a file or URL and read url to memory
// If URL, then read to memory, if normal file - use direct access
url = null;
// String mime = null; // use to select jp4/tiff later? Or to check it is correct
content_fileName = null;
try {
url = new URL(path_url);
} catch (MalformedURLException e) {
LOGGER.warn("Bad URL: " + path_url);
}
if (url != null) {
LOGGER.info("Read "+ path_url +" to memory first");
URLConnection connection = url.openConnection();
String content_disposition = connection.getHeaderField("Content-Disposition");
// raw = "attachment; filename=abc.jpg"
if(content_disposition != null && content_disposition.indexOf("=") != -1) {
content_fileName = content_disposition.split("=")[1]; //getting value after '='
// trim quotes
content_fileName= content_fileName.substring(1, content_fileName.length()-1);
} else {
String mime = connection.getContentType();
int slash = mime.lastIndexOf("/");
String suffix = slash < 0 ? "" : mime.substring(slash+1);
content_fileName = "unknown." + suffix;
}
InputStream is = url.openStream (); //
byte[] inBytes = IOUtils.toByteArray(is);
if (is != null) is.close();
LOGGER.info("Bytes read: "+ inBytes.length);
Location.mapFile(content_fileName, new ByteArrayHandle(inBytes));
// HashMap<String,Object> dbg_loc = Location.getIdMap();
// super.setId(content_fileName);
} else { // read file normally
content_fileName = path_url;
LOGGER.info("read '"+path_url+"' file directly");
}
reader.setId(content_fileName);
byte [] bytes = null;
ImagePlus imp= null;
bytes = reader.openBytes(0);
int bpp = reader.getBitsPerPixel();
boolean is_le = reader.isLittleEndian();
int bytes_per_pixel = (bpp + 7) / 9;
float [] pixels = new float [bytes.length/bytes_per_pixel];
if (bytes_per_pixel == 1) {
for (int i = 0; i < pixels.length; i++) {
pixels[i] = ((bytes[i])) & 0xff;
}
} else {
ByteBuffer bb = ByteBuffer.wrap(bytes);
if (is_le) {
bb.order( ByteOrder.LITTLE_ENDIAN);
} else {
bb.order( ByteOrder.BIG_ENDIAN);
}
for (int i = 0; i < pixels.length; i++) {
pixels[i] = ((bb.getShort())) & 0xffff;
}
}
ImageProcessor ip=new FloatProcessor(reader.getSizeX(), reader.getSizeY());
ip.setPixels(pixels);
ip.resetMinAndMax();
Hashtable<String, Object> meta_hash = reader.getGlobalMetadata();
String prefix = ElphelTiffReader.ELPHEL_PROPERTY_PREFIX;
String imageName = content_fileName; // path;
String imageNameKey = prefix+ElphelTiffReader.CONTENT_FILENAME;
if (meta_hash.containsKey(imageNameKey)) {
imageName = meta_hash.get(imageNameKey).toString();
}
imp = new ImagePlus(imageName, ip); // original jp46 reader had full path as title
// first - save all as properties, later - only ELPHEL_*
for (String key:meta_hash.keySet()) {
if (key.startsWith(prefix)) {
imp.setProperty(key.substring(prefix.length()), meta_hash.get(key).toString());
} else if (std != null) {
imp.setProperty(std+(key.replace(" ","_").replace("/","_")), meta_hash.get(key).toString());
}
}
encodeProperiesToInfo(imp);
Location.mapFile(content_fileName, null);
return imp;
}
// -- Helper methods --
public static ImagePlus encodeProperiesToInfo(ImagePlus imp){
String info="<?xml version=\"1.0\" encoding=\"UTF-8\"?><properties>";
Set<Object> jp4_set;
Properties jp4_prop;
Iterator<Object> itr;
String str;
jp4_prop=imp.getProperties();
if (jp4_prop!=null) {
jp4_set=jp4_prop.keySet();
itr=jp4_set.iterator();
while(itr.hasNext()) {
str = (String) itr.next();
// if (!str.equals("Info")) info+="<"+str+">\""+jp4_prop.getProperty(str)+"\"</"+str+">";
if (!str.equals("Info")) info+="<"+str+">"+jp4_prop.getProperty(str)+"</"+str+">";
}
}
info+="</properties>\n";
imp.setProperty("Info", info);
return imp;
}
}
# This document is a configuration file identifying all file format readers
# available to Bio-Formats, and the order in which they should be used.
# Please do not edit unless you know what you are doing, see
# https://docs.openmicroscopy.org/latest/bio-formats/developers/reader-guide.html
#Elphel readers
com.elphel.imagej.readers.ElphelTiffJp4Reader # tiff,jp4
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment