/* gcc -o aperio-bad-jp2k-tiles aperio-bad-jp2k-tiles.c -lopenjpeg -ltiff */
/* Check for unreadable JP2K tiles in an Aperio slide. */
/*
* OpenSlide, a library for reading whole slide image files
*
* Copyright (c) 2007-2013 Carnegie Mellon University
* Copyright (c) 2011 Google, Inc.
* All rights reserved.
*
* OpenSlide is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, version 2.1.
*
* OpenSlide 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with OpenSlide. If not, see
* .
*
*/
#include
#include
#include
#include
#include
struct decode_state {
tdir_t dir;
ttile_t tile;
void *buf;
toff_t size;
};
static void error_callback(const char *msg, void *data) {
struct decode_state *state = data;
char filename[80];
FILE *fp;
printf("Tile %d error: %s", state->tile, msg);
snprintf(filename, sizeof(filename), "failed-%d-%d", state->dir,
state->tile);
fp = fopen(filename, "wb");
if (fp == NULL) {
printf("Couldn't open file %s\n", filename);
return;
}
if (fwrite(state->buf, state->size, 1, fp) != 1) {
printf("Couldn't write file %s\n", filename);
}
fclose(fp);
}
static void decode_tile(struct decode_state *state)
{
opj_cio_t *stream = NULL;
opj_dinfo_t *dinfo = NULL;
opj_image_t *image = NULL;
opj_event_mgr_t event_callbacks = {
.error_handler = error_callback,
};
// init decompressor
opj_dparameters_t parameters;
dinfo = opj_create_decompress(CODEC_J2K);
opj_set_default_decoder_parameters(¶meters);
opj_setup_decoder(dinfo, ¶meters);
stream = opj_cio_open((opj_common_ptr) dinfo, state->buf, state->size);
opj_set_event_mgr((opj_common_ptr) dinfo, &event_callbacks, state);
// decode
image = opj_decode(dinfo, stream);
// sanity check
if (image && image->numcomps != 3) {
error_callback("numcomps != 3", state);
}
// clean up
if (image) opj_image_destroy(image);
if (stream) opj_cio_close(stream);
if (dinfo) opj_destroy_decompress(dinfo);
}
int main(int argc, char **argv)
{
TIFF *tiff;
if (argc != 2) {
fprintf(stderr, "Usage: %s \n", argv[0]);
return 1;
}
tiff = TIFFOpen(argv[1], "r");
if (!tiff) {
fprintf(stderr, "Couldn't read TIFF\n");
return 1;
}
do {
uint16_t compression;
uint32_t depth;
ttile_t tiles;
toff_t *sizes;
struct decode_state state = {0};
// ensure tiled and JP2K compression
if (!TIFFGetField(tiff, TIFFTAG_COMPRESSION, &compression)) {
fprintf(stderr, "Can't read compression scheme\n");
TIFFClose(tiff);
return 1;
}
if (!TIFFIsTiled(tiff) || (compression != 33003 && compression != 33005)) {
continue;
}
state.dir = TIFFCurrentDirectory(tiff);
printf("Directory: %d\n", state.dir);
// check depth
if (TIFFGetField(tiff, TIFFTAG_IMAGEDEPTH, &depth) && depth != 1) {
printf("Depth != 1: %d\n", depth);
continue;
}
// read tiles
tiles = TIFFNumberOfTiles(tiff);
if (!TIFFGetField(tiff, TIFFTAG_TILEBYTECOUNTS, &sizes)) {
printf("No tile byte counts\n");
continue;
}
for (state.tile = 0; state.tile < tiles; state.tile++) {
if (!(state.tile % 50) || state.tile == tiles - 1) {
fprintf(stderr, " Reading: %d/%d\r", state.tile, tiles);
}
state.size = sizes[state.tile];
state.buf = malloc(state.size);
if (TIFFReadRawTile(tiff, state.tile, state.buf, state.size) ==
state.size) {
decode_tile(&state);
} else {
printf("Tile %d: couldn't read\n", state.tile);
}
free(state.buf);
}
fprintf(stderr, "\n");
} while (TIFFReadDirectory(tiff));
// close
TIFFClose(tiff);
return 0;
}