diff --git a/image_readers/.classpath b/image_readers/.classpath
index 6b6fea16a4ad237fad666553337f3e01e626f4a8..2cb8df3f38db4a6e209d3f17dd67788c39657687 100644
--- a/image_readers/.classpath
+++ b/image_readers/.classpath
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
 	<classpathentry kind="src" path="source/java"/>
+	<classpathentry kind="src" path="sourceTest/java"/>
 	<classpathentry kind="lib" path="/libraries/cisd-base/cisd-base.jar" sourcepath="/libraries/cisd-base/cisd-base-src.zip"/>
 	<classpathentry kind="lib" path="/libraries/commons-lang/commons-lang.jar" sourcepath="/libraries/commons-lang/src.zip"/>
 	<classpathentry kind="lib" path="/libraries/commons-io/commons-io.jar" sourcepath="/libraries/commons-io/src.zip"/>
@@ -12,5 +13,8 @@
 	<classpathentry kind="lib" path="/libraries/slf4j/log4j12/slf4j-log4j12.jar" sourcepath="/libraries/slf4j/log4j12/src.jar"/>
 	<classpathentry kind="lib" path="/libraries/log4j/log4j.jar" sourcepath="/libraries/log4j/src.zip"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.launching.macosx.MacOSXType/JVM 1.6"/>
+	<classpathentry kind="lib" path="/libraries/testng/testng-jdk15.jar" sourcepath="/libraries/testng/src.zip"/>
+	<classpathentry kind="lib" path="/libraries/jmock/jmock.jar"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/common"/>
 	<classpathentry kind="output" path="targets/classes"/>
 </classpath>
diff --git a/image_readers/source/java/META-INF/services/ch.systemsx.cisd.imagereaders.IImageReaderLibrary b/image_readers/source/java/META-INF/services/ch.systemsx.cisd.imagereaders.IImageReaderLibrary
new file mode 100644
index 0000000000000000000000000000000000000000..0d171e0c047de3ccf9d70f3f8a1d592ec5481ae8
--- /dev/null
+++ b/image_readers/source/java/META-INF/services/ch.systemsx.cisd.imagereaders.IImageReaderLibrary
@@ -0,0 +1 @@
+ch.systemsx.cisd.imagereaders.bioformats.BioFormatsReaderLibrary
\ No newline at end of file
diff --git a/image_readers/source/java/ch/systemsx/cisd/imagereaders/AbstractImageReader.java b/image_readers/source/java/ch/systemsx/cisd/imagereaders/AbstractImageReader.java
new file mode 100644
index 0000000000000000000000000000000000000000..108838052fedacfb2c3f35e40580fc6200cdb21d
--- /dev/null
+++ b/image_readers/source/java/ch/systemsx/cisd/imagereaders/AbstractImageReader.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2011 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.imagereaders;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+
+import ch.systemsx.cisd.base.exceptions.IOExceptionUnchecked;
+import ch.systemsx.cisd.base.io.ByteBufferRandomAccessFile;
+import ch.systemsx.cisd.base.io.IRandomAccessFile;
+import ch.systemsx.cisd.base.io.RandomAccessFileImpl;
+
+/**
+ * A convenience abstract class to be extended by {@link IImageReader} implementations.
+ * 
+ * @author Kaloyan Enimanev
+ */
+public abstract class AbstractImageReader implements IImageReader
+{
+    private final String libraryName;
+
+    private final String readerName;
+
+    public AbstractImageReader(String libraryName, String readerName)
+    {
+        this.libraryName = libraryName;
+        this.readerName = readerName;
+    }
+
+    @Override
+    public String getLibraryName()
+    {
+        return libraryName;
+    }
+
+    @Override
+    public String getName()
+    {
+        return readerName;
+    }
+
+    @Override
+    public BufferedImage readImage(File file, int page) throws IOExceptionUnchecked
+    {
+        IRandomAccessFile raf = new RandomAccessFileImpl(file, "r");
+        return readImage(raf, page);
+    }
+
+    @Override
+    public BufferedImage readImage(byte[] bytes, int page)
+    {
+        IRandomAccessFile raf = new ByteBufferRandomAccessFile(bytes);
+        return readImage(raf, page);
+    }
+
+}
diff --git a/image_readers/source/java/ch/systemsx/cisd/imagereaders/Constants.java b/image_readers/source/java/ch/systemsx/cisd/imagereaders/Constants.java
new file mode 100644
index 0000000000000000000000000000000000000000..c126ebacd232a3fa1b04d8572e5b66eefb0e6612
--- /dev/null
+++ b/image_readers/source/java/ch/systemsx/cisd/imagereaders/Constants.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2011 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.imagereaders;
+
+/**
+ * Constants to be used when working with the image readers API.
+ * 
+ * @author Kaloyan Enimanev
+ */
+public final class Constants
+{
+
+    public static final String BIOFORMATS_LIBRARY = "BioFormats";
+
+    public static final String BIOFORMATS_RESCALING_LIBRARY = "bioformats-rescaling";
+
+    public static final String JAI_LIBRARY = "JAI";
+}
diff --git a/image_readers/source/java/ch/systemsx/cisd/imagereaders/IImageReader.java b/image_readers/source/java/ch/systemsx/cisd/imagereaders/IImageReader.java
index 5bfcaeb0f491848cd3652f83b604eaae86e36fef..aed420edb7738ab7ad1e426cc1af21f13f8f729c 100644
--- a/image_readers/source/java/ch/systemsx/cisd/imagereaders/IImageReader.java
+++ b/image_readers/source/java/ch/systemsx/cisd/imagereaders/IImageReader.java
@@ -23,18 +23,45 @@ import ch.systemsx.cisd.base.exceptions.IOExceptionUnchecked;
 import ch.systemsx.cisd.base.io.IRandomAccessFile;
 
 /**
+ * {@link IImageReader} can read images in a particular format (e.g. TIFF, JPG, GIF etc.). To obtain
+ * the correct {@link IImageReader} instance use the API of {@link ImageReaderFactory}.
+ * 
  * @author Bernd Rinn
  */
 public interface IImageReader
 {
+    /**
+     * Return the name of the library for this reader.
+     */
     public String getLibraryName();
 
+    /**
+     * Return the name of the reader.
+     */
     public String getName();
 
+    /**
+     * Reads a {@link BufferedImage} from a {@link File}.
+     * 
+     * @param file the image file
+     * @param page specified the image page to be read (in case of multiple pages)
+     */
     public BufferedImage readImage(File file, int page) throws IOExceptionUnchecked;
 
-    public BufferedImage readImage(String filename, IRandomAccessFile handle, int page)
-            throws IOExceptionUnchecked;
+    /**
+     * Reads a {@link BufferedImage} from a byte array.
+     * 
+     * @param bytes the image file as a byte array
+     * @param page specified the image page to be read (in case of multiple pages)
+     */
+    public BufferedImage readImage(byte[] bytes, int page);
 
-    public BufferedImage readImage(String filename, byte[] bytes, int page);
+    /**
+     * Reads a {@link BufferedImage} from a handle.
+     * 
+     * @param handle the image file as {@link IRandomAccessFile}
+     * @param page specified the image page to be read (in case of multiple pages)
+     */
+    public BufferedImage readImage(IRandomAccessFile handle, int page)
+            throws IOExceptionUnchecked;
 }
