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

Commit

Permalink
fixes for #5
Browse files Browse the repository at this point in the history
  • Loading branch information
kahlep committed Nov 20, 2018
1 parent c9e78da commit 24c9260
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 221 deletions.
240 changes: 19 additions & 221 deletions src/main/java/eu/transkribus/interfaces/types/util/ImageUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,16 @@
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.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
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.spi.ImageWriterSpi;
import javax.imageio.stream.ImageInputStream;

import org.opencv.core.Core;
import org.opencv.core.CvType;
Expand All @@ -48,21 +36,6 @@
*/
public class ImageUtils {
private static final Logger logger = LoggerFactory.getLogger(ImageUtils.class);

private static final boolean USE_IMAGE_IO_READ_IMPL = false;

private static List<ImageReaderSpi> iioReaderList = new ArrayList<>();
private static List<ImageWriterSpi> iioWriterList = new ArrayList<>();

//Store instances of plugins to be registered AND unregistered. Not doing this will leave a mess in IIORegistry/Tomcat after hot (un)deployment
static {
//tiff writers
// iioWriterList.add(new com.sun.media.imageioimpl.plugins.tiff.TIFFImageWriterSpi());
iioWriterList.add(new com.twelvemonkeys.imageio.plugins.tiff.TIFFImageWriterSpi());
//tiff readers
// iioReaderList.add(new com.sun.media.imageioimpl.plugins.tiff.TIFFImageReaderSpi());
iioReaderList.add(new com.twelvemonkeys.imageio.plugins.tiff.TIFFImageReaderSpi());
}

public static BufferedImage convertToBufferedImage(Mat m) {
// BufferedImage imageBufferedImage = new
Expand Down Expand Up @@ -93,13 +66,13 @@ public static BufferedImage convertToBufferedImage(Mat m) {
}

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

if (b == null && u.getProtocol().startsWith("http")) {
//ImageIO.read() can't handle 302 status code on url
File tmpFile = ImageUtils.downloadImgFile(u);
b = read(tmpFile);
b = TrpImageIO.read(tmpFile);
logger.debug("read buffered image from file: "+b);
if (!tmpFile.delete()) {
logger.warn("Temp file could not be deleted: " + tmpFile.getAbsolutePath());
Expand All @@ -110,108 +83,6 @@ public static BufferedImage convertToBufferedImage(URL u) throws IOException {
}
return b;
}

public static BufferedImage read(URL u) throws IOException {
//always try ImageIO::read first and only loop through all readers if null is returned.
BufferedImage bi = ImageIO.read(u);
if(bi == null) {
logger.debug("ImageIO failed to read image. Checking other readers on URL: " + u);
try (InputStream is = u.openStream();) {
ImageInputStream iis = ImageIO.createImageInputStream(is);
logger.debug("Got stream: " + iis);
bi = read(iis);
if(bi == null) {
iis.close();
}
}
}
return bi;
}

public static BufferedImage read(File f) throws IOException {
//always try ImageIO::read first and only loop through all readers if null is returned.
BufferedImage bi = ImageIO.read(f);
if(bi == null) {
logger.debug("ImageIO failed to read image. Checking other readers on file: " + f.getAbsolutePath());
ImageInputStream iis = ImageIO.createImageInputStream(f);
if (iis == null) {
throw new IIOException("Can't create an ImageInputStream!");
}
logger.debug("Got stream: " + iis);
try {
bi = read(iis);
} catch (IIOException e) {
iis.close();
throw e;
}
}
return bi;
}

/**
* 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);
logger.debug("Success with " + reader.getClass().getCanonicalName());
} 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()
+ " (IIOException at " + ste.toString() + ")");
}
catch (NoClassDefFoundError error) {
/*
* This should never happen but it does in webapps/Tomcat when a stale reader plugin from a previous deployment is retrieved from IIORegistry.
* Proper cleanup of registered plugins should solve this in the long run (see TranskribusInterfaces #4) but for now we must handle it here
*/
logger.debug(reader.getClass().getCanonicalName() + ": " + error.getMessage());
StackTraceElement ste = error.getStackTrace()[0];
fails.add(reader.getClass().getCanonicalName() + ": " + error.getMessage()
+ " (NoClassDefFoundError at " + ste.toString() + ")");
} finally {
reader.dispose();
}
if(bi != null) {
break;
}
} else {
logger.debug(rSpi.getPluginClassName() + " denied reading the source.");
}
}
if(!fails.isEmpty()) {
//Generate an error report
StringBuffer sb = new StringBuffer();
for(String s : fails) {
sb.append("\n"+s);
}
if(bi == null) {
//All suitable readers failed. Throw IIOExceptions with the report
throw new IIOException("All ImageIO failed to read the source!" + sb.toString());
} else {
//Just log a warning
logger.warn("Some ImageReaders failed on this source!" + sb.toString());
}
}
return bi;
}

