{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n# Circular motion\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import pandas as pd\nimport torch\n\nimport matplotlib.pyplot as plt\nfrom matplotlib import animation\nfrom IPython.display import HTML\n\nfrom deadleaves import (\n LeafGeometryGenerator,\n LeafAppearanceSampler,\n ImageRenderer,\n LeafTopology,\n)\n\nimage_shape = (512, 731)\narea = image_shape[0] * image_shape[1]\n\ngeometry = LeafGeometryGenerator(\n leaf_shape=\"circular\",\n shape_param_distributions={\n \"area\": {\"uniform\": {\"low\": area * 0.005, \"high\": area * 0.01}},\n },\n image_shape=image_shape,\n n_sample=250,\n position_mask={\n \"shape\": \"circular\",\n \"params\": {\n \"area\": area * 0.35,\n },\n },\n)\n\nleaf_table, segmentation_map = geometry.generate_segmentation()\n\ncolor_params = {\n \"H\": {\"uniform\": {\"low\": 0.0, \"high\": 0.2}},\n \"S\": {\"uniform\": {\"low\": 0.0, \"high\": 1.0}},\n \"V\": {\"uniform\": {\"low\": 0.0, \"high\": 1.0}},\n}\n\nappearance = LeafAppearanceSampler(\n leaf_table=leaf_table,\n)\n\ntable = appearance.sample_color(color_param_distributions=color_params)\n\ntopology = LeafTopology(image_shape=image_shape)\n\n\ndef apply_circular_motion(\n leaf_table: pd.DataFrame,\n image_size: tuple[int, int] | torch.Size,\n angle_step: float,\n angular_jitter: float = 0.0,\n radial_jitter: float = 0.0,\n) -> pd.DataFrame:\n\n table = leaf_table.copy()\n\n cy = image_size[0] / 2\n cx = image_size[1] / 2\n\n x = torch.tensor(table[\"x_pos\"]) - cx\n y = torch.tensor(table[\"y_pos\"]) - cy\n\n # Polar coordinates\n r = torch.sqrt(x**2 + y**2)\n theta = torch.arctan2(y, x)\n\n # update angle\n theta += angle_step\n if angular_jitter > 0:\n theta += torch.distributions.normal.Normal(0.0, angular_jitter).sample(\n (len(theta),)\n )\n\n # update radius\n if radial_jitter > 0:\n r += torch.distributions.normal.Normal(0.0, radial_jitter).sample((len(r),))\n r = torch.clip(r, 0.0, None)\n\n # back to Cartesian\n table[\"x_pos\"] = cx + r * torch.cos(theta)\n table[\"y_pos\"] = cy + r * torch.sin(theta)\n\n return table\n\n\nframes = []\nfor t in range(10):\n table = apply_circular_motion(\n leaf_table=table,\n image_size=segmentation_map.shape,\n angle_step=torch.pi / 10,\n angular_jitter=0.1,\n )\n\n renderer = ImageRenderer(\n leaf_table=table,\n image_shape=image_shape,\n background_color=torch.tensor(1.0),\n )\n image = renderer.render_image()\n frames.append(image.cpu())\n\n\n# Generate gif\nfig, ax = plt.subplots(figsize=(7.31, 5.12))\nim = ax.imshow(frames[0])\nax.axis(\"off\")\nax.set_position((0.0, 0.0, 1.0, 1.0))\n\n\ndef update(i):\n im.set_data(frames[i])\n return [im]\n\n\nani = animation.FuncAnimation(\n fig,\n update,\n frames=len(frames),\n interval=100,\n blit=True,\n)\n\nplt.close(fig)\nHTML(ani.to_jshtml())" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.3" } }, "nbformat": 4, "nbformat_minor": 0 }