/* * #%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]); } } } }