From 2034fc9c142978b02165b56f87e3afc5ff6902dc Mon Sep 17 00:00:00 2001
From: alisterburt
Date: Thu, 13 Jan 2022 15:42:47 +0000
Subject: [PATCH 1/5] prepend extra dims following numpy broadcasting rules
---
tiler/tiler.py | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/tiler/tiler.py b/tiler/tiler.py
index 3b1b55a..871c291 100644
--- a/tiler/tiler.py
+++ b/tiler/tiler.py
@@ -101,11 +101,20 @@ def recalculate(
raise ValueError(
"Tile and data shapes must be tuple or lists of positive numbers."
)
+ if self.tile_shape.size <= self.data_shape.size:
+ size_difference = self.data_shape.size - self.tile_shape.size
+ self.tile_shape = np.insert(
+ arr=self.tile_shape,
+ obj=0,
+ values=np.ones(size_difference),
+ axis=0
+ )
if self.tile_shape.size != self.data_shape.size:
raise ValueError(
- "Tile and data shapes must have the same length. "
- "Hint: if you require tiles with less dimensions than data, put 1 in sliced dimensions, "
- "e.g. to get 1d 64px lines of 2d 64x64px image would mean tile_shape of (64, 1)."
+ "Tile shape must have less elements than the data shape."
+ "Hint: your tile shape will be prepended with ones to match the data shape,"
+ "following numpy broadcasting semantics."
+ "e.g. data_shape=(28, 28), tile_shape=(28) -> tile_shape=(1, 28)"
)
# Tiling mode
From ab50246bb6c6bfa2f669d6f9dcb80f8979007bc0 Mon Sep 17 00:00:00 2001
From: alisterburt
Date: Thu, 13 Jan 2022 15:49:45 +0000
Subject: [PATCH 2/5] add test
---
tests/test_tiler.py | 4 ++++
tiler/tiler.py | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/tests/test_tiler.py b/tests/test_tiler.py
index d9b34f3..8d5b009 100644
--- a/tests/test_tiler.py
+++ b/tests/test_tiler.py
@@ -35,6 +35,10 @@ def test_init(self):
overlap="unsupported_overlap",
)
+ # test tile shape broadcasting
+ tiler = Tiler(data_shape=(300, 300), tile_shape=(10))
+ assert np.allclose(tiler.tile_shape, (1, 10))
+
def test_repr(self):
# gotta get that coverage
tiler = Tiler(
diff --git a/tiler/tiler.py b/tiler/tiler.py
index 871c291..32f0a68 100644
--- a/tiler/tiler.py
+++ b/tiler/tiler.py
@@ -95,7 +95,7 @@ def recalculate(
if data_shape is not None:
self.data_shape = np.asarray(data_shape).astype(int)
if tile_shape is not None:
- self.tile_shape = np.asarray(tile_shape).astype(int)
+ self.tile_shape = np.atleast_1d(np.asarray(tile_shape).astype(int))
self._n_dim: int = len(self.data_shape)
if (self.tile_shape <= 0).any() or (self.data_shape <= 0).any():
raise ValueError(
From 2d16b8d1b57533b5681f049f7a8f00b38d2d283e Mon Sep 17 00:00:00 2001
From: alisterburt
Date: Thu, 13 Jan 2022 15:52:48 +0000
Subject: [PATCH 3/5] update README.md to reflect changes
---
README.md | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index 851ce88..80cc88a 100644
--- a/README.md
+++ b/README.md
@@ -136,7 +136,11 @@ This section is a work in progress.
**How do I create tiles with less dimensions than the data array?**
-Tiler expects `tile_shape` to have the same length as `data_shape`.
-However you can specify the unwanted dimension as just 1.
-For example, if you want to get 2d tiles out from 3d array you can specify depth dimension of 1:
+Tiler expects `tile_shape` to have less than or the same number of elements as `data_shape`.
+If `tile_shape` has less elements than `data_shape`, `tile_shape` will be prepended with
+ones to match the size of `data_shape`.
+For example, if you want to get 2d tiles out from 3d array:
+`Tiler(data_shape=(128,128,128), tile_shape=(128, 128))`.
+
+This is equivalent to:
`Tiler(data_shape=(128,128,128), tile_shape=(1, 128, 128))`.
From aa291e2aadcf3f1c5632840467232b11f10a43d7 Mon Sep 17 00:00:00 2001
From: the-lay
Date: Wed, 19 Jan 2022 20:43:27 +0200
Subject: [PATCH 4/5] Updated tile_shape matching; data_shape and tile_shape
are now int64
---
README.md | 7 ++-----
tests/test_tiler.py | 2 +-
tiler/tiler.py | 34 ++++++++++++++++++----------------
3 files changed, 21 insertions(+), 22 deletions(-)
diff --git a/README.md b/README.md
index 80cc88a..53a5f15 100644
--- a/README.md
+++ b/README.md
@@ -138,9 +138,6 @@ This section is a work in progress.
Tiler expects `tile_shape` to have less than or the same number of elements as `data_shape`.
If `tile_shape` has less elements than `data_shape`, `tile_shape` will be prepended with
-ones to match the size of `data_shape`.
-For example, if you want to get 2d tiles out from 3d array:
-`Tiler(data_shape=(128,128,128), tile_shape=(128, 128))`.
-
-This is equivalent to:
+ones to match the size of `data_shape`.
+For example, if you want to get 2d tiles out from 3d array you can initialize Tiler like this: `Tiler(data_shape=(128,128,128), tile_shape=(128, 128))` and it will be equivalent to
`Tiler(data_shape=(128,128,128), tile_shape=(1, 128, 128))`.
diff --git a/tests/test_tiler.py b/tests/test_tiler.py
index 8d5b009..dfa54ea 100644
--- a/tests/test_tiler.py
+++ b/tests/test_tiler.py
@@ -36,7 +36,7 @@ def test_init(self):
)
# test tile shape broadcasting
- tiler = Tiler(data_shape=(300, 300), tile_shape=(10))
+ tiler = Tiler(data_shape=(300, 300), tile_shape=(10,))
assert np.allclose(tiler.tile_shape, (1, 10))
def test_repr(self):
diff --git a/tiler/tiler.py b/tiler/tiler.py
index 32f0a68..f51cbbe 100644
--- a/tiler/tiler.py
+++ b/tiler/tiler.py
@@ -47,7 +47,8 @@ def __init__(
If there is a channel dimension, it should be included in the shape.
tile_shape (tuple, list or np.ndarray): Shape of a tile, e.g. (256, 256, 3), [64, 64, 64] or np.ndarray([3, 128, 128]).
- Tile must have the same number of dimensions as data.
+ Tile must have the same number of dimensions as data or less.
+ If less, the shape will be automatically prepended with ones to match data_shape size.
overlap (int, float, tuple, list or np.ndarray): Specifies overlap between tiles.
If integer, the same overlap of overlap pixels applied in each dimension, except channel_dimension.
@@ -93,28 +94,29 @@ def recalculate(
# Data and tile shapes
if data_shape is not None:
- self.data_shape = np.asarray(data_shape).astype(int)
+ self.data_shape = np.asarray(data_shape, dtype=np.int64)
if tile_shape is not None:
- self.tile_shape = np.atleast_1d(np.asarray(tile_shape).astype(int))
+ self.tile_shape = np.atleast_1d(np.asarray(tile_shape, dtype=np.int64))
+
+ # Append ones to match data_shape size
+ if self.tile_shape.size <= self.data_shape.size:
+ size_difference = self.data_shape.size - self.tile_shape.size
+ self.tile_shape = np.insert(
+ arr=self.tile_shape, obj=0, values=np.ones(size_difference), axis=0
+ )
+ warnings.warn(
+ f"Tiler automatically adjusted tile_shape from {tuple(tile_shape)} to {tuple(self.tile_shape)}."
+ )
self._n_dim: int = len(self.data_shape)
if (self.tile_shape <= 0).any() or (self.data_shape <= 0).any():
raise ValueError(
- "Tile and data shapes must be tuple or lists of positive numbers."
- )
- if self.tile_shape.size <= self.data_shape.size:
- size_difference = self.data_shape.size - self.tile_shape.size
- self.tile_shape = np.insert(
- arr=self.tile_shape,
- obj=0,
- values=np.ones(size_difference),
- axis=0
+ "Tile and data shapes must be tuple, list or ndarray of positive integers."
)
if self.tile_shape.size != self.data_shape.size:
raise ValueError(
- "Tile shape must have less elements than the data shape."
- "Hint: your tile shape will be prepended with ones to match the data shape,"
- "following numpy broadcasting semantics."
- "e.g. data_shape=(28, 28), tile_shape=(28) -> tile_shape=(1, 28)"
+ "Tile shape must have less or equal number of elements compared to the data shape. "
+ "If less, your tile shape will be prepended with ones to match the data shape, "
+ "e.g. data_shape=(28, 28), tile_shape=(28) -> tile_shape=(1, 28)."
)
# Tiling mode
From c0b5a5baff74bc7bca92bb4887355ee6bb1eafcc Mon Sep 17 00:00:00 2001
From: the-lay
Date: Wed, 19 Jan 2022 20:44:45 +0200
Subject: [PATCH 5/5] Updated documentation to most recent version; removed
baby logo from the docs website
---
README.md | 2 +-
docs/index.html | 77 +++++++++++++++++++++++++++++++--------------
misc/baby_logo.png | Bin 0 -> 1969 bytes
misc/docs.sh | 2 +-
tiler/__init__.py | 11 ++++---
5 files changed, 62 insertions(+), 30 deletions(-)
create mode 100644 misc/baby_logo.png
diff --git a/README.md b/README.md
index 53a5f15..dd63456 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# ![tiler_logo](https://user-images.githubusercontent.com/1889128/148645934-2b1545bc-e7ea-4f02-88fb-10100b6aa9be.png) tiler
+# ![tiler_baby_logo](misc/baby_logo.png) tiler
![Tiler teaser image](misc/teaser/tiler_teaser.png)
diff --git a/docs/index.html b/docs/index.html
index 6867bad..5f7bf29 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -135,6 +135,9 @@
such as semantic segmentation in deep learning, especially in domains where images do not fit into GPU memory
(e.g., hyperspectral satellite images, whole slide images, videos, tomography data).
+Please see Quick start section.
+If you want to use tiler interactively, I highly recommend napari and napari-tiler plugin.
+
Features
@@ -149,8 +152,7 @@ Features
Quick start
-This is an example of basic functionality.
-You can find more examples in examples.
+
You can find more examples in examples.
For more Tiler and Merger functionality, please check documentation.
import numpy as np
@@ -263,9 +265,10 @@ Frequently asked questions
How do I create tiles with less dimensions than the data array?
-Tiler expects tile_shape
to have the same length as data_shape
.
-However you can specify the unwanted dimension as just 1.
-For example, if you want to get 2d tiles out from 3d array you can specify depth dimension of 1:
+
Tiler expects tile_shape
to have less than or the same number of elements as data_shape
.
+If tile_shape
has less elements than data_shape
, tile_shape
will be prepended with
+ones to match the size of data_shape
.
+For example, if you want to get 2d tiles out from 3d array you can initialize Tiler like this: Tiler(data_shape=(128,128,128), tile_shape=(128, 128))
and it will be equivalent to
Tiler(data_shape=(128,128,128), tile_shape=(1, 128, 128))
.
@@ -298,10 +301,13 @@ Frequently asked questions
if "pdoc" in sys.modules: # pragma: no cover
with open("README.md", "r") as f:
_readme = f.read()
- _readme = _readme.split("# tiler", 1)[1] # remove header
- _readme = _readme.replace(
- "misc/teaser/tiler_teaser.png", "tiler_teaser.png"
- ) # replace image path
+
+ # remove baby logo and header
+ _readme = _readme.split("\n", 2)[2]
+
+ # replace teaser image path
+ _readme = _readme.replace("misc/teaser/tiler_teaser.png", "tiler_teaser.png")
+ _readme = _readme.replace("misc/baby_logo.png", "baby_logo.png")
__doc__ = _readme
@@ -360,7 +366,8 @@ Frequently asked questions
If there is a channel dimension, it should be included in the shape.
tile_shape (tuple, list or np.ndarray): Shape of a tile, e.g. (256, 256, 3), [64, 64, 64] or np.ndarray([3, 128, 128]).
- Tile must have the same number of dimensions as data.
+ Tile must have the same number of dimensions as data or less.
+ If less, the shape will be automatically prepended with ones to match data_shape size.
overlap (int, float, tuple, list or np.ndarray): Specifies overlap between tiles.
If integer, the same overlap of overlap pixels applied in each dimension, except channel_dimension.
@@ -406,19 +413,29 @@ Frequently asked questions
# Data and tile shapes
if data_shape is not None:
- self.data_shape = np.asarray(data_shape).astype(int)
+ self.data_shape = np.asarray(data_shape, dtype=np.int64)
if tile_shape is not None:
- self.tile_shape = np.asarray(tile_shape).astype(int)
+ self.tile_shape = np.atleast_1d(np.asarray(tile_shape, dtype=np.int64))
+
+ # Append ones to match data_shape size
+ if self.tile_shape.size <= self.data_shape.size:
+ size_difference = self.data_shape.size - self.tile_shape.size
+ self.tile_shape = np.insert(
+ arr=self.tile_shape, obj=0, values=np.ones(size_difference), axis=0
+ )
+ warnings.warn(
+ f"Tiler automatically adjusted tile_shape from {tuple(tile_shape)} to {tuple(self.tile_shape)}."
+ )
self._n_dim: int = len(self.data_shape)
if (self.tile_shape <= 0).any() or (self.data_shape <= 0).any():
raise ValueError(
- "Tile and data shapes must be tuple or lists of positive numbers."
+ "Tile and data shapes must be tuple, list or ndarray of positive integers."
)
if self.tile_shape.size != self.data_shape.size:
raise ValueError(
- "Tile and data shapes must have the same length. "
- "Hint: if you require tiles with less dimensions than data, put 1 in sliced dimensions, "
- "e.g. to get 1d 64px lines of 2d 64x64px image would mean tile_shape of (64, 1)."
+ "Tile shape must have less or equal number of elements compared to the data shape. "
+ "If less, your tile shape will be prepended with ones to match the data shape, "
+ "e.g. data_shape=(28, 28), tile_shape=(28) -> tile_shape=(1, 28)."
)
# Tiling mode
@@ -960,7 +977,8 @@ Frequently asked questions
If there is a channel dimension, it should be included in the shape.
tile_shape (tuple, list or np.ndarray): Shape of a tile, e.g. (256, 256, 3), [64, 64, 64] or np.ndarray([3, 128, 128]).
- Tile must have the same number of dimensions as data.
+ Tile must have the same number of dimensions as data or less.
+ If less, the shape will be automatically prepended with ones to match data_shape size.
overlap (int, float, tuple, list or np.ndarray): Specifies overlap between tiles.
If integer, the same overlap of overlap pixels applied in each dimension, except channel_dimension.
@@ -1002,7 +1020,8 @@ Args
- data_shape (tuple, list or np.ndarray): Input data shape, e.g. (1920, 1080, 3), [512, 512, 512] or np.ndarray([3, 1024, 768]).
If there is a channel dimension, it should be included in the shape.
- tile_shape (tuple, list or np.ndarray): Shape of a tile, e.g. (256, 256, 3), [64, 64, 64] or np.ndarray([3, 128, 128]).
-Tile must have the same number of dimensions as data.
+Tile must have the same number of dimensions as data or less.
+If less, the shape will be automatically prepended with ones to match data_shape size.
- overlap (int, float, tuple, list or np.ndarray): Specifies overlap between tiles.
If integer, the same overlap of overlap pixels applied in each dimension, except channel_dimension.
If float, percentage of a tile_shape to overlap (from 0.0 to 1.0), except channel_dimension.
@@ -1087,19 +1106,29 @@
Args
# Data and tile shapes
if data_shape is not None:
- self.data_shape = np.asarray(data_shape).astype(int)
+ self.data_shape = np.asarray(data_shape, dtype=np.int64)
if tile_shape is not None:
- self.tile_shape = np.asarray(tile_shape).astype(int)
+ self.tile_shape = np.atleast_1d(np.asarray(tile_shape, dtype=np.int64))
+
+ # Append ones to match data_shape size
+ if self.tile_shape.size <= self.data_shape.size:
+ size_difference = self.data_shape.size - self.tile_shape.size
+ self.tile_shape = np.insert(
+ arr=self.tile_shape, obj=0, values=np.ones(size_difference), axis=0
+ )
+ warnings.warn(
+ f"Tiler automatically adjusted tile_shape from {tuple(tile_shape)} to {tuple(self.tile_shape)}."
+ )
self._n_dim: int = len(self.data_shape)
if (self.tile_shape <= 0).any() or (self.data_shape <= 0).any():
raise ValueError(
- "Tile and data shapes must be tuple or lists of positive numbers."
+ "Tile and data shapes must be tuple, list or ndarray of positive integers."
)
if self.tile_shape.size != self.data_shape.size:
raise ValueError(
- "Tile and data shapes must have the same length. "
- "Hint: if you require tiles with less dimensions than data, put 1 in sliced dimensions, "
- "e.g. to get 1d 64px lines of 2d 64x64px image would mean tile_shape of (64, 1)."
+ "Tile shape must have less or equal number of elements compared to the data shape. "
+ "If less, your tile shape will be prepended with ones to match the data shape, "
+ "e.g. data_shape=(28, 28), tile_shape=(28) -> tile_shape=(1, 28)."
)
# Tiling mode
diff --git a/misc/baby_logo.png b/misc/baby_logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..bee964aae8fee00e989b92591e0d09d3803c92ce
GIT binary patch
literal 1969
zcmV;i2Tu5jP)9BB6~KS@e%owbVvl!hV-sU=FiU6?5n8H10U@DQN>GtS
zL|dukrTbH#`q=hAC`#LxRxOH9TBn;DsZ!G&sDoG%QfL&u7uY>JbzxDQE#*W9H
zG26Jxkw&BMF6aKfd(S!Fxd$pM5{Oh%p;iec64Bdj#=o~Oy!q_@aQ9DRGV~=IKb8sX
z2_X3>y2T?*Y^&3vCw5wGr~0g>CqvR)^Rfv)?Oe-EzB}g44~}~ag9GXH=7ScrD-Mu8->C^BO*-zQh|+6fR08~ng}7t297^uPhbATo&No8cmCA?H3Xi4D^}J$K!e|T53?r&oSqx5eVX1jEH8IRpb|kWmh9;3I_~DlN1Ua{+RJ
zOD1sCgtvHO)J^>HhBH0r5xfHgB>^?$?jEyY;Bm9zWRKa{A%zfu3P=VvN+}GHKgOJL
z$ps!#2x991tb74OYFI)KvdRtS{fwLT9akbfU20xH;Q1=>JmtIB-I?J#?!sVBdH+zT
zj|+ez)Lh^DiakGe1^jD)QeJ@a0`L^dQ%ZRX2nd>WG|N_A64Zo(y9zu|UjBdi=TiI}
z09`=5(DxxFyjvhTS{nOLZ*JRndTQqC;LWkOexw5kf&TRFGsjFla%_0~!sXkO@4q}X
z`|){Dqd+4NEiCk!gTG`eai9wDfr&!@YH0xK3S+JVS68&h3+?k&(Wc{FoA!TwOUHp{
z!&Z$UgkXB^8UXrAZKfHPy3JSs}`Rv{4}5i
zFn|n@E&u>b12ZMSXEn|2>T28nt=7i=f!4+yQB5;T`h#AoQ3!!$R_R+h4(!?4``n&P
zcJ3#)#xDMIWc=-Oi>W(*0TL^Q`@kg7TG*{N;BEmvU8EP$+8Ez5kT?`wZQZ
z+1xyovEj0St6`&&xYfV)D@S+q96z#{x|10md;7JK@k{5^+1Y=Q4-YOd#tQEP>dD)k
zjD+iUb+>=v8{O?+JRA$hLqgnt_`N4!F{~6dRc+DT+fO{dqv!Z@Q*+lBCucwU^T_z6
zmvi>Q3}7z})ugehqxGq8wl(cK+0xM0WE!#ZRH}TuejvbAw4$!2v!<^xQOmOF0NpI*qI_;9BtmkLsw+
zw$3OW%@N|YF;;r95RxG9(8^?{6+Ys;y*X|M$4M>k<>&kA+1k-iX3qZ+5Cl1A5nZ>4
z7VOFM9dfo)euj|vfrkMEflnrz$lLA40D@)2u*_HyfaBU_$J>rgDm{zqfe;$eNIgQx
zjd2no#F`9KAaOl^`B^E&RAP>iiA6@I91@E)-@&q%rF>RhsuHstW_;fc*7QDUxP6T69|9GkGBbmGdcVG}0;Xn}RR?>!4*j5~^U%?dRi&*5n99r|
zge>fsT!yx+Dm3}2Pb#IDN!*ybIr`?gk%>z$1%Y>!67lANO2C>l`nxtg^WEO=BLlHW
zJXUfCbMu%+1bsz?HJh8qG@@vlf#YU_k@1V~-57cO7xT%n^FV67H6N;oE`)4ts^4~|
zr}Nn}?Jb{cTNysG_5y|;E&{Zj6v@=&!mY6juMCe}JnQ-PWs2)Kl?7ZOk=p9^fo)xf
zzq5JMzFnF$M9yAB*DQ41!uM@8GXCzB8~=U%S99~j=RhUyt+KL!OQMIZ+QU7Y4}QPB
zW$!`XbDWWhOMkj``>nH%o4H87ME6+{(F3BXQri9p0(&mMRfJ>~00000NkvXXu0mjf
D{>jL>
literal 0
HcmV?d00001
diff --git a/misc/docs.sh b/misc/docs.sh
index 3b4e6f7..e6df956 100755
--- a/misc/docs.sh
+++ b/misc/docs.sh
@@ -1,4 +1,4 @@
-cd $(dirname "$file_name")/..
+cd "$(dirname "$file_name")"/.. || exit
# build docs
pdoc -o docs -d google tiler
diff --git a/tiler/__init__.py b/tiler/__init__.py
index a719054..51bcf9e 100644
--- a/tiler/__init__.py
+++ b/tiler/__init__.py
@@ -25,8 +25,11 @@
if "pdoc" in sys.modules: # pragma: no cover
with open("README.md", "r") as f:
_readme = f.read()
- _readme = _readme.split("# tiler", 1)[1] # remove header
- _readme = _readme.replace(
- "misc/teaser/tiler_teaser.png", "tiler_teaser.png"
- ) # replace image path
+
+ # remove baby logo and header
+ _readme = _readme.split("\n", 2)[2]
+
+ # replace teaser image path
+ _readme = _readme.replace("misc/teaser/tiler_teaser.png", "tiler_teaser.png")
+ _readme = _readme.replace("misc/baby_logo.png", "baby_logo.png")
__doc__ = _readme