/*
* #%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.AssertJUnit.fail;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
import loci.common.services.ServiceFactory;
import loci.formats.IFormatReader;
import loci.formats.ImageReader;
import loci.formats.in.TiffReader;
import loci.formats.meta.IMetadata;
import loci.formats.meta.MetadataConverter;
import loci.formats.out.TiffWriter;
import loci.formats.services.OMEXMLService;
import loci.formats.tiff.IFD;
import loci.formats.tiff.TiffCompression;
/**
* Tests writing of tiles in a tiff.
*
* @author Jean-Marie Burel <j dot burel at dundee dot ac dot uk>
*/
public class TiffWriterTest {
/** Reader */
private IFormatReader reader;
/** The metadata store. */
private IMetadata metadata;
/** Service to create the metadata store. */
private OMEXMLService service;
/** The compression levels to test. */
private final static String[] COMPRESSION;
/** The big tiff flags. */
private final static Boolean[] BIG_TIFF;
static {
COMPRESSION = new String[2];
COMPRESSION[0] = TiffCompression.UNCOMPRESSED.getCodecName();
COMPRESSION[1] = TiffCompression.JPEG_2000.getCodecName();
BIG_TIFF = new Boolean[2];
BIG_TIFF[0] = Boolean.valueOf(false);
BIG_TIFF[1] = Boolean.valueOf(true);
}
/**
* Initializes the writer.
* @param output The file where to write the compressed data.
* @param compression The compression to use.
* @param bigTiff Pass <code>true</code> to set the <code>bigTiff</code> flag,
* <code>false</code> otherwise.
* @return See above.
* @throws Exception Thrown if an error occurred.
*/
private TiffWriter initializeWriter(String output, String compression, boolean
bigTiff)
throws Exception {
IMetadata newMetadata = service.createOMEXMLMetadata();
MetadataConverter.convertMetadata(metadata, newMetadata);
TiffWriter writer = new TiffWriter();
writer.setMetadataRetrieve(newMetadata);
writer.setCompression(compression);
writer.setWriteSequentially(true);
writer.setInterleaved(false);
writer.setBigTiff(bigTiff);
writer.setId(output);
return writer;
}
/**
* Tests the writing of the tiles.
* @param output The output where to write the data.
* @param compression The compression to use.
* @param n The value by which to divide the width of the image.
* @param m The value by which to divide the height of the image.
* @param bigTiff Pass <code>true</code> to set the <code>bigTiff</code> flag,
* <code>false</code> otherwise.
*/
private void assertTiles(String output, String compression, int n, int m,
boolean bigTiff)
throws Exception {
TiffWriter writer = initializeWriter(output, compression, bigTiff);
int x, y;
byte[] tile;
long[] rowPerStrip;
int w, h;
IFD ifd;
int count;
int series = reader.getSeriesCount();
String[][][] tileMD5s = new String[series][][];
for (int s = 0; s < series; s++) {
reader.setSeries(s);
w = reader.getSizeX()/n;
h = reader.getSizeY()/m;
rowPerStrip = new long[1];
rowPerStrip[0] = h;
count = reader.getImageCount();
tileMD5s[s] = new String[count][m * n];
for (int k = 0; k < count; k++) {
ifd = new IFD();
ifd.put(IFD.TILE_WIDTH, w);
ifd.put(IFD.TILE_LENGTH, h);
ifd.put(IFD.ROWS_PER_STRIP, rowPerStrip);
for (int i = 0; i < m; i++) {
y = h*i;
for (int j = 0; j < n; j++) {
x = w*j;
tile = reader.openBytes(k, x, y, w, h);
tileMD5s[s][k][(i * n) + j] = TestTools.md5(tile);
writer.saveBytes(k, tile, ifd, x, y, w, h);
}
}
}
}
writer.close();
//Now going to read the output.
TiffReader outputReader = new TiffReader();
outputReader.setId(output);
//first series.
String writtenDigest;
String readDigest;
for (int s = 0; s < series; s++) {
outputReader.setSeries(s);
count = outputReader.getImageCount();
h = outputReader.getSizeY()/m;
w = outputReader.getSizeX()/n;
for (int k = 0; k < count; k++) {
for (int i = 0; i < m; i++) {
y = h*i;
for (int j = 0; j < n; j++) {
x = w*j;
tile = outputReader.openBytes(k, x, y, w, h);
writtenDigest = tileMD5s[s][k][(i * n) + j];
readDigest = TestTools.md5(tile);
if (!writtenDigest.equals(readDigest)) {
fail(String.format(
"Compression:%s MD5:%d;%d;%d;%d;%d; %s != %s",
compression, k, x, y, w, h, writtenDigest, readDigest));
}
}
}
}
}
outputReader.close();
}
/**
* Tests the writing of the tiles.
* @param output The output where to write the data.
* @param compression The compression to use.
* @param blockWidth The width of block to write.
* @param blockHeight The height of block to write.
* @param bigTiff Pass <code>true</code> to set the <code>bigTiff</code> flag,
* <code>false</code> otherwise.
*/
private void assertUnevenTiles(String output, String compression,
int blockWidth, int blockHeight, boolean bigTiff)
throws Exception {
TiffWriter writer = initializeWriter(output, compression, bigTiff);
int x, y;
byte[] tile;
long[] rowPerStrip;
int w, h;
IFD ifd;
int count;
int sizeX, sizeY;
int n, m;
int diffWidth, diffHeight;
int series = reader.getSeriesCount();
String[][][] tileMD5s = new String[series][][];
for (int s = 0; s < series; s++) {
reader.setSeries(s);
sizeX = reader.getSizeX();
sizeY = reader.getSizeY();
if (blockWidth <= 0) blockWidth = sizeX;
if (blockHeight <= 0) blockHeight = sizeY;
n = sizeX/blockWidth;
m = sizeY/blockHeight;
if (n == 0) {
blockWidth = sizeX;
n = 1;
}
if (m == 0) {
blockHeight = sizeY;
m = 1;
}
diffWidth = sizeX-n*blockWidth;
diffHeight = sizeY-m*blockHeight;
if (diffWidth > 0) n++;
if (diffHeight > 0) m++;
rowPerStrip = new long[1];
rowPerStrip[0] = blockHeight;
count = reader.getImageCount();
tileMD5s[s] = new String[count][m * n];
for (int k = 0; k < count; k++) {
x = 0;
y = 0;
ifd = new IFD();
ifd.put(IFD.TILE_WIDTH, blockWidth);
ifd.put(IFD.TILE_LENGTH, blockHeight);
ifd.put(IFD.ROWS_PER_STRIP, rowPerStrip);
for (int i = 0; i < m; i++) {
if (diffHeight > 0 && i == (m-1)) {
y = sizeY-diffHeight;
h = diffHeight;
} else {
y = blockHeight*i;
h = blockHeight;
}
for (int j = 0; j < n; j++) {
if (diffWidth > 0 && j == (n-1)) {
x = sizeX-diffWidth;
w = diffWidth;
} else {
x = blockWidth*j;
w = blockWidth;
}
tile = reader.openBytes(k, x, y, w, h);
tileMD5s[s][k][(i * n) + j] = TestTools.md5(tile);
writer.saveBytes(0, tile, ifd, x, y, w, h);
}
}
}
}
writer.close();
//Now going to read the output.
TiffReader outputReader = new TiffReader();
outputReader.setId(output);
//first series.
String writtenDigest;
String readDigest;
for (int s = 0; s < series; s++) {
outputReader.setSeries(s);
count = outputReader.getImageCount();
for (int k = 0; k < count; k++) {
sizeX = outputReader.getSizeX();
sizeY = outputReader.getSizeY();
n = sizeX/blockWidth;
m = sizeY/blockHeight;
diffWidth = sizeX-n*blockWidth;
diffHeight = sizeY-m*blockHeight;
if (diffWidth > 0) n++;
if (diffHeight > 0) m++;
for (int i = 0; i < m; i++) {
if (diffHeight > 0 && i == (m-1)) {
y = sizeY-diffHeight;
h = diffHeight;
} else {
y = blockHeight*i;
h = blockHeight;
}
for (int j = 0; j < n; j++) {
if (diffWidth > 0 && j == (n-1)) {
x = sizeX-diffWidth;
w = diffWidth;
} else {
x = blockWidth*j;
w = blockWidth;
}
tile = outputReader.openBytes(k, x, y, w, h);
writtenDigest = tileMD5s[s][k][(i * n) + j];
readDigest = TestTools.md5(tile);
if (!writtenDigest.equals(readDigest)) {
fail(String.format(
"Compression:%s MD5:%d;%d;%d;%d;%d; %s != %s",
compression, k, x, y, w, h, writtenDigest, readDigest));
}
}
}
}
}
outputReader.close();
}
@Parameters({"id"})
@BeforeClass
public void parse(String id) throws Exception {
ServiceFactory factory = new ServiceFactory();
service = factory.getInstance(OMEXMLService.class);
metadata = service.createOMEXMLMetadata();
reader = new ImageReader();
reader.setMetadataStore(metadata);
reader.setId(id);
}
@AfterClass
public void tearDown() throws Exception {
reader.close();
}
/**
* Tests the writing of the full size image as JPEG200 stream.
* @throws Exception Throw if an error occurred while writing.
*/
@Test(enabled=true)
public void testWriteFullImage() throws Exception {
File f;
for (int i = 0; i < COMPRESSION.length; i++) {
for (int j = 0; j < BIG_TIFF.length; j++) {
f = File.createTempFile("testWriteFullImage_"+j+"_"+
COMPRESSION[i], ".tiff");
f.deleteOnExit();
assertTiles(f.getAbsolutePath(), COMPRESSION[i], 1, 1, BIG_TIFF[j]);
}
}
}
/**
* Tests the writing of the image divided in 4 blocks.
* @throws Exception Throw if an error occurred while writing.
*/
@Test(enabled=true)
public void testWriteImageFourTiles() throws Exception {
File f;
for (int i = 0; i < COMPRESSION.length; i++) {
for (int j=0; j<BIG_TIFF.length; j++) {
f = File.createTempFile("testWriteImageFourTiles_" + j + "_" +
COMPRESSION[i], ".tiff");
f.deleteOnExit();
assertTiles(f.getAbsolutePath(), COMPRESSION[i], 2, 2, BIG_TIFF[j]);
}
}
}
/**
* Tests the writing of the image with 2 tiles with full width.
* @throws Exception Throw if an error occurred while writing.
*/
@Test(enabled=true)
public void testWriteImageSplitHorizontal() throws Exception {
File f;
for (int i = 0; i < COMPRESSION.length; i++) {
for (int j=0; j<BIG_TIFF.length; j++) {
f = File.createTempFile("testWriteImageSplitHorizontal_" + j + "_" +
COMPRESSION[i], ".tiff");
f.deleteOnExit();
assertTiles(f.getAbsolutePath(), COMPRESSION[i], 1, 2, BIG_TIFF[j]);
}
}
}
/**
* Tests the writing of the image with 2 tiles with full height.
* @throws Exception Throw if an error occurred while writing.
*/
@Test(enabled=true)
public void testWriteImageSplitVertical() throws Exception {
File f;
for (int i = 0; i < COMPRESSION.length; i++) {
for (int j=0; j<BIG_TIFF.length; j++) {
f = File.createTempFile("testWriteImageSplitVertical_" + j + "_" +
COMPRESSION[i], ".tiff");
f.deleteOnExit();
assertTiles(f.getAbsolutePath(), COMPRESSION[i], 2, 1, BIG_TIFF[j]);
}
}
}
/**
* Tests the writing of blocks of 256x256. Tiles should be square and size
* multiple of 16.
* @throws Exception Throw if an error occurred while writing.
*/
@Test(enabled=true)
public void testWriteUnevenTilesImage128x128Block() throws Exception {
File f;
for (int i = 0; i < COMPRESSION.length; i++) {
for (int j = 0; j < BIG_TIFF.length; j++) {
f = File.createTempFile("testWriteUnevenTilesImage128x128Block_"+j+"_"+
COMPRESSION[i], ".tiff");
f.deleteOnExit();
assertUnevenTiles(f.getAbsolutePath(), COMPRESSION[i], 128, 128,
BIG_TIFF[j]);
}
}
}
/**
* Tests the writing of blocks of 256x256. Tiles should be square and size
* multiple of 16.
* @throws Exception Throw if an error occurred while writing.
*/
@Test(enabled=true)
public void testWriteUnevenTilesImage256x256Block() throws Exception {
File f;
for (int i = 0; i < COMPRESSION.length; i++) {
for (int j = 0; j < BIG_TIFF.length; j++) {
f = File.createTempFile("testWriteUnevenTilesImage256x256Block_"+j+"_"+
COMPRESSION[i], ".tiff");
f.deleteOnExit();
assertUnevenTiles(f.getAbsolutePath(), COMPRESSION[i], 256, 256,
BIG_TIFF[j]);
}
}
}
}