{ "cells": [ { "cell_type": "markdown", "id": "b88dcaf9", "metadata": {}, "source": [ "# Tutorial on image moments in scikit-image\n", "\n", "Read on for some brief technical background on what image moments are,\n", "followed by some practical notes on how to calculate and interpret the\n", "different kinds of moments available in the scikit-image package." ] }, { "cell_type": "markdown", "id": "40f8c6ca", "metadata": {}, "source": [ "## Introduction to image moments\n", "\n", "The moments of a shape are particular weighted averages (in statistics, \"moments\")\n", "of the pixel intensities. In physics they're the distribution of matter about\n", "a point or an axis etc.\n", "\n", "They are usually described as a function $f(x,y)$ which can be read as\n", "_the value (or intensity) of the pixel in an image at position $(x,y)$_, where $f$ is the 'image function' (or sometimes $I$).\n", "\n", "If the image is binary (e.g. a binary blob), then we'd say $f$ takes\n", "values in $[0,1]$.\n", "\n", "A moment of order $p + q$ is defined for a 2D continuous function on a region as:\n", "\n", "$$M_{pq} = \\int x^p y^q\\hspace{2pt}f(x,y) \\cdot dx\\hspace{2pt}dy$$\n", "\n", "Usually we work with discrete (not continuous) image functions (as images\n", "are usually made of discrete pixels), which conveniently means the integrals\n", "simplify to summations over x and y.\n", "\n", "$$M_{pq} = \\sum_x \\sum_y x^p y^q\\hspace{2pt}f(x,y)$$\n", "\n", "So the steps to obtain a moment $M_{pq}$ are:\n", "\n", "- count the pixels over an image $f(x,y)$,\n", "- scale this count by a power of $x$ and/or $y$ ($p$ and/or $q$)\n", "- the sum of these powers $p+q$ is the moment's _order_" ] }, { "cell_type": "markdown", "id": "d9cb9c55", "metadata": {}, "source": [ "### Zeroth order moments\n", "\n", "Since the only values of $(p, q)$ that sum to 0 are $(0, 0)$, there is only one $0^{th}$ order moment: $M_{00}$\n", "\n", "$$M_{00} = \\sum_x \\sum_y x^0 y^0\\hspace{2pt}f(x,y)$$\n", "\n", "These coefficients $x^0 = y^0 = 1$, so they can be removed:\n", "\n", "$$M_{00} = \\sum_x \\sum_y f(x,y)$$\n", "\n", "The $0^{th}$ moment is just the total count\n", "of pixels in an image, i.e. its area (it's usually called the area moment for binary\n", "images or otherwise the 'mass' of the image for grayscale etc.)." ] }, { "cell_type": "markdown", "id": "13ac1960", "metadata": {}, "source": [ "### First order moments\n", "\n", "By _\"first order moments\"_ we mean $M_{10}$, $M_{01}$\n", "\n", "$$M_{10} = \\sum_x \\sum_y x \\hspace{2pt}f(x,y) \\hspace{10pt}and\\hspace{10pt}M_{01} = \\sum_x \\sum_y y \\hspace{2pt}f(x,y)$$\n", "\n", "\n", "### Second order moments\n", "\n", "By _\"second order moments\"_ we mean $M_{11}$, $M_{20}$, $M_{02}$\n", "\n", "$$M_{11} = \\sum_x \\sum_y xy\\hspace{2pt}f(x,y) \\hspace{10pt},\\hspace{10pt}M_{20} = \\sum_x \\sum_y x^2 \\hspace{2pt}f(x,y) \\hspace{10pt},\\hspace{10pt}M_{02} = \\sum_x \\sum_y y^2 \\hspace{2pt}f(x,y)$$\n", "\n", "\n", "Second order moments $M_{20}$ and $M_{02}$ describe the \"distribution of mass\"\n", "of the image with respect to the coordinate axes. In mechanics they're the\n", "_moments of inertia_.\n", "\n", "Another mechanical quality is the _radius of gyration_\n", "with respect to an axis, expressed as \n", "\n", "$$\\sqrt{\\frac{M_{20}}{M_{00}}} \\hspace{10pt}and\\hspace{10pt} \\sqrt{\\frac{M_{02}}{M_{00}}}$$\n", "\n", "There are others: scale invariant moments and rotation invariant moments\n", "(the latter are a.k.a. Hu moments).\n", "\n", "Next let's run through moment calculation in `scikit-image`." ] }, { "cell_type": "markdown", "id": "01112cbb", "metadata": {}, "source": [ "## Image moments in scikit-image\n", "\n", "To calculate image moments, scikit-image provides [`skimage.measure.moments`](https://scikit-image.org/docs/dev/api/skimage.measure.html#moments).\n", "\n", "Firstly, I'll calculate the moments for a small circular binary blob." ] }, { "cell_type": "code", "execution_count": 1, "id": "11d6c95a", "metadata": {}, "outputs": [], "source": [ "from skimage.measure import moments\n", "from skimage.draw import disk\n", "import numpy as np" ] }, { "cell_type": "code", "execution_count": 2, "id": "5e313141", "metadata": {}, "outputs": [], "source": [ "shape = (9,9)\n", "rr, cc = disk(center=(4,4), radius=4, shape=None)" ] }, { "cell_type": "code", "execution_count": 3, "id": "34af9700", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0, 0, 0, 0, 0, 0, 0, 0, 0],\n", " [0, 0, 1, 1, 1, 1, 1, 0, 0],\n", " [0, 1, 1, 1, 1, 1, 1, 1, 0],\n", " [0, 1, 1, 1, 1, 1, 1, 1, 0],\n", " [0, 1, 1, 1, 1, 1, 1, 1, 0],\n", " [0, 1, 1, 1, 1, 1, 1, 1, 0],\n", " [0, 1, 1, 1, 1, 1, 1, 1, 0],\n", " [0, 0, 1, 1, 1, 1, 1, 0, 0],\n", " [0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8)" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "img = np.zeros(shape=shape, dtype=np.uint8)\n", "img[rr, cc] = 1\n", "img" ] }, { "cell_type": "code", "execution_count": 4, "id": "ed422259", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", "import matplotlib.ticker as plticker\n", "plt.gca().xaxis.set_major_locator(plt.MultipleLocator(1))\n", "plt.xlabel(\"x\")\n", "plt.ylabel(\"y\")\n", "plt.grid()\n", "plt.imshow(img)" ] }, { "cell_type": "markdown", "id": "1a304900", "metadata": {}, "source": [ "By adding unit grid lines to the plot, the area of the disk can be counted simply by counting the number of grid intersections (each of which is the centre point of a disk pixel).\n", "\n", "- The first and last row (at y=1 and y=6) has 5 of these intersecting gridlines,\n", "- The 2nd-6th rows each have 7,\n", "- ...so the total is (5 x 2)+(7 x 5) = 45\n", "\n", "So I'd expect the first image moment (the \"mass\") to be 45, which it is:" ] }, { "cell_type": "code", "execution_count": 5, "id": "cc5b6a6b", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[45.]])" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m00 = moments(img, order=0)\n", "m00" ] }, { "cell_type": "markdown", "id": "b7bf7a18", "metadata": {}, "source": [ "In practice you wouldn't calculate just one moment at a time like this, you'd calculate all up to a particular order.\n", "\n", "By default in scikit-image this order is 3, but here I only want up to the 2nd order." ] }, { "cell_type": "code", "execution_count": 6, "id": "b6389465", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[ 45. 180. 880.]\n", " [ 180. 720. 3520.]\n", " [ 880. 3520. 17100.]]\n" ] } ], "source": [ "moments_array = moments(img, order=2)\n", "print(moments_array)" ] }, { "cell_type": "markdown", "id": "5739f2ec", "metadata": {}, "source": [ "Note that this array of moments is symmetrical: the reason why is self-evident (the image of the circular disk is symmetrical about the x and y axes which are the basis for the value of the moments as introduced earlier).\n", "\n", "To move away from this, let's change our image.\n", "\n", "- Also note that all the moments are multiples of the $M_{00}$ ($0^{th}$ order moment)" ] }, { "cell_type": "markdown", "id": "50c14487", "metadata": {}, "source": [ "---" ] }, { "cell_type": "markdown", "id": "0361b763", "metadata": {}, "source": [ "[The source code](https://github.com/scikit-image/scikit-image/blob/main/skimage/measure/_moments.py#L196-L255) contains another example, but again it's symmetrical (the image is a small square), but with a little editing it can be made to look more like a wonky Tetris block (or maybe a low poly cow)." ] }, { "cell_type": "code", "execution_count": 7, "id": "7a9692c1", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "cow = np.zeros((5, 6), dtype=np.uint8)\n", "cow[1:2, 1:3] = 1 # \"head\" of the cow\n", "cow[2:4, 2:5] = 1 # \"body\" of the cow\n", "plt.gca().xaxis.set_major_locator(plt.MultipleLocator(1))\n", "plt.gca().yaxis.set_major_locator(plt.MultipleLocator(1))\n", "plt.xlabel(\"x\"); plt.ylabel(\"y\"); plt.grid()\n", "plt.imshow(cow)" ] }, { "cell_type": "code", "execution_count": 8, "id": "739f5b90", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 8, 21, 63],\n", " [ 17, 48, 150],\n", " [ 41, 120, 382]])" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "moments(cow, order=2).astype(int)" ] }, { "cell_type": "markdown", "id": "10dc82c1", "metadata": {}, "source": [ "Now the array of moments is no longer symmetrical, and the values are no longer simply multiples of the $0^{th}$ order moment.\n", "\n", "- The values of interest are along the antidiagonals from the top-left of the array\n", "- If we want at most the $2^{nd}$ order moments, then these are the values along the\n", " main antidiagonal of the array\n", "\n", "To write out all the equations shown in this array:\n", "\n", "$$0^{th} order:$$\n", "\n", "$$M_{00} = \\sum_x \\sum_y f(x,y) = 8$$\n", "\n", "$$1^{st} order:$$\n", "\n", "$$M_{10} = \\sum_x \\sum_y x \\hspace{2pt}f(x,y) = 21$$\n", "$$M_{01} = \\sum_x \\sum_y y \\hspace{2pt}f(x,y) = 17$$\n", "\n", "$$2^{nd} order:$$\n", "\n", "$$M_{20} = \\sum_x \\sum_y x^2 \\hspace{2pt}f(x,y) = 63$$\n", "$$M_{11} = \\sum_x \\sum_y xy\\hspace{2pt}f(x,y) = 48$$\n", "$$M_{02} = \\sum_x \\sum_y y^2 \\hspace{2pt}f(x,y) = 41$$" ] }, { "cell_type": "markdown", "id": "dc2081d5", "metadata": {}, "source": [ "By manual calculation (looking at the \"cow\" image), these values can be verified visually to show that the array is in the format:\n", "\n", "$$\\begin{bmatrix}\n", "M_{00} & M_{10} & M_{20}\\\\\n", "M_{01} & M_{11} & M_{21}\\\\\n", "M_{02} & M_{12} & M_{22}\n", "\\end{bmatrix}$$\n", "\n", "In plain English: the value of $p$ increments along the columns of the array (left to right), and the value of $q$ increments down the rows of the array (top to bottom).\n", "\n", "So the moments of interest (only up to 2nd order) can be extracted as:" ] }, { "cell_type": "code", "execution_count": 9, "id": "693e530d", "metadata": {}, "outputs": [], "source": [ "(M_00, M_10, M_20), (M_01, M_11, _), (M_02, *_) = moments(cow, order=2).astype(int)" ] }, { "cell_type": "markdown", "id": "55b343d3", "metadata": {}, "source": [ "To step through a couple of examples:" ] }, { "cell_type": "markdown", "id": "81bcd4ef", "metadata": {}, "source": [ "### $M_{01}$\n", "\n", "- The \"head\" on row 1 has 2 pixels\n", "- The \"body\" on rows 2 and 3 has 3 pixels on each row\n", "- $M_{01} = 2(1) + 3(2) + 3(3) = 2 + 6 + 9 = 17$" ] }, { "cell_type": "markdown", "id": "e50e9711", "metadata": {}, "source": [ "### $M_{20}$\n", "\n", "- Column 1 has only a \"head\" pixel\n", "- Column 2 has both a \"head\" pixel and 2 \"body\" pixels\n", "- Columns 3 and 4 have only the 2 \"body\" pixels each\n", "- $M_{20} = 1^2(1) + 2^2(3) + 3^2(2) + 4^2(2) = 1 + 12 + 18 + 32 = 63$" ] }, { "cell_type": "markdown", "id": "6d086c55", "metadata": {}, "source": [ "## Central moments\n", "\n", "Image moments are sensitive to position, but by subtracting the average x\n", "and y position in the above equation we can obtain a translation-invariant\n", "\"central moment\".\n", "\n", "$$μ_pq = \\int {(x-\\bar{x})}^p {(y-\\bar{y})}^q f(x,y) dx dy$$\n", "\n", "Again we can reduce this to summation:\n", "\n", "$$μ_pq = \\sum_x \\sum_y {(x-\\bar{x})}^p {(y-\\bar{y})}^q f(x,y)$$\n", "\n", "where\n", "\n", "$$\\bar{x} = \\frac{M_{10}}{M_{00}} \\hspace{10pt}and\\hspace{10pt} \\bar{y} = \\frac{M_{01}}{M_{00}}$$\n", "\n", "Meaning first order x or y moment divided by the area moment\n", "\n", "- $(\\bar{x},\\bar{y})$ is the centroid (a.k.a. centre of gravity)" ] }, { "cell_type": "markdown", "id": "8856ff18", "metadata": {}, "source": [ "To calculate central moments, scikit-image provides\n", "[`skimage.measure.moments_central`](https://scikit-image.org/docs/dev/api/skimage.measure.html#moments-central), which also\n", "takes a `center` keyword argument." ] }, { "cell_type": "markdown", "id": "ef49c292", "metadata": {}, "source": [ "## Orientation information from image moments\n", "\n", "The covariance matrix of an image can be obtained from second order central moments as below\n", "(credit: [Wikipedia](https://en.wikipedia.org/wiki/Image_moment#Examples_2))\n", "\n", "$$\\mu^\\prime_{20} = \\frac{\\mu_{20}}{\\mu_{00}} = \\frac{M_{20}}{M_{00}} - \\bar{x}^2 $$\n", "\n", "$$\\mu^\\prime_{02} = \\frac{\\mu_{02}}{\\mu_{00}} = \\frac{M_{02}}{M_{00}} - \\bar{y}^2 $$\n", "\n", "$$\\mu^\\prime_{11} = \\frac{\\mu_{11}}{\\mu_{00}} = \\frac{M_{11}}{M_{00}} - \\bar{x}\\bar{y} $$\n", "\n", "The covariance matrix of the image $I(x,y)$ is then:\n", "\n", "$$cov[I(x,y)] = \\begin{bmatrix}\n", "\\mu^\\prime_{20} &\\mu^\\prime_{11}\\\\\n", "\\mu^\\prime_{02} &\\mu^\\prime_{11}\n", "\\end{bmatrix}$$\n", "\n", "> The eigenvectors of this matrix correspond to the major and minor axes of the\n", "> image intensity, so the **orientation** can thus be extracted from the angle\n", "> of the eigenvector associated with the largest eigenvalue towards the axis\n", "> closest to this eigenvector...\n", "\n", "- See [this article](https://www.visiondummy.com/2014/04/geometric-interpretation-covariance-matrix/)\n", " for a nice geometric interpretation of covariance, which makes this usage intuitive" ] }, { "cell_type": "markdown", "id": "2936453b", "metadata": {}, "source": [ "### Telling the time with orientation information from image moments\n", "\n", "Realistically you'd want to use the orientation information for obtaining\n", "angles in your image without a reference direction.\n", "\n", "Here though, just for fun, let's use image moments to find the orientation\n", "of the image of a \"clock\" (so we could tell the time). For simplicity, let's\n", "pretend clocks just have one hand!\n", "\n", "We'll set up an image like the disk at the start, but \"cut out\" a smaller\n", "disk, and add hands using `skimage.draw.line`:" ] }, { "cell_type": "code", "execution_count": 10, "id": "cdc0b469", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "hand_angle=3.14 radians (internal angle representation)\n", "clock_hand_angle=0.00° (clock hand angle from 'top')\n" ] }, { "data": { "text/plain": [ "array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n", " [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0],\n", " [0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0],\n", " [0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0],\n", " [0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0],\n", " [0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0],\n", " [0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0],\n", " [0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0],\n", " [0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0],\n", " [0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0],\n", " [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],\n", " [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],\n", " [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],\n", " [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],\n", " [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],\n", " [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],\n", " [0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0],\n", " [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0],\n", " [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],\n", " dtype=uint8)" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from skimage.draw import circle_perimeter, line\n", "from numpy import sin, cos, pi\n", "import numpy as np\n", "\n", "def hour_hand(centre, hand_length, hour=12):\n", " r, c = centre\n", " # \"Rotate 180 degrees\" by adding 6 because y axis is inverted\n", " hour -= 6\n", " if not 1 <= hour <= 12:\n", " hour %= 12\n", " day_progress = hour % 12 / 12\n", " hand_angle = day_progress * 2 * pi\n", " hand_end = (\n", " r + np.round(hand_length * cos(hand_angle)).astype(int),\n", " c - np.round(hand_length * sin(hand_angle)).astype(int)\n", " )\n", " print(f\"{hand_angle=:.2f} radians (internal angle representation)\")\n", " clock_hand_angle = ((hand_angle + pi) % (2*pi)) / (2*pi) * 360\n", " print(f\"{clock_hand_angle=:.2f}° (clock hand angle from 'top')\")\n", " return line(*centre, *hand_end)\n", "\n", "def draw_clock(size=19, hour=12, edged=True):\n", " shape=(size,size)\n", " r = c = size // 2\n", " radius = r-1\n", " rr, cc = circle_perimeter(r, c, radius=radius, shape=None)\n", " hour_rr, hour_cc = hour_hand(centre=(r,c), hand_length=radius-2, hour=hour)\n", " clock = np.zeros(shape=shape, dtype=np.uint8)\n", " clock[rr, cc] = int(edged)\n", " clock[hour_rr, hour_cc] = 1\n", " return clock\n", "\n", "clock = draw_clock()\n", "clock" ] }, { "cell_type": "markdown", "id": "c9b71c6a", "metadata": {}, "source": [ "By default that drew the face at 12 o'clock, with the hour hand going straight up,\n", "and here's 7 o'clock:" ] }, { "cell_type": "code", "execution_count": 11, "id": "d7c8fbdd", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "8c08c0a3b9de4b12ac3cdf16f9c7bf44", "version_major": 2, "version_minor": 0 }, "text/plain": [ "interactive(children=(IntSlider(value=15, description='size', max=200, min=5), IntSlider(value=7, description=…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from ipywidgets import interact\n", "\n", "@interact(size=(5,200), h_val=(1,12))\n", "def plot_clock(size=15, h_val=7, edged=True):\n", " plt.gca().xaxis.set_major_locator(plt.MultipleLocator(10))\n", " plt.gca().yaxis.set_major_locator(plt.MultipleLocator(10))\n", " plt.xlabel(\"x\")\n", " plt.ylabel(\"y\")\n", " o_clock = draw_clock(size=size, hour=h_val, edged=edged)\n", " plt.imshow(o_clock)" ] }, { "cell_type": "markdown", "id": "5e844e74", "metadata": {}, "source": [ "The orientation is printed out above the clock... but now imagine this is just an\n", "image of a clock you came across (rather than programmed), and you want to assess\n", "its orientation using image moments.\n", "\n", "You can do so with the machinery introduced above. Recall:\n", "\n", "The covariance matrix of the image $I(x,y)$ is then:\n", "\n", "$$cov[I(x,y)] = \\begin{bmatrix}\n", "\\mu^\\prime_{20} &\\mu^\\prime_{11}\\\\\n", "\\mu^\\prime_{02} &\\mu^\\prime_{11}\n", "\\end{bmatrix}$$" ] }, { "cell_type": "code", "execution_count": 12, "id": "694062b9", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "hand_angle=0.52 radians (internal angle representation)\n", "clock_hand_angle=210.00° (clock hand angle from 'top')\n", "[[ 36 -4 586]\n", " [ 6 -9 15]\n", " [ 594 -23 4305]]\n" ] } ], "source": [ "from skimage.measure import moments_central\n", "seven_o_clock = draw_clock(size=15, hour=7)\n", "r = c = 15 // 2\n", "cent_7 = (r,c)\n", "c7_central_moments = moments_central(seven_o_clock, center=cent_7, order=2).astype(int)\n", "print(c7_central_moments)" ] }, { "cell_type": "code", "execution_count": 13, "id": "b4ff0af6", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[586 -9]\n", " [594 -9]]\n" ] } ], "source": [ "(mu_00, mu_10, mu_20), (mu_01, mu_11, _), (mu_02, *_) = c7_central_moments\n", "c7_cov = np.array([[mu_20, mu_11],[mu_02, mu_11]])\n", "print(c7_cov)" ] }, { "cell_type": "markdown", "id": "1ca97af1", "metadata": {}, "source": [ "To get the orientation, we need the principle eigenvector, which we get\n", "from PCA using the Singular Value Decomposition (SVD)." ] }, { "cell_type": "code", "execution_count": 14, "id": "b15ce42f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[-0.70229782 -0.71188326]\n" ] } ], "source": [ "svd_u, *_ = np.linalg.svd(c7_cov)\n", "print(svd_u[:, 0])" ] }, { "cell_type": "markdown", "id": "0bef2af4", "metadata": {}, "source": [ "Collecting these steps into a function for reuse:" ] }, { "cell_type": "code", "execution_count": 15, "id": "948f913a", "metadata": {}, "outputs": [], "source": [ "def extract_orientation(clock):\n", " central_moments = moments_central(clock, order=2).astype(int)\n", " (mu_00, mu_10, mu_20), (mu_01, mu_11, _), (mu_02, *_) = central_moments\n", " cov = np.array([[mu_20, mu_11],[mu_02, mu_11]])\n", " svd_u, *_ = np.linalg.svd(cov)\n", " return svd_u[:, 0]" ] }, { "cell_type": "code", "execution_count": 16, "id": "a945a758", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "0f46aed5b6de4a3cb9e4e0aad6ae060a", "version_major": 2, "version_minor": 0 }, "text/plain": [ "interactive(children=(IntSlider(value=15, description='size', max=200, min=5), IntSlider(value=7, description=…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "@interact(size=(5,200), h_val=(1,12))\n", "def plot_clock(size=15, h_val=7, edged=True):\n", " plt.gca().xaxis.set_major_locator(plt.MultipleLocator(10))\n", " plt.gca().yaxis.set_major_locator(plt.MultipleLocator(10))\n", " plt.xlabel(\"x\")\n", " plt.ylabel(\"y\")\n", " o_clock = draw_clock(size=size, hour=h_val, edged=edged)\n", " orient = extract_orientation(o_clock)\n", " print(f\"{orient=}\")\n", " plt.imshow(o_clock)" ] }, { "cell_type": "markdown", "id": "8832787b", "metadata": {}, "source": [ "The interpretation of this angle is somewhat subjective to the application, but\n", "if you untick the checkbox and draw only the hour hand of the clock you'll see:\n", "\n", "- the values at 3 and 9 are the same\n", "- the values at 3 (or 9) and 6 (or 12) are orthogonal" ] }, { "cell_type": "markdown", "id": "1f629027", "metadata": {}, "source": [ "## Further info\n", "\n", "The moments above are all _geometric moments_ since the polynomial basis\n", "is a standard power basis $x^p$ multiplied by $y^q$.\n", "\n", "If you want to read more, check out\n", "Flusser et al's book _Moments and Moment Invariants in Pattern Recognition_" ] } ], "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.9.4" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": { "03cdddb5680a4af9b16812498e525a43": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "07268bb1992f40f1b06bef015241bc0a": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "description": "size", "layout": "IPY_MODEL_c7440b6c195b4c33b37e06599ce80a70", "max": 200, "min": 5, "style": "IPY_MODEL_8ad640e037c34e0eb306c88bf0208fd0", "value": 15 } }, "0b25d2720c3a46e79e636cb0cd114e67": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "0b984b4de69d4dab89670cb5f2215b80": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "description": "size", "layout": "IPY_MODEL_b6325fe573f949d5989e30f386629703", "max": 200, "min": 5, "style": "IPY_MODEL_4c7bd9bb02cf4bfaae18dcea2f09b969", "value": 15 } }, "0f46aed5b6de4a3cb9e4e0aad6ae060a": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "_dom_classes": [ "widget-interact" ], "children": [ "IPY_MODEL_d9b126c3761748df9abe1eb87af591f0", "IPY_MODEL_1ac34400111344488682c5687764f2b7", "IPY_MODEL_785c1628ca204a7298dfbdf0ec76087e", "IPY_MODEL_944625dc86464dc5b5fd5bc668a88a0a" ], "layout": "IPY_MODEL_7810a76810914b74a8587d9d00c5fb26" } }, "10ca09c4852246339605ac5f31d38c2e": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "131db83f185f4408872e57356857039b": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_de56b980df904e61b70e315714bd4602", "outputs": [ { "name": "stdout", "output_type": "stream", "text": "hand_angle=4.71 radians (internal angle representation)\nclock_hand_angle=90.00° (clock hand angle from 'top')\norient=array([-0.72300391, -0.69084394])\n" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQkAAAEGCAYAAAB2PmCxAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAH9klEQVR4nO3dTaicZxnH4f/d9CO2ukiprbWtUiQIwYWLWMGVWIqpCCmC0G6MIGRV3ClZiG78wpWbboqGZmNLcWGzKJQSF10oEhdFWqQkFGpDamPpTqi1+rjoiKcx587JOWfmnZlzXRBm5p0Dc3PO8OOZjydvjTECsJnrph4AWG4iAbREAmiJBNASCaB1/dQDbMWNddPYn1umHgPW1jv5e94d/6gr3bcSkdifW/L5un/qMWBt/WGc2fQ+LzeAlkgArUkiUVVHquqVqjpfVSemmAHYmoVHoqr2JXksyYNJDiV5pKoOLXoOYGumWEncl+T8GOPVMca7SZ5KcnSCOYAtmCISdyV5fcPtC7NjwBKa4iPQK30W+39bUavqeJLjSbI/N897JmATU6wkLiS5Z8Ptu5NcvPyHxhiPjzEOjzEO35CbFjYc8EFTROJskoNVdW9V3Zjk4SSnJ5gD2IKFv9wYY7xXVY8meS7JviQnxxgvL3oOYGsm+Vr2GOPZJM9O8djAtfGNS6AlEkBrJXaBcnXPXXxx6hGW2pc//tmpR1hZVhJASySAlkgALZEAWiIBtEQCaIkE0BIJoCUSQEskgJZIAC2RAFoiAbTsAl0iO9nJaZdjz+92+6wkgJZIAC2RAFoiAbREAmiJBNASCaAlEkBLJICWSAAtkQBaIgG0RAJoiQTQslV8Dra7LXmvb0mep538bvf639NKAmiJBNASCaAlEkBLJICWSAAtkQBaIgG0RAJoiQTQEgmgJRJASySAll2gm3CCWf5ru3/PdXkOWUkALZEAWiIBtEQCaIkE0BIJoCUSQEskgJZIAC2RAFoiAbREAmiJBNASCaC11lvF12WrLqtpXU5SbCUBtEQCaIkE0BIJoCUSQEskgJZIAC2RAFoiAbREAmiJBNASCaAlEkBLJICWSAAtkQBaIgG0RAJoiQTQEgmgJRJASySAlkgALZEAWiIBtEQCaIkE0BIJoLXWJwxmvnZyQua9YF1OOm0lAbREAmiJBNASCaAlEkDrqpGoqker6sAihgGWz1ZWEh9Lcraqnq6qI1VV8x4KWB5XjcQY43tJDib5ZZJvJjlXVT+uqk/NeTZgCWzpPYkxxkjy19m/95IcSPLrqvrZHGcDlsBVv3FZVd9OcizJW0l+keQ7Y4x/VtV1Sc4l+e58RwSmtJWvZd+W5GtjjNc2Hhxj/LuqvjqfsYBlcdVIjDG+39z3590dB1g2vicBtOwCZdvWZZcjPSsJoCUSQEskgJZIAC2RAFoiAbREAmiJBNASCaAlEkBLJICWSAAtkQBaIgG0RAJoiQTQEgmgJRJASySAlkgALZEAWiIBtEQCaIkE0BIJoCUSQEskgJZIAK21PmHwTk5o+9zFFyd5XNbHujyHrCSAlkgALZEAWnOLRFWdrKpLVfXShmO3VtXzVXVudnlgXo8P7I55riSeSHLksmMnkpwZYxxMcmZ2G1hic4vEGOOFJG9fdvhoklOz66eSPDSvxwd2x6Lfk7hjjPFGkswub1/w4wPXaGm/J1FVx5McT5L9uXniaWDvWvRK4s2qujNJZpeXNvvBMcbjY4zDY4zDN+SmhQ0IfNCiI3E6ybHZ9WNJnlnw4wPXaJ4fgT6Z5PdJPl1VF6rqW0l+muSBqjqX5IHZbWCJze09iTHGI5vcdf+8HhPYfb5xCbREAmgt7UegU5tim/kybQ/mf/b639NKAmiJBNASCaAlEkBLJICWSAAtkQBaIgG0RAJoiQTQEgmgJRJASySAll2gc7Dd3X/rcoLZZeR3u31WEkBLJICWSAAtkQBaIgG0RAJoiQTQEgmgJRJASySAlkgALZEAWiIBtEQCaNkqvkSmOEnxXrHXt3vvhJUE0BIJoCUSQEskgJZIAC2RAFoiAbREAmiJBNASCaAlEkBLJICWSAAtu0DXhF2OzIuVBNASCaAlEkBLJICWSAAtkQBaIgG0RAJoiQTQEgmgJRJASySAlkgALZEAWjXGmHqGq6qqvyV5bZO7b0vy1gLHYf14DiWfHGN89Ep3rEQkOlX1xzHG4annYHV5DvW83ABaIgG01iESj089ACvPc6ix8u9JAPO1DisJYI5EAmitdCSq6khVvVJV56vqxNTzsPyq6mRVXaqqlzYcu7Wqnq+qc7PLA1POuGxWNhJVtS/JY0keTHIoySNVdWjaqVgBTyQ5ctmxE0nOjDEOJjkzu83MykYiyX1Jzo8xXh1jvJvkqSRHJ56JJTfGeCHJ25cdPprk1Oz6qSQPLXKmZbfKkbgryesbbl+YHYNrdccY440kmV3ePvE8S2WVI1FXOObzXNhlqxyJC0nu2XD77iQXJ5qF1fZmVd2ZJLPLSxPPs1RWORJnkxysqnur6sYkDyc5PfFMrKbTSY7Nrh9L8syEsyydlf7GZVV9JcnPk+xLcnKM8aNpJ2LZVdWTSb6Y97eHv5nkB0l+k+TpJJ9I8pckXx9jXP7m5p610pEA5m+VX24ACyASQEskgJZIAC2RAFoiAbREAmiJBLumqj5XVX+qqv1VdUtVvVxVn5l6LnbGl6nYVVX1wyT7k3woyYUxxk8mHokdEgl21Wwfzdkk7yT5whjjXxOPxA55ucFuuzXJh5N8JO+vKFhxVhLsqqo6nff/l7B7k9w5xnh04pHYoeunHoD1UVXfSPLeGONXs/+D9HdV9aUxxm+nno3ts5IAWt6TAFoiAbREAmiJBNASCaAlEkBLJIDWfwCHmVdC2dIh6QAAAABJRU5ErkJggg==\n", "text/plain": "
" }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ] } }, "13b81ef35730431e9c09bf3a37ea1671": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_acb7a0f202724ed7a9884d4146254ccb", "outputs": [ { "name": "stdout", "output_type": "stream", "text": "hand_angle=0.52 radians (internal angle representation)\nclock_hand_angle=210.00° (clock hand angle from 'top')\n" }, { "data": { "image/png": "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\n", "text/plain": "
" }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ] } }, "17f73802ef944a95ae0b748c8770f447": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "19b3e801fd834c0c9839a0c2e3ef6278": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "1a436b8c869145c4b180635d82d2712c": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "CheckboxModel", "state": { "description": "edged", "disabled": false, "layout": "IPY_MODEL_ada9938bc91f42cbaa8149d7841b05a4", "style": "IPY_MODEL_57984829373f4ed8b279d05a37e41b42", "value": true } }, "1ac34400111344488682c5687764f2b7": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "description": "h_val", "layout": "IPY_MODEL_66b67e3ea4a542e18824713f48c34b60", "max": 12, "min": 1, "style": "IPY_MODEL_4a35003e01dc41cdb0ba9ccbd6e1f1b6", "value": 7 } }, "1b7ade7f1b6347019e6e9166efd484cf": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "235d2ae064c44740bf3ae5861e45640a": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "2ce0c9636a704d71b70bb73a98ed33f8": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "_dom_classes": [ "widget-interact" ], "children": [ "IPY_MODEL_07268bb1992f40f1b06bef015241bc0a", "IPY_MODEL_d88b15a68ede493b919037a74e637c54", "IPY_MODEL_72a6b1e41d104fa2b6e026d2f929dbf3", "IPY_MODEL_131db83f185f4408872e57356857039b" ], "layout": "IPY_MODEL_b35d847a6dc14871ae17eded1227cb94" } }, "2d6d87e7d4d440e3979118efd202aab3": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "2fbd7b800c0b483c900c3bf919c31dd6": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "324b6ac675c64ff380a13416857539c6": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "CheckboxModel", "state": { "description": "edged", "disabled": false, "layout": "IPY_MODEL_ad02b02cec9b401fb52c40e91e2a4e45", "style": "IPY_MODEL_cd942807b2ad4b308d801352e1d8775e", "value": true } }, "3733bddbb974435e84cff3ad56a4ea4a": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "3b82ad421b7c4576ada62b0d633b598e": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "4518fe56bf69457d83c4eabfc53d0c71": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "description": "size", "layout": "IPY_MODEL_2fbd7b800c0b483c900c3bf919c31dd6", "max": 200, "min": 5, "style": "IPY_MODEL_3b82ad421b7c4576ada62b0d633b598e", "value": 15 } }, "4a35003e01dc41cdb0ba9ccbd6e1f1b6": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "4c7bd9bb02cf4bfaae18dcea2f09b969": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "4f9732b9c5624adcaeb6230658573efd": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "52a28c631e9a469ab9a77bfc7f05a740": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "57984829373f4ed8b279d05a37e41b42": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "5c7deb5f8f3c4c3893dc5565222ebf58": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "627a261c08c846d8899ba8f6aaba881e": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "630e0040d9b545d1b08f689034d9158c": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "66b67e3ea4a542e18824713f48c34b60": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "693f6428d8604c10aa65901662e000dd": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "CheckboxModel", "state": { "description": "edged", "disabled": false, "layout": "IPY_MODEL_cab916f3f406430cb81fd61310707beb", "style": "IPY_MODEL_235d2ae064c44740bf3ae5861e45640a", "value": true } }, "6b1c06b5684e4c098a713bfb6407ef5d": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "6d67b24a730e4a4f9b6ce5d2e9e24061": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_3733bddbb974435e84cff3ad56a4ea4a", "outputs": [ { "name": "stdout", "output_type": "stream", "text": "hand_angle=0.52 radians (internal angle representation)\nclock_hand_angle=210.00° (clock hand angle from 'top')\norient=array([-0.70228939, -0.71189157])\n" }, { "data": { "image/png": "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\n", "text/plain": "
" }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ] } }, "72a6b1e41d104fa2b6e026d2f929dbf3": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "CheckboxModel", "state": { "description": "edged", "disabled": false, "layout": "IPY_MODEL_8acac22df0f4422f98b475f8fd870a07", "style": "IPY_MODEL_a50aaf9e42794a5480116081ae59b5cf", "value": true } }, "75bf8e58b140440d844830679d690d48": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "762c651a597c4d648df524499c18c565": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "7810a76810914b74a8587d9d00c5fb26": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "785c1628ca204a7298dfbdf0ec76087e": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "CheckboxModel", "state": { "description": "edged", "disabled": false, "layout": "IPY_MODEL_630e0040d9b545d1b08f689034d9158c", "style": "IPY_MODEL_c1f2efd1f8d64d489eeaf377d94c01cc", "value": true } }, "833a867dbdf34a61af3d2ce1f48d8627": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "CheckboxModel", "state": { "description": "edged", "disabled": false, "layout": "IPY_MODEL_c715b71959f44b169b8222ea44c48af7", "style": "IPY_MODEL_03cdddb5680a4af9b16812498e525a43", "value": true } }, "8acac22df0f4422f98b475f8fd870a07": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "8ad640e037c34e0eb306c88bf0208fd0": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "8be73d93465b4522bf3ca25b5576a8f7": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "8c08c0a3b9de4b12ac3cdf16f9c7bf44": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "_dom_classes": [ "widget-interact" ], "children": [ "IPY_MODEL_c4b65729ff05474a9c513bb6ef00a2f4", "IPY_MODEL_aa790cdce6c14ea08b6173fdbee29917", "IPY_MODEL_693f6428d8604c10aa65901662e000dd", "IPY_MODEL_a96ec7b24ed5461ea668a2c9e1d9b244" ], "layout": "IPY_MODEL_0b25d2720c3a46e79e636cb0cd114e67" } }, "92818caf95ae490f826ea0c2b104bebe": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "9316b8e7583844deb79b44943096c790": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "944625dc86464dc5b5fd5bc668a88a0a": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_4f9732b9c5624adcaeb6230658573efd", "outputs": [ { "name": "stdout", "output_type": "stream", "text": "hand_angle=0.52 radians (internal angle representation)\nclock_hand_angle=210.00° (clock hand angle from 'top')\norient=array([-0.70228939, -0.71189157])\n" }, { "data": { "image/png": "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\n", "text/plain": "
" }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ] } }, "9c0aaad9758f423b97f8ae9d0a1a3e5b": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "description": "h_val", "layout": "IPY_MODEL_8be73d93465b4522bf3ca25b5576a8f7", "max": 12, "min": 1, "style": "IPY_MODEL_2d6d87e7d4d440e3979118efd202aab3", "value": 7 } }, "a08b911c0fff40bb97f0e9f56f4550fe": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "description": "h_val", "layout": "IPY_MODEL_be8b0df63ea24b8498b8ff1ff6a25af8", "max": 12, "min": 1, "style": "IPY_MODEL_f9aba4fc2af54c7ba4123b12940298ae", "value": 7 } }, "a0b30d9370ac440e8f6c673ee08eb116": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "_dom_classes": [ "widget-interact" ], "children": [ "IPY_MODEL_0b984b4de69d4dab89670cb5f2215b80", "IPY_MODEL_9c0aaad9758f423b97f8ae9d0a1a3e5b", "IPY_MODEL_324b6ac675c64ff380a13416857539c6", "IPY_MODEL_dae701a062914c3399c871f89746d0c5" ], "layout": "IPY_MODEL_9316b8e7583844deb79b44943096c790" } }, "a50aaf9e42794a5480116081ae59b5cf": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "a96ec7b24ed5461ea668a2c9e1d9b244": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_c0caa7cf55f1433ea64aeed5b4949041", "outputs": [ { "name": "stdout", "output_type": "stream", "text": "hand_angle=0.52 radians (internal angle representation)\nclock_hand_angle=210.00° (clock hand angle from 'top')\n" }, { "data": { "image/png": "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\n", "text/plain": "
" }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ] } }, "aa790cdce6c14ea08b6173fdbee29917": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "description": "h_val", "layout": "IPY_MODEL_75bf8e58b140440d844830679d690d48", "max": 12, "min": 1, "style": "IPY_MODEL_762c651a597c4d648df524499c18c565", "value": 7 } }, "acb7a0f202724ed7a9884d4146254ccb": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "ad02b02cec9b401fb52c40e91e2a4e45": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "ada9938bc91f42cbaa8149d7841b05a4": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "b35d847a6dc14871ae17eded1227cb94": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "b6325fe573f949d5989e30f386629703": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "be8b0df63ea24b8498b8ff1ff6a25af8": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "c051cd55eb5d467eb5e4b7313d5fa7ae": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_1b7ade7f1b6347019e6e9166efd484cf", "style": "IPY_MODEL_e794df8972e34326968cce59a28d5f11", "value": 40 } }, "c0caa7cf55f1433ea64aeed5b4949041": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "c1f2efd1f8d64d489eeaf377d94c01cc": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "c4b65729ff05474a9c513bb6ef00a2f4": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "description": "size", "layout": "IPY_MODEL_92818caf95ae490f826ea0c2b104bebe", "max": 200, "min": 5, "style": "IPY_MODEL_19b3e801fd834c0c9839a0c2e3ef6278", "value": 15 } }, "c59b6047ee494a1db941ded73b3eb2b3": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "c6b01869be63464d839d4ec718a2c782": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "c715b71959f44b169b8222ea44c48af7": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "c7440b6c195b4c33b37e06599ce80a70": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "cab916f3f406430cb81fd61310707beb": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "cd942807b2ad4b308d801352e1d8775e": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "cdd0fe9bf80545b0988546dfbadce29c": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "d17cf31c30a4462da24cbdda352641f0": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "_dom_classes": [ "widget-interact" ], "children": [ "IPY_MODEL_eeb853acc3174dcbb19e1901f68c4e5b", "IPY_MODEL_fb99cee76f8144ca94dce8dc54341ef0", "IPY_MODEL_833a867dbdf34a61af3d2ce1f48d8627", "IPY_MODEL_6d67b24a730e4a4f9b6ce5d2e9e24061" ], "layout": "IPY_MODEL_6b1c06b5684e4c098a713bfb6407ef5d" } }, "d88b15a68ede493b919037a74e637c54": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "description": "h_val", "layout": "IPY_MODEL_17f73802ef944a95ae0b748c8770f447", "max": 12, "min": 1, "style": "IPY_MODEL_627a261c08c846d8899ba8f6aaba881e", "value": 3 } }, "d9b126c3761748df9abe1eb87af591f0": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "description": "size", "layout": "IPY_MODEL_10ca09c4852246339605ac5f31d38c2e", "max": 200, "min": 5, "style": "IPY_MODEL_ebb0926fc9554493bd07689247bc9912", "value": 15 } }, "dae701a062914c3399c871f89746d0c5": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_5c7deb5f8f3c4c3893dc5565222ebf58", "outputs": [ { "name": "stdout", "output_type": "stream", "text": "hand_angle=0.52 radians (internal angle representation)\nclock_hand_angle=210.00° (clock hand angle from 'top')\n" }, { "data": { "image/png": "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\n", "text/plain": "
" }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ] } }, "de56b980df904e61b70e315714bd4602": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "e3bb5fe469bb4a6b8b676fe5e1f460b8": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "e794df8972e34326968cce59a28d5f11": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "ebb0926fc9554493bd07689247bc9912": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "ed10f04ecad04b12a635994420d3bc7b": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "_dom_classes": [ "widget-interact" ], "children": [ "IPY_MODEL_4518fe56bf69457d83c4eabfc53d0c71", "IPY_MODEL_a08b911c0fff40bb97f0e9f56f4550fe", "IPY_MODEL_1a436b8c869145c4b180635d82d2712c", "IPY_MODEL_13b81ef35730431e9c09bf3a37ea1671" ], "layout": "IPY_MODEL_52a28c631e9a469ab9a77bfc7f05a740" } }, "eeb853acc3174dcbb19e1901f68c4e5b": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "description": "size", "layout": "IPY_MODEL_c6b01869be63464d839d4ec718a2c782", "max": 200, "min": 5, "style": "IPY_MODEL_cdd0fe9bf80545b0988546dfbadce29c", "value": 15 } }, "f9aba4fc2af54c7ba4123b12940298ae": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "fb99cee76f8144ca94dce8dc54341ef0": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "description": "h_val", "layout": "IPY_MODEL_c59b6047ee494a1db941ded73b3eb2b3", "max": 12, "min": 1, "style": "IPY_MODEL_e3bb5fe469bb4a6b8b676fe5e1f460b8", "value": 7 } } }, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 5 }