Coverage for qubalab/images/region_2d.py: 93%

30 statements  

« prev     ^ index     » next       coverage.py v7.6.10, created at 2025-01-31 11:24 +0000

1from __future__ import annotations 

2from dataclasses import dataclass 

3import shapely 

4 

5 

6@dataclass(frozen=True) 

7class Region2D: 

8 """ 

9 Simple data class to represent the bounding box for a region in 2D. 

10 

11 :param x: the top left x-coordinate of the bounding box 

12 :param y: the top left y-coordinate of the bounding box 

13 :param width: the width of the bounding box 

14 :param height: the height of the bounding box 

15 :param z: the z-slice index of the bounding box 

16 :param t: the time point index of the bounding box 

17 """ 

18 x: int = 0 

19 y: int = 0 

20 width: int = -1 

21 height: int = -1 

22 z: int = 0 

23 t: int = 0 

24 

25 @property 

26 def geometry(self) -> shapely.Geometry: 

27 """ 

28 A shapely geometry describing the x/y coordinates of the region. 

29 """ 

30 return shapely.box(self.x, self.y, self.x+self.width, self.y+self.height) 

31 

32 def scale_region(self, scale_factor: float) -> Region2D: 

33 """ 

34 Scale the bounding box of this region on the x and y axis. 

35 

36 :param scale_factor: the scale factor to apply to this region 

37 :returns: a Region2D scaled by the provided factor 

38 :raises ValueError: when scale_factor is 0 

39 """ 

40 return self.downsample_region(1.0 / scale_factor) 

41 

42 def downsample_region(self, downsample: float) -> Region2D: 

43 """ 

44 Downsample the bounding box of this region on the x and y axis. 

45 

46 This can be used to convert coordinates from (for example) the full image resolution  

47 to a different pyramidal level. 

48  

49 :param downsample: the downsample to apply to this region 

50 :returns: a Region2D downsampled by the provided factor 

51 :raises ValueError: when downsample is 0 

52 """ 

53 if downsample == 1: 

54 return self 

55 

56 if downsample == 0: 

57 raise ValueError('Downsample cannot be 0!') 

58 

59 x = int(self.x / downsample) 

60 y = int(self.y / downsample) 

61 

62 # Handle -1 for width & height, i.e. until the full image width 

63 if self.width == -1: 

64 x2 = x - 1 

65 else: 

66 x2 = int(round(self.x + self.width) / downsample) 

67 

68 if self.height == -1: 

69 y2 = y - 1 

70 else: 

71 y2 = int(round(self.y + self.height) / downsample) 

72 

73 return Region2D(x=x, y=y, width=x2 - x, height=y2 - y, z=self.z, t=self.t)