diff --git a/image_readers/source/java/ch/systemsx/cisd/imagereaders/IImageReaderLibrary.java b/image_readers/source/java/ch/systemsx/cisd/imagereaders/IImageReaderLibrary.java
index 6eb42df6c460f24dafee0e91f0d26fdc15126ff0..10580e48b21017eb4983c63420f7263fdf36cb98 100644
--- a/image_readers/source/java/ch/systemsx/cisd/imagereaders/IImageReaderLibrary.java
+++ b/image_readers/source/java/ch/systemsx/cisd/imagereaders/IImageReaderLibrary.java
@@ -25,12 +25,30 @@ import java.util.List;
  */
 public interface IImageReaderLibrary
 {
+    /**
+     * Return library name.
+     */
     public String getName();
     
+    /**
+     * Return a collection with the names of the image readers available in the library.
+     */
     public List<String> getReaderNames();
     
+    /**
+     * Returns an {@link IImageReader} for a specified name. Can return <code>null</code> if no
+     * reader with the specified name is available.
+     */
     public IImageReader tryGetReader(String readerName);
-    
+
+    /**
+     * Tries to find a suitable reader for a specified <var>fileName</var>. May return
+     * <code>null</code> if no suitable reader is found.
+     * <p>
+     * The behavior of this method may vary across libraries. For example, some image libraries can
+     * use the suffix of <var>fileName</var> to find the right reader, while others might attempt to
+     * open the file and apply heuristics on its content to determine the appropriate reader.
+     */
     public IImageReader tryGetReaderForFile(String fileName);
     
 }
diff --git a/image_readers/source/java/ch/systemsx/cisd/imagereaders/ImageReaderFactory.java b/image_readers/source/java/ch/systemsx/cisd/imagereaders/ImageReaderFactory.java
index 0043a792cb47b7867f050f984f1622f7ceacae55..7b452274a117aeaefe444b68133364b30be724c6 100644
--- a/image_readers/source/java/ch/systemsx/cisd/imagereaders/ImageReaderFactory.java
+++ b/image_readers/source/java/ch/systemsx/cisd/imagereaders/ImageReaderFactory.java
@@ -16,16 +16,60 @@
 
 package ch.systemsx.cisd.imagereaders;
 
+import java.util.Iterator;
 import java.util.ServiceLoader;
 
 /**
  * A factory for image readers.
  * <p>
  * Uses {@link ServiceLoader}s underneath to find out about the available libraries and readers.
- *
+ * 
  * @author Bernd Rinn
+ * @author Kaloyan Enimanev
  */
 public class ImageReaderFactory
 {
 
+    private static final ServiceLoader<IImageReaderLibrary> libraryServiceLoader = ServiceLoader
+            .load(IImageReaderLibrary.class);
+
+    /**
+     * Returns an {@link IImageReader} for specified library name and reader name. Can return
+     * <code>null</code> if no matching reader is found.
+     */
+    public static IImageReader tryGetReader(String libraryName, String readerName)
+    {
+        IImageReaderLibrary library = tryGetLibrary(libraryName);
+        return (library == null) ? null : library.tryGetReader(readerName);
+    }
+
+    /**
+     * Tries to find a suitable reader in a library for a specified <var>fileName</var>. May return
+     * <code>null</code> if no suitable reader is found.
+     * <p>
+     * The behavior of this method may vary across libraries. For example, some image libraries can
+     * use the suffix of <var>fileName</var> to find the right reader, while others might attempt to
+     * open the file and apply heuristics on its content to determine the appropriate reader.
+     */
+    public static IImageReader tryGetImageReaderForFile(String libraryName, String fileName)
+    {
+        IImageReaderLibrary library = tryGetLibrary(libraryName);
+        return (library == null) ? null : library.tryGetReaderForFile(fileName);
+    }
+
+    private static IImageReaderLibrary tryGetLibrary(String libraryName)
+            throws IllegalArgumentException
+    {
+        Iterator<IImageReaderLibrary> iterator = libraryServiceLoader.iterator();
+        while (iterator.hasNext())
+        {
+            IImageReaderLibrary library = iterator.next();
+            if (library.getName().equals(libraryName))
+            {
+                return library;
+            }
+        }
+        return null;
+    }
+
 }
