Skip to content
This repository has been archived by the owner on Nov 16, 2020. It is now read-only.

Commit

Permalink
More robust code for creating BufferedImages. See #4
Browse files Browse the repository at this point in the history
  • Loading branch information
kahlep committed Apr 9, 2018
1 parent d168fc1 commit cd631bd
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 127 deletions.
79 changes: 0 additions & 79 deletions src/main/java/eu/transkribus/interfaces/types/Image.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,9 @@
import java.io.IOException;
import java.net.URL;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.spi.IIORegistry;
import javax.imageio.spi.ImageReaderWriterSpi;

import org.opencv.core.Mat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sun.media.imageioimpl.plugins.tiff.TIFFImageReaderSpi;
import com.sun.media.imageioimpl.plugins.tiff.TIFFImageWriterSpi;

import eu.transkribus.interfaces.types.util.ImageUtils;

Expand All @@ -31,7 +20,6 @@
* @author gundram
*/
public class Image {
private static final Logger logger = LoggerFactory.getLogger(Image.class);

private URL imageUrl;
private BufferedImage imageBufferedImage;
Expand Down Expand Up @@ -199,68 +187,6 @@ private Object getImageObject(Type type) {
}
}

static {
registerImageIOServices();
listImageIOServices();
}

public static void listImageIOServices() {
IIORegistry registry = IIORegistry.getDefaultInstance();
logger.debug("image-io services:");
Iterator<Class<?>> cats = registry.getCategories();
while (cats.hasNext()) {
Class<?> cat = cats.next();
logger.debug("image-io category = " + cat);

Iterator<?> providers = registry.getServiceProviders(cat, true);
while (providers.hasNext()) {
logger.debug("image-io provider = " + providers.next());
}
}
}

public static void registerImageIOServices() {
logger.debug("registering image readers / writers");

IIORegistry registry = IIORegistry.getDefaultInstance();

// have to programmatically register tiff reader / writer in tomcat
registerImageIOService(registry, new TIFFImageWriterSpi(), javax.imageio.spi.ImageWriterSpi.class);
registerImageIOService(registry, new TIFFImageReaderSpi(), javax.imageio.spi.ImageReaderSpi.class);

registerImageIOService(registry, new com.twelvemonkeys.imageio.plugins.tiff.TIFFImageWriterSpi(),
javax.imageio.spi.ImageWriterSpi.class);
registerImageIOService(registry, new com.twelvemonkeys.imageio.plugins.tiff.TIFFImageReaderSpi(),
javax.imageio.spi.ImageReaderSpi.class);
}

private static <T extends ImageReaderWriterSpi> void registerImageIOService(IIORegistry registry, T provider, Class<T> clazz) {
if(registry == null || provider == null || clazz == null) {
throw new IllegalArgumentException("An argument is null!");
}
if(registry.registerServiceProvider(provider, clazz)) {
logger.info("Registered IIOServiceProvider: " + provider.getPluginClassName());
} else {
logger.info("Ignoring already registered IIOServiceProvider: " + provider.getPluginClassName());
}
}

public static void testReaders() throws IOException {
String[] formats = { "JPEG", "TIFF", "TIF", "PNG" };
for (String format : formats) {
logger.info("testing readers for format: " + format);
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName(format);

if (!readers.hasNext()) {
throw new IOException("No reader found for format: " + format);
}

while (readers.hasNext()) {
logger.info("reader: " + readers.next());
}
}
}

public void dispose() {
if(imageBufferedImage != null) {
imageBufferedImage.flush();
Expand All @@ -271,9 +197,4 @@ public void dispose() {
imageOpenCVImage = null;
}
}

public static void main(String[] args) throws IOException {
testReaders();
}

}
196 changes: 169 additions & 27 deletions src/main/java/eu/transkribus/interfaces/types/util/ImageUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,26 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.UUID;

import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.spi.IIORegistry;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.spi.ImageReaderWriterSpi;
import javax.imageio.stream.ImageInputStream;

import org.opencv.core.Core;
import org.opencv.core.CvType;
Expand All @@ -24,6 +35,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sun.media.imageioimpl.plugins.tiff.TIFFImageReaderSpi;
import com.sun.media.imageioimpl.plugins.tiff.TIFFImageWriterSpi;

import eu.transkribus.interfaces.util.SysPathUtils;

