--- jupytext: formats: md:myst text_representation: extension: .md format_name: myst kernelspec: display_name: Python 3 language: python name: python3 --- # Shapes The shape of the objects in a Dead Leaves image are specified via the `leaf_shape`argument of the `LeafGeometryGenerator`. Currently support shapes are: - `circular` - `ellipsoid` - `rectangular` - `polygon` Shape-specific parameters are passed through the `shape_param_distributions`dictionary. The required parameters depend on the chosen shape. ```{note} For comparability all shape sizes are parameterized via an `area` parameter, which describes the distribution of the leaves size in terms of number of pixels (i.e. squared pixels). ``` ## Circles Circular leaves (`leaf_shape = "circular"`) are the simplest, requiring only the `area` or `radius` distribution: ```python { "area": } ``` or ```python { "radius": } ``` The leaf mask for a circular leaf with position $(\bar{x},\bar{y})$ and area $A$ or radius $r$ is then generated via $$ L(x,y) = \begin{cases} 1, & \text{if } \sqrt{(x-\bar{x})^2 + (y-\bar{y})^2} \leq \sqrt{\frac{A}{\pi}} = r \\ 0, & \text{else.} \end{cases} $$ ```{tip} To generate images with powerlaw distributed leaf radius (for a $1/f$ power spectrum) simply use a powerlaw distributed area with exponent `k`. For parameterization via the area the value of `k` should be half of what you would use for the radius to achieve the same distribution. ``` **Example** ```{code-cell} :tags: [hide-input] from deadleaves import LeafGeometryGenerator, LeafAppearanceSampler, ImageRenderer model = LeafGeometryGenerator( "circular", {"area": {"powerlaw": {"low": 100.0, "high": 10000.0, "k": 1.5}}}, (256, 256) ) leaf_table, segmentation_map = model.generate_segmentation() colormodel = LeafAppearanceSampler(leaf_table) colormodel.sample_color({"gray": {"uniform": {"low": 0.0, "high": 1.0}}}) renderer = ImageRenderer(colormodel.leaf_table, segmentation_map) renderer.render_image() renderer.show(figsize = (3,3)) ``` ## Ellipsoids Ellipsoidal leaves (`leaf_shape = "ellipsoid"`) require distributions for - `area`: size of the ellipse - `aspect_ratio`: ratio of minor to major axis - `orientation`: rotation angle. ```python { "area": , "orientation": , "aspect_ratio": } ``` The leaf mask for a ellipsoidal leaf with position $(\bar{x},\bar{y})$, area $A$, aspect ratio $\rho$, and orientation $\phi$ is then generated via $$ a = \sqrt{A \cdot \frac{\rho}{\pi}} \qquad b = \sqrt{\frac{A}{\pi \cdot \rho}} \\ u = (x-\bar{x}) \cdot \cos(\phi) - (y-\bar{y}) \cdot \sin(\phi) \\ v = (x-\bar{x}) \cdot \sin(\phi) + (y-\bar{y}) \cdot \cos(\phi)\\ L(x,y) = \begin{cases} 1, & \text{if } \sqrt{\left(\frac{u}{a}\right)^2 + \left(\frac{v}{b}\right)^2} \leq 1 \\ 0, & \text{else.} \end{cases} $$ **Example** ```{code-cell} :tags: [hide-input] from deadleaves import LeafGeometryGenerator, LeafAppearanceSampler, ImageRenderer import torch model = LeafGeometryGenerator( "ellipsoid", { "area": {"powerlaw": {"low": 100.0, "high": 10000.0, "k": 1.5}}, "orientation": {"uniform": {"low": 0.0, "high": 2 * torch.pi}}, "aspect_ratio": {"uniform": {"low": 0.5, "high": 2}} }, (256, 256) ) leaf_table, segmentation_map = model.generate_segmentation() colormodel = LeafAppearanceSampler(leaf_table) colormodel.sample_color({"gray": {"uniform": {"low": 0.0, "high": 1.0}}}) renderer = ImageRenderer(colormodel.leaf_table, segmentation_map) renderer.render_image() renderer.show(figsize = (3,3)) ``` ## Rectangles Rectangular leaves (`leaf_shape = "rectangular"`) use the same parameters as ellipsoids: ```python { "area": , "orientation": , "aspect_ratio": } ``` The leaf mask for a rectangular leaf with position $(\bar{x},\bar{y})$, area $A$, aspect ratio $R$, and orientation $\phi$ is then generated via $$ h = \sqrt{\frac{A}{\rho}} \qquad w = h \cdot \rho \\ u = (x-\bar{x})\cdot \cos(\phi) - (y-\bar{y})\cdot \sin(\phi) \\ v = (x-\bar{x})\cdot \sin(\phi) - (y-\bar{y})\cdot \cos(\phi) \\ L(x,y) = \begin{cases} 1, & \text{if } \vert u \vert \leq \frac{w}{2} \text{ and } \vert v \vert \leq \frac{h}{2} \\ 0, & \text{else.} \end{cases} $$ **Example** ```{code-cell} :tags: [hide-input] from deadleaves import LeafGeometryGenerator, LeafAppearanceSampler, ImageRenderer import torch model = LeafGeometryGenerator( "rectangular", { "area": {"powerlaw": {"low": 100.0, "high": 10000.0, "k": 1.5}}, "orientation": {"uniform": {"low": 0.0, "high": 2 * torch.pi}}, "aspect_ratio": {"uniform": {"low": 0.5, "high": 2}} }, (256, 256) ) leaf_table, segmentation_map = model.generate_segmentation() colormodel = LeafAppearanceSampler(leaf_table) colormodel.sample_color({"gray": {"uniform": {"low": 0.0, "high": 1.0}}}) renderer = ImageRenderer(colormodel.leaf_table, segmentation_map) renderer.render_image() renderer.show(figsize = (3,3)) ``` ## Regular polygons Currently only regular polygons with fixed orientation are supported (`leaf_shape = "polygon"`). The parameters are `area` and number of vertices `n_vertices`: ```python { "area": , "n_vertices": } ``` The leaf mask for a regular polygon leaf with position $(\bar{x},\bar{y})$, area $A$, and number of vertices $n$ is then generated by computing the positions of the vertices via $$ r = \sqrt{2\cdot \frac{A}{n\cdot \sin(2\cdot\frac{\pi}{n})}} \\ \psi_k = \frac{2\pi k}{n} \qquad v_k = \begin{pmatrix}\bar{x} + r\cdot \cos(\psi_k) \\ \bar{y} + r\cdot \sin(\psi_k) \end{pmatrix}. $$ We then use a simple [Ray-casting algorithm](https://rosettacode.org/wiki/Ray-casting_algorithm#) to check if a pixel $(x,y)$ is with in the convex hull of the vertices, i.e. the polygon. **Example** ```{code-cell} :tags: [hide-input] from deadleaves import LeafGeometryGenerator, LeafAppearanceSampler, ImageRenderer model = LeafGeometryGenerator( "polygon", { "area": {"powerlaw": {"low": 100.0, "high": 10000.0, "k": 1.5}}, "n_vertices": {"poisson": {"rate": 5}}, }, (256, 256) ) leaf_table, segmentation_map = model.generate_segmentation() colormodel = LeafAppearanceSampler(leaf_table) colormodel.sample_color({"gray": {"uniform": {"low": 0.0, "high": 1.0}}}) renderer = ImageRenderer(colormodel.leaf_table, segmentation_map) renderer.render_image() renderer.show(figsize = (3,3)) ```