public static void loadOpenCV() throws Exception {

Expand All @@ -227,7 +98,7 @@ public static void loadOpenCV() throws Exception {
String libPath = p.getProperty("OPENCV_LIB_PATH");
opencv2lib = p.getProperty("OPENCV2_FALLBACK_LIBNAME");

SysPathUtils.addDirToPath(libPath);
SysPathUtils.addDirToJavaLibraryPath(libPath);
// System.out.println("PATH = "+SysPathUtils.getPath());

try {
Expand Down Expand Up @@ -258,8 +129,17 @@ public static void loadOpenCV() throws Exception {
}

public static Mat convertToOpenCvImage(URL u) throws IOException {
File tmpFile = ImageUtils.downloadImgFile(u);

File tmpFile;
if(u.getProtocol().startsWith("file")) {
try {
tmpFile = new File(u.toURI());
} catch (URISyntaxException e) {
throw new IOException("Could get file for URL: " + u, e);
}
} else {
tmpFile = ImageUtils.downloadImgFile(u);
}

try {
loadOpenCV();
} catch (Exception e) {
Expand Down Expand Up @@ -407,103 +287,21 @@ public static File saveAsFile(BufferedImage b, String path) throws IOException {
* Check issue #4 before using this method!
*/
public static void registerImageIOServices() {
logger.debug("Registering ImageIO readers / writers");
IIORegistry registry = IIORegistry.getDefaultInstance();

for(ImageReaderSpi spi : iioReaderList) {
registerImageIOService(registry, spi, javax.imageio.spi.ImageReaderSpi.class);
}

for(ImageWriterSpi spi : iioWriterList) {
registerImageIOService(registry, spi, javax.imageio.spi.ImageWriterSpi.class);
}
TrpImageIO.registerImageIOServices();
}

public static void unregisterImageIOServices() {
logger.debug("Unregistering ImageIO readers / writers");
IIORegistry registry = IIORegistry.getDefaultInstance();

for(ImageReaderSpi spi : iioReaderList) {
unregisterImageIOService(registry, spi, javax.imageio.spi.ImageReaderSpi.class);
}

for(ImageWriterSpi spi : iioWriterList) {
unregisterImageIOService(registry, spi, javax.imageio.spi.ImageWriterSpi.class);
}
TrpImageIO.unregisterImageIOServices();
}

/**
* @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());
}
}

/**
* @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 unregisterImageIOService(IIORegistry registry, T provider, Class<T> category) {
if(registry == null || provider == null || category == null) {
throw new IllegalArgumentException("An argument is null!");
}
if(registry.deregisterServiceProvider(provider, category)) {
logger.debug("Unregistered IIOServiceProvider: " + provider.getPluginClassName());
} else {
/*
* Actually ImageIO unregistered the old instance of the provider and registered the new one here!
*/
logger.debug("Unregister failed. IIOServiceProvider was not in registry: " + provider.getPluginClassName());
}
}

/**
* this method logs all registered ImageIO service providers and the ClassLoader they belong to
*/
public static void listImageIOServices() {
IIORegistry registry = IIORegistry.getDefaultInstance();
logger.info("ImageIO services:");
Iterator<Class<?>> cats = registry.getCategories();
while (cats.hasNext()) {
Class<?> cat = cats.next();
logger.info("ImageIO category = " + cat);

Iterator<?> providers = registry.getServiceProviders(cat, true);
while (providers.hasNext()) {
Object o = providers.next();
logger.debug("ImageIO provider of type " + o.getClass().getCanonicalName() + " in " + o.getClass().getClassLoader());
}
}
TrpImageIO.listImageIOServices();
}

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()) {
Object o = readers.next();
ClassLoader cl = o.getClass().getClassLoader();
logger.info("ImageReader: " + o + " in " + (cl == null ? "System ClassLoader" : cl));
}
}
TrpImageIO.testReaders();
}
}
66 changes: 66 additions & 0 deletions src/test/java/eu/transkribus/interfaces/types/ImageTest.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
package eu.transkribus.interfaces.types;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;

