/*
* #%L
* OME Bio-Formats manual and automated test suite.
* %%
* Copyright (C) 2006 - 2015 Open Microscopy Environment:
* - Board of Regents of the University of Wisconsin-Madison
* - Glencoe Software, Inc.
* - University of Dundee
* %%
* 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, see
* <http://www.gnu.org/licenses/gpl-2.0.html>.
* #L%
*/
package loci.tests.testng;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import java.io.File;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import loci.common.Constants;
import loci.common.services.DependencyException;
import loci.common.services.ServiceException;
import loci.common.services.ServiceFactory;
import loci.formats.FormatException;
import loci.formats.FormatTools;
import loci.formats.ImageReader;
import loci.formats.ImageWriter;
import loci.formats.MetadataTools;
import loci.formats.codec.CompressionType;
import loci.formats.meta.IMetadata;
import loci.formats.services.OMEXMLService;
import org.testng.annotations.Test;
/**
*
* @author Melissa Linkert <melissa at glencoesoftware.com>
*/
public class ConversionTest {
private static final int WIDTH = 64;
private static final int HEIGHT = 64;
private static final boolean[] LITTLE_ENDIAN = {true, false};
private static final String[] DEFAULT_TYPES = {"int8", "uint8", "int16",
"uint16", "int32", "uint32", "float", "double"};
private byte[] getPlane(int width, int height, int bytes) {
byte[] plane = new byte[width * height * bytes];
for (int i=0; i<plane.length; i++) {
plane[i] = (byte) i;
}
return plane;
}
private IMetadata createMetadata(String pixelType, int rgbChannels,
int seriesCount, boolean littleEndian) throws Exception
{
IMetadata metadata;
try {
ServiceFactory factory = new ServiceFactory();
OMEXMLService service = factory.getInstance(OMEXMLService.class);
metadata = service.createOMEXMLMetadata();
}
catch (DependencyException exc) {
throw new FormatException("Could not create OME-XML store.", exc);
}
catch (ServiceException exc) {
throw new FormatException("Could not create OME-XML store.", exc);
}
for (int i=0; i<seriesCount; i++) {
MetadataTools.populateMetadata(metadata, i, "image #" + i, littleEndian,
"XYCZT", pixelType, WIDTH, HEIGHT, 1, rgbChannels, 1, rgbChannels);
}
return metadata;
}
private void testCompressDecompress(String ext, String compression,
boolean lossy, int seriesCount, String pixelType) throws Exception
{
testCompressDecompress(
ext, compression, lossy, 1, seriesCount, pixelType, true);
testCompressDecompress(
ext, compression, lossy, 1, seriesCount, pixelType, false);
testCompressDecompress(
ext, compression, lossy, 3, seriesCount, pixelType, true);
testCompressDecompress(
ext, compression, lossy, 3, seriesCount, pixelType, false);
}
private void testCompressDecompress(String ext, String compression,
boolean lossy, int rgbChannels, int seriesCount, String pixelType,
boolean littleEndian)
throws Exception
{
File tmp = File.createTempFile("conversionTest", ext);
tmp.deleteOnExit();
ImageWriter writer = new ImageWriter();
writer.setMetadataRetrieve(createMetadata(
pixelType, rgbChannels, seriesCount, littleEndian));
writer.setId(tmp.getAbsolutePath());
int bytes = FormatTools.getBytesPerPixel(pixelType);
byte[] plane = getPlane(WIDTH, HEIGHT, bytes * rgbChannels);
Plane originalPlane = new Plane(plane, littleEndian,
!writer.isInterleaved(), rgbChannels, pixelType);
for (int s=0; s<seriesCount; s++) {
writer.setSeries(s);
writer.saveBytes(0, plane);
}
writer.close();
ImageReader reader = new ImageReader();
reader.setId(tmp.getAbsolutePath());
assertEquals(reader.getSeriesCount(), seriesCount);
for (int s=0; s<seriesCount; s++) {
reader.setSeries(s);
assertEquals(reader.getSizeC(), rgbChannels);
assertTrue(reader.getImageCount() == rgbChannels || reader.isRGB());
byte[] readPlane = reader.openBytes(0);
if (!lossy) {
Plane newPlane = new Plane(readPlane, reader.isLittleEndian(),
!reader.isInterleaved(), reader.getRGBChannelCount(),
FormatTools.getPixelTypeString(reader.getPixelType()));
assertTrue(originalPlane.equals(newPlane), tmp.getAbsolutePath());
}
}
reader.close();
}
@Test
public void testTIFF() throws Exception {
String ext = ".tiff";
String[] jpegTypes = {"int8", "uint8", "int16", "uint16"};
String[] jpeg2000Types = {"int8", "uint8", "int16", "uint16", "int32",
"uint32", "float"};
for (String jpegType : jpegTypes) {
String compression = CompressionType.JPEG.getCompression();
testCompressDecompress(ext, compression, true, 1, jpegType);
}
for (String jpeg2KType : jpeg2000Types) {
String compression = CompressionType.J2K.getCompression();
testCompressDecompress(ext, compression, false, 1, jpeg2KType);
}
for (String type : DEFAULT_TYPES) {
String compression = CompressionType.UNCOMPRESSED.getCompression();
testCompressDecompress(ext, compression, false, 1, type);
compression = CompressionType.LZW.getCompression();
testCompressDecompress(ext, compression, false, 1, type);
}
}
@Test
public void testOMETIFF() throws Exception {
String ext = ".ome.tiff";
String[] jpegTypes = {"int8", "uint8", "int16", "uint16"};
String[] jpeg2000Types = {"int8", "uint8", "int16", "uint16", "int32",
"uint32", "float"};
int[] seriesCounts = {1, 2};
for (int seriesCount : seriesCounts) {
for (String jpegType : jpegTypes) {
String compression = CompressionType.JPEG.getCompression();
testCompressDecompress(ext, compression, true, seriesCount, jpegType);
}
for (String jpeg2KType : jpeg2000Types) {
String compression = CompressionType.J2K.getCompression();
testCompressDecompress(
ext, compression, false, seriesCount, jpeg2KType);
}
for (String type : DEFAULT_TYPES) {
String compression = CompressionType.UNCOMPRESSED.getCompression();
testCompressDecompress(ext, compression, false, seriesCount, type);
compression = CompressionType.LZW.getCompression();
testCompressDecompress(ext, compression, false, seriesCount, type);
}
}
}
@Test
public void testOMEXML() throws Exception {
String ext = ".ome";
int[] seriesCounts = {1, 2};
String[] types = {"int8", "uint8", "int16", "uint16", "int32",
"uint32", "float"};
for (int seriesCount : seriesCounts) {
for (String type : types) {
String compression = CompressionType.UNCOMPRESSED.getCompression();
testCompressDecompress(ext, compression, false, seriesCount, type);
compression = CompressionType.ZLIB.getCompression();
testCompressDecompress(ext, compression, false, seriesCount, type);
}
}
}
@Test
public void testAPNG() throws Exception {
String ext = ".png";
String[] pixelTypes = {"int8", "uint8", "int16", "uint16"};
for (String type : pixelTypes) {
String compression = CompressionType.UNCOMPRESSED.getCompression();
testCompressDecompress(ext, compression, false, 1, type);
}
}
@Test
public void testAVI() throws Exception {
String ext = ".avi";
String compression = CompressionType.UNCOMPRESSED.getCompression();
testCompressDecompress(ext, compression, false, 1, "uint8");
}
@Test
public void testEPS() throws Exception {
String ext = ".eps";
String compression = CompressionType.UNCOMPRESSED.getCompression();
testCompressDecompress(ext, compression, false, 1, "uint8");
}
@Test
public void testICS() throws Exception {
String ext = ".ics";
String[] pixelTypes = {"int8", "uint8", "int16", "uint16", "int32",
"uint32", "float"};
for (String type : pixelTypes) {
String compression = CompressionType.UNCOMPRESSED.getCompression();
testCompressDecompress(ext, compression, false, 1, type);
}
}
@Test
public void testJPEG2000() throws Exception {
String ext = ".jp2";
String[] pixelTypes = {"int8", "uint8", "int16", "uint16"};
for (String type : pixelTypes) {
String compression = CompressionType.J2K.getCompression();
testCompressDecompress(ext, compression, false, 1, type);
compression = CompressionType.J2K_LOSSY.getCompression();
testCompressDecompress(ext, compression, true, 1, type);
}
}
@Test
public void testJPEG() throws Exception {
String ext = ".jpg";
String compression = CompressionType.JPEG.getCompression();
testCompressDecompress(ext, compression, true, 1, "uint8");
}
@Test
public void testQuickTime() throws Exception {
String ext = ".mov";
String compression = CompressionType.UNCOMPRESSED.getCompression();
testCompressDecompress(ext, compression, false, 1, "uint8");
}
class Plane {
public ByteBuffer backingBuffer;
public boolean rgbPlanar;
public int rgbChannels;
public String pixelType;
public Plane(byte[] buffer, boolean littleEndian, boolean planar,
int rgbChannels, String pixelType)
{
backingBuffer = ByteBuffer.wrap(buffer);
backingBuffer.order(
littleEndian ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
this.rgbPlanar = planar;
this.pixelType = pixelType;
this.rgbChannels = rgbChannels;
}
public boolean equals(Plane other) {
backingBuffer.position(0);
other.backingBuffer.position(0);
int bytes = FormatTools.getBytesPerPixel(pixelType);
boolean fp =
FormatTools.isFloatingPoint(FormatTools.pixelTypeFromString(pixelType));
while (backingBuffer.position() < backingBuffer.capacity()) {
int otherPos = backingBuffer.position();
if (rgbPlanar != other.rgbPlanar) {
int channel = -1;
int pixel = -1;
int pos = backingBuffer.position() / bytes;
int capacity = backingBuffer.capacity();
if (rgbPlanar) {
pixel = pos % (capacity / (rgbChannels * bytes));
channel = pos / (capacity / (rgbChannels * bytes));
}
else {
channel = pos % rgbChannels;
pixel = pos / rgbChannels;
}
if (other.rgbPlanar) {
otherPos = channel * (capacity / rgbChannels) + pixel * bytes;
}
else {
otherPos = (pixel * rgbChannels + channel) * bytes;
}
}
if (otherPos >= other.backingBuffer.capacity()) {
break;
}
other.backingBuffer.position(otherPos);
switch (bytes) {
case 1:
byte thisB = backingBuffer.get();
byte otherB = other.backingBuffer.get();
if (thisB != otherB) {
if (!pixelType.equals(other.pixelType)) {
if ((byte) (thisB - 128) != otherB) {
return false;
}
}
else {
return false;
}
}
break;
case 2:
short thisS = backingBuffer.getShort();
short otherS = other.backingBuffer.getShort();
if (thisS != otherS) {
if (!pixelType.equals(other.pixelType)) {
if ((short) (thisS - 32768) != otherS) {
return false;
}
}
else {
return false;
}
}
break;
case 4:
if (fp) {
float thisF = backingBuffer.getFloat();
float otherF = other.backingBuffer.getFloat();
if (Math.abs(thisF - otherF) > Constants.EPSILON) {
return false;
}
}
else {
int thisI = backingBuffer.getInt();
int otherI = other.backingBuffer.getInt();
if (thisI != otherI) {
return false;
}
}
break;
case 8:
if (fp) {
double thisD = backingBuffer.getDouble();
double otherD = other.backingBuffer.getDouble();
if (Math.abs(thisD - otherD) > Constants.EPSILON) {
return false;
}
}
else {
long thisL = backingBuffer.getLong();
long otherL = other.backingBuffer.getLong();
if (thisL != otherL) {
return false;
}
}
break;
}
}
return true;
}
}
}