{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Tutorial about regions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Regions define a support for localization data or specify a hull that captures a set of localizations. Locan provides various region classes with a standard set of attributes and methods." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", "\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "\n", "import locan as lc" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "lc.show_versions(system=False, dependencies=False, verbose=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Region definitions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The standard set of attributes and methods is defined by the abstract base class `Region` that all region classes inherit." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "lc.Region?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"Methods:\")\n", "[method for method in dir(lc.Region) if not method.startswith('_')]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Further definitions can be found in the abstract classes `Region1D`, `Region2D` and `Region3D` and all specific region classes." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import inspect\n", "inspect.getmembers(lc.data.regions, inspect.isabstract)" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## Use Region classes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Use one of the following classes to define a region in 1, 2 or 3 dimensions:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(\"Empty Region:\\n\", [lc.EmptyRegion.__name__], \"\\n\")\n", "print(\"Regions in 1D:\\n\", [cls.__name__ for cls in lc.Region1D.__subclasses__()], \"\\n\")\n", "print(\"Regions in 2D:\\n\", [cls.__name__ for cls in lc.Region2D.__subclasses__()], \"\\n\")\n", "print(\"Regions in 3D:\\n\", [cls.__name__ for cls in lc.Region3D.__subclasses__()], \"\\n\")\n", "print(\"Regions in nD:\\n\", [cls.__name__ for cls in lc.RegionND.__subclasses__()], \"\\n\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The region constructors take different parameters. \n", "\n", "REMEMBER: Angles are taken in degrees." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "region = lc.Rectangle(corner=(0, 0), width=1, height=2, angle=45)\n", "region" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "points = ((0, 0), (0, 1), (1, 1), (1, 0.5), (0, 0))\n", "holes = [((0.2, 0.2), (0.2, 0.4), (0.4, 0.4), (0.3, 0.25)), ((0.5, 0.5), (0.5, 0.8), (0.8, 0.8), (0.7, 0.45))]\n", "region = lc.Polygon(points, holes)\n", "print(region)\n", "region" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Several attributes are available, e.g. about the area or circumference." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "dict(dimension=region.dimension, bounds=region.bounds, extent=region.extent, bounding_box=region.bounding_box, centroid=region.centroid, max_distance=region.max_distance, \n", " region_measure= region.region_measure, subregion_measure=region.subregion_measure) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A list of points defining a polygon that resembles the region is available." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "print(\"Points:\\n\", region.points, \"\\n\")\n", "print(\"Holes:\\n\", region.holes)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Region can be constructed from interval tuples indicating feature ranges." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "region_1d = lc.Region.from_intervals((0, 1))\n", "region_2d = lc.Region.from_intervals(((0, 1), (0, 1)))\n", "region_3d = lc.Region.from_intervals([(0, 1)] * 3)\n", "region_4d = lc.Region.from_intervals([(0, 1)] * 4)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for region in (region_1d, region_2d, region_3d, region_4d):\n", " print(region)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Plot regions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Regions can be plotted as patch in mathplotlib figures." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "points = ((0, 0), (0, 1), (1, 1), (1, 0.5), (0, 0))\n", "holes = [((0.2, 0.2), (0.2, 0.4), (0.4, 0.4), (0.3, 0.25)), ((0.5, 0.5), (0.5, 0.8), (0.8, 0.8), (0.7, 0.45))]\n", "region = lc.Polygon(points, holes)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots(nrows=1, ncols=1)\n", "ax.add_patch(region.as_artist(fill=False, color='Blue'))\n", "ax.add_patch(region.bounding_box.as_artist(fill=False, color='Grey'))\n", "ax.plot(*region.centroid, '*', color='Red')\n", "ax.axis('equal')\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Intersection, union, difference of regions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Methods are provided to check for intersection, difference, union and membership." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "other_region = lc.Rectangle(corner=(0.5, 0.2), width=1.5, height=1.5, angle=45)\n", "other_region.shapely_object" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "result = region.intersection(other_region)\n", "print(result)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots(nrows=1, ncols=1)\n", "ax.add_patch(result.as_artist(fill=True, color='Blue'))\n", "ax.add_patch(region.as_artist(fill=False, color='Red'))\n", "ax.add_patch(other_region.as_artist(fill=False, color='Green'))\n", "ax.axis('equal')\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "result = region.symmetric_difference(other_region)\n", "print(result)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots(nrows=1, ncols=1)\n", "ax.add_patch(result.as_artist(fill=True, color='Blue'))\n", "ax.add_patch(region.as_artist(fill=False, color='Red'))\n", "ax.add_patch(other_region.as_artist(fill=False, color='Green'))\n", "ax.axis('equal')\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "result = region.union(other_region)\n", "print(result)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots(nrows=1, ncols=1)\n", "ax.add_patch(result.as_artist(fill=True, color='Blue'))\n", "ax.add_patch(region.as_artist(fill=False, color='Red'))\n", "ax.add_patch(other_region.as_artist(fill=False, color='Green'))\n", "ax.axis('equal')\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Check if point is in region" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`Region` has a `contains` method to select points that are within the region." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "inside_indices = other_region.contains(region.points)\n", "contained_points = region.points[inside_indices]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots(nrows=1, ncols=1)\n", "ax.scatter(*region.points.T, color='Grey')\n", "ax.scatter(*contained_points.T, color='Black')\n", "ax.add_patch(other_region.as_artist(fill=False, color='Blue'))\n", "ax.axis('equal')\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## LocData and regions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "LocData bring various hulls that define regions. Also LocData typically has a unique region defined as support. This can e.g. result from the definition of a region of interest using the ROI function or as specified in a corresponding yaml file." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Create data in region:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A random dataset is created within a specified region (for other methods see simulation tutorial)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "region = lc.Rectangle(corner=(0, 0), width=1, height=1, angle=45)\n", "locdata = lc.simulate_uniform(n_samples=1000, region=region, seed=1)\n", "locdata.print_summary()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "region" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Show scatter plots together with regions" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots(nrows=1, ncols=1)\n", "locdata.data.plot.scatter(x='position_x', y='position_y', ax=ax, color='Blue', label='locdata')\n", "ax.add_patch(locdata.region.as_artist(fill=False, color='Red'))\n", "ax.add_patch(locdata.region.bounding_box.as_artist(fill=False, color='Blue'))\n", "ax.axis('equal')\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Select localizations within regions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "LocData can be selected for localizations being inside the region." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "region = lc.Ellipse(center=(0, 0.5), width=1, height=0.5, angle=45)\n", "locdata_in_region = lc.select_by_region(locdata, region)\n", "locdata_in_region.region" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots(nrows=1, ncols=1)\n", "locdata.data.plot.scatter(x='position_x', y='position_y', ax=ax, color='Blue', label='locdata', alpha=0.1)\n", "ax.add_patch(region.as_artist(fill=False, color='Red'))\n", "locdata_in_region.data.plot.scatter(x='position_x', y='position_y', ax=ax, color='Red', label='locdata_in_region')\n", "ax.plot(*region.centroid, '*', color='Green')\n", "ax.axis('equal')\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Regions of interest" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Roi class is an object that defines a region of interest for a specific localization dataset. It is mostly used to save and reload regions of interest after having selected them interactively, e.g. in napari. It is therefore related to region specifications and a unique LocData object. \n", "\n", "Define a region of interest (roi):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "roi = lc.Roi(reference=locdata, region=lc.Ellipse(center=(0, 0.5), width=1, height=0.5, angle=80))\n", "roi" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Create new LocData instance by selecting localizations within a roi." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "locdata_roi = roi.locdata()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots(nrows=1, ncols=1)\n", "locdata.data.plot.scatter(x='position_x', y='position_y', ax=ax, color='Blue', label='locdata', alpha=0.1)\n", "ax.add_patch(roi.region.as_artist(fill=False))\n", "locdata_roi.data.plot.scatter(x='position_x', y='position_y', ax=ax, color='Red', label='locdata_roi')\n", "ax.plot(*roi.region.centroid, '*', color='Green')\n", "ax.axis('equal')\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### ROI input/output" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you have prepared rois and saved them as roi.yaml file you can read that data back in:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import tempfile\n", "from pathlib import Path\n", "\n", "with tempfile.TemporaryDirectory() as tmp_directory:\n", " file_path = Path(tmp_directory) / 'roi.yaml'\n", "\n", " roi.to_yaml(path=file_path)\n", "\n", " roi_new = lc.Roi.from_yaml(path = file_path)\n", " roi_new.reference = roi.reference\n", " \n", "new_locdata = roi_new.locdata()\n", "new_locdata.meta" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "roi_new" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.6" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": {}, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 }