diff --git a/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/AbstractBioFormatsReaderLibrary.java b/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/AbstractBioFormatsReaderLibrary.java
new file mode 100644
index 0000000000000000000000000000000000000000..7fa6d87c41076cbb2b9558977784ed8080d1c93d
--- /dev/null
+++ b/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/AbstractBioFormatsReaderLibrary.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2011 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.imagereaders.bioformats;
+
+import java.util.List;
+
+import loci.formats.IFormatReader;
+
+import ch.systemsx.cisd.imagereaders.IImageReader;
+import ch.systemsx.cisd.imagereaders.IImageReaderLibrary;
+
+/**
+ * {@link IImageReaderLibrary} for BioFormats readers with intensity rescaling.
+ * 
+ * @author Kaloyan Enimanev
+ */
+abstract class AbstractBioFormatsReaderLibrary implements IImageReaderLibrary
+{
+
+    /**
+     * Adapt a BioFormats {@link IFormatReader} to {@link IImageReader}.
+     * 
+     * @param formatReader this parameter is guaranteed to be non-null.
+     */
+    protected abstract IImageReader adaptFormatReader(final IFormatReader formatReader);
+
+
+    @Override
+    public List<String> getReaderNames()
+    {
+        return BioFormatsImageUtils.getReaderNames();
+    }
+
+    @Override
+    public IImageReader tryGetReader(String readerName)
+    {
+        final IFormatReader formatReaderOrNull = BioFormatsImageUtils.tryFindReaderByName(readerName);
+        return tryAdaptFormatReader(formatReaderOrNull);
+    }
+
+    @Override
+    public IImageReader tryGetReaderForFile(String fileName)
+    {
+        IFormatReader formatReaderOrNull = BioFormatsImageUtils.tryFindReaderForFile(fileName);
+        return tryAdaptFormatReader(formatReaderOrNull);
+    }
+
+    /**
+     * Delegate the wrapping of non-null readers to an abstract method.
+     */
+    protected IImageReader tryAdaptFormatReader(final IFormatReader formatReaderOrNull)
+    {
+        return (formatReaderOrNull == null) ? null : adaptFormatReader(formatReaderOrNull);
+    }
+
+}
diff --git a/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsImageJUtils.java b/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsImageJUtils.java
deleted file mode 100644
index a63b0353abfeb9c9b58eb1bc824b2fa2b39a96c5..0000000000000000000000000000000000000000
--- a/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsImageJUtils.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright 2011 ETH Zuerich, CISD
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package ch.systemsx.cisd.imagereaders.bioformats;
-
-import ij.ImagePlus;
-import ij.ImageStack;
-import ij.process.ImageProcessor;
-
-import java.awt.image.BufferedImage;
-import java.io.File;
-import java.io.IOException;
-
-import loci.common.ByteArrayHandle;
-import loci.common.IRandomAccess;
-import loci.common.Location;
-import loci.common.NIOFileHandle;
-import loci.formats.FormatException;
-import loci.formats.IFormatReader;
-
-import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel;
-import ch.systemsx.cisd.base.exceptions.IOExceptionUnchecked;
-import ch.systemsx.cisd.base.io.IRandomAccessFile;
-
-/**
- * A utility class to use bio-formats readers to read images and ImageJ to do a basic intensity
- * rescaling.
- * 
- * @author Bernd Rinn
- */
-public final class BioFormatsImageJUtils
-{
-
-    /**
-     * Returns the image <var>filename</var> represented by <var>bytes</var> as
-     * {@link BufferedImage}.
-     * <p>
-     * Note that the suffix of <var>filename</var> is used to find the right reader.
-     * 
-     * @throws IOExceptionUnchecked If access to <var>handle</var> fails.
-     * @throws IllegalArgumentException If no suitable reader can be found.
-     */
-    public static BufferedImage readAndTransformImage(String filename, byte[] bytes, int page,
-            int channel) throws IOExceptionUnchecked, IllegalArgumentException
-    {
-        return readAndTransformImage(filename, new ByteArrayHandle(bytes), page, channel);
-    }
-
-    /**
-     * Returns the image <var>file</var> represented by <var>handle</var> as {@link BufferedImage}.
-     * <p>
-     * Note that the suffix of <var>file</var> is used to find the right reader.
-     * 
-     * @throws IOExceptionUnchecked If access to <var>handle</var> fails.
-     * @throws IllegalArgumentException If no suitable reader can be found.
-     */
-    public static BufferedImage readAndTransformImage(File file, int page, int channel)
-            throws IOExceptionUnchecked, IllegalArgumentException
-    {
-        try
-        {
-            return readAndTransformImage(file.getPath(), new NIOFileHandle(file, "r"), page,
-                    channel);
-        } catch (IOException ex)
-        {
-            throw CheckedExceptionTunnel.wrapIfNecessary(ex);
-        }
-    }
-
-    /**
-     * Returns the image <var>filename</var> represented by <var>handle</var> as
-     * {@link BufferedImage}.
-     * <p>
-     * Note that the suffix of <var>filename</var> is used to find the right reader.
-     * 
-     * @throws IOExceptionUnchecked If access to <var>handle</var> fails.
-     * @throws IllegalArgumentException If no suitable reader can be found.
-     */
-    public static BufferedImage readAndTransformImage(String filename, IRandomAccessFile handle,
-            int page, int channel) throws IOExceptionUnchecked, IllegalArgumentException
-    {
-        return readAndTransformImage(filename, new BioFormatsRandomAccessAdapter(handle), page,
-                channel);
-    }
-
-    /**
-     * Returns the image <var>filename</var> represented by <var>handle</var> as
-     * {@link BufferedImage}.
-     * <p>
-     * Note that the suffix of <var>filename</var> is used to find the right reader.
-     * 
-     * @throws IOExceptionUnchecked If access to <var>handle</var> fails.
-     * @throws IllegalArgumentException If no suitable reader can be found.
-     */
-    private static BufferedImage readAndTransformImage(String filename, IRandomAccess handle,
-            int page, int channel) throws IOExceptionUnchecked, IllegalArgumentException
-    {
-        // Add to static map.
-        Location.mapFile(filename, handle);
-        try
-        {
-            final IFormatReader reader = BioFormatsImageUtils.findReaderForFile(filename);
-            // This does the actual parsing.
-            reader.setId(filename);
-            int width = reader.getSizeX();
-            int height = reader.getSizeY();
-            final ImageStack stack = new ImageStack(width, height);
-            final ImageProcessor ip = BioFormatsImageProcessor.openProcessor(reader, page, channel);
-            stack.addSlice("", ip);
-            final ImagePlus imp = new ImagePlus(filename, stack);
-
-            final BufferedImage image = imp.getBufferedImage();
-            reader.close();
-            return image;
-        } catch (FormatException ex)
-        {
-            throw CheckedExceptionTunnel.wrapIfNecessary(ex);
-        } catch (IOException ex)
-        {
-            throw CheckedExceptionTunnel.wrapIfNecessary(ex);
-        } finally
-        {
-            // Remove from static map.
-            Location.mapFile(filename, null);
-        }
-    }
-
-    private BioFormatsImageJUtils()
-    {
-        // Not to be instantiated.
-    }
-
-}
diff --git a/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsImageUtils.java b/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsImageUtils.java
index 1e532d5649b48fefaa604b69270af7ab9de518b0..3c811c973d65a3732b1c74fcc883fb468a49bd27 100644
--- a/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsImageUtils.java
+++ b/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsImageUtils.java
@@ -16,16 +16,19 @@
 
 package ch.systemsx.cisd.imagereaders.bioformats;
 
+import ij.ImagePlus;
+import ij.ImageStack;
+import ij.process.ImageProcessor;
+
 import java.awt.image.BufferedImage;
-import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.UUID;
 
-import loci.common.ByteArrayHandle;
 import loci.common.IRandomAccess;
 import loci.common.Location;
-import loci.common.NIOFileHandle;
 import loci.formats.FormatException;
 import loci.formats.IFormatReader;
 import loci.formats.ImageReader;
@@ -33,43 +36,30 @@ import loci.formats.gui.BufferedImageReader;
 
 import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel;
 import ch.systemsx.cisd.base.exceptions.IOExceptionUnchecked;
-import ch.systemsx.cisd.base.io.IRandomAccessFile;
 
 /**
  * A utility class to use bio-formats to read images.
  * 
  * @author Bernd Rinn
  */