import javax.imageio.ImageIO;

import org.junit.Assert;
import org.junit.Test;
import org.opencv.core.Mat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -64,6 +72,64 @@ public void testFromUrlToBufferedImage() throws IOException {

}

@Test
public void testImageOrientationJpg() throws IOException {
URL imgUrl = new URL("https://files-test.transkribus.eu/Get?id=XBNTVRNUTFEDVFPPGIGIJRSN&fileType=view");

logger.debug("Using file: " + imgUrl);

BufferedImage bi2 = testImageOrientation(imgUrl);

try (OutputStream output = new FileOutputStream(new File("/tmp/test.jpg"))) {
ImageIO.write(bi2, "jpg", output);
}
}

// @Test
// public void testImageOrientationTif() throws IOException {
// URL imgUrl = ImageTest.class.getClassLoader().getResource("img/exif_orientation_6.tif");
//
// BufferedImage bi2 = testImageOrientation(imgUrl);
//
// try (OutputStream output = new FileOutputStream(new File("/tmp/test.tif"))) {
// ImageIO.write(bi2, "tif", output);
// }
// }

public BufferedImage testImageOrientation(URL imgUrl) throws IOException {
logger.debug("Test to load an image with exif orientation set: " + imgUrl);
BufferedImage bi = ImageIO.read(imgUrl);
Image img = new Image(imgUrl);
BufferedImage bi2 = img.getImageBufferedImage(true);

int xRes = bi2.getWidth();
final int xResWhenCorrectlyRotated = bi.getHeight();
Assert.assertEquals("Image orientation is incorrect. Width does not match expected value.", xResWhenCorrectlyRotated, xRes);

int yRes = bi2.getHeight();
final int yResWhenCorrectlyRotated = bi.getWidth();
Assert.assertEquals("Image orientation is incorrect. Height does not match expected value.", yResWhenCorrectlyRotated, yRes);

return bi2;
}

@Test
public void testImageOrientationOpenCv() throws IOException {
logger.debug("Test to load an image with exif orientation set.");
URL imgUrl = ImageTest.class.getClassLoader().getResource("img/exif_orientation_6.jpg");
BufferedImage bi = ImageIO.read(imgUrl);
Image img = new Image(imgUrl);
Mat opencvImage = img.getImageOpenCVImage(true);

int xRes = opencvImage.width();
final int xResWhenCorrectlyRotated = bi.getHeight();
Assert.assertEquals("Image orientation is incorrect. Width does not match expected value.", xResWhenCorrectlyRotated, xRes);

int yRes = opencvImage.height();
final int yResWhenCorrectlyRotated = bi.getWidth();
Assert.assertEquals("Image orientation is incorrect. Height does not match expected value.", yResWhenCorrectlyRotated, yRes);
}

/**
* A main for testing the behavior without JUnit
*
Expand Down

0 comments on commit 24c9260

Please sign in to comment.