def test_rgb_stripped_bottom_of_tile_coincides_with_bottom_of_strip(self): """ Scenario: input TIFF is evenly divided into strips, but the tile size does not evenly divide either dimension. The strip size is 32. The tile size is 13x13, so the jp2k tile in tile row 4 and column 0 will have it's last row only one pixel past the last row of the tiff tile in row 2 and column 0. Expected Result: no errors """ with Tiff2Jp2k(self.goodstuff_path, self.temp_jp2_filename, tilesize=(75, 75)) as j: j.run() jp2 = Jp2k(self.temp_jp2_filename) actual = jp2[:] np.testing.assert_array_equal(actual, self.goodstuff_data) c = jp2.get_codestream() self.assertEqual(c.segment[1].xsiz, 480) self.assertEqual(c.segment[1].ysiz, 800) self.assertEqual(c.segment[1].xtsiz, 75) self.assertEqual(c.segment[1].ytsiz, 75)
def test_psnr(self): """ SCENARIO: Convert TIFF file to JP2 with the irreversible transform. EXPECTED RESULT: data matches, the irreversible transform is confirmed """ with Tiff2Jp2k(self.minisblack_spp1_path, self.temp_jp2_filename, psnr=(30, 35, 40, 0)) as j: j.run() j = Jp2k(self.temp_jp2_filename) d = {} for layer in range(4): j.layer = layer d[layer] = j[:] with warnings.catch_warnings(): # MSE is zero for that first image, resulting in a divide-by-zero # warning warnings.simplefilter('ignore') psnr = [ fixtures.skimage.metrics.peak_signal_noise_ratio( fixtures.skimage.data.moon(), d[j]) for j in range(4) ] # That first image should be lossless. self.assertTrue(np.isinf(psnr[0])) # None of the subsequent images should have inf PSNR. self.assertTrue(not np.any(np.isinf(psnr[1:]))) # PSNR should increase for the remaining images. self.assertTrue(np.all(np.diff(psnr[1:])) > 0)
def test_floating_point(self): """ SCENARIO: The sample format is 32bit floating point. EXPECTED RESULT: RuntimeError """ data = fixtures.skimage.data.moon().astype(np.float32) h, w = data.shape th, tw = h // 2, w // 2 fp = libtiff.open(self.temp_tiff_filename, mode='w') libtiff.setField(fp, 'Photometric', libtiff.Photometric.MINISBLACK) libtiff.setField(fp, 'Compression', libtiff.Compression.DEFLATE) libtiff.setField(fp, 'SampleFormat', libtiff.SampleFormat.IEEEFP) libtiff.setField(fp, 'ImageLength', data.shape[0]) libtiff.setField(fp, 'ImageWidth', data.shape[1]) libtiff.setField(fp, 'TileLength', th) libtiff.setField(fp, 'TileWidth', tw) libtiff.setField(fp, 'BitsPerSample', 32) libtiff.setField(fp, 'SamplesPerPixel', 1) libtiff.writeEncodedTile(fp, 0, data[:th, :tw].copy()) libtiff.writeEncodedTile(fp, 1, data[:th, tw:w].copy()) libtiff.writeEncodedTile(fp, 2, data[th:h, :tw].copy()) libtiff.writeEncodedTile(fp, 3, data[th:h, tw:w].copy()) libtiff.close(fp) with Tiff2Jp2k(self.temp_tiff_filename, self.temp_jp2_filename) as j: with self.assertRaises(RuntimeError): j.run()
def test_tiff_file_not_there(self): """ Scenario: The input TIFF file is not present. Expected Result: FileNotFoundError """ with self.assertRaises(FileNotFoundError): Tiff2Jp2k(self.test_dir_path / 'not_there.tif', self.temp_jp2_filename)
def test_minisblack_spp1_bigtiff(self): """ SCENARIO: Convert minisblack BigTIFF file to JP2. The TIFF has tag XResolution. EXPECTED RESULT: no errors. """ with ir.path('tests.data', 'albers27-8.tif') as path: with Tiff2Jp2k(path, self.temp_jp2_filename) as j: j.run()
def test_verbosity(self): """ SCENARIO: Convert TIFF file to JP2, use INFO log level. EXPECTED RESULT: data matches """ with Tiff2Jp2k(self.astronaut_ycbcr_jpeg_tif, self.temp_jp2_filename, verbosity=logging.INFO) as j: with self.assertLogs(logger='tiff2jp2', level=logging.INFO) as cm: j.run() self.assertEqual(len(cm.output), 1)
def test_separated_configuration(self): """ SCENARIO: The TIFF has a planar configuration of SEPARATE which is not supported if a tilesize is specified. EXPECTED RESULT: RuntimeError """ with self.assertRaises(RuntimeError): with ir.path('tests.data', 'flower-separated-planar-08.tif') as path: with Tiff2Jp2k(path, self.temp_jp2_filename, tilesize=(64, 64)) as j: j.run()
def test_smoke(self): """ SCENARIO: Convert TIFF file to JP2 EXPECTED RESULT: data matches, number of resolution is the default. There should be just one layer. The number of resolutions should be the default (5). There are not PLT segments. There are no EPH markers. There are no SOP markers. The progression order is LRCP. The irreversible transform will NOT be used. PSNR cannot be tested if it is not applied. There is a UUID box appended at the end containing the metadata. """ with Tiff2Jp2k(self.astronaut_ycbcr_jpeg_tif, self.temp_jp2_filename) as j: j.run() j = Jp2k(self.temp_jp2_filename) actual = j[:] self.assertEqual(actual.shape, (512, 512, 3)) c = j.get_codestream(header_only=False) actual = c.segment[2].code_block_size expected = (64, 64) self.assertEqual(actual, expected) self.assertEqual(c.segment[2].layers, 1) self.assertEqual(c.segment[2].num_res, 5) at_least_one_eph = any( isinstance(seg, glymur.codestream.EPHsegment) for seg in c.segment) self.assertFalse(at_least_one_eph) at_least_one_plt = any( isinstance(seg, glymur.codestream.PLTsegment) for seg in c.segment) self.assertFalse(at_least_one_plt) at_least_one_sop = any( isinstance(seg, glymur.codestream.SOPsegment) for seg in c.segment) self.assertFalse(at_least_one_sop) self.assertEqual(c.segment[2].prog_order, glymur.core.LRCP) self.assertEqual(c.segment[2].xform, glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) self.assertEqual(j.box[-1].box_id, 'uuid') self.assertEqual(j.box[-1].data['ImageWidth'], 512) self.assertEqual(j.box[-1].data['ImageLength'], 512)
def test_stripped_logging(self): """ Scenario: input TIFF is organized by strips and logging is turned on. Expected result: there are 104 log messages, one for each tile """ with Tiff2Jp2k(self.goodstuff_path, self.temp_jp2_filename, tilesize=(64, 64), verbosity=logging.INFO) as j: with self.assertLogs(logger='tiff2jp2', level=logging.INFO) as cm: j.run() self.assertEqual(len(cm.output), 104)
def test_tiled_logging(self): """ SCENARIO: Convert monochromatic TIFF file to JP2. The TIFF is evenly tiled 2x2. Logging is turned on. EXPECTED RESULT: there are four messages logged, one for each tile """ with Tiff2Jp2k(self.minisblack_spp1_path, self.temp_jp2_filename, tilesize=(256, 256)) as j: with self.assertLogs(logger='tiff2jp2', level=logging.INFO) as cm: j.run() self.assertEqual(len(cm.output), 4)
def test_progression_order(self): """ SCENARIO: Convert TIFF file to JP2 with EPH markers. EXPECTED RESULT: data matches, plt markers confirmed """ with Tiff2Jp2k(self.astronaut_ycbcr_jpeg_tif, self.temp_jp2_filename, prog='rlcp') as j: j.run() j = Jp2k(self.temp_jp2_filename) c = j.get_codestream(header_only=False) self.assertEqual(c.segment[2].prog_order, glymur.core.RLCP)
def test_bad_tile_size(self): """ SCENARIO: Specify a tilesize that exceeds the image size. This will cause a segfault unless caught. EXPECTED RESULT: RuntimeError """ with self.assertRaises(RuntimeError): with ir.path('tests.data', 'albers27-8.tif') as path: with Tiff2Jp2k( path, self.temp_jp2_filename, tilesize=(256, 256), ) as j: j.run()
def test_irreversible(self): """ SCENARIO: Convert TIFF file to JP2 with the irreversible transform. EXPECTED RESULT: data matches, the irreversible transform is confirmed """ with Tiff2Jp2k(self.astronaut_ycbcr_jpeg_tif, self.temp_jp2_filename, irreversible=True) as j: j.run() j = Jp2k(self.temp_jp2_filename) c = j.get_codestream(header_only=False) self.assertEqual(c.segment[2].xform, glymur.core.WAVELET_XFORM_9X7_IRREVERSIBLE)
def test_geotiff(self): """ SCENARIO: Convert GEOTIFF file to JP2 EXPECTED RESULT: there is a geotiff UUID. """ with warnings.catch_warnings(): warnings.simplefilter('ignore') with ir.path('tests.data', 'albers27.tif') as path: with Tiff2Jp2k(path, self.temp_jp2_filename) as j: j.run() j = Jp2k(self.temp_jp2_filename) self.assertEqual(j.box[-1].box_id, 'uuid') self.assertEqual(j.box[-1].uuid, UUID('b14bf8bd-083d-4b43-a5ae-8cd7d5a6ce03'))
def test_plt(self): """ SCENARIO: Convert TIFF file to JP2 with PLT markers. EXPECTED RESULT: data matches, plt markers confirmed """ with Tiff2Jp2k(self.astronaut_ycbcr_jpeg_tif, self.temp_jp2_filename, plt=True) as j: j.run() j = Jp2k(self.temp_jp2_filename) c = j.get_codestream(header_only=False) at_least_one_plt = any( isinstance(seg, glymur.codestream.PLTsegment) for seg in c.segment) self.assertTrue(at_least_one_plt)
def test_layers(self): """ SCENARIO: Convert TIFF file to JP2 with multiple compression layers EXPECTED RESULT: data matches, number of layers is 3 """ with Tiff2Jp2k(self.astronaut_ycbcr_jpeg_tif, self.temp_jp2_filename, cratios=[200, 50, 10]) as j: j.run() j = Jp2k(self.temp_jp2_filename) actual = j[:] self.assertEqual(actual.shape, (512, 512, 3)) c = j.get_codestream() self.assertEqual(c.segment[2].layers, 3)
def test_rgb_stripped(self): """ Scenario: input TIFF is evenly divided into strips, but the tile size does not evenly divide either dimension. """ with Tiff2Jp2k(self.goodstuff_path, self.temp_jp2_filename, tilesize=(64, 64)) as j: j.run() jp2 = Jp2k(self.temp_jp2_filename) actual = jp2[:] np.testing.assert_array_equal(actual, self.goodstuff_data) c = jp2.get_codestream() self.assertEqual(c.segment[1].xsiz, 480) self.assertEqual(c.segment[1].ysiz, 800) self.assertEqual(c.segment[1].xtsiz, 64) self.assertEqual(c.segment[1].ytsiz, 64)
def test_codeblock_size(self): """ SCENARIO: Convert TIFF file to JP2 with a specific code block size EXPECTED RESULT: data matches, number of resolution is the default """ expected = (32, 32) with Tiff2Jp2k(self.astronaut_ycbcr_jpeg_tif, self.temp_jp2_filename, cbsize=expected) as j: j.run() j = Jp2k(self.temp_jp2_filename) actual = j[:] self.assertEqual(actual.shape, (512, 512, 3)) c = j.get_codestream() actual = c.segment[2].code_block_size self.assertEqual(actual, expected)
def test_no_uuid(self): """ SCENARIO: Convert TIFF file to JP2, but do not include the UUID box for the TIFF IFD. EXPECTED RESULT: data matches, no UUID box """ with Tiff2Jp2k(self.astronaut_ycbcr_jpeg_tif, self.temp_jp2_filename, create_uuid=False) as j: j.run() j = Jp2k(self.temp_jp2_filename) actual = j[:] self.assertEqual(actual.shape, (512, 512, 3)) at_least_one_uuid = any( isinstance(box, glymur.jp2box.UUIDBox) for box in j.box) self.assertFalse(at_least_one_uuid)
def test_resolutions(self): """ SCENARIO: Convert TIFF file to JP2 with 4 resolution layers instead of the default, which is 5. EXPECTED RESULT: data matches, number of resolution layers is 4. """ expected = 4 with Tiff2Jp2k(self.astronaut_ycbcr_jpeg_tif, self.temp_jp2_filename, numres=expected) as j: j.run() j = Jp2k(self.temp_jp2_filename) actual = j[:] self.assertEqual(actual.shape, (512, 512, 3)) c = j.get_codestream() actual = c.segment[2].num_res self.assertEqual(actual, expected - 1)
def test_ycbcr_jpeg_unevenly_tiled(self): """ SCENARIO: Convert YCBCR/JPEG TIFF file to JP2. The TIFF is evenly tiled 2x2. The JPEG 2000 file will be tiled 75x75. EXPECTED RESULT: The data matches. No errors """ with Tiff2Jp2k(self.astronaut_ycbcr_jpeg_tif, self.temp_jp2_filename, tilesize=(75, 75)) as j: j.run() jp2 = Jp2k(self.temp_jp2_filename) actual = jp2[:] np.testing.assert_array_equal(actual, self.astronaut_ycbcr_jpeg_data) c = jp2.get_codestream() self.assertEqual(c.segment[1].xsiz, 512) self.assertEqual(c.segment[1].ysiz, 512) self.assertEqual(c.segment[1].xtsiz, 75) self.assertEqual(c.segment[1].ytsiz, 75)
def test_rgb_uint16(self): """ SCENARIO: Convert RGB TIFF file to JP2. The TIFF is evenly tiled 2x2 and uint16. EXPECTED RESULT: The data matches. The JP2 file has 4 tiles. """ with Tiff2Jp2k(self.astronaut_uint16_filename, self.temp_jp2_filename, tilesize=(256, 256)) as j: j.run() jp2 = Jp2k(self.temp_jp2_filename) actual = jp2[:] np.testing.assert_array_equal(actual, self.astronaut_uint16_data) c = jp2.get_codestream() self.assertEqual(c.segment[1].xsiz, 512) self.assertEqual(c.segment[1].ysiz, 512) self.assertEqual(c.segment[1].xtsiz, 256) self.assertEqual(c.segment[1].ytsiz, 256)
def test_minisblack_3x3__larger_tilesize_specified(self): """ SCENARIO: Convert monochromatic TIFF file to JP2. The TIFF is evenly tiled 3x3, but we want 2x2. EXPECTED RESULT: The data matches. The JP2 file has 4 tiles. """ with Tiff2Jp2k(self.minisblack_3x3_tif, self.temp_jp2_filename, tilesize=(240, 240)) as j: j.run() jp2 = Jp2k(self.temp_jp2_filename) actual = jp2[:] np.testing.assert_array_equal(actual, self.minisblack_3x3_data) c = jp2.get_codestream() self.assertEqual(c.segment[1].xsiz, 480) self.assertEqual(c.segment[1].ysiz, 480) self.assertEqual(c.segment[1].xtsiz, 240) self.assertEqual(c.segment[1].ytsiz, 240)
def test_minisblack__smaller_tilesize_specified(self): """ SCENARIO: Convert monochromatic TIFF file to JP2. The TIFF is evenly tiled 2x2, but we want 4x4. EXPECTED RESULT: The data matches. The JP2 file has 16 tiles. """ with Tiff2Jp2k(self.minisblack_spp1_path, self.temp_jp2_filename, tilesize=(128, 128)) as j: j.run() jp2 = Jp2k(self.temp_jp2_filename) actual = jp2[:] np.testing.assert_array_equal(actual, self.minisblack_spp1_data) c = jp2.get_codestream() self.assertEqual(c.segment[1].xsiz, 512) self.assertEqual(c.segment[1].ysiz, 512) self.assertEqual(c.segment[1].xtsiz, 128) self.assertEqual(c.segment[1].ytsiz, 128)
def test_partial_last_strip(self): """ SCENARIO: Convert monochromatic TIFF file to JP2. The TIFF has a partial last strip. EXPECTED RESULT: The data matches. The JP2 file has 4 tiles. """ with Tiff2Jp2k(self.minisblack_3strip_partial_last_strip, self.temp_jp2_filename, tilesize=(240, 240)) as j: j.run() jp2 = Jp2k(self.temp_jp2_filename) actual = jp2[:] np.testing.assert_array_equal(actual, self.minisblack_3x3_data) c = jp2.get_codestream() self.assertEqual(c.segment[1].xsiz, 480) self.assertEqual(c.segment[1].ysiz, 480) self.assertEqual(c.segment[1].xtsiz, 240) self.assertEqual(c.segment[1].ytsiz, 240)
def test_ycbcr_jpeg_single_tile(self): """ SCENARIO: Convert YCBCR/JPEG TIFF file to JP2. The TIFF is evenly tiled 2x2, but no tilesize is specified. EXPECTED RESULT: The data matches. """ with Tiff2Jp2k( self.astronaut_ycbcr_jpeg_tif, self.temp_jp2_filename, ) as j: j.run() jp2 = Jp2k(self.temp_jp2_filename) actual = jp2[:] np.testing.assert_array_equal(actual, self.astronaut_ycbcr_jpeg_data) c = jp2.get_codestream() self.assertEqual(c.segment[1].xsiz, 512) self.assertEqual(c.segment[1].ysiz, 512) self.assertEqual(c.segment[1].xtsiz, 512) self.assertEqual(c.segment[1].ytsiz, 512)
def test_cmyk(self): """ Scenario: CMYK (or separated) is not a supported colorspace. Expected result: RuntimeError """ data = fixtures.skimage.data.moon() data = np.dstack((data, data)) h, w, spp = data.shape # instead of 160, this will cause a partially empty last strip rps = 512 fp = libtiff.open(self.temp_tiff_filename, mode='w') libtiff.setField(fp, 'Photometric', libtiff.Photometric.SEPARATED) libtiff.setField(fp, 'Compression', libtiff.Compression.DEFLATE) libtiff.setField(fp, 'ImageLength', data.shape[0]) libtiff.setField(fp, 'ImageWidth', data.shape[1]) libtiff.setField(fp, 'RowsPerStrip', rps) libtiff.setField(fp, 'BitsPerSample', 8) libtiff.setField(fp, 'SamplesPerPixel', spp) libtiff.setField(fp, 'PlanarConfig', libtiff.PlanarConfig.CONTIG) libtiff.setField(fp, 'InkSet', libtiff.InkSet.MULTIINK) libtiff.writeEncodedStrip(fp, 0, data.copy()) libtiff.close(fp) with Tiff2Jp2k(self.temp_tiff_filename, self.temp_jp2_filename) as j: with warnings.catch_warnings(): # weird warning about extra samples warnings.simplefilter('ignore') with self.assertRaises(RuntimeError): j.run()