-public final class BioFormatsImageUtils
+final class BioFormatsImageUtils
 {
 
-    private final static List<IFormatReader> readers = new ArrayList<IFormatReader>();
+    private final static List<IFormatReader> readers;
 
     static
     {
-        final Class<? extends IFormatReader>[] classes =
-                ImageReader.getDefaultReaderClasses().getClasses();
-        for (int i = 0; i < classes.length; i++)
-        {
-            IFormatReader reader = null;
-            try
-            {
-                reader = classes[i].newInstance();
-            } catch (IllegalAccessException exc)
-            {
-            } catch (InstantiationException exc)
-            {
-            }
-            if (reader == null)
-            {
-                continue;
-            }
-            readers.add(reader);
-        }
+        IFormatReader[] formatReaders = new ImageReader().getReaders();
+        readers = Arrays.asList(formatReaders);
     }
 
-    public static IFormatReader tryFindReaderForFile(String fileName) throws IOException,
-            IllegalArgumentException
+    /**
+     * Tries to find a suitable reader for the file specified with <var>fileName</var>. May return
+     * <code>null</code> if no suitable reader is found.
+     * <p>
+     * Note that the suffix of <var>fileName</var> is used to find the right reader.
+     */
+    public static IFormatReader tryFindReaderForFile(String fileName)
     {
         for (IFormatReader r : readers)
         {
@@ -81,19 +71,10 @@ public final class BioFormatsImageUtils
         return null;
     }
 
-    public static IFormatReader findReaderForFile(String fileName) throws IOException,
-            IllegalArgumentException
-    {
-        final IFormatReader readerOrNull = tryFindReaderForFile(fileName);
-        if (readerOrNull == null)
-        {
-            throw new IllegalArgumentException("Cannot find reader.");
-        } else
-        {
-            return readerOrNull;
-        }
-    }
-
+    /**
+     * Return an {@link IFormatReader} for a specified name. May return <code>null</code> if no
+     * corresponding reader is found.
+     */
     public static IFormatReader tryFindReaderByName(String readerName)
             throws IllegalArgumentException
     {
@@ -108,147 +89,44 @@ public final class BioFormatsImageUtils
 
     }
 
-    public static IFormatReader findReaderByName(String readerName) throws IllegalArgumentException
-    {
-        final IFormatReader readerOrNull = tryFindReaderByName(readerName);
-        if (readerOrNull == null)
-        {
-            throw new IllegalArgumentException("Cannot find reader.");
-        } else
-        {
-            return readerOrNull;
-        }
-
-    }
-
+    /**
+     * Return a list with the names of all known readers.
+     */
     public static List<String> getReaderNames()
     {
         final List<String> readerNames = new ArrayList<String>(readers.size());
         for (IFormatReader reader : readers)
         {
-            readerNames.add(reader.getClass().getSimpleName());
+            String readerName = getReaderName(reader);
+            readerNames.add(readerName);
         }
         return readerNames;
     }
 
     /**
-     * Returns all images of the image <var>filename</var> represented by <var>bytes</var> as
-     * {@link BufferedImage}.
-     * <p>
-     * Note that the suffix of <var>filename</var> is used to find the right reader.
-     * 
-     * @throws IOExceptionUnchecked If access to <var>handle</var> fails.
-     * @throws IllegalArgumentException If no suitable reader can be found.
-     */
-    public static BufferedImage[] readImages(String filename, byte[] bytes)
-            throws IOExceptionUnchecked, IllegalArgumentException
-    {
-        return readImages(filename, new ByteArrayHandle(bytes));
-    }
-
-    /**
-     * Returns the image <var>filename</var> represented by <var>bytes</var> as
-     * {@link BufferedImage}.
-     * <p>
-     * Note that the suffix of <var>filename</var> is used to find the right reader.
-     * 
-     * @throws IOExceptionUnchecked If access to <var>handle</var> fails.
-     * @throws IllegalArgumentException If no suitable reader can be found.
-     */
-    public static BufferedImage readImage(String filename, byte[] bytes, int page)
-            throws IOExceptionUnchecked, IllegalArgumentException
-    {
-        return readImage(filename, new ByteArrayHandle(bytes), page);
-    }
-
-    /**
-     * Returns all images of the image file given by <var>file</var> as {@link BufferedImage}.
-     * <p>
-     * Note that the suffix of <var>file</var> is used to find the right reader.
-     * 
-     * @throws IOExceptionUnchecked If access to <var>handle</var> fails.
-     * @throws IllegalArgumentException If no suitable reader can be found.
-     */
-    public static BufferedImage[] readImages(File file) throws IOExceptionUnchecked,
-            IllegalArgumentException
-    {
-        NIOFileHandle handle = null;
-        try
-        {
-            handle = new NIOFileHandle(file, "r");
-            return readImages(file.getPath(), handle);
-        } catch (IOException ex)
-        {
-            throw CheckedExceptionTunnel.wrapIfNecessary(ex);
-        } finally
-        {
-            if (handle != null)
-            {
-                try
-                {
-                    handle.close();
-                } catch (IOException ex)
-                {
-                    // Silence.
-                }
-            }
-        }
-    }
-
-    /**
-     * Returns the image <var>file</var> represented by <var>handle</var> as {@link BufferedImage}.
-     * <p>
-     * Note that the suffix of <var>file</var> is used to find the right reader.
-     * 
-     * @throws IOExceptionUnchecked If access to <var>handle</var> fails.
-     * @throws IllegalArgumentException If no suitable reader can be found.
+     * Return the name of a {@link IFormatReader}.
      */
-    public static BufferedImage readImage(File file, int page) throws IOExceptionUnchecked,
-            IllegalArgumentException
+    public static String getReaderName(IFormatReader reader)
     {
-        try
-        {
-            return readImage(file.getPath(), new NIOFileHandle(file, "r"), page);
-        } catch (IOException ex)
-        {
-            throw CheckedExceptionTunnel.wrapIfNecessary(ex);
-        }
+        return reader.getClass().getSimpleName();
     }
 
     /**
      * Returns the image <var>page</var> of the image file given by <var>filename</var> represented
      * by <var>handle</var> as {@link BufferedImage}.
-     * <p>
-     * Note that the suffix of <var>filename</var> is used to find the right reader.
      * 
      * @throws IOExceptionUnchecked If access to <var>handle</var> fails.
-     * @throws IllegalArgumentException If no suitable reader can be found.
      */
-    public static BufferedImage readImage(String filename, IRandomAccessFile handle, int page)
-            throws IOExceptionUnchecked, IllegalArgumentException
-    {
-        return readImage(filename, new BioFormatsRandomAccessAdapter(handle), page);
-    }
-
-    /**
-     * Returns the image <var>page</var> of the image file given by <var>filename</var> represented
-     * by <var>handle</var> as {@link BufferedImage}.
-     * <p>
-     * Note that the suffix of <var>filename</var> is used to find the right reader.
-     * 
-     * @throws IOExceptionUnchecked If access to <var>handle</var> fails.
-     * @throws IllegalArgumentException If no suitable reader can be found.
-     */
-    private static BufferedImage readImage(String filename, IRandomAccess handle, int page)
-            throws IOExceptionUnchecked, IllegalArgumentException
+    static BufferedImage readImage(IFormatReader reader,
+            IRandomAccess handle, int page) throws IOExceptionUnchecked, IllegalArgumentException
     {
+        String handleId = generateHandleId();
         // Add to static map.
-        Location.mapFile(filename, handle);
+        Location.mapFile(handleId, handle);
         try
         {
-            final IFormatReader reader = findReaderForFile(filename);
             // This does the actual parsing.
-            reader.setId(filename);
+            reader.setId(handleId);
             final BufferedImageReader biReader =
                     BufferedImageReader.makeBufferedImageReader(reader);
             final BufferedImage image = biReader.openImage(page);
@@ -263,32 +141,39 @@ public final class BioFormatsImageUtils
         } finally
         {
             // Remove from static map.
-            Location.mapFile(filename, null);
+            Location.mapFile(handleId, null);
         }
     }
 
     /**
-     * Returns the image <var>page</var> of the image file given by <var>filename</var> represented
-     * by <var>handle</var> as {@link BufferedImage}.
+     * An utility method that uses bio-formats reader to read an image and ImageJ to do a basic
+     * intensity rescaling.
      * <p>
-     * Note that the suffix of <var>filename</var> is used to find the right reader.
+     * Returns the image <var>filename</var> represented by <var>handle</var> as
+     * {@link BufferedImage}.
      * 
      * @throws IOExceptionUnchecked If access to <var>handle</var> fails.
      * @throws IllegalArgumentException If no suitable reader can be found.
      */
-    static BufferedImage readImage(IFormatReader formatReader, String filename,
-            IRandomAccess handle, int page) throws IOExceptionUnchecked, IllegalArgumentException
+    static BufferedImage readImageWithIntensityRescaling(IFormatReader reader,
+            IRandomAccess handle, int page, int channel) throws IOExceptionUnchecked,
+            IllegalArgumentException
     {
         // Add to static map.
-        Location.mapFile(filename, handle);
+        String handleId = generateHandleId();
+        Location.mapFile(handleId, handle);
         try
         {
-            final IFormatReader reader = findReaderForFile(filename);
             // This does the actual parsing.
-            reader.setId(filename);
-            final BufferedImageReader biReader =
-                    BufferedImageReader.makeBufferedImageReader(reader);
-            final BufferedImage image = biReader.openImage(page);
+            reader.setId(handleId);
+            int width = reader.getSizeX();
+            int height = reader.getSizeY();
+            final ImageStack stack = new ImageStack(width, height);
+            final ImageProcessor ip = BioFormatsImageProcessor.openProcessor(reader, page, channel);
+            stack.addSlice("", ip);
+            final ImagePlus imp = new ImagePlus(handleId, stack);
+
+            final BufferedImage image = imp.getBufferedImage();
             reader.close();
             return image;
         } catch (FormatException ex)
@@ -300,71 +185,13 @@ public final class BioFormatsImageUtils
         } finally
         {
             // Remove from static map.
-            Location.mapFile(filename, null);
+            Location.mapFile(handleId, null);
         }
     }
 
-    /**
-     * Returns all images of the image file given by <var>filename</var> represented by
-     * <var>handle</var> as {@link BufferedImage}.
-     * <p>
-     * Note that the suffix of <var>filename</var> is used to find the right reader.
-     * 
-     * @param filename The name of the image, suffix used to determine the reader
-     * @param handle The handle of the content of the file, may be used to determine the reader as
-     *            well. Will <i>not</i> be closed!
-     * @throws IOExceptionUnchecked If access to <var>handle</var> fails.
-     * @throws IllegalArgumentException If no suitable reader can be found.
-     */
-    public static BufferedImage[] readImages(String filename, IRandomAccessFile handle)
-            throws IOExceptionUnchecked, IllegalArgumentException
+    public static String generateHandleId()
     {
-        return readImages(filename, new BioFormatsRandomAccessAdapter(handle));
-    }
-
-    /**
-     * Returns all images of the image file given by <var>filename</var> represented by
-     * <var>handle</var> as {@link BufferedImage}.
-     * <p>
-     * Note that the suffix of <var>filename</var> is used to find the right reader.
-     * 
-     * @param filename The name of the image, suffix used to determine the reader
-     * @param handle The handle of the content of the file, may be used to determine the reader as
-     *            well. Will <i>not</i> be closed!
-     * @throws IOExceptionUnchecked If access to <var>handle</var> fails.
-     * @throws IllegalArgumentException If no suitable reader can be found.
-     */
-    private static BufferedImage[] readImages(String filename, IRandomAccess handle)
-            throws IOExceptionUnchecked, IllegalArgumentException
-    {
-        BufferedImage[] images = null;
-        // Add to static map.
-        Location.mapFile(filename, handle);
-        try
-        {
-            final IFormatReader reader = findReaderForFile(filename);
-            // This does the actual parsing.
-            reader.setId(filename);
-            final BufferedImageReader biReader =
-                    BufferedImageReader.makeBufferedImageReader(reader);
-            images = new BufferedImage[biReader.getImageCount()];
-            for (int i = 0; i < images.length; ++i)
-            {
-                images[i] = biReader.openImage(i);
-            }
-            reader.close();
-            return images;
-        } catch (FormatException ex)
-        {
-            throw CheckedExceptionTunnel.wrapIfNecessary(ex);
-        } catch (IOException ex)
-        {
-            throw CheckedExceptionTunnel.wrapIfNecessary(ex);
-        } finally
-        {
-            // Remove from static map.
-            Location.mapFile(filename, null);
-        }
+        return UUID.randomUUID().toString();
     }
 
     private BioFormatsImageUtils()
diff --git a/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsRandomAccessAdapter.java b/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsRandomAccessAdapter.java
index 2952746965724b452e5c554170e11fbb9c9c0920..57fdabf2d3f216b58d19c1bdf2eeae9eefc2cea3 100644
--- a/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsRandomAccessAdapter.java
+++ b/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsRandomAccessAdapter.java
@@ -51,17 +51,6 @@ final class BioFormatsRandomAccessAdapter implements IRandomAccess
         }
     }
 
-    public int read() throws IOException
-    {
-        try
-        {
-        return randomAccessFile.read();
-        } catch (IOExceptionUnchecked ex)
-        {
-            throw ex.getCause();
-        }
-    }
-
     @Override
     public int read(byte[] b) throws IOException
     {
@@ -122,17 +111,6 @@ final class BioFormatsRandomAccessAdapter implements IRandomAccess
         }
     }
 
-    public void setLength(long newLength) throws IOException
-    {
-        try
-        {
-        randomAccessFile.setLength(newLength);
-        } catch (IOExceptionUnchecked ex)
-        {
-            throw ex.getCause();
-        }
-    }
-
     @Override
     public void readFully(byte[] b) throws IOException
     {
diff --git a/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsReaderLibrary.java b/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsReaderLibrary.java
index f9508f17cccd889d995d382c8a3c297a95a28d58..04e6bc4daf19a2b6ee53b824e6a0a6f9f57dbec0 100644
--- a/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsReaderLibrary.java
+++ b/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsReaderLibrary.java
@@ -17,90 +17,43 @@
 package ch.systemsx.cisd.imagereaders.bioformats;
 
 import java.awt.image.BufferedImage;
-import java.io.File;
-import java.util.List;
 
+import loci.common.IRandomAccess;
 import loci.formats.IFormatReader;
 
 import ch.systemsx.cisd.base.exceptions.IOExceptionUnchecked;
 import ch.systemsx.cisd.base.io.IRandomAccessFile;
+import ch.systemsx.cisd.imagereaders.AbstractImageReader;
+import ch.systemsx.cisd.imagereaders.Constants;
 import ch.systemsx.cisd.imagereaders.IImageReader;
-import ch.systemsx.cisd.imagereaders.IImageReaderLibrary;
 
 /**
- * 
- *
  * @author Bernd Rinn
  */
-public class BioFormatsReaderLibrary implements IImageReaderLibrary
+public class BioFormatsReaderLibrary extends AbstractBioFormatsReaderLibrary
 {
 
     @Override
     public String getName()
     {
-        return "bioformats";
-    }
-
-    @Override
-    public List<String> getReaderNames()
-    {
-        return BioFormatsImageUtils.getReaderNames();
+        return Constants.BIOFORMATS_LIBRARY;
     }
 
     @Override
-    public IImageReader tryGetReader(String readerName)
+    protected IImageReader adaptFormatReader(final IFormatReader formatReader)
     {
-        final IFormatReader formatReaderOrNull = BioFormatsImageUtils.tryFindReaderByName(readerName);
-        if (formatReaderOrNull == null)
-        {
-            return null;
-        } else
-        {
-            return new IImageReader()
+        final String libraryName = getName();
+        final String readerName = BioFormatsImageUtils.getReaderName(formatReader);
+
+        return new AbstractImageReader(libraryName, readerName)
+            {
+                @Override
+                public BufferedImage readImage(IRandomAccessFile handle, int page)
+                        throws IOExceptionUnchecked
                 {
-                    
-                    @Override
-                    public BufferedImage readImage(String filename, byte[] bytes, int page)
-                    {
-                        // TODO Auto-generated method stub
-                        return null;
-                    }
-                    
-                    @Override
-                    public BufferedImage readImage(String filename, IRandomAccessFile handle, int page)
-                            throws IOExceptionUnchecked
-                    {
-                        // TODO Auto-generated method stub
-                        return null;
-                    }
-                    
-                    @Override
-                    public BufferedImage readImage(File file, int page) throws IOExceptionUnchecked
-                    {
-                        // TODO Auto-generated method stub
-                        return null;
-                    }
-                    
-                    @Override
-                    public String getName()
-                    {
-                        return formatReaderOrNull.getClass().getSimpleName();
-                    }
-                    
-                    @Override
-                    public String getLibraryName()
-                    {
-                        return BioFormatsReaderLibrary.this.getName();
-                    }
-                };
-        }
+                    IRandomAccess input = new BioFormatsRandomAccessAdapter(handle);
+                    return BioFormatsImageUtils.readImage(formatReader, input, page);
+                }
+            };
     }
-
-    @Override
-    public IImageReader tryGetReaderForFile(String fileName)
-    {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
 }
diff --git a/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsRescalingReaderLibrary.java b/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsRescalingReaderLibrary.java
new file mode 100644
index 0000000000000000000000000000000000000000..976c65ac804de4be7f3de7287888d7b0a05c6183
--- /dev/null
+++ b/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsRescalingReaderLibrary.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2011 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.imagereaders.bioformats;
+
+import java.awt.image.BufferedImage;
+
+import loci.common.IRandomAccess;
+import loci.formats.IFormatReader;
+
+import ch.systemsx.cisd.base.exceptions.IOExceptionUnchecked;
+import ch.systemsx.cisd.base.io.IRandomAccessFile;
+import ch.systemsx.cisd.imagereaders.AbstractImageReader;
+import ch.systemsx.cisd.imagereaders.Constants;
+import ch.systemsx.cisd.imagereaders.IImageReader;
+import ch.systemsx.cisd.imagereaders.IImageReaderLibrary;
+
+/**
+ * {@link IImageReaderLibrary} for bioformat readers with intensity rescaling.
+ * 
+ * @author Kaloyan Enimanev
+ */
+public class BioFormatsRescalingReaderLibrary extends AbstractBioFormatsReaderLibrary
+{
+
+    @Override
+    public String getName()
+    {
+        return Constants.BIOFORMATS_RESCALING_LIBRARY;
+    }
+
+    @Override
+    protected IImageReader adaptFormatReader(final IFormatReader formatReader)
+    {
+        final String libraryName = getName();
+        final String readerName = BioFormatsImageUtils.getReaderName(formatReader);
+
+        return new AbstractImageReader(libraryName, readerName)
+            {
+                @Override
+                public BufferedImage readImage(IRandomAccessFile handle, int page)
+                        throws IOExceptionUnchecked
+                {
+                    IRandomAccess input = new BioFormatsRandomAccessAdapter(handle);
+                    // TODO KE: Is 0 a sensible default for channel ?
+                    return BioFormatsImageUtils.readImageWithIntensityRescaling(formatReader,
+                            input, page, 0);
+                }
+            };
+    }
+}
diff --git a/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/MetaDataExtraction.java b/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/MetaDataExtraction.java
deleted file mode 100644
index 37a241e82b94a2896230392a20180b47bdb187ad..0000000000000000000000000000000000000000
--- a/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/MetaDataExtraction.java
+++ /dev/null
@@ -1,458 +0,0 @@
-//
-// MetaDataExtraction.java
-//
-
-/*
- OME Bio-Formats package for reading and converting biological file formats.
- Copyright (C) 2005-@year@ UW-Madison LOCI and Glencoe Software, Inc.
-
- This program 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 2 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, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-package ch.systemsx.cisd.imagereaders.bioformats;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Hashtable;
-import java.util.regex.Pattern;
-
-import loci.common.DebugTools;
-import loci.common.services.DependencyException;
-import loci.common.services.ServiceException;
-import loci.common.services.ServiceFactory;
-import loci.common.xml.XMLTools;
-import loci.formats.FormatException;
-import loci.formats.IFormatReader;
-import loci.formats.ImageReader;
-import loci.formats.MetadataTools;
-import loci.formats.MissingLibraryException;
-import loci.formats.gui.BufferedImageReader;
-import loci.formats.in.DefaultMetadataOptions;
-import loci.formats.in.MetadataLevel;
-import loci.formats.in.MetadataOptions;
-import loci.formats.in.OMETiffReader;
-import loci.formats.meta.MetadataRetrieve;
-import loci.formats.meta.MetadataStore;
-import loci.formats.services.OMEXMLService;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- *
- */
-public class MetaDataExtraction
-{
-
-    // -- Constants --
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(MetaDataExtraction.class);
-
-    // -- Fields --
-
-    private String directory = null;
-
-    private String filePattern = ".*";
-
-    private String fileName = null;
-
-    private String pathName = null;
-
-    private boolean doOriginal = false;
-
-    private boolean doGlobal = false;
-
-    private boolean doCSV = false;
-
-    private boolean doOMEXML = false;
-
-    private String omexmlVersion = null;
-
-    private Hashtable<String, Object> vCSVKeyToKeyMapping;
-
-    private Hashtable<String, Object> vCSVMetaData;
-
-    private String[] vCSVKeys;
-
-    private String[] vCSVMetaKeys;
-
-    private IFormatReader reader;
-
-    private IFormatReader baseReader;
-
-    // -- MetaDataExtraction methods --
-
-    public boolean parseArgs(String[] args)
-    {
-        directory = null;
-        filePattern = ".*";
-        fileName = null;
-        pathName = null;
-        doOriginal = false;
-        doGlobal = false;
-        doCSV = false;
-        doOMEXML = false;
-        omexmlVersion = null;
-        if (args == null)
-        {
-            return false;
-        }
-        for (int i = 0; i < args.length; i++)
-        {
-            if (args[i].length() == 0)
-            {
-                continue;
-            }
-            if (args[i].startsWith("-"))
-            {
-                if (args[i].equals("-csv"))
-                {
-                    doCSV = true;
-                } else if (args[i].equals("-original"))
-                {
-                    doOriginal = true;
-                } else if (args[i].equals("-global"))
-                {
-                    doGlobal = true;
-                } else if (args[i].equals("-omexml"))
-                {
-                    doOMEXML = true;
-                } else if (args[i].equals("-xmlversion"))
-                {
-                    omexmlVersion = args[++i];
-                } else if (args[i].equals("-directory"))
-                {
-                    directory = args[++i];
-                } else if (args[i].equals("-pattern"))
-                {
-                    filePattern = args[++i];
-                } else if (args[i].equals("-help") || args[i].equals("--help"))
-                {
-                    printUsage();
-                    return false;
-                } else
-                {
-                    LOGGER.error("Found unknown command flag: '{}' exiting.", args[i]);
-                    return false;
-                }
-            } else
-            {
-                LOGGER.error("Found unknown argument: '{}' exiting.", args[i]);
-                return false;
-            }
-        }
-        return true;
-    }
-
-    public void printUsage()
-    {
-        String[] s =
-                    {
-                            "To test read a file, run:",
-                            "  MetaDataExtraction -directory <directory> -pattern <pattern> [-omexml] [-csv]",
-                            "                     [-original] [-global]", "",
-                            "    -directory: the directory with image file(s) to read",
-                            "    -pattern:   regular expression for file names to read",
-                            "    -original:  print original metadata (unformatted)",
-                            "    -global:    print global metadata (unformatted)",
-                            "    -csv:       print metadata in CSV format",
-                            "    -omexml:    print metadata in OME-XML format", "" };
-        for (int i = 0; i < s.length; i++)
-        {
-            LOGGER.error(s[i]);
-        }
-    }
-
-    public void printGlobalMetadata()
-    {
-        Hashtable<String, Object> meta = reader.getGlobalMetadata();
-        String[] vOriginalMetaKeys = MetadataTools.keys(meta);
-        for (String key : vOriginalMetaKeys)
-        {
-            System.out.println(key + ": " + meta.get(key));
-        }
-    }
-
-    public void printOriginalMetadata()
-    {
-        Hashtable<String, Object> meta = reader.getSeriesMetadata();
-        String[] vOriginalMetaKeys = MetadataTools.keys(meta);
-        for (int i = 0; i < vOriginalMetaKeys.length; i++)
-        {
-            System.out.println(vOriginalMetaKeys[i] + ": " + meta.get(vOriginalMetaKeys[i]));
-        }
-    }
-
-    public void initCSVMetadata()
-    {
-        vCSVKeyToKeyMapping = new Hashtable<String, Object>();
-        vCSVKeyToKeyMapping.put("ApplicationName", "ApplicationName");
-        vCSVKeyToKeyMapping.put("ApplicationVersion", "ApplicationVersion");
-        vCSVKeyToKeyMapping.put("Camera Bit Depth", "Camera Bit Depth");
-        vCSVKeyToKeyMapping.put("Experiment base name", "Experiment base name");
-        vCSVKeyToKeyMapping.put("Experiment set", "Experiment set");
-        vCSVKeyToKeyMapping.put("Exposure", "ExposureTime");
-        vCSVKeyToKeyMapping.put("Frames to Average", "Frames to Average");
-        vCSVKeyToKeyMapping.put("Gain", "Gain");
-        vCSVKeyToKeyMapping.put("ImageXpress Micro Filter Cube", "ImageXpress Micro Filter Cube");
-        vCSVKeyToKeyMapping.put("ImageXpress Micro Objective", "ImageXpress Micro Objective");
-        vCSVKeyToKeyMapping.put("Laser focus score", "Laser focus score");
-        vCSVKeyToKeyMapping.put("Shading", "Shading");
-        vCSVKeyToKeyMapping.put("Subtract", "Subtract");
-        vCSVKeyToKeyMapping.put("Temperature", "Temperature");
-        // vCSVKeyToKeyMapping.put("_IllumSetting_", "_IllumSetting_");
-        // vCSVKeyToKeyMapping.put("_MagSetting_", "_MagSetting_");
-        vCSVKeyToKeyMapping.put("acquisition-time-local", "AcquiredDate");
-        vCSVKeyToKeyMapping.put("camera-binning-x", "camera-binning-x");
-        vCSVKeyToKeyMapping.put("camera-binning-y", "camera-binning-y");
-        vCSVKeyToKeyMapping.put("camera-chip-offset-x", "camera-chip-offset-x");
-        vCSVKeyToKeyMapping.put("camera-chip-offset-y", "camera-chip-offset-y");
-        vCSVKeyToKeyMapping.put("gamma", "gamma");
-        vCSVKeyToKeyMapping.put("number-of-planes", "SizeZ");
-        vCSVKeyToKeyMapping.put("pixel-size-x", "SizeX");
-        vCSVKeyToKeyMapping.put("pixel-size-y", "SizeY");
-        vCSVKeyToKeyMapping.put("spatial-calibration-state", "spatial-calibration-state");
-        vCSVKeyToKeyMapping.put("spatial-calibration-units", "spatial-calibration-units");
-        vCSVKeyToKeyMapping.put("spatial-calibration-x", "PhysicalSizeX");
-        vCSVKeyToKeyMapping.put("spatial-calibration-y", "PhysicalSizeY");
-        vCSVKeyToKeyMapping.put("stage-label", "stage-label");
-        vCSVKeyToKeyMapping.put("stage-position-x", "PositionX");
-        vCSVKeyToKeyMapping.put("stage-position-y", "PositionY");
-        vCSVKeyToKeyMapping.put("z-position", "PositionZ");
-        vCSVKeyToKeyMapping.put("wavelength", "wavelength");
-        vCSVKeys = MetadataTools.keys(vCSVKeyToKeyMapping);
-
-        vCSVMetaData = new Hashtable<String, Object>();
-        for (int i = 0; i < vCSVKeys.length; i++)
-        {
-            String vCSVMappedKey = "" + vCSVKeyToKeyMapping.get(vCSVKeys[i]);
-            String vOriginalMetaValue = "-";
-            vCSVMetaData.put(vCSVMappedKey, vOriginalMetaValue);
-        }
-        vCSVMetaKeys = MetadataTools.keys(vCSVMetaData);
-    }
-
-    public void printCSVHeader()
-    {
-        System.out.print("\"" + "Filename" + "\",");
-        for (int i = 0; i < vCSVMetaKeys.length; i++)
-        {
-            System.out.print("\"" + vCSVMetaKeys[i] + "\",");
-        }
-        System.out.println("");
-    }
-
-    public void printCSVMetadata()
-    {
-        Hashtable<String, Object> vOriginalMetaData = reader.getSeriesMetadata();
-        // String[] vOriginalMetaKeys = MetadataTools.keys(vOriginalMetaData);
-
-        vCSVMetaData = new Hashtable<String, Object>();
-        for (int i = 0; i < vCSVKeys.length; i++)
-        {
-            String vCSVMappedKey = "" + vCSVKeyToKeyMapping.get(vCSVKeys[i]);
-            String vOriginalMetaValue;
-            if (vOriginalMetaData.containsKey(vCSVKeys[i]))
-            {
-                vOriginalMetaValue = "" + vOriginalMetaData.get(vCSVKeys[i]);
-            } else
-            {
-                vOriginalMetaValue = "-";
-            }
-            vCSVMetaData.put(vCSVMappedKey, vOriginalMetaValue);
-        }
-
-        // Print the CSV output
-        System.out.print("\"" + fileName + "\",");
-        for (int i = 0; i < vCSVMetaKeys.length; i++)
-        {
-            System.out.print("\"" + vCSVMetaData.get(vCSVMetaKeys[i]) + "\",");
-        }
-        System.out.println("");
-    }
-
-    public void printOMEXML() throws MissingLibraryException, ServiceException
-    {
-        MetadataStore ms = reader.getMetadataStore();
-
-        if (baseReader instanceof ImageReader)
-        {
-            baseReader = ((ImageReader) baseReader).getReader();
-        }
-        if (baseReader instanceof OMETiffReader)
-        {
-            ms = ((OMETiffReader) baseReader).getMetadataStoreForDisplay();
-        }
-
-        OMEXMLService service;
-        try
-        {
-            ServiceFactory factory = new ServiceFactory();
-            service = factory.getInstance(OMEXMLService.class);
-        } catch (DependencyException de)
-        {
-            throw new MissingLibraryException(OMETiffReader.NO_OME_XML_MSG, de);
-        }
-        if (ms instanceof MetadataRetrieve)
-        {
-            String xml = service.getOMEXML((MetadataRetrieve) ms);
-            if (service.validateOMEXML(xml))
-            {
-                System.out.print(XMLTools.indentXML(xml, true));
-            } else
-            {
-                LOGGER.error("Could not validate OME-XML.");
-            }
-        } else
-        {
-            LOGGER.error("The metadata could not be converted to OME-XML.");
-            if (omexmlVersion == null)
-            {
-                LOGGER.error("The OME-XML Java library is probably not available.");
-            } else
-            {
-                LOGGER.error("{} is probably not a legal schema version.", omexmlVersion);
-            }
-        }
-    }
-
-    /**
-     * A utility method for reading a file from the command line, and displaying the results in a
-     * simple display.
-     */
-    public boolean testRead(String[] args) throws FormatException, ServiceException, IOException
-    {
-        // DebugTools.enableLogging("INFO");
-        DebugTools.enableLogging("ERROR");
-        boolean validArgs = parseArgs(args);
-        if (validArgs == false)
-        {
-            return false;
-        }
-        if (directory == null)
-        {
-            printUsage();
-            return false;
-        }
-        Pattern vPattern = Pattern.compile(filePattern, Pattern.CASE_INSENSITIVE);
-
-        // initialize required structures
-        boolean shownCSVHeader = false;
-        if (doCSV)
-        {
-            initCSVMetadata();
-        }
-
-        // initialize the reader
-        baseReader = new ImageReader();
-        reader = new BufferedImageReader(baseReader);
-
-        MetadataOptions metaOptions = new DefaultMetadataOptions(MetadataLevel.ALL);
-
-        if (doOMEXML)
-        {
-            reader.setOriginalMetadataPopulated(true);
-            try
-            {
-                ServiceFactory factory = new ServiceFactory();
-                OMEXMLService service = factory.getInstance(OMEXMLService.class);
-                reader.setMetadataStore(service.createOMEXMLMetadata(null, omexmlVersion));
-            } catch (DependencyException de)
-            {
-                throw new MissingLibraryException(OMETiffReader.NO_OME_XML_MSG, de);
-            } catch (ServiceException se)
-            {
-                throw new FormatException(se);
-            }
-        }
-
-        // Get a sorted directory listing
-        File directoryObject = new File(directory);
-        pathName = directoryObject.getAbsolutePath();
-        String[] directoryObjectEntries = directoryObject.list();
-        Arrays.sort(directoryObjectEntries);
-
-        // Iterate over the available files
-        for (int vFileIdx = 0; vFileIdx < directoryObjectEntries.length; vFileIdx++)
-        {
-            File directoryObjectEntry = new File(directoryObjectEntries[vFileIdx]);
-            if (directoryObjectEntry.isDirectory())
-            {
-                continue;
-            }
-
-            fileName = directoryObjectEntry.getName();
-
-            boolean vMatches = vPattern.matcher(fileName).matches();
-            if (vMatches == false)
-            {
-                continue;
-            }
-
-            // System.out.println("Found " + vFileIdx + ": " + FileName + ": " + vMatches);
-
-            reader.close();
-            reader.setMetadataOptions(metaOptions);
-
-            // initialize reader
-            try
-            {
-                // mapLocation();
-                reader.setId(pathName + "/" + fileName);
-
-                // read format-specific metadata table
-                if (doOriginal)
-                {
-                    printOriginalMetadata();
-                }
-                if (doGlobal)
-                {
-                    printGlobalMetadata();
-                }
-                if (doCSV)
-                {
-                    if (shownCSVHeader == false)
-                    {
-                        printCSVHeader();
-                        shownCSVHeader = true;
-                    }
-                    printCSVMetadata();
-                }
-                if (doOMEXML)
-                {
-                    printOMEXML();
-                }
-            } catch (FormatException e)
-            {
-            } catch (IOException e)
-            {
-            }
-        }
-
-        return true;
-    }
-
-    // -- Main method --
-
-    public static void main(String[] args) throws Exception
-    {
-        if (new MetaDataExtraction().testRead(args) == false)
-        {
-            System.exit(1);
-        }
-    }
-
-}
diff --git a/image_readers/sourceTest/java/ch/systemsx/cisd/imagereaders/ImageReaderFactoryTest.java b/image_readers/sourceTest/java/ch/systemsx/cisd/imagereaders/ImageReaderFactoryTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..00982caaba469db5f453ac40523ab3b9c28c037f
--- /dev/null
+++ b/image_readers/sourceTest/java/ch/systemsx/cisd/imagereaders/ImageReaderFactoryTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2011 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.imagereaders;
+
+import static ch.systemsx.cisd.imagereaders.Constants.BIOFORMATS_LIBRARY;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+
+import org.testng.AssertJUnit;
+import org.testng.annotations.Test;
+
+import ch.systemsx.cisd.base.exceptions.IOExceptionUnchecked;
+
+/**
+ * Unit test for {@link ImageReaderFactory}.
+ * 
+ * @author Kaloyan Enimanev
+ */
+public class ImageReaderFactoryTest extends AssertJUnit
+{
+    private static final String IMAGES_DIR = "./sourceTest/resources/images/";
+
+    @Test
+    public void testBioFormatReaders()
+    {
+        assertImageReadable(BIOFORMATS_LIBRARY, getResourceFile("demo.tif"));
+        assertImageReadable(BIOFORMATS_LIBRARY, getResourceFile("annapolis.jpg"));
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void testGetInvalidLibrary()
+    {
+        assertImageReadable("Invalid_library", getResourceFile("demo.tif"));
+    }
+
+    @Test(expectedExceptions = IOExceptionUnchecked.class)
+    public void testReadNonExistingFile()
+    {
+        final String invalidName = "invalid_file_path.jpg";
+        final File invalidFile = new File(invalidName);
+
+        IImageReader reader =
+                ImageReaderFactory.tryGetImageReaderForFile(BIOFORMATS_LIBRARY, invalidName);
+        reader.readImage(invalidFile, 0);
+    }
+
+    private void assertImageReadable(String libraryName, File file)
+    {
+        IImageReader reader =
+                ImageReaderFactory.tryGetImageReaderForFile(libraryName, file.getAbsolutePath());
+        assertNotNull("Reader should not have NULL name", reader.getName());
+        assertEquals(libraryName, reader.getLibraryName());
+
+        String error =
+                String.format("Cannot find appropriate reader for file '%s' " + "in library '%s'",
+                        libraryName, file.getAbsolutePath());
+        assertNotNull(error, reader);
+
+        BufferedImage image = reader.readImage(file, 0);
+        assertNotNull("Read image should not be null", image);
+        assertTrue("Image should have non-negative height", image.getHeight() > 0);
+        assertTrue("Image should have non-negative width", image.getWidth() > 0);
+    }
+
+    private File getResourceFile(String name)
+    {
+        return new File(IMAGES_DIR, name);
+    }
+
+}
diff --git a/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsImageJImageViewer.java b/image_readers/sourceTest/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsImageJImageViewer.java
similarity index 77%
rename from image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsImageJImageViewer.java
rename to image_readers/sourceTest/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsImageJImageViewer.java
index d7d346db879800ce404057a36725c4b782fafa8f..861129118bbd618fd5e9709e45781dd44e465c79 100644
--- a/image_readers/source/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsImageJImageViewer.java
+++ b/image_readers/sourceTest/java/ch/systemsx/cisd/imagereaders/bioformats/BioFormatsImageJImageViewer.java
@@ -19,15 +19,19 @@ package ch.systemsx.cisd.imagereaders.bioformats;
 import java.awt.Frame;
 import java.awt.image.BufferedImage;
 import java.awt.image.RenderedImage;
+import java.io.File;
 
 import javax.media.jai.widget.ScrollingImagePanel;
 
 import org.apache.log4j.BasicConfigurator;
 
-import ch.systemsx.cisd.base.io.RandomAccessFileImpl;
+import ch.systemsx.cisd.imagereaders.Constants;
+import ch.systemsx.cisd.imagereaders.IImageReader;
+import ch.systemsx.cisd.imagereaders.ImageReaderFactory;
 
 /**
  * An image viewer that uses BioFormats for reading and ImageJ for intensity rescaling.
+ * <p>
  * 
  * @author Bernd Rinn
  */
@@ -54,9 +58,14 @@ public class BioFormatsImageJImageViewer
             System.exit(1);
         }
         BasicConfigurator.configure();
-        final BufferedImage image =
-                BioFormatsImageJUtils.readAndTransformImage(args[0], new RandomAccessFileImpl(
-                        args[0], "r"), 0, 0);
+
+        final String fileName = args[0];
+        IImageReader imageReader =
+                ImageReaderFactory.tryGetImageReaderForFile(Constants.BIOFORMATS_RESCALING_LIBRARY,
+                        fileName);
+
+        File file = new File(fileName);
+        final BufferedImage image = imageReader.readImage(file, 0);
         showImage(image);
     }
 }
diff --git a/image_readers/sourceTest/resources/images/annapolis.jpg b/image_readers/sourceTest/resources/images/annapolis.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..61bc6775f0f6394f8e684338df416f7d1732d64f
Binary files /dev/null and b/image_readers/sourceTest/resources/images/annapolis.jpg differ
diff --git a/image_readers/sourceTest/resources/images/demo.tif b/image_readers/sourceTest/resources/images/demo.tif
new file mode 100644
index 0000000000000000000000000000000000000000..b8290408d1d313b80c144fa74baab7eb31eacaab
Binary files /dev/null and b/image_readers/sourceTest/resources/images/demo.tif differ