geotiffjs / geotiff.js

geotiff.js is a small library to parse TIFF files for visualization or analysis. It is written in pure JavaScript, and is usable in both the browser and node.js applications.
https://geotiffjs.github.io/
MIT License
859 stars 179 forks source link

Write raw values bigger than 255 (Uint8), or: how to write Uint32, Float32 etc? #365

Open daumann opened 1 year ago

daumann commented 1 year ago

Here is a snippet of the code I am testing with:

        const values = [1, 2, 3, 4, 5, 6, 7, 8, 4020];

    const metadata = {
        ImageLength: 3,
        ImageWidth: 3,
    };

    const arrayBuffer = await GeoTIFF.writeArrayBuffer( new Uint32Array(values), metadata);

    var blob = new Blob([arrayBuffer], {type: "image/tiff"});
    var link = document.createElement('a');
    link.href = window.URL.createObjectURL(blob);
    link.download = "test.tiff";

Unfortunately this won't work because when inspecting the resulting tiff with gdalinfo -stats test.tiff -mm The values have been corrupted:

Band 1 Block=3x3 Type=Byte, ColorInterp=Gray Min=1.000 Max=180.000 Computed Min/Max=1.000,180.000 Minimum=1.000, Maximum=180.000, Mean=24.000, StdDev=55.197

while the Minimum is correct, the Maximum is off (because it exceeds the Uint8 limit).

I tried to change my code by adding

SamplesPerPixel: 1, 
BitsPerSample: [32],

to the metadata but this gives me the following issue when inspecting the result:

Band 1 Block=3x3 Type=UInt32, ColorInterp=Gray ERROR 1: TIFFReadEncodedStrip:Read error at scanline 4294967295; got 9 bytes, expected 36 ERROR 1: TIFFReadEncodedStrip() failed. ERROR 1: r2.tiff, band 1: IReadBlock failed at X offset 0, Y offset 0: TIFFReadEncodedStrip() failed.

Notice that the Type has now changed from Byte to UInt32, but the data does not seem to have been encoded correctly.

Any pointers are very welcome!

DanielJDufour commented 1 year ago

Hello, thank you for posting this important issue. It helps gauge community interest in different features. Unfortunately, it's a current limitation of the writer that it can only write Uint8 values. However, updating the code to support other data types is very achievable. Please let me know if you have the time to write a PR and I can provide some more background. Hope you have a nice rest of your day.

daumann commented 1 year ago

Thanks Daniel for the quick response! Yes I can look into adding that feature. If you could give me some starting points, that would be great!

DanielJDufour commented 1 year ago

Hey, great to hear. Here's a few links to the relevant parts:

The main file that does the writing: https://github.com/geotiffjs/geotiff.js/blob/master/src/geotiffwriter.js. I tried to put all the writing functionality in one file to keep things modular (and separate from the reading functionality). It does however use globals.js and utils.js.

This line is currently assuming uint8 data type, but will need to be dynamic depending on nbit value I assume: https://github.com/geotiffjs/geotiff.js/blob/master/src/geotiffwriter.js#L255. I think the other lines in encodeImage will have be refactored as well.

Hopefully this helps a little, but it already seems like you have a good sense of it.

Happy to answer any more questions or help with code review later on.

Best of luck!

DanielJDufour commented 1 year ago

Hi, @daumann . I'm sorry. I totally forgot about this old PR until I came across it today: https://github.com/geotiffjs/geotiff.js/pull/106. I think you could definitely build off of it or consult the comments. Let me know if you have any more questions.

daumann commented 1 year ago

@DanielJDufour Here is the PR that adds support for writing uint8, uint16, uint32 and float32: https://github.com/geotiffjs/geotiff.js/pull/366

I also added tests for each new data type (which all pass)

DanielJDufour commented 1 year ago

That's awesome! I'll try to review it as soon as I can. Thank you!

daumann commented 1 year ago

@DanielJDufour Quick reminder, so this PR isn't forgotten...