openslide_rs/
deepzoom.rs

1//! Support for Deep Zoom images.
2//! This module provides functionality for generating Deep Zoom images from `OpenSlide` objects.
3//! This is a simple translation of python `DeepZoomGenerator` implementation
4
5use crate::{
6    Address, DeepZoomGenerator, Region, Result, Size,
7    errors::OpenSlideError,
8    traits::Slide,
9    utils::{resize_rgb_image, resize_rgba_image},
10};
11use image::{RgbImage, RgbaImage};
12use std::borrow::Borrow;
13
14impl<S: Slide, B: Borrow<S>> DeepZoomGenerator<S, B> {
15    pub fn new(slide: B, tile_size: u32, overlap: u32, limit_bounds: bool) -> Result<Self> {
16        let nb_level = slide.borrow().get_level_count()?;
17
18        let (slide_level_dimensions, l0_offset) = if limit_bounds {
19            let bounds = slide.borrow().get_bounds();
20            let bounds_x = bounds.x.unwrap_or(0);
21            let bounds_y = bounds.y.unwrap_or(0);
22
23            // Level 0 coordinate offset
24
25            let l0_offset = Address {
26                x: bounds_x,
27                y: bounds_y,
28            };
29
30            // Slide level dimensions scale factor in each axis
31            let slide_dimensions = slide.borrow().get_level_dimensions(0)?;
32            let slide_dimensions = &slide_dimensions;
33
34            let bounds_width = bounds.width.unwrap_or(slide_dimensions.w);
35            let bounds_height = bounds.height.unwrap_or(slide_dimensions.h);
36
37            let size_scale = (
38                bounds_width as f32 / slide_dimensions.w as f32,
39                bounds_height as f32 / slide_dimensions.h as f32,
40            );
41
42            let slide_level_dimensions: Result<Vec<Size>> = (0..nb_level)
43                .map(|level| match slide.borrow().get_level_dimensions(level) {
44                    Ok(size) => Ok(Size {
45                        w: (size.w as f32 * size_scale.0).ceil() as _,
46                        h: (size.h as f32 * size_scale.1).ceil() as _,
47                    }),
48                    Err(err) => Err(err),
49                })
50                .collect();
51            (slide_level_dimensions?, l0_offset)
52        } else {
53            let l0_offset = Address { x: 0, y: 0 };
54            let slide_level_dimensions: Result<Vec<Size>> = (0..nb_level)
55                .map(|level| slide.borrow().get_level_dimensions(level))
56                .collect();
57            (slide_level_dimensions?, l0_offset)
58        };
59        let slide_level0_dimensions = slide_level_dimensions[0];
60
61        // Deep Zooom levels
62
63        let level_dimensions = {
64            let mut z_size = Size {
65                w: slide_level0_dimensions.w,
66                h: slide_level0_dimensions.h,
67            };
68            let mut level_dimensions = vec![z_size];
69
70            while z_size.w > 1 || z_size.h > 1 {
71                z_size.w = ((z_size.w as f32 / 2.0).ceil() as u32).max(1) as _;
72                z_size.h = ((z_size.h as f32 / 2.0).ceil() as u32).max(1) as _;
73                level_dimensions.push(z_size);
74            }
75            level_dimensions.reverse();
76            level_dimensions
77        };
78
79        // Tile
80        let level_tiles: Vec<Size> = level_dimensions
81            .iter()
82            .map(|Size { w, h }| Size {
83                w: (*w as f32 / tile_size as f32).ceil() as _,
84                h: (*h as f32 / tile_size as f32).ceil() as _,
85            })
86            .collect();
87
88        // Deep Zoom level count
89        let level_count = level_dimensions.len();
90
91        // Total downsamples for each Deep Zoom level
92        let l0_z_downsamples: Vec<f64> = (0..level_count)
93            .map(|level| 2_u64.pow((level_count - level - 1) as _) as f64)
94            .collect();
95
96        // Preferred slide levels for each Deep Zoom level
97        let slide_from_dz_level: Result<Vec<u32>> = l0_z_downsamples
98            .iter()
99            .map(|downsample| slide.borrow().get_best_level_for_downsample(*downsample))
100            .collect();
101        let slide_from_dz_level = slide_from_dz_level?;
102
103        // Piecewise downsamples
104        let l0_l_downsamples: Result<Vec<f64>> = (0..nb_level)
105            .map(|level| slide.borrow().get_level_downsample(level))
106            .collect();
107        let l0_l_downsamples = l0_l_downsamples?;
108
109        let l_z_downsamples: Vec<f64> = (0..level_count)
110            .map(|dz_level| {
111                l0_z_downsamples[dz_level]
112                    / l0_l_downsamples[slide_from_dz_level[dz_level] as usize]
113            })
114            .collect();
115
116        Ok(DeepZoomGenerator {
117            slide,
118            _phantom: Default::default(),
119            tile_size,
120            overlap,
121            l0_offset,
122            level_dimensions,
123            slide_level_dimensions,
124            level_tiles,
125            level_count,
126            slide_from_dz_level,
127            l0_l_downsamples,
128            l_z_downsamples,
129        })
130    }
131
132    pub fn level_count(&self) -> usize {
133        self.level_count
134    }
135
136    pub fn level_tiles(&self) -> &[Size] {
137        &self.level_tiles
138    }
139
140    pub fn level_dimensions(&self) -> &[Size] {
141        &self.level_dimensions
142    }
143
144    pub fn tile_count(&self) -> u32 {
145        self.level_tiles.iter().map(|&size| size.w * size.h).sum()
146    }
147
148    pub fn get_tile_rgba(&self, level: u32, location: Address) -> Result<RgbaImage> {
149        let (region, final_size) = self.get_tile_info(level, location)?;
150
151        let image = self.slide.borrow().read_image_rgba(&region)?;
152
153        let size = Size {
154            w: image.width(),
155            h: image.height(),
156        };
157
158        if final_size != size {
159            Ok(resize_rgba_image(image, &final_size)?)
160        } else {
161            Ok(image)
162        }
163    }
164
165    pub fn get_tile_rgb(&self, level: u32, location: Address) -> Result<RgbImage> {
166        let (region, final_size) = self.get_tile_info(level, location)?;
167        let image = self.slide.borrow().read_image_rgb(&region)?;
168
169        let size = Size {
170            w: image.width(),
171            h: image.height(),
172        };
173
174        if final_size != size {
175            Ok(resize_rgb_image(image, &final_size)?)
176        } else {
177            Ok(image)
178        }
179    }
180
181    pub fn get_tile_info(&self, level: u32, address: Address) -> Result<(Region, Size)> {
182        if level as usize >= self.level_count() {
183            return Err(OpenSlideError::CoreError("Invalid level".to_string()));
184        }
185        if address.x >= self.level_tiles[level as usize].w
186            || address.y >= self.level_tiles[level as usize].h
187        {
188            return Err(OpenSlideError::CoreError("Invalid address".to_string()));
189        }
190
191        let level_tiles = self.level_tiles[level as usize];
192        let level_dimensions = self.level_dimensions[level as usize];
193
194        // Get preferred slide level
195        let slide_level = self.slide_from_dz_level[level as usize];
196        let slide_level_dimensions = self.slide_level_dimensions[slide_level as usize];
197
198        // Calculate top/left and bottom/right overlap
199        let z_overlap_topleft = Address {
200            x: if address.x != 0 { self.overlap } else { 0 },
201            y: if address.y != 0 { self.overlap } else { 0 },
202        };
203
204        // Calculate top/left and bottom/right overlap
205        let z_overlap_bottomright = Address {
206            x: if address.x != (level_tiles.w - 1) {
207                self.overlap
208            } else {
209                0
210            },
211            y: if address.y != (level_tiles.h - 1) {
212                self.overlap
213            } else {
214                0
215            },
216        };
217
218        // Get final size of the tile
219        let z_size = Size {
220            w: self
221                .tile_size
222                .min(level_dimensions.w - self.tile_size * address.x)
223                + z_overlap_topleft.x
224                + z_overlap_bottomright.x,
225            h: self
226                .tile_size
227                .min(level_dimensions.h - self.tile_size * address.y)
228                + z_overlap_topleft.y
229                + z_overlap_bottomright.y,
230        };
231
232        // Obtain the region coordinates
233        let z_location = Address {
234            x: address.x * self.tile_size,
235            y: address.y * self.tile_size,
236        };
237
238        let l_location = (
239            self.l_z_downsamples[level as usize] * f64::from(z_location.x - z_overlap_topleft.x),
240            self.l_z_downsamples[level as usize] * f64::from(z_location.y - z_overlap_topleft.y),
241        );
242
243        // Round location down and size up, and add offset of active area
244        let l0_location = Address {
245            x: (self.l0_l_downsamples[slide_level as usize] * l_location.0
246                + f64::from(self.l0_offset.x)) as _,
247            y: (self.l0_l_downsamples[slide_level as usize] * l_location.1
248                + f64::from(self.l0_offset.y)) as _,
249        };
250
251        let l_size = Size {
252            w: (slide_level_dimensions.w - l_location.0.ceil() as u32)
253                .min((self.l_z_downsamples[level as usize] * f64::from(z_size.w)).ceil() as _),
254            h: (slide_level_dimensions.h - l_location.1.ceil() as u32)
255                .min((self.l_z_downsamples[level as usize] * f64::from(z_size.h)).ceil() as _),
256        };
257
258        let region = Region {
259            address: l0_location,
260            level: slide_level,
261            size: l_size,
262        };
263
264        Ok((region, z_size))
265    }
266}
267
268pub struct Bounds {
269    pub x: Option<u32>,
270    pub y: Option<u32>,
271    pub width: Option<u32>,
272    pub height: Option<u32>,
273}