/**
Expand All @@ -36,6 +50,8 @@
public class ImageUtils {
private static final Logger logger = LoggerFactory.getLogger(ImageUtils.class);

private static final boolean USE_IMAGE_IO_READ_IMPL = false;

public static BufferedImage convertToBufferedImage(Mat m) {
// BufferedImage imageBufferedImage = new
// BufferedImage(imageOpenCVImage.width(), imageOpenCVImage.height(),
Expand Down Expand Up @@ -65,23 +81,98 @@ public static BufferedImage convertToBufferedImage(Mat m) {
}

public static BufferedImage convertToBufferedImage(URL u) throws IOException {
BufferedImage b = ImageIO.read(u);
BufferedImage b = read(u);
logger.debug("read buffered image from url: "+b);

if (b == null) {
//ImageIO.read() can't handle 302 status code on url
File tmpFile = ImageUtils.downloadImgFile(u);
b = ImageIO.read(tmpFile);
b = read(tmpFile);
logger.debug("read buffered image from file: "+b);
if (!tmpFile.delete()) {
logger.warn("Temp file could not be deleted: " + tmpFile.getAbsolutePath());
}
}
if (b == null) {
throw new IOException("Could read buffered image from URL: " + u.toString());
throw new IOException("Could not read buffered image from URL: " + u.toString());
}
return b;
}

public static BufferedImage read(URL u) throws IOException {
if(USE_IMAGE_IO_READ_IMPL) {
return ImageIO.read(u);
} else {
try (
InputStream is = u.openStream();
ImageInputStream iis = ImageIO.createImageInputStream(is)
) {
return read(iis);
}
}
}

public static BufferedImage read(File f) throws IOException {
if(USE_IMAGE_IO_READ_IMPL) {
return ImageIO.read(f);
} else {
try (ImageInputStream iis = ImageIO.createImageInputStream(f)) {
return read(iis);
}
}
}

/**
* Alternative implementation of ImageIO::read.</br>
* If one image reader claims to be able to read an ImageInputStream but fails then,
* this method, in contrast to the ImageIO impl., will try with other readers before throwing an Exception.
*
* @param iis the ImageInputStream
* @return a BufferedImage or null only if no reader claimed to support the ImageInputSource
* @throws IOException
*/
private static BufferedImage read(ImageInputStream iis) throws IOException {
Iterator<ImageReaderSpi> it = IIORegistry.getDefaultInstance().getServiceProviders(ImageReaderSpi.class, true);
BufferedImage bi = null;
List<String> fails = new LinkedList<>();
while(it.hasNext()) {
ImageReaderSpi rSpi = it.next();
if(rSpi.canDecodeInput(iis)) {
logger.debug(rSpi.getPluginClassName() + " claims to support image source.");
ImageReader reader = rSpi.createReaderInstance();
ImageReadParam param = reader.getDefaultReadParam();
reader.setInput(iis, true, true);
try {
bi = reader.read(0, param);
} catch(IIOException iioe) {
logger.debug(reader.getClass().getCanonicalName() + " failed to read image source!");
StackTraceElement ste = iioe.getStackTrace()[0];
fails.add(reader.getClass().getCanonicalName() + ": " + iioe.getMessage()
+ " (" + ste.getClassName() + "::" + ste.getMethodName() + " in " + ste.getFileName() + ")");
} finally {
reader.dispose();
}
if(bi != null) {
break;
}
}
}
if(!fails.isEmpty()) {
//Generate an error report
StringBuffer sb = new StringBuffer("ImageReaders failed to read the source!");
for(String s : fails) {
sb.append("\n"+s);
}
if(bi == null) {
//All suitable readers failed. Throw IIOExceptions with the report
throw new IIOException(sb.toString());
} else {
//Just log a warning
logger.warn(sb.toString());
}
}
return bi;
}

