Skip to content

Is this conversion to TIFF correct, it's very slow #6

@jonasteuwen

Description

@jonasteuwen

Hi,

Thanks for relicensing the library! I wanted to write an isyntax -> tiff converter using libtiff. I came up with this:

#include "libisyntax.h"

#include <stdint.h>
#include <assert.h>

#include "tiffio.h"
#include <string.h>

#define LOG_VAR(fmt, var) printf("%s: %s=" fmt "\n", __FUNCTION__, #var, var)



uint32_t bgra_to_rgba(uint32_t val) {
    return ((val & 0xff) << 16) | (val & 0x00ff00) | ((val & 0xff0000) >> 16) | (val & 0xff000000);
}


void write_pyramid_level(isyntax_t* isyntax, isyntax_cache_t *isyntax_cache, isyntax_level_t* level, TIFF* output_tiff, int32_t tile_size, int32_t num_channels) {
    int32_t num_tiles_x = libisyntax_level_get_width_in_tiles(level);
    int32_t num_tiles_y = libisyntax_level_get_height_in_tiles(level);

    int32_t width = tile_size * libisyntax_level_get_width_in_tiles(level);
    int32_t height = tile_size * libisyntax_level_get_height_in_tiles(level);

    int32_t scale = libisyntax_level_get_scale(level);
    // TODO: Compute the spacing

    TIFFSetField(output_tiff, TIFFTAG_IMAGEWIDTH, width);
    TIFFSetField(output_tiff, TIFFTAG_IMAGELENGTH, height);

    for (uint32_t tile_y = 0; tile_y < num_tiles_y; ++tile_y) {
        for (uint32_t tile_x = 0; tile_x < num_tiles_x; ++tile_x) {
            // Let's print some progress for each percentage of the image that we have processed
            int32_t progress = (int32_t) (100.0 * (tile_y * num_tiles_x + tile_x) / (num_tiles_x * num_tiles_y));
            if (progress % 5 == 0) {
                printf("Progress: %d%%\r", progress);
                fflush(stdout);
            }

            uint32_t *pixels = NULL;
            assert(libisyntax_tile_read(isyntax, isyntax_cache, scale, tile_x, tile_y, &pixels) == LIBISYNTAX_OK);

            // convert data to the correct pixel format (bgra->rgba).
            for (int i = 0; i < tile_size * tile_size; ++i) {
                pixels[i] = bgra_to_rgba(pixels[i]);
            }

            // Copy the tile data to the output buffer
            uint32_t tile_width = (tile_x < num_tiles_x - 1) ? tile_size : (width % tile_size);
            uint32_t tile_height = (tile_y < num_tiles_y - 1) ? tile_size : (height % tile_size);

            if (tile_x == num_tiles_x - 1 || tile_y == num_tiles_y - 1) {
                // TODO: Not sure we need to adjust here for the border tiles?
                // Create a new buffer of the correct size based on the adjusted tile_width and tile_height
                unsigned char *buf = (unsigned char *)_TIFFmalloc(tile_size * tile_size * num_channels);

                // Copy the relevant data from the original buffer (pixels) to the new buffer
                for (uint32_t y = 0; y < tile_height; ++y) {
                    memcpy(buf + y * tile_size * num_channels, pixels + y * tile_width * num_channels, tile_width * num_channels);
                }

                TIFFWriteTile(output_tiff, buf, tile_x * tile_size, tile_y * tile_size, 0, 0);
                // Free the adjusted buffer
                _TIFFfree(buf);
            } else {
                // Pass the original buffer (pixels) to TIFFWriteTile
                TIFFWriteTile(output_tiff, pixels, tile_x * tile_size, tile_y * tile_size, 0, 0);
            }

            libisyntax_tile_free_pixels(pixels);

        }
    }
}


int main(int argc, char** argv) {

    if (argc <= 1) {
        printf("Usage: %s <isyntax_file> <output.tiff> - convert an isyntax image to a tiff.", argv[0]);
        return 0;
    }

    char* filename = argv[1];

    libisyntax_init();

    isyntax_t* isyntax;
    if (libisyntax_open(filename, /*is_init_allocators=*/0, &isyntax) != LIBISYNTAX_OK) {
        fprintf(stderr, "Failed to open %s\n", filename);
        return -1;
    }
    printf("Successfully opened %s\n", filename);

    TIFF *output_tiff = TIFFOpen(argv[2], "w8");
    if (!output_tiff) {
        fprintf(stderr, "Failed to open output TIFF file\n");
        libisyntax_close(isyntax);
        return 1;
    }
    LOG_VAR("%s", argv[2]);

    int32_t tile_width = libisyntax_get_tile_width(isyntax);
    int32_t tile_height = libisyntax_get_tile_height(isyntax);
    LOG_VAR("%d", tile_width);
    LOG_VAR("%d", tile_height);

    isyntax_cache_t *isyntax_cache = NULL;
    assert(libisyntax_cache_create("tiff conversion cache", 2000, &isyntax_cache) == LIBISYNTAX_OK);
    assert(libisyntax_cache_inject(isyntax_cache, isyntax) == LIBISYNTAX_OK);

    int wsi_image_idx = libisyntax_get_wsi_image_index(isyntax);
    const isyntax_image_t* wsi_image = libisyntax_get_image(isyntax, wsi_image_idx);
    int32_t num_levels = libisyntax_image_get_level_count(wsi_image);
    int32_t num_channels = 4;
    int32_t bits_per_sample = 8;

    int32_t quality = 70;
    TIFFSetField(output_tiff, TIFFTAG_SAMPLESPERPIXEL, num_channels);
    TIFFSetField(output_tiff, TIFFTAG_BITSPERSAMPLE, bits_per_sample);
    TIFFSetField(output_tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
    TIFFSetField(output_tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
    TIFFSetField(output_tiff, TIFFTAG_TILEWIDTH, tile_width);
    TIFFSetField(output_tiff, TIFFTAG_TILELENGTH, tile_height);
    TIFFSetField(output_tiff, TIFFTAG_COMPRESSION, COMPRESSION_JPEG);
    TIFFSetField(output_tiff, TIFFTAG_JPEGQUALITY, quality);

    assert(tile_width == tile_height);

    for (int32_t level_idx = 0; level_idx < num_levels; ++level_idx) {
        LOG_VAR("%d", level_idx);
        isyntax_level_t* current_level = libisyntax_image_get_level(wsi_image, level_idx);
        write_pyramid_level(isyntax, isyntax_cache,current_level, output_tiff, tile_width, num_channels);
        if (level_idx < num_levels - 1) {
            TIFFWriteDirectory(output_tiff);
        }
    }

    // Close the iSyntax file and the TIFF file
    TIFFClose(output_tiff);
    libisyntax_cache_destroy(isyntax_cache);
    libisyntax_close(isyntax);
    return 0;
}

However, this is very slow, and my profiler tells me a major part is actually in the read_tile. Am I using the library as intended here?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions