TIFF decoding and encoding library in pure Rust

Overview

image-tiff

Build Status Documentation Further crate info

TIFF decoding and encoding library in pure Rust

Supported

Features

  • Baseline spec (other than formats and tags listed below as not supported)
  • Multipage
  • BigTIFF
  • Incremental decoding

Formats

This table lists photometric interpretations and sample formats which are supported for encoding and decoding. The entries are ColorType variants for which sample bit depths are supported. Only samples where all bit depths are equal are currently supported. For example, RGB(8) means that the bit depth [8, 8, 8] is supported and will be interpreted as an 8 bit per channel RGB color type.

PhotometricInterpretation UINT Format IEEEFP Format
WhiteIsZero Gray(8|16|32|64) Gray(32|64)
BlackIsZero Gray(8|16|32|64) Gray(32|64)
RGB RGB(8|16|32|64), RGBA(8|16|32|64) RGB(32|64), RGBA(32|64)
RGBPalette
Mask
CMYK CMYK(8|16|32|64) CMYK(32|64)
YCbCr
CIELab

Compressions

Decoding Encoding
None
LZW
Deflate
PackBits

Not yet supported

Formats and interpretations not listed above or with empty entries are unsupported.

  • Baseline tags
    • ExtraSamples
  • Extension tags
Comments
  • WIP: add encoding with compression, additional tags, float support and bigtiff

    WIP: add encoding with compression, additional tags, float support and bigtiff

    This is a work in progress and i need some advice about the implementation.

    First the lzw lib is unmaintained but there is a fork in image-rs but there is not this pull request merged https://github.com/nwin/lzw/pull/1. To enable the tiff encoding with lzw we need this.

    Then I think there is a better way to encode the value to bytes but i didn't find it so i create new TiffWriter :/

            CompressionMethod::LZW => {
                    let mut buf = TiffWriter::new(Cursor::new(Vec::new()));
                    value.write(&mut buf)?;
                    let mut compressed = vec![];
                    {
                        let mut lzw_encoder = EncoderTIFF::new(MsbWriter::new(&mut compressed), 8)?;
                        lzw_encoder.encode_bytes(&buf.writer.into_inner()[..])?;
                    }
                    self.encoder.write_data(&compressed[..])?
            },
    

    And the big issue is with the additional_tags i can't find a way to allow to pass an array of object implementing the trait TiffValue with Vec<(Tag, Box<dyn TiffValue>)>, the compiler complain:

    the trait `encoder::TiffValue` cannot be made into an object
    consider moving `write` to another trait
    consider moving `BYTE_LEN` to another trait
    consider moving `FIELD_TYPE` to another trait rustc(E0038)
    mod.rs(38, 8): ...because method `write` has generic type parameters
    mod.rs(32, 11): ...because it contains this associated `const`
    mod.rs(33, 11): ...because it contains this associated `const`
    mod.rs(31, 11): this trait cannot be made into an object...
    

    So currently you can only use one type of TiffValue and need to specify it when you write the image or create the encoder like the colortype :cry:

    opened by Farkal 16
  • Tiff encoding

    Tiff encoding

    This is a very much WIP implementation of tiff encoding. I believe that it is correct and can handle most cases, however the api is currently not very ergonomic or safe. It also lacks built-inn compression, however that kind of requires https://github.com/nwin/lzw/pull/1.

    Currently the api looks something like this, but I would very much like to improve it.

    let file = std::fs::File::create("test.tiff").unwrap();
    let mut tiff = TiffEncoder::new_le(file);
    
    tiff.write_header().unwrap();
    
    tiff.new_ifd().unwrap();
    tiff.new_ifd_entry(Tag::ImageWidth, &[100u32][..]);
    tiff.new_ifd_entry(Tag::ImageLength, &[100u32][..]);
    tiff.new_ifd_entry(Tag::BitsPerSample, &[8u16,8,8][..]);
    tiff.new_ifd_entry(Tag::Compression, &[1u16][..]);
    tiff.new_ifd_entry(Tag::PhotometricInterpretation, &[2u16][..]);
    tiff.new_ifd_entry(Tag::SamplesPerPixel, &[3u16][..]);
    tiff.new_ifd_entry(Tag::RowsPerStrip, &[100u32][..]);
    tiff.new_ifd_entry(Tag::XResolution, &[Rational {n: 1, d: 1}][..]);
    tiff.new_ifd_entry(Tag::YResolution, &[Rational {n: 1, d: 1}][..]);
    tiff.new_ifd_entry(Tag::ResolutionUnit, &[1u16][..]);
    // The two following tags are overwritten by `write_strip`
    tiff.new_ifd_entry(Tag::StripOffsets, &[0u32][..]);
    tiff.new_ifd_entry(Tag::StripByteCounts, &[0u32][..]);
    tiff.finish_ifd().unwrap();
    
    tiff.write_strip(&image_data).unwrap();
    
    opened by birktj 14
  • Add ScanImage BigTiff decoder

    Add ScanImage BigTiff decoder

    Hi,

    In my scientific field (neuroscientific imaging) we usually record tiffs in the ScanImage BigTiff format, which is a format designed for microscopists. It's very similar to the standard BigTiff format, but it does add two blocks of metadata that can be parsed during the initial header parsing step.

    This PR adds support for this data format, following the introduction of BigTiff support to this library. The PR is not complete due to a few issues:

    1. I'm generally new to Rust, so this will definitely show.
    2. I've added a special error type that reports failure to parse ScanImage's metadata, but I'm not sure how to make my code use it again due to my inexperience in Rust.
    3. I haven't added a test that actually reads the entirety of the demo file I uploaded. This is because I wanted to first make sure I'm not doing something completely off in this PR. In principle, though, the standard BigTiff parsing code should work here as well.
    4. The metadata is parsed as a string, but it's actually more like a hashmap (the ROI section) or a JSON-like document (the non-varying frame data section). I wasn't sure how to handle that currently, so I kept them as 'raw' strings.

    Another possible blocking issue is that the demo file is a bit big (3 MB I think). I'll try to make a new one ASAP, depending on the status of this PR of course.

    Thanks for this great library!

    opened by HagaiHargil 13
  • Allow custom encoding process

    Allow custom encoding process

    Added custom encoding function, tests and keep the compatibility with existing functions. My PR #80 was too complicated and with lot of changes so i prefer to split the features for better integration with the lib and ease the review.

    opened by Farkal 12
  • Add support for encoding BigTiff

    Add support for encoding BigTiff

    This pull request implements basic encoding support for BigTiff images by making the encoder types generic over a new TiffKind trait.

    TODO:

    • [x] Do we need to adjust pad_word_boundary? No
    • [x] Right now users need to explicitly specify whether they want to create a Tiff or BigTiff file. It might be also possible to (roughly) derive this from the image dimensions and color type. However, I'm not sure if that's a good idea since BigTiff is not supported everywhere. (@HeroicKatora no)
    • [x] This change is technically breaking because it introduces a new generic parameter to the encoder types. For the main TiffEncoder type I made this parameter optional by setting a default, so there should not be any breakage through this. However, if people use the DirectoryEncoder or ImageEncoder types explicitly, it might lead to breakage. Is this ok or should I try to avoid breakage there as well? Breaking is okay

    cc #3 @Farkal

    opened by phil-opp 10
  • Remove several panics in write_image

    Remove several panics in write_image

    Closes #35. It's not decided this is the preferred behavior, but my opinion is that since write_image returns a result rather than (), the least surprising behavior is to panic in as few cases as possible. It's ambiguous whether InvalidData or UnexpectedEof would be more appropriate here.

    The buffer length is compared vs width * height to avoid a redundant comparison for every strip (because Rust still doesn't have non-panicking range indexing 🙁), but this limits the width * height to u32, so would have to change if BigTIFF support is planned.

    Two other unwraps are converted to trys. These should be uncontroversial.

    opened by aschampion 9
  • Infinite loop on malformed input

    Infinite loop on malformed input

    Decoding these samples with Decoder.read_image() causes 100% CPU usage. I've run it for 10 minutes before giving up. The code is likely entering an infinite loop.

    The exact reproduction code can be found in #28. Found via AFL.rs, tested on image-tiff version 0.2.2

    opened by Shnatsel 9
  • Add ability to select image directory and tile using index

    Add ability to select image directory and tile using index

    Currently it is only possible to cycle through the image directories and the tiles in order. For handling WSI files (e.g. .svs), where the image data is stored as a 'pyramid' (each image directory has a copy of the image at a different resolution), I need to be able to select which tiles (part of an image) and which image directory (which resolution) I want to access.

    This PR adds in this functionality, and can be used as so:

    // Select the image directory at the third position (4th directory) in the linked list
    decoder.image_at(3)?;
    
    let tiles = decoder.tile_count()?;
    let (width, height) = decoder.dimensions()?;
    
    println!("# tiles {}", tiles);
    
    for tile in 0..tiles {
        // As some tiles (on the boundaries) do not have the same size 
        // as the rest (due to padding) we need to be able to determine its size
        let chunk_info = decoder.chunk_info(tile as usize)?;
    
        match decoder.read_tile_at(tile as usize)? {
                DecodingResult::U8(res) => {
                    // Do some thing with the tile, e.g. save out as a .png 
                    image::save_buffer(
                        format!("image_{}.png", tile),
                        &res,
                        chunk_info.data_width as u32,
                        chunk_info.data_height as u32,
                        image::ColorType::Rgb8,
                    )?;
                }
                _ => panic!("Wrong bit depth"),
        }
    }
    
    opened by AlanRace 8
  • Improve deflate support

    Improve deflate support

    • Switch to streaming decompression via flate2. Aside from performance improvements and lower RAM consumption, this fixes a bug where max_uncompressed_length was precalculated for a single tile but then used as a hard limit on the whole data, failing to decompress any tiled images.
    • Deprecate InflateError - this was used for wrapping miniz_oxide raw status, but flate2 already reports them as io::Error instead. This makes errors from both Lzw and Deflate modes more consistent - they're all now just io::Error.
    • Add support for new Deflate tag in addition to OldDeflate - format-wise it's same Zlib, and on practice decodes successfully with the same underlying code.
    • Slightly simplify partial reads from the Reader by using the std::io::copy helper instead of doing it manually.

    Fixes #131.

    opened by RReverser 8
  • Compression method Deflate is unsupported

    Compression method Deflate is unsupported

    I'm trying to use image crate to read TIFF images produced (compressed) by ImageMagick with -compress zip. My understanding is that image crate relies on this one to do actual reading, so raising issue here.

    This is what Exiftool reports on those images:

    > exiftool -compression pleiades4.tif
    Compression                     : Adobe Deflate
    

    README suggests that decoding Deflate TIFFs should be supported:

    image

    However, when trying to read them using image::open, I'm getting:

    The decoder for Tiff does not support the format features Compression method Deflate is unsupported
    

    Am I holding it wrong? Or is there some specific to this "Adobe Deflate" that ImageMagick uses that makes it different from Deflate supported by this crate?

    opened by RReverser 8
  • Multipage image patch

    Multipage image patch

    While using image-tiff for multipage image I ran into 2 things:

    1. the ifd offset is written twice for each image, which results into that the ifd offset of the second and later images was 0 and followed by the correct location.
    2. the returned offset of the TiffWriter was 2 bytes off, starting at the second page. The easiest way to resolve this was by using the seek(SeekFrom::Current(0)) functionality of the writer object, instead of fixing the offset housekeeping.
    opened by onnotechno 8
  • Encoding images in CMYKA

    Encoding images in CMYKA

    Would it be hard to implement CMYKA ColorType (I'm interested in encoding only)? Currently there's RGB, RGBA and CMYK implemented, but not CMYKA. I've tried to define CMYKA on my own analogously to RGBA, and some graphics editor can open produced images, but some does not know what to do with the extra data.

    opened by wkordalski 1
  • Error: invalid code in LZW stream.

    Error: invalid code in LZW stream.

    Hi

    The attached TIFF triggers an error related the the LZW compression, but libtiff will decode this. It was created with Paint.NET. It works after re-saving it with GIMP.

    If the LZW steam is indeed invalid then that is acceptable, bit it would be interesting to know why.

    b40_fog_noise.zip

    opened by Vaporeon 2
  • Added support for the YCbCr PhotometricInterpretation

    Added support for the YCbCr PhotometricInterpretation

    I added support for the YCbCr PhotometricInterpretation. This works fine for my use case. I am not sure if this is all there was to do to get this working or whether I missed something.

    opened by kurtkuehnert 4
  • TIFF files with unassociated extra samples are unsupported.

    TIFF files with unassociated extra samples are unsupported.

    TIFF files with 2 samples per pixel (Luminance + Alpha) are unsupported by this library. Such TIFF files are the default output of imagemagick if the input is grayscale.

    Attached is an example. example_tiff.zip

    opened by Vaporeon 2
  • Test failure: test encoder::compression::deflate::tests::test_deflate

    Test failure: test encoder::compression::deflate::tests::test_deflate

    When building image-tiff (vesion 0.7.2), I get the following error:

    running 8 tests
    test encoder::compression::packbits::tests::test_packbits_rept ... ok
    test encoder::compression::packbits::tests::test_packbits_large_rept_nonrept ... ok
    test decoder::stream::test::test_packbits ... ok
    test encoder::compression::packbits::tests::test_packbits_single_byte ... ok
    test encoder::compression::packbits::tests::test_packbits ... ok
    test encoder::compression::uncompressed::tests::test_no_compression ... ok
    test encoder::compression::lzw::tests::test_lzw ... ok
    test encoder::compression::deflate::tests::test_deflate ... FAILED
    
    failures:
    
    ---- encoder::compression::deflate::tests::test_deflate stdout ----
    thread 'encoder::compression::deflate::tests::test_deflate' panicked at 'assertion failed: `(left == right)`
      left: `[120, 156, 21, 199, 209, 13, 128, 32, 12, 4, 208, 85, 110, 2, 167, 113, 129, 166, 65, 218, 40, 212, 244, 208, 249, 129, 228, 253, 188, 211, 156, 88, 4, 28, 233, 189, 226, 138, 132, 90, 209, 123, 231, 151, 244, 248, 8, 141, 246, 102, 33, 61, 58, 228, 169, 145, 62, 172, 241, 152, 185, 112, 23, 19]`,
     right: `[120, 156, 21, 199, 209, 13, 128, 32, 12, 5, 192, 85, 222, 4, 78, 227, 2, 77, 131, 180, 81, 40, 233, 3, 230, 87, 147, 251, 185, 211, 156, 248, 8, 56, 211, 123, 197, 21, 9, 181, 162, 247, 159, 45, 233, 177, 8, 141, 54, 178, 144, 30, 29, 242, 212, 72, 159, 214, 120, 188, 185, 112, 23, 19]`', src/encoder/compression/deflate.rs:81:9
    stack backtrace:
       0:     0x7ffff7e3361c - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::h7767eb774a4d4f42
       1:     0x7ffff7ec4bfc - core::fmt::write::hf3fb1fb91b38bb59
       2:     0x7ffff7e01bd5 - <unknown>
       3:     0x7ffff7e151bb - <unknown>
       4:     0x7ffff7e14d61 - <unknown>
       5:     0x7ffff7e1598b - std::panicking::rust_panic_with_hook::h19ec2b337ecb8872
       6:     0x7ffff7e34450 - <unknown>
       7:     0x7ffff7e33744 - <unknown>
       8:     0x7ffff7e15402 - rust_begin_unwind
       9:     0x7ffff7df5681 - core::panicking::panic_fmt::h964c785332841a13
      10:     0x7ffff7ea87f8 - core::panicking::assert_failed_inner::h45014c2f24de47a0
      11:     0x55555555836f - core::panicking::assert_failed::h71b70f1e561685f9
                                   at /tmp/guix-build-rust-1.57.0.drv-0/rustc-1.57.0-src/library/core/src/panicking.rs:138:5
      12:     0x55555555bcb2 - lib::encoder::compression::deflate::tests::test_deflate::h70bb6a3f73b96997
                                   at /tmp/guix-build-rust-tiff-0.7.2.drv-0/tiff-0.7.2/src/encoder/compression/deflate.rs:81:9
      13:     0x7ffff7f58293 - <unknown>
      14:     0x7ffff7f5845b - <unknown>
      15:     0x7ffff7f6df75 - <unknown>
      16:     0x7ffff7f66d0f - <unknown>
      17:     0x7ffff7e250e3 - <unknown>
      18:     0x7ffff7b7cd7e - <unknown>
      19:     0x7ffff7c8aeff - clone
      20:                0x0 - <unknown>
    
    
    failures:
        encoder::compression::deflate::tests::test_deflate
    
    test result: FAILED. 7 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
    
    opened by emixa-d 3
Owner
image-rs
Image libraries for Rust
image-rs
A performant binary encoding for geographic data based on flatbuffers

FlatGeobuf A performant binary encoding for geographic data based on flatbuffers that can hold a collection of Simple Features including circular inte

FlatGeobuf 477 Jan 5, 2023
Geo-rasterize - a pure-rust 2D rasterizer for geospatial applications

geo-rasterize: a pure-rust 2D rasterizer for geospatial applications This crate is intended for folks who have some vector data (like a geo::Polygon)

null 23 Dec 26, 2022
Geocoding library for Rust.

geocoding Rust utilities to enrich addresses, cities, countries, and landmarks with geographic coordinates through third-party geocoding web services.

GeoRust 55 Dec 12, 2022
R*-tree library for the rust ecosystem

rstar A flexible, n-dimensional r*-tree implementation for the rust ecosystem. Please refer to the crate README for more information. Demo To run the

Stefan Altmayer 0 Dec 18, 2021
Geocoding library for Rust.

geocoding Rust utilities to enrich addresses, cities, countries, and landmarks with geographic coordinates through third-party geocoding web services.

GeoRust 55 Dec 12, 2022
Computational Geometry library written in Rust

RGeometry Computational Geometry in Rust! What is RGeometry? RGeometry is a collection of data types such as points, polygons, lines, and segments, an

null 101 Dec 6, 2022
Library for serializing the GeoJSON vector GIS file format

geojson Documentation Library for serializing the GeoJSON vector GIS file format Minimum Rust Version This library requires a minimum Rust version of

GeoRust 176 Dec 27, 2022
Library for serializing the GeoJSON vector GIS file format

geojson Documentation Library for serializing the GeoJSON vector GIS file format Minimum Rust Version This library requires a minimum Rust version of

GeoRust 176 Dec 27, 2022
A TinyVG vector graphics format parsing library.

tinyvg-rs A TinyVG vector graphics format parsing library. Testing This library uses the example files from the TinyVG/examples repo for integration t

null 2 Dec 31, 2021
Optimized geometry primitives for Microsoft platforms with the same memory layout as DirectX and Direct2D and types.

geoms Geometry for Microsoft platforms - a set of geometry primitives with memory layouts optimized for native APIs (Win32, Direct2D, and Direct3D). T

Connor Power 2 Dec 11, 2022
Geospatial primitives and algorithms for Rust

geo Geospatial Primitives, Algorithms, and Utilities The geo crate provides geospatial primitive types such as Point, LineString, and Polygon, and pro

GeoRust 989 Dec 29, 2022
Geospatial primitives and algorithms for Rust

geo Geospatial Primitives, Algorithms, and Utilities The geo crate provides geospatial primitive types such as Point, LineString, and Polygon, and pro

GeoRust 990 Jan 1, 2023
A single-binary, GPU-accelerated LLM server (HTTP and WebSocket API) written in Rust

Poly Poly is a versatile LLM serving back-end. What it offers: High-performance, efficient and reliable serving of multiple local LLM models Optional

Tommy van der Vorst 13 Nov 5, 2023
Blazing fast and lightweight PostGIS vector tiles server

Martin Martin is a PostGIS vector tiles server suitable for large databases. Martin is written in Rust using Actix web framework. Requirements Install

Urbica 921 Jan 7, 2023
Zero-Copy reading and writing of geospatial data.

GeoZero Zero-Copy reading and writing of geospatial data. GeoZero defines an API for reading geospatial data formats without an intermediate represent

GeoRust 155 Dec 29, 2022
OpenStreetMap flatdata format and compiler

osmflat Flat OpenStreetMap (OSM) data format providing an efficient random data access through memory mapped files. The data format is described and i

null 31 Dec 7, 2022
A traffic simulation game exploring how small changes to roads affect cyclists, transit users, pedestrians, and drivers.

A/B Street Ever been stuck in traffic on a bus, wondering why is there legal street parking instead of a dedicated bus lane? A/B Street is a game expl

A/B Street 6.8k Jan 4, 2023
Calculates a stars position and velocity in the cartesian coordinate system.

SPV Calculates a stars position and velocity in the cartesian coordinate system. Todo Expand the number of available operation Batch processing by tak

Albin Sjögren 11 Feb 18, 2022
Didactic implementation of the type checker described in "Complete and Easy Bidirectional Typechecking for Higher-Rank Polymorphism" written in OCaml

bidi-higher-rank-poly Didactic implementation of the type checker described in "Complete and Easy Bidirectional Typechecking for Higher-Rank Polymorph

Søren Nørbæk 23 Oct 18, 2022