public static void loadOpenCV() throws Exception {

Expand Down Expand Up @@ -126,15 +217,6 @@ public static void loadOpenCV() throws Exception {

logger.debug("Loaded opencv ("+opencvlibname+") in t = "+(System.currentTimeMillis()-t0));
}

// static {
// try {
// loadOpenCV();
// } catch (Exception e) {
// e.printStackTrace();
// System.exit(1);
// }
// }

public static Mat convertToOpenCvImage(URL u) throws IOException {
File tmpFile = ImageUtils.downloadImgFile(u);
Expand All @@ -145,26 +227,15 @@ public static Mat convertToOpenCvImage(URL u) throws IOException {
throw new IOException(e);
}

// try {
// System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
// } catch (java.lang.UnsatisfiedLinkError error) {
// try {
// System.loadLibrary("opencv_java2410");
// } catch (UnsatisfiedLinkError error2) {
//// throw error;
// throw new IOException("Could not find opencv: "+Core.NATIVE_LIBRARY_NAME+"/"+"opencv_java2410");
// }
// }

Mat imageOpenCVImage = null;
try {
Class clazz = Class.forName("org.opencv.imgcodecs.Imgcodecs");
Class<?> clazz = Class.forName("org.opencv.imgcodecs.Imgcodecs");
Method method = clazz.getMethod("imread", String.class);
imageOpenCVImage = (Mat) method.invoke(null, tmpFile.getAbsolutePath());
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
try {
logger.info("expect OpenCV 2.* - use other static loader");
Class clazz = Class.forName("org.opencv.highgui.Highgui");
Class<?> clazz = Class.forName("org.opencv.highgui.Highgui");
Method method = clazz.getMethod("imread", String.class);
imageOpenCVImage = (Mat) method.invoke(null, tmpFile.getAbsolutePath());
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex2) {
Expand Down Expand Up @@ -247,7 +318,7 @@ public static File downloadImgFile(URL imageUrl) throws IOException {

public static File saveAsFile(Mat m, String path) throws IOException {
try {
Class clazz = Class.forName("org.opencv.imgcodecs.Imgcodecs");
Class<?> clazz = Class.forName("org.opencv.imgcodecs.Imgcodecs");
Method method = clazz.getMethod("imwrite", String.class, Mat.class);
Boolean res = (Boolean) method.invoke(null, path, m);
if (res != null && res) {
Expand All @@ -256,7 +327,7 @@ public static File saveAsFile(Mat m, String path) throws IOException {
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
try {
logger.info("expect OpenCV 2.* - use other static loader");
Class clazz = Class.forName("org.opencv.highgui.Highgui");
Class<?> clazz = Class.forName("org.opencv.highgui.Highgui");
Method method = clazz.getMethod("imwrite", String.class, Mat.class);
Boolean res = (Boolean) method.invoke(null, path, m);
if (res != null && res) {
Expand Down Expand Up @@ -288,4 +359,75 @@ public static File saveAsFile(BufferedImage b, String path) throws IOException {
throw new IOException("Could not write image to: " + path);
}
}

// ImageIO related methods ==========================================
/**
* Java applications register plugins on the classpath automatically and in Tomcat the
* ContextListener (included in the Twelvemonkeys servlet artifact) can be used for that.</br>
* Check issue #4 before using this method!
*/
public static void registerImageIOServices() {
logger.debug("registering image readers / writers");

IIORegistry registry = IIORegistry.getDefaultInstance();

// have to programmatically register tiff reader / writer in tomcat
registerImageIOService(registry, new TIFFImageWriterSpi(), javax.imageio.spi.ImageWriterSpi.class);
registerImageIOService(registry, new TIFFImageReaderSpi(), javax.imageio.spi.ImageReaderSpi.class);

registerImageIOService(registry, new com.twelvemonkeys.imageio.plugins.tiff.TIFFImageWriterSpi(),
javax.imageio.spi.ImageWriterSpi.class);
registerImageIOService(registry, new com.twelvemonkeys.imageio.plugins.tiff.TIFFImageReaderSpi(),
javax.imageio.spi.ImageReaderSpi.class);
}

/**
* @param registry the ImageIO registry to use
* @param provider the service provider object to be registered.
* @param category the category under which to register the provider.
*/
private static <T extends ImageReaderWriterSpi> void registerImageIOService(IIORegistry registry, T provider, Class<T> category) {
if(registry == null || provider == null || category == null) {
throw new IllegalArgumentException("An argument is null!");
}
if(registry.registerServiceProvider(provider, category)) {
logger.debug("Registered IIOServiceProvider: " + provider.getPluginClassName());
} else {
/*
* Actually ImageIO unregistered the old instance of the provider and registered the new one here!
*/
logger.debug("Replaced existing IIOServiceProvider in registry: " + provider.getPluginClassName());
}
}

public static void listImageIOServices() {
IIORegistry registry = IIORegistry.getDefaultInstance();
logger.debug("image-io services:");
Iterator<Class<?>> cats = registry.getCategories();
while (cats.hasNext()) {
Class<?> cat = cats.next();
logger.debug("image-io category = " + cat);

Iterator<?> providers = registry.getServiceProviders(cat, true);
while (providers.hasNext()) {
logger.debug("image-io provider = " + providers.next());
}
}
}

public static void testReaders() throws IOException {
String[] formats = { "JPEG", "TIFF", "TIF", "PNG" };
for (String format : formats) {
logger.info("testing readers for format: " + format);
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName(format);

if (!readers.hasNext()) {
throw new IOException("No reader found for format: " + format);
}

while (readers.hasNext()) {
logger.info("reader: " + readers.next());
}
}
}
}
Loading

0 comments on commit cd631bd

Please sign in to comment.