Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ CogReader ndarray method to decode TIFF into an ndarray Array #10

Merged
merged 2 commits into from
Mar 15, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 39 additions & 11 deletions src/io/geotiff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,25 @@ impl<R: Read + Seek> CogReader<R> {
Ok(Self { decoder })
}

/// Decode GeoTIFF image to an [`ndarray::Array`]
fn ndarray(&mut self) -> TiffResult<Array2<f32>> {
// Get image dimensions
let (width, height): (u32, u32) = self.decoder.dimensions()?;

// Get image pixel data
let decode_result = self.decoder.read_image()?;
let image_data: Vec<f32> = match decode_result {
DecodingResult::F32(img_data) => img_data,
_ => unimplemented!("Data types other than float32 are not yet supported."),
};

// Put image pixel data into an ndarray
let vec_data = Array2::from_shape_vec((height as usize, width as usize), image_data)
.map_err(|_| TiffFormatError::InvalidDimensions(height, width))?;

Ok(vec_data)
}
Comment on lines +24 to +41
Copy link
Owner Author

@weiji14 weiji14 Mar 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was playing around with wrapping parts of this in a TryFrom trait, but got caught up fighting the borrow checker when trying to use it in another part of the crate, so will leave things here for now.


/// Affine transformation for 2D matrix extracted from TIFF tag metadata, used to transform
/// image pixel (row, col) coordinates to and from geographic/projected (x, y) coordinates.
///
Expand Down Expand Up @@ -76,17 +95,8 @@ pub fn read_geotiff<R: Read + Seek>(stream: R) -> TiffResult<Array2<f32>> {
// Open TIFF stream with decoder
let mut reader = CogReader::new(stream)?;

// Get image dimensions
let (width, height): (u32, u32) = reader.decoder.dimensions()?;

// Get image pixel data
let DecodingResult::F32(img_data) = reader.decoder.read_image()? else {
panic!("Cannot read band data")
};

// Put image pixel data into an ndarray
let vec_data = Array2::from_shape_vec((height as usize, width as usize), img_data)
.map_err(|_| TiffFormatError::InvalidDimensions(height, width))?;
// Decode TIFF into ndarray
let vec_data: Array2<f32> = reader.ndarray()?;

Ok(vec_data)
}
Expand All @@ -96,6 +106,7 @@ mod tests {
use std::io::{Cursor, Seek, SeekFrom};

use geo::AffineTransform;
use ndarray::array;
use object_store::parse_url;
use tempfile::tempfile;
use tiff::encoder::{colortype, TiffEncoder};
Expand Down Expand Up @@ -131,6 +142,23 @@ mod tests {
assert_eq!(arr.mean(), Some(14.0));
}

#[tokio::test]
async fn test_cogreader_ndarray() {
let cog_url: &str = "https://github.com/rasterio/rasterio/raw/1.3.9/tests/data/float32.tif";
let tif_url = Url::parse(cog_url).unwrap();
let (store, location) = parse_url(&tif_url).unwrap();

let result = store.get(&location).await.unwrap();
let bytes = result.bytes().await.unwrap();
let stream = Cursor::new(bytes);

let mut reader = CogReader::new(stream).unwrap();
let array = reader.ndarray().unwrap();

assert_eq!(array.shape(), [2, 3]);
assert_eq!(array, array![[1.41, 1.23, 0.78], [0.32, -0.23, -1.88]])
}

#[tokio::test]
async fn test_cogreader_transform() {
let cog_url: &str =
Expand Down
Loading