From 93891e7da0f49152b7b3f2617268454ba85679d5 Mon Sep 17 00:00:00 2001 From: Bakin Denis Date: Fri, 19 Jul 2024 03:46:51 +0300 Subject: [PATCH 1/9] feat: added jupiter notebook --- research.ipynb | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 research.ipynb diff --git a/research.ipynb b/research.ipynb new file mode 100644 index 0000000..f90ba2c --- /dev/null +++ b/research.ipynb @@ -0,0 +1,75 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Overview of triangulation precision dependencies\n", + "\n", + "This research is a part of project \"Handy\" that is conducted by HSE Robotics Groups.\n", + "The aims of this research are the following:\n", + "- to determine the best placement of cameras around the table\n", + "- to measure precision fluctuations caused by various center-determing algorithms, segmentation errors and FOV\n", + "\n", + "Let us assume that intrinsic parameters of the cameras are known to us with ideal precision. Also, cameras' positions relative to each other \n", + "(parameters of stereo calibration) are also known with maximum precision." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import cv2\n", + "from scipy.spatial.transform import Rotation\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# let us introduce intrinsic parameters and assume that they provide zero reprojection error for an arbitrary image-world correlation\n", + "\n", + "IMAGE_SIZE = (1920, 1200)\n", + "K_1 = [672.2824725267757, 0, 984.0472159818853, 0, 672.6886411532304, 602.96669930345, 0, 0, 1]\n", + "distortion_coefs_1 = [-0.09715103386082896, 0.06788948036532018,\n", + " -0.0007157453506997161, 0.0003048354358359307, -0.003636308978789861]\n", + "K_2 = [685.7143789189881, 0, 991.0247637161314, 0, 686.3020333004097, 601.2442243349392, 0, 0, 1]\n", + "distortion_coefs_2 = [-0.09781628655937251, 0.07153618281495966,\n", + " -0.001066517414175782, 0.0004679942401339674, -0.003645360450147547]\n", + "\n", + "# other constant and measurements\n", + "TABLE_LENGTH = 2.74\n", + "TABLE_WIDTH = 1.525" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def display_scene():\n", + " pass" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From aa6b3adea7102a3ef276690887bc49e791009dfd Mon Sep 17 00:00:00 2001 From: Denis Bakin Date: Sat, 20 Jul 2024 20:00:59 +0300 Subject: [PATCH 2/9] feat: added sphere projections feat: added all functions without implementation fix: casted intrinsics to np.array --- research.ipynb | 167 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 157 insertions(+), 10 deletions(-) diff --git a/research.ipynb b/research.ipynb index f90ba2c..974bf4d 100644 --- a/research.ipynb +++ b/research.ipynb @@ -17,44 +17,183 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import cv2\n", - "from scipy.spatial.transform import Rotation\n" + "from scipy.spatial.transform import Rotation\n", + "from scipy.ndimage import center_of_mass\n", + "\n", + "from typing import Tuple, List\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "# let us introduce intrinsic parameters and assume that they provide zero reprojection error for an arbitrary image-world correlation\n", "\n", "IMAGE_SIZE = (1920, 1200)\n", - "K_1 = [672.2824725267757, 0, 984.0472159818853, 0, 672.6886411532304, 602.96669930345, 0, 0, 1]\n", - "distortion_coefs_1 = [-0.09715103386082896, 0.06788948036532018,\n", - " -0.0007157453506997161, 0.0003048354358359307, -0.003636308978789861]\n", - "K_2 = [685.7143789189881, 0, 991.0247637161314, 0, 686.3020333004097, 601.2442243349392, 0, 0, 1]\n", - "distortion_coefs_2 = [-0.09781628655937251, 0.07153618281495966,\n", - " -0.001066517414175782, 0.0004679942401339674, -0.003645360450147547]\n", + "K_1 = np.array([672.2824725267757, 0, 984.0472159818853, 0, 672.6886411532304, 602.96669930345, 0, 0, 1]).reshape((3, 3))\n", + "distortion_coefs_1 = np.array([-0.09715103386082896, 0.06788948036532018,\n", + " -0.0007157453506997161, 0.0003048354358359307, -0.003636308978789861])\n", + "K_2 = np.array([685.7143789189881, 0, 991.0247637161314, 0, 686.3020333004097, 601.2442243349392, 0, 0, 1]).reshape((3, 3))\n", + "distortion_coefs_2 = np.array([-0.09781628655937251, 0.07153618281495966,\n", + " -0.001066517414175782, 0.0004679942401339674, -0.003645360450147547])\n", "\n", "# other constant and measurements\n", "TABLE_LENGTH = 2.74\n", "TABLE_WIDTH = 1.525" ] }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Any\n", + "\n", + "\n", + "class Transformation:\n", + " def __init__(self, R, t):\n", + " self.R = R\n", + " self.t = t\n", + " \n", + " def __call__(self, point):\n", + " return self.R @ point + self.t\n", + "\n", + " # right transformation is applied first\n", + " def __mult__(self, other):\n", + " return Transformation(self.R @ other.R, self.t + other.t)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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Dvv9V1dXq6amJvT7j6f1P//888rMzNS0adOOOS+ej4Hc3FxlZWWF/c6DwaDKysrCfueNjY0qLy8PzSkpKVFnZ2co3OXn52vjxo1qa2sLzVm7dq1GjBgRcy+bd6crvOzcuVNvvvmmBg4ceNzbVFZWKiEhIfTWiuv74Ov++te/6uDBg2HHfLwfA12effZZjRs3TmPGjDnuXOePgWifRRxpy5cvN6/Xay+88ILt2LHDbr31VktPTw+74sJVt99+u6Wlpdlbb70VdhncV199ZWZmn3zyiT3wwAO2detW2717t61atcrOOussu+yyy0L30XUJ7aRJk6yystJef/11GzRoUMxeQvtN99xzj7311lu2e/due+edd6ygoMAyMjKsvr7ezP5+GXVOTo6VlJTY1q1bLT8/3/Lz80O3d339XTo6OiwnJ8fmzp0btj0ej4GmpiarqKiwiooKk2SPPvqoVVRUhK6wKS4utvT0dFu1apVt27bNpk+f3u1l1GPHjrWysjJ7++23bfjw4WGX0DY2Nprf77cbb7zRqqqqbPny5Zaamhozl48eax+0trbaVVddZUOGDLHKysqw54auq0neffdde+yxx6yystJ27dplS5cutUGDBtkvfvGL0GPE8j441vqbmprsn//5n620tNR2795tb775pl144YU2fPhwa25uDt1HPB8DXQKBgKWmptrixYuPuL3rx0B34i7AmJn97ne/s5ycHEtKSrKLL77Y3nvvvWi3FBGSuq3nn3/ezMxqamrssssuswEDBpjX67VzzjnH7r333rDPADEz++yzz2zq1KmWkpJiGRkZds8991hbW1sUVnTyrr/+esvOzrakpCQ744wz7Prrr7dPPvkkNH748GH79a9/bf3797fU1FS7+uqr7cCBA2H34fL6u7zxxhsmyaqrq8O2x+MxsH79+m6P+5kzZ5rZ3y+lnjdvnvn9fvN6vTZx4sQj9svBgwdtxowZ1rdvX/P5fDZr1ixramoKm/PBBx/YhAkTzOv12hlnnGHFxcWnaonHdax9sHv37qM+N3R9NlB5ebnl5eVZWlqaJScn2/e+9z17+OGHw/7Am8XuPjjW+r/66iubNGmSDRo0yHr37m3Dhg2zW2655Yj/aY3nY6DLM888YykpKdbY2HjE7V0/BrrjMTPr0Zd4AAAAIiyuzoEBAACnBwIMAABwDgEGAAA4hwADAACcQ4ABAADOIcAAAADnEGAAAIBzCDAAAMA5BBgAAOAcAgwAAHAOAQYAADiHAAMAAJzzf4JWHHZO/tD7AAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[ 897.26006165 1278.1550229 ]\n" + ] + } + ], + "source": [ + "def validate_image_point(point):\n", + " return point[0] < 0 or point[0] >= IMAGE_SIZE[0] or point[1] < 0 or point[1] >= IMAGE_SIZE[1]\n", + " # raise ValueError(f\"Invalid image point coordinates: out of FOV?\")\n", + "\n", + "def project_point_to_image(point, transformation, camera_matrix):\n", + " return camera_matrix @ transformation(point)\n", + "\n", + "def project_sphere_to_image(center: Tuple[int], radius: int, camera_matrix: np.ndarray, world2cam) -> np.ndarray:\n", + " image = np.zeros(IMAGE_SIZE[::-1])\n", + " center = np.array(center)\n", + " camera_matrix_inv = np.linalg.inv(camera_matrix)\n", + "\n", + " # projecting center and some edge point to approximate radius after projection\n", + " projected_center = camera_matrix @ center.reshape((3, 1))\n", + " projected_center /= projected_center[2]\n", + " projected_edge_point = camera_matrix @ (center + np.array([radius, 0, 0])).reshape((3, 1))\n", + " projected_edge_point /= projected_edge_point[2]\n", + " approx_projected_radius = np.linalg.norm(projected_edge_point - projected_center, ord=2)\n", + "\n", + " # calculating bounding box for calculations with 1.5 margin\n", + " x_start = int(max(0, projected_center[0].item() - 1.5 * approx_projected_radius))\n", + " y_start = int(max(0, projected_center[1].item() - 1.5 * approx_projected_radius))\n", + " x_stop = int(min(IMAGE_SIZE[0], projected_center[0].item() + 1.5 * approx_projected_radius))\n", + " y_stop = int(min(IMAGE_SIZE[1], projected_center[1].item() + 1.5 * approx_projected_radius))\n", + "\n", + " for x in range(x_start, x_stop):\n", + " for y in range(y_start, y_stop):\n", + " # back project image point\n", + " world_ray = camera_matrix_inv @ np.array([x, y, 1]).reshape(3, 1)\n", + " # measure distance from the sphere center\n", + " distance = np.linalg.norm(np.cross(world_ray.flatten(), center), ord=2) / np.linalg.norm(world_ray, ord=2)\n", + " # if back-projected ray intersects with the sphere, paint the pixel in the mask\n", + " if distance <= radius:\n", + " image[y, x] = 1\n", + " return image\n", + "\n", + "image = project_sphere_to_image((300, 300, 700), 100, K_1, None)\n", + "plt.imshow(image, cmap='gray')\n", + "plt.show()\n", + "\n", + "print(get_mask_centroid(image))\n", + " \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def triangulate_position(points, world2cam, cam2cam):\n", + " pass" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "def get_bbox(mask: np.ndarray) -> List[float]:\n", + " # x_min, y_min, x_max, y_max\n", + " horizontal_indicies = np.where(np.any(mask, axis=0))[0]\n", + " vertical_indicies = np.where(np.any(mask, axis=1))[0]\n", + " x1, x2 = horizontal_indicies[[0, -1]]\n", + " y1, y2 = vertical_indicies[[0, -1]]\n", + " bbox = list(map(float, [x1, y1, x2, y2]))\n", + " return bbox\n", + "\n", + "def get_mask_center(mask):\n", + " bbox = get_bbox(mask)\n", + " centroid_x = (bbox[0] + bbox[2]) / 2\n", + " centroid_y = (bbox[1] + bbox[3]) / 2\n", + " return (centroid_x, centroid_y)\n", + "\n", + "\n", + "def get_mask_centroid(mask):\n", + " return np.array(center_of_mass(mask))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def evaluate_precision(spheres, triangulated_points):\n", + " pass" + ] + }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "def display_scene():\n", + "def evaluate_camera_position(world_to_master: Transformation, master_to_second: Transformation):\n", " pass" ] } @@ -66,7 +205,15 @@ "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.10.12" } }, From f4d42f409fe6fbfbcb4d8560d29e3351cf20b324 Mon Sep 17 00:00:00 2001 From: Bakin Denis Date: Sun, 21 Jul 2024 01:58:34 +0300 Subject: [PATCH 3/9] feat: added evaluation of the whole method --- research.ipynb | 189 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 145 insertions(+), 44 deletions(-) diff --git a/research.ipynb b/research.ipynb index 974bf4d..287ac4b 100644 --- a/research.ipynb +++ b/research.ipynb @@ -17,7 +17,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -27,24 +27,62 @@ "from scipy.spatial.transform import Rotation\n", "from scipy.ndimage import center_of_mass\n", "\n", - "from typing import Tuple, List\n" + "from typing import Tuple, List" ] }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# let us introduce intrinsic parameters and assume that they provide zero reprojection error for an arbitrary image-world correlation\n", "\n", "IMAGE_SIZE = (1920, 1200)\n", - "K_1 = np.array([672.2824725267757, 0, 984.0472159818853, 0, 672.6886411532304, 602.96669930345, 0, 0, 1]).reshape((3, 3))\n", - "distortion_coefs_1 = np.array([-0.09715103386082896, 0.06788948036532018,\n", - " -0.0007157453506997161, 0.0003048354358359307, -0.003636308978789861])\n", - "K_2 = np.array([685.7143789189881, 0, 991.0247637161314, 0, 686.3020333004097, 601.2442243349392, 0, 0, 1]).reshape((3, 3))\n", - "distortion_coefs_2 = np.array([-0.09781628655937251, 0.07153618281495966,\n", - " -0.001066517414175782, 0.0004679942401339674, -0.003645360450147547])\n", + "K_1 = np.array(\n", + " [\n", + " 672.2824725267757,\n", + " 0,\n", + " 984.0472159818853,\n", + " 0,\n", + " 672.6886411532304,\n", + " 602.96669930345,\n", + " 0,\n", + " 0,\n", + " 1,\n", + " ]\n", + ").reshape((3, 3))\n", + "distortion_coefs_1 = np.array(\n", + " [\n", + " -0.09715103386082896,\n", + " 0.06788948036532018,\n", + " -0.0007157453506997161,\n", + " 0.0003048354358359307,\n", + " -0.003636308978789861,\n", + " ]\n", + ")\n", + "K_2 = np.array(\n", + " [\n", + " 685.7143789189881,\n", + " 0,\n", + " 991.0247637161314,\n", + " 0,\n", + " 686.3020333004097,\n", + " 601.2442243349392,\n", + " 0,\n", + " 0,\n", + " 1,\n", + " ]\n", + ").reshape((3, 3))\n", + "distortion_coefs_2 = np.array(\n", + " [\n", + " -0.09781628655937251,\n", + " 0.07153618281495966,\n", + " -0.001066517414175782,\n", + " 0.0004679942401339674,\n", + " -0.003645360450147547,\n", + " ]\n", + ")\n", "\n", "# other constant and measurements\n", "TABLE_LENGTH = 2.74\n", @@ -53,7 +91,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -64,10 +102,17 @@ " def __init__(self, R, t):\n", " self.R = R\n", " self.t = t\n", - " \n", + " self.R_inv = np.linalg.inv(self.R)\n", + "\n", " def __call__(self, point):\n", " return self.R @ point + self.t\n", "\n", + " def transform(self, point):\n", + " return self(point)\n", + "\n", + " def inverse_transform(self, point):\n", + " return self.R_inv @ (point - self.t)\n", + "\n", " # right transformation is applied first\n", " def __mult__(self, other):\n", " return Transformation(self.R @ other.R, self.t + other.t)" @@ -75,36 +120,44 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 4, "metadata": {}, "outputs": [ { "data": { - "image/png": "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Dvv9V1dXq6amJvT7j6f1P//888rMzNS0adOOOS+ej4Hc3FxlZWWF/c6DwaDKysrCfueNjY0qLy8PzSkpKVFnZ2co3OXn52vjxo1qa2sLzVm7dq1GjBgRcy+bd6crvOzcuVNvvvmmBg4ceNzbVFZWKiEhIfTWiuv74Ov++te/6uDBg2HHfLwfA12effZZjRs3TmPGjDnuXOePgWifRRxpy5cvN6/Xay+88ILt2LHDbr31VktPTw+74sJVt99+u6Wlpdlbb70VdhncV199ZWZmn3zyiT3wwAO2detW2717t61atcrOOussu+yyy0L30XUJ7aRJk6yystJef/11GzRoUMxeQvtN99xzj7311lu2e/due+edd6ygoMAyMjKsvr7ezP5+GXVOTo6VlJTY1q1bLT8/3/Lz80O3d339XTo6OiwnJ8fmzp0btj0ej4GmpiarqKiwiooKk2SPPvqoVVRUhK6wKS4utvT0dFu1apVt27bNpk+f3u1l1GPHjrWysjJ7++23bfjw4WGX0DY2Nprf77cbb7zRqqqqbPny5Zaamhozl48eax+0trbaVVddZUOGDLHKysqw54auq0neffdde+yxx6yystJ27dplS5cutUGDBtkvfvGL0GPE8j441vqbmprsn//5n620tNR2795tb775pl144YU2fPhwa25uDt1HPB8DXQKBgKWmptrixYuPuL3rx0B34i7AmJn97ne/s5ycHEtKSrKLL77Y3nvvvWi3FBGSuq3nn3/ezMxqamrssssuswEDBpjX67VzzjnH7r333rDPADEz++yzz2zq1KmWkpJiGRkZds8991hbW1sUVnTyrr/+esvOzrakpCQ744wz7Prrr7dPPvkkNH748GH79a9/bf3797fU1FS7+uqr7cCBA2H34fL6u7zxxhsmyaqrq8O2x+MxsH79+m6P+5kzZ5rZ3y+lnjdvnvn9fvN6vTZx4sQj9svBgwdtxowZ1rdvX/P5fDZr1ixramoKm/PBBx/YhAkTzOv12hlnnGHFxcWnaonHdax9sHv37qM+N3R9NlB5ebnl5eVZWlqaJScn2/e+9z17+OGHw/7Am8XuPjjW+r/66iubNGmSDRo0yHr37m3Dhg2zW2655Yj/aY3nY6DLM888YykpKdbY2HjE7V0/BrrjMTPr0Zd4AAAAIiyuzoEBAACnBwIMAABwDgEGAAA4hwADAACcQ4ABAADOIcAAAADnEGAAAIBzCDAAAMA5BBgAAOAcAgwAAHAOAQYAADiHAAMAAJzzf4JWHHZO/tD7AAAAAElFTkSuQmCC", + "image/png": "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ZMkS27t3rz344IOWlpZm1dXVnd3aHyw3N9dee+0127Nnj+3cudP+9E//1DIzM+3kyZPenLvuussefPBBO378uFeRSMQbb25utlGjRllOTo7t2LHD1q5da/3797eCgoLOWNJle/LJJ+3rX/96zPp++9vfeuMPPfSQDR482EpKSmz79u32x3/8x/bNb37TG3d9/WZmNTU1MesvLi42Sfb++++bWfxdA2vXrrWf/OQn9tZbb5kke/vtt2PGi4qKLBAI2KpVq2zXrl12zz33WFZWlp0+fdqbM2nSJBszZox9+OGH9sEHH9iNN95o06dP98YjkYgFg0HLy8uzPXv22PLlyy01NdVeffXVq7XMC7rQOairq7OcnBx788037ZNPPrHS0lK79dZbbdy4cTHHGDJkiD3zzDMx18WXv3d05XNwsWtgxowZNmnSpJi11dbWxsyJ52vAzGLWfvz4cVuyZIklJCTYoUOHvDkuXwNtibsAc+utt1p+fr739dmzZ23gwIFWWFjYiV11jJqaGpNkGzdu9LbdddddNnfu3PPus3btWktMTLSqqipv2+LFi83v91tDQ0NHttsunnzySRszZkybY3V1dda9e3dbuXKlt23//v0myUpLS83M/fW3Ze7cuXbDDTdYS0uLmcX3NfDVb9wtLS0WCoVs0aJF3ra6ujrz+Xy2fPlyMzPbt2+fSbJt27Z5c9atW2cJCQl27NgxMzN7+eWXrU+fPjHrnz9/vg0fPryDV3T52vrh9VVbt241SXbkyBFv25AhQ+yFF1447z6unIPzBZipU6eed59r8RqYOnWqfetb34rZFi/XQKu4+hVSY2OjysvLlZOT421LTExUTk6OSktLO7GzjhGJRCT9/w+ubPXGG2+of//+GjVqlAoKCnTq1ClvrLS0VKNHj1YwGPS25ebmKhqNau/evVen8T/QgQMHNHDgQA0dOlR5eXmqrKyUJJWXl6upqSnm+R8xYoQyMzO95z8e1v9ljY2Nev311/XAAw8oISHB2x7v10Crw4cPq6qqKuY5DwQCys7OjnnO09LSNH78eG9OTk6OEhMTVVZW5s258847lZyc7M3Jzc1VRUWFPv/886u0mvYTiUSUkJCgtLS0mO1FRUXq16+fxo4dq0WLFsX82tD1c7Bhwwalp6dr+PDhmjNnjk6cOOGNXWvXQHV1tf7rv/5Ls2bNOmcsnq6BuPowx9/97nc6e/ZszDdmSQoGg/rkk086qauO0dLSokceeUS33XabRo0a5W3/q7/6Kw0ZMkQDBw7U7t27NX/+fFVUVOitt96SJFVVVbV5flrHurrs7GwtXbpUw4cP1/Hjx/X000/rjjvu0J49e1RVVaXk5ORzvmkHg0Fvba6v/6tWrVqluro6/eAHP/C2xfs18GWt/ba1ni8/5+np6THjSUlJ6tu3b8ycrKysc47ROtanT58O6b8jnDlzRvPnz9f06dNjPrjvxz/+sW6++Wb17dtXW7ZsUUFBgY4fP67nn39ektvnYNKkSZo2bZqysrJ06NAh/f3f/70mT56s0tJSdevW7Zq7BpYtW6bevXtr2rRpMdvj7RqIqwBzLcnPz9eePXu0efPmmO2zZ8/2/j169GhlZGRowoQJOnTokG644Yar3Wa7mzx5svfvm266SdnZ2RoyZIh+8YtfKDU1tRM76xw///nPNXny5JiPnY/3awDn19TUpL/8y7+UmWnx4sUxY4899pj375tuuknJycn667/+axUWFjr/J+bvv/9+79+jR4/WTTfdpBtuuEEbNmzQhAkTOrGzzrFkyRLl5eUpJSUlZnu8XQNx9Suk/v37q1u3bufcdVJdXa1QKNRJXbW/hx9+WGvWrNH777+vQYMGXXBudna2JOngwYOSpFAo1Ob5aR1zTVpamv7oj/5IBw8eVCgUUmNjo+rq6mLmfPn5j6f1HzlyRO+9955++MMfXnBePF8Drf1e6L/5UCikmpqamPHm5mbV1tbG1XXRGl6OHDmi4uLimFdf2pKdna3m5mZ9+umnkuLjHLQaOnSo+vfvH3PNXwvXgCR98MEHqqiouOj3Bcn9ayCuAkxycrLGjRunkpISb1tLS4tKSkoUDoc7sbP2YWZ6+OGH9fbbb2v9+vXnvNTXlp07d0qSMjIyJEnhcFgff/xxzH/Mrd/sRo4c2SF9d6STJ0/q0KFDysjI0Lhx49S9e/eY57+iokKVlZXe8x9P63/ttdeUnp6uKVOmXHBePF8DWVlZCoVCMc95NBpVWVlZzHNeV1en8vJyb8769evV0tLihbtwOKxNmzapqanJm1NcXKzhw4d3uZfN29IaXg4cOKD33ntP/fr1u+g+O3fuVGJioverFdfPwZf95je/0YkTJ2Ku+Xi/Blr9/Oc/17hx4zRmzJiLznX+GujsdxG3txUrVpjP57OlS5favn37bPbs2ZaWlhZzx4Wr5syZY4FAwDZs2BBzG9ypU6fMzOzgwYP2zDPP2Pbt2+3w4cO2evVqGzp0qN15553eMVpvoZ04caLt3LnT3n33XRswYECXvYX2qx5//HHbsGGDHT582P7nf/7HcnJyrH///lZTU2Nmv7+NOjMz09avX2/bt2+3cDhs4XDY29/19bc6e/asZWZm2vz582O2x+M1UF9fbzt27LAdO3aYJHv++edtx44d3h02RUVFlpaWZqtXr7bdu3fb1KlT27yNeuzYsVZWVmabN2+2YcOGxdxCW1dXZ8Fg0L73ve/Znj17bMWKFdajR48uc/vohc5BY2Oj3XPPPTZo0CDbuXNnzPeG1rtJtmzZYi+88ILt3LnTDh06ZK+//roNGDDAvv/973uP0ZXPwYXWX19fb3/7t39rpaWldvjwYXvvvffs5ptvtmHDhtmZM2e8Y8TzNdAqEolYjx49bPHixefs7/o10Ja4CzBmZv/6r/9qmZmZlpycbLfeeqt9+OGHnd1Su5DUZr322mtmZlZZWWl33nmn9e3b13w+n9144402b968mL8BYmb26aef2uTJky01NdX69+9vjz/+uDU1NXXCii7ffffdZxkZGZacnGzXXXed3XfffXbw4EFv/PTp0/ajH/3I+vTpYz169LDvfOc7dvz48ZhjuLz+Vr/61a9MklVUVMRsj8dr4P3332/zup8xY4aZ/f5W6gULFlgwGDSfz2cTJkw457ycOHHCpk+fbr169TK/328zZ860+vr6mDm7du2y22+/3Xw+n1133XVWVFR0tZZ4URc6B4cPHz7v94bWvw1UXl5u2dnZFggELCUlxb72ta/ZT3/605gf8GZd9xxcaP2nTp2yiRMn2oABA6x79+42ZMgQe/DBB8/5n9Z4vgZavfrqq5aammp1dXXn7O/6NdCWBDOzDn2JBwAAoJ3F1XtgAADAtYEAAwAAnEOAAQAAziHAAAAA5xBgAACAcwgwAADAOQQYAADgHAIMAABwDgEGAAA4hwADAACcQ4ABAADOIcAAAADn/D/mOsDeY92JWgAAAABJRU5ErkJggg==", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[ 897.26006165 1278.1550229 ]\n" - ] } ], "source": [ "def validate_image_point(point):\n", - " return point[0] < 0 or point[0] >= IMAGE_SIZE[0] or point[1] < 0 or point[1] >= IMAGE_SIZE[1]\n", + " return (\n", + " point[0] < 0\n", + " or point[0] >= IMAGE_SIZE[0]\n", + " or point[1] < 0\n", + " or point[1] >= IMAGE_SIZE[1]\n", + " )\n", " # raise ValueError(f\"Invalid image point coordinates: out of FOV?\")\n", "\n", + "\n", "def project_point_to_image(point, transformation, camera_matrix):\n", " return camera_matrix @ transformation(point)\n", "\n", - "def project_sphere_to_image(center: Tuple[int], radius: int, camera_matrix: np.ndarray, world2cam) -> np.ndarray:\n", + "\n", + "def normilise_image_point(point, camera_matrix):\n", + " x_normalised = (point[0] - camera_matrix[0, 2]) / camera_matrix[0, 0]\n", + " y_normalised = (point[1] - camera_matrix[1, 2]) / camera_matrix[1, 1]\n", + " return np.array([x_normalised, y_normalised, 1]).reshape(3, 1)\n", + "\n", + "\n", + "def project_sphere_to_image(\n", + " center: Tuple[int], radius: int, camera_matrix: np.ndarray, world2cam\n", + ") -> np.ndarray:\n", " image = np.zeros(IMAGE_SIZE[::-1])\n", " center = np.array(center)\n", " camera_matrix_inv = np.linalg.inv(camera_matrix)\n", @@ -112,48 +165,63 @@ " # projecting center and some edge point to approximate radius after projection\n", " projected_center = camera_matrix @ center.reshape((3, 1))\n", " projected_center /= projected_center[2]\n", - " projected_edge_point = camera_matrix @ (center + np.array([radius, 0, 0])).reshape((3, 1))\n", + " projected_edge_point = camera_matrix @ (center + np.array([radius, 0, 0])).reshape(\n", + " (3, 1)\n", + " )\n", " projected_edge_point /= projected_edge_point[2]\n", - " approx_projected_radius = np.linalg.norm(projected_edge_point - projected_center, ord=2)\n", + " approx_projected_radius = np.linalg.norm(\n", + " projected_edge_point - projected_center, ord=2\n", + " )\n", "\n", " # calculating bounding box for calculations with 1.5 margin\n", " x_start = int(max(0, projected_center[0].item() - 1.5 * approx_projected_radius))\n", " y_start = int(max(0, projected_center[1].item() - 1.5 * approx_projected_radius))\n", - " x_stop = int(min(IMAGE_SIZE[0], projected_center[0].item() + 1.5 * approx_projected_radius))\n", - " y_stop = int(min(IMAGE_SIZE[1], projected_center[1].item() + 1.5 * approx_projected_radius))\n", + " x_stop = int(\n", + " min(IMAGE_SIZE[0], projected_center[0].item() + 1.5 * approx_projected_radius)\n", + " )\n", + " y_stop = int(\n", + " min(IMAGE_SIZE[1], projected_center[1].item() + 1.5 * approx_projected_radius)\n", + " )\n", "\n", " for x in range(x_start, x_stop):\n", " for y in range(y_start, y_stop):\n", " # back project image point\n", - " world_ray = camera_matrix_inv @ np.array([x, y, 1]).reshape(3, 1)\n", + " world_ray = camera_matrix_inv @ np.array([x, y, 1]).reshape((3, 1))\n", " # measure distance from the sphere center\n", - " distance = np.linalg.norm(np.cross(world_ray.flatten(), center), ord=2) / np.linalg.norm(world_ray, ord=2)\n", + " distance = np.linalg.norm(\n", + " np.cross(world_ray.flatten(), center), ord=2\n", + " ) / np.linalg.norm(world_ray, ord=2)\n", " # if back-projected ray intersects with the sphere, paint the pixel in the mask\n", " if distance <= radius:\n", " image[y, x] = 1\n", " return image\n", "\n", - "image = project_sphere_to_image((300, 300, 700), 100, K_1, None)\n", - "plt.imshow(image, cmap='gray')\n", - "plt.show()\n", "\n", - "print(get_mask_centroid(image))\n", - " \n" + "image = project_sphere_to_image((-0.25, 0.25, 0.5), 0.02, K_1, None)\n", + "plt.imshow(image, cmap=\"gray\")\n", + "plt.show()" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ - "def triangulate_position(points, world2cam, cam2cam):\n", - " pass" + "def triangulate_position(\n", + " points_by_view, world2cam, cam2cam, camera_matrix_1, camera_matrix_2\n", + "):\n", + " proj_1 = camera_matrix_1 @ world2cam\n", + " proj_2 = camera_matrix_2 @ cam2cam @ world2cam\n", + " # TODO save 4D points?\n", + " return cv2.triangulatePoints(\n", + " proj_1, proj_2, points_by_view[:, 0, :], points_by_view[:, 1, :]\n", + " )[:, :3]" ] }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -166,11 +234,12 @@ " bbox = list(map(float, [x1, y1, x2, y2]))\n", " return bbox\n", "\n", + "\n", "def get_mask_center(mask):\n", " bbox = get_bbox(mask)\n", " centroid_x = (bbox[0] + bbox[2]) / 2\n", " centroid_y = (bbox[1] + bbox[3]) / 2\n", - " return (centroid_x, centroid_y)\n", + " return np.array([centroid_x, centroid_y])\n", "\n", "\n", "def get_mask_centroid(mask):\n", @@ -179,22 +248,54 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ - "def evaluate_precision(spheres, triangulated_points):\n", - " pass" + "def evaluate_precision(sphere_centers, triangulated_points):\n", + " errors = sphere_centers - triangulated_points\n", + " print(\"precision of triangulated points:\")\n", + " print(np.mean(errors * errors))" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ - "def evaluate_camera_position(world_to_master: Transformation, master_to_second: Transformation):\n", - " pass" + "def evaluate_camera_position(\n", + " world2master: Transformation,\n", + " master2second: Transformation,\n", + " center_extranctor,\n", + " camera_matrix_1,\n", + " camera_matrix_2,\n", + "):\n", + " sphere_centers = np.row_stack(\n", + " (\n", + " np.linspace(-TABLE_LENGTH / 2, TABLE_LENGTH / 2, 10),\n", + " np.linspace(-TABLE_WIDTH / 2, TABLE_WIDTH / 2, 10),\n", + " )\n", + " )\n", + "\n", + " points = []\n", + " world2second = master2second @ world2master\n", + " for i in range(sphere_centers.shape[1]):\n", + " mask_1 = project_sphere_to_image(sphere_centers[:, i], 0.02, K_1, world2master)\n", + " mask_2 = project_sphere_to_image(sphere_centers[:, i], 0.02, K_2, world2second)\n", + " points.append((center_extranctor(mask_1), center_extranctor(mask_2)))\n", + " triangulated_points = triangulate_position(\n", + " points, world2master, master2second, camera_matrix_1, camera_matrix_2\n", + " )\n", + " print(f\"triangulated_points shape is {triangulated_points.shape}\")\n", + "\n", + " evaluate_precision(sphere_centers, triangulated_points)\n", + " print(\"evalution complete\")\n", + "\n", + "# main test: opposite camera placement\n", + "world2master = Transformation(Rotation.from_euler(\"xyz\", [], degrees=True).as_matrix(), np.array([0, 0, 0]))\n", + "master2second = Transformation(Rotation.from_euler(\"xyz\", [], degrees=True).as_matrix(), np.array([0, 0, 0]))\n", + "evaluate_camera_position(world2master, master2second, K_1, K_2)\n" ] } ], From b7a7da965162a5c902cc4d04da8d2ca62d62c93f Mon Sep 17 00:00:00 2001 From: Bakin Denis Date: Mon, 22 Jul 2024 01:55:12 +0300 Subject: [PATCH 4/9] sync --- research.ipynb | 176 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 123 insertions(+), 53 deletions(-) diff --git a/research.ipynb b/research.ipynb index 287ac4b..e60338b 100644 --- a/research.ipynb +++ b/research.ipynb @@ -17,7 +17,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 72, "metadata": {}, "outputs": [], "source": [ @@ -32,7 +32,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 73, "metadata": {}, "outputs": [], "source": [ @@ -91,7 +91,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 91, "metadata": {}, "outputs": [], "source": [ @@ -101,11 +101,14 @@ "class Transformation:\n", " def __init__(self, R, t):\n", " self.R = R\n", - " self.t = t\n", + " self.t = t.reshape((3, 1))\n", " self.R_inv = np.linalg.inv(self.R)\n", "\n", " def __call__(self, point):\n", " return self.R @ point + self.t\n", + " \n", + " def __mul__(self, other):\n", + " return Transformation(self.R @ other.R, self.R @ other.t + self.t)\n", "\n", " def transform(self, point):\n", " return self(point)\n", @@ -120,20 +123,9 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 97, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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ZMkS27t3rz344IOWlpZm1dXVnd3aHyw3N9dee+0127Nnj+3cudP+9E//1DIzM+3kyZPenLvuussefPBBO378uFeRSMQbb25utlGjRllOTo7t2LHD1q5da/3797eCgoLOWNJle/LJJ+3rX/96zPp++9vfeuMPPfSQDR482EpKSmz79u32x3/8x/bNb37TG3d9/WZmNTU1MesvLi42Sfb++++bWfxdA2vXrrWf/OQn9tZbb5kke/vtt2PGi4qKLBAI2KpVq2zXrl12zz33WFZWlp0+fdqbM2nSJBszZox9+OGH9sEHH9iNN95o06dP98YjkYgFg0HLy8uzPXv22PLlyy01NdVeffXVq7XMC7rQOairq7OcnBx788037ZNPPrHS0lK79dZbbdy4cTHHGDJkiD3zzDMx18WXv3d05XNwsWtgxowZNmnSpJi11dbWxsyJ52vAzGLWfvz4cVuyZIklJCTYoUOHvDkuXwNtibsAc+utt1p+fr739dmzZ23gwIFWWFjYiV11jJqaGpNkGzdu9LbdddddNnfu3PPus3btWktMTLSqqipv2+LFi83v91tDQ0NHttsunnzySRszZkybY3V1dda9e3dbuXKlt23//v0myUpLS83M/fW3Ze7cuXbDDTdYS0uLmcX3NfDVb9wtLS0WCoVs0aJF3ra6ujrz+Xy2fPlyMzPbt2+fSbJt27Z5c9atW2cJCQl27NgxMzN7+eWXrU+fPjHrnz9/vg0fPryDV3T52vrh9VVbt241SXbkyBFv25AhQ+yFF1447z6unIPzBZipU6eed59r8RqYOnWqfetb34rZFi/XQKu4+hVSY2OjysvLlZOT421LTExUTk6OSktLO7GzjhGJRCT9/w+ubPXGG2+of//+GjVqlAoKCnTq1ClvrLS0VKNHj1YwGPS25ebmKhqNau/evVen8T/QgQMHNHDgQA0dOlR5eXmqrKyUJJWXl6upqSnm+R8xYoQyMzO95z8e1v9ljY2Nev311/XAAw8oISHB2x7v10Crw4cPq6qqKuY5DwQCys7OjnnO09LSNH78eG9OTk6OEhMTVVZW5s258847lZyc7M3Jzc1VRUWFPv/886u0mvYTiUSUkJCgtLS0mO1FRUXq16+fxo4dq0WLFsX82tD1c7Bhwwalp6dr+PDhmjNnjk6cOOGNXWvXQHV1tf7rv/5Ls2bNOmcsnq6BuPowx9/97nc6e/ZszDdmSQoGg/rkk086qauO0dLSokceeUS33XabRo0a5W3/q7/6Kw0ZMkQDBw7U7t27NX/+fFVUVOitt96SJFVVVbV5flrHurrs7GwtXbpUw4cP1/Hjx/X000/rjjvu0J49e1RVVaXk5ORzvmkHg0Fvba6v/6tWrVqluro6/eAHP/C2xfs18GWt/ba1ni8/5+np6THjSUlJ6tu3b8ycrKysc47ROtanT58O6b8jnDlzRvPnz9f06dNjPrjvxz/+sW6++Wb17dtXW7ZsUUFBgY4fP67nn39ektvnYNKkSZo2bZqysrJ06NAh/f3f/70mT56s0tJSdevW7Zq7BpYtW6bevXtr2rRpMdvj7RqIqwBzLcnPz9eePXu0efPmmO2zZ8/2/j169GhlZGRowoQJOnTokG644Yar3Wa7mzx5svfvm266SdnZ2RoyZIh+8YtfKDU1tRM76xw///nPNXny5JiPnY/3awDn19TUpL/8y7+UmWnx4sUxY4899pj375tuuknJycn667/+axUWFjr/J+bvv/9+79+jR4/WTTfdpBtuuEEbNmzQhAkTOrGzzrFkyRLl5eUpJSUlZnu8XQNx9Suk/v37q1u3bufcdVJdXa1QKNRJXbW/hx9+WGvWrNH777+vQYMGXXBudna2JOngwYOSpFAo1Ob5aR1zTVpamv7oj/5IBw8eVCgUUmNjo+rq6mLmfPn5j6f1HzlyRO+9955++MMfXnBePF8Drf1e6L/5UCikmpqamPHm5mbV1tbG1XXRGl6OHDmi4uLimFdf2pKdna3m5mZ9+umnkuLjHLQaOnSo+vfvH3PNXwvXgCR98MEHqqiouOj3Bcn9ayCuAkxycrLGjRunkpISb1tLS4tKSkoUDoc7sbP2YWZ6+OGH9fbbb2v9+vXnvNTXlp07d0qSMjIyJEnhcFgff/xxzH/Mrd/sRo4c2SF9d6STJ0/q0KFDysjI0Lhx49S9e/eY57+iokKVlZXe8x9P63/ttdeUnp6uKVOmXHBePF8DWVlZCoVCMc95NBpVWVlZzHNeV1en8vJyb8769evV0tLihbtwOKxNmzapqanJm1NcXKzhw4d3uZfN29IaXg4cOKD33ntP/fr1u+g+O3fuVGJioverFdfPwZf95je/0YkTJ2Ku+Xi/Blr9/Oc/17hx4zRmzJiLznX+GujsdxG3txUrVpjP57OlS5favn37bPbs2ZaWlhZzx4Wr5syZY4FAwDZs2BBzG9ypU6fMzOzgwYP2zDPP2Pbt2+3w4cO2evVqGzp0qN15553eMVpvoZ04caLt3LnT3n33XRswYECXvYX2qx5//HHbsGGDHT582P7nf/7HcnJyrH///lZTU2Nmv7+NOjMz09avX2/bt2+3cDhs4XDY29/19bc6e/asZWZm2vz582O2x+M1UF9fbzt27LAdO3aYJHv++edtx44d3h02RUVFlpaWZqtXr7bdu3fb1KlT27yNeuzYsVZWVmabN2+2YcOGxdxCW1dXZ8Fg0L73ve/Znj17bMWKFdajR48uc/vohc5BY2Oj3XPPPTZo0CDbuXNnzPeG1rtJtmzZYi+88ILt3LnTDh06ZK+//roNGDDAvv/973uP0ZXPwYXWX19fb3/7t39rpaWldvjwYXvvvffs5ptvtmHDhtmZM2e8Y8TzNdAqEolYjx49bPHixefs7/o10Ja4CzBmZv/6r/9qmZmZlpycbLfeeqt9+OGHnd1Su5DUZr322mtmZlZZWWl33nmn9e3b13w+n9144402b968mL8BYmb26aef2uTJky01NdX69+9vjz/+uDU1NXXCii7ffffdZxkZGZacnGzXXXed3XfffXbw4EFv/PTp0/ajH/3I+vTpYz169LDvfOc7dvz48ZhjuLz+Vr/61a9MklVUVMRsj8dr4P3332/zup8xY4aZ/f5W6gULFlgwGDSfz2cTJkw457ycOHHCpk+fbr169TK/328zZ860+vr6mDm7du2y22+/3Xw+n1133XVWVFR0tZZ4URc6B4cPHz7v94bWvw1UXl5u2dnZFggELCUlxb72ta/ZT3/605gf8GZd9xxcaP2nTp2yiRMn2oABA6x79+42ZMgQe/DBB8/5n9Z4vgZavfrqq5aammp1dXXn7O/6NdCWBDOzDn2JBwAAoJ3F1XtgAADAtYEAAwAAnEOAAQAAziHAAAAA5xBgAACAcwgwAADAOQQYAADgHAIMAABwDgEGAAA4hwADAACcQ4ABAADOIcAAAADn/D/mOsDeY92JWgAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "def validate_image_point(point):\n", " return (\n", @@ -156,18 +148,17 @@ "\n", "\n", "def project_sphere_to_image(\n", - " center: Tuple[int], radius: int, camera_matrix: np.ndarray, world2cam\n", + " center, radius: int, camera_matrix: np.ndarray, world2cam\n", ") -> np.ndarray:\n", " image = np.zeros(IMAGE_SIZE[::-1])\n", - " center = np.array(center)\n", + " center = center.reshape((3, 1))\n", " camera_matrix_inv = np.linalg.inv(camera_matrix)\n", "\n", " # projecting center and some edge point to approximate radius after projection\n", - " projected_center = camera_matrix @ center.reshape((3, 1))\n", + " projected_center = project_point_to_image(center, world2cam, camera_matrix)\n", " projected_center /= projected_center[2]\n", - " projected_edge_point = camera_matrix @ (center + np.array([radius, 0, 0])).reshape(\n", - " (3, 1)\n", - " )\n", + " edge_point = center + np.array([radius, 0, 0]).reshape((3, 1))\n", + " projected_edge_point = project_point_to_image(edge_point, world2cam, camera_matrix)\n", " projected_edge_point /= projected_edge_point[2]\n", " approx_projected_radius = np.linalg.norm(\n", " projected_edge_point - projected_center, ord=2\n", @@ -189,7 +180,7 @@ " world_ray = camera_matrix_inv @ np.array([x, y, 1]).reshape((3, 1))\n", " # measure distance from the sphere center\n", " distance = np.linalg.norm(\n", - " np.cross(world_ray.flatten(), center), ord=2\n", + " np.cross(world_ray.flatten(), center.flatten()), ord=2\n", " ) / np.linalg.norm(world_ray, ord=2)\n", " # if back-projected ray intersects with the sphere, paint the pixel in the mask\n", " if distance <= radius:\n", @@ -197,35 +188,43 @@ " return image\n", "\n", "\n", - "image = project_sphere_to_image((-0.25, 0.25, 0.5), 0.02, K_1, None)\n", - "plt.imshow(image, cmap=\"gray\")\n", - "plt.show()" + "# image = project_sphere_to_image((-0.25, 0.25, 0.5), 0.02, K_1, None)\n", + "# plt.imshow(image, cmap=\"gray\")\n", + "# plt.show()" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 76, "metadata": {}, "outputs": [], "source": [ "def triangulate_position(\n", " points_by_view, world2cam, cam2cam, camera_matrix_1, camera_matrix_2\n", "):\n", - " proj_1 = camera_matrix_1 @ world2cam\n", - " proj_2 = camera_matrix_2 @ cam2cam @ world2cam\n", - " # TODO save 4D points?\n", - " return cv2.triangulatePoints(\n", - " proj_1, proj_2, points_by_view[:, 0, :], points_by_view[:, 1, :]\n", - " )[:, :3]" + " world2cam_Rt = np.column_stack((world2cam.R, world2cam.t))\n", + " world2second_cam = cam2cam * world2cam\n", + " world2second_cam_Rt = np.column_stack((world2second_cam.R, world2second_cam.t))\n", + " proj_1 = camera_matrix_1 @ world2cam_Rt\n", + " proj_2 = camera_matrix_2 @ world2second_cam_Rt\n", + " # TODO preserve 4D points?\n", + " res = cv2.triangulatePoints(\n", + " proj_1, proj_2, points_by_view[:, :, 0].T, points_by_view[:, :, 1].T\n", + " )\n", + " for i in range(res.shape[1]):\n", + " res[:, i] /= res[3, i]\n", + " return res[:3, :]" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 95, "metadata": {}, "outputs": [], "source": [ "def get_bbox(mask: np.ndarray) -> List[float]:\n", + " if not np.any(mask):\n", + " return [0., 0., 0., 0.]\n", " # x_min, y_min, x_max, y_max\n", " horizontal_indicies = np.where(np.any(mask, axis=0))[0]\n", " vertical_indicies = np.where(np.any(mask, axis=1))[0]\n", @@ -248,21 +247,74 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 107, "metadata": {}, "outputs": [], "source": [ "def evaluate_precision(sphere_centers, triangulated_points):\n", + " print(sphere_centers[:, :5])\n", + " print(triangulated_points[:, :5])\n", " errors = sphere_centers - triangulated_points\n", " print(\"precision of triangulated points:\")\n", - " print(np.mean(errors * errors))" + " print(np.mean(errors * errors))\n", + "\n", + " fig = plt.figure()\n", + " ax = fig.add_subplot(projection='3d')\n", + " ax.scatter(sphere_centers[0, :], sphere_centers[1, :], sphere_centers[2, :], marker='o')\n", + " ax.scatter(triangulated_points[0, :], triangulated_points[1, :], triangulated_points[2, :], marker='o')\n", + "\n", + " ax.set_xlabel('X Label')\n", + " ax.set_ylabel('Y Label')\n", + " ax.set_zlabel('Z Label')\n", + "\n", + " plt.show()" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 114, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "20\n", + "40\n", + "60\n", + "80\n", + "100\n", + "120\n", + "triangulated_points shape is (3, 125)\n", + "[[-1.37 -0.7625 0. -1.37 -0.7625 ]\n", + " [ 0.25 -0.685 0.38125 0.5 -0.685 ]\n", + " [-0.38125 0.75 0.685 -0.38125 1. ]]\n", + "[[ 1.29940542 1.29940542 1.29940542 1.29940542 1.29940542]\n", + " [ 1.03681671 1.03681671 1.03681671 1.03681671 1.03681671]\n", + " [-1.40207606 -1.40207606 -1.40207606 -1.40207606 -1.40207606]]\n", + "precision of triangulated points:\n", + "2.744184139499578\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "evalution complete\n" + ] + } + ], "source": [ "def evaluate_camera_position(\n", " world2master: Transformation,\n", @@ -271,31 +323,49 @@ " camera_matrix_1,\n", " camera_matrix_2,\n", "):\n", - " sphere_centers = np.row_stack(\n", - " (\n", - " np.linspace(-TABLE_LENGTH / 2, TABLE_LENGTH / 2, 10),\n", - " np.linspace(-TABLE_WIDTH / 2, TABLE_WIDTH / 2, 10),\n", - " )\n", - " )\n", - "\n", + " NUMBER_OF_SPHERES = 5\n", + " sphere_centers = []\n", + " for x in np.linspace(-TABLE_LENGTH / 2, TABLE_LENGTH / 2, NUMBER_OF_SPHERES):\n", + " for y in np.linspace(-TABLE_WIDTH / 2, TABLE_WIDTH / 2, NUMBER_OF_SPHERES):\n", + " for z in np.linspace(0, 1, NUMBER_OF_SPHERES):\n", + " sphere_centers.append((x, y, z))\n", + " sphere_centers = np.array(sphere_centers).reshape((3, NUMBER_OF_SPHERES**3))\n", " points = []\n", - " world2second = master2second @ world2master\n", + " world2second = master2second * world2master\n", " for i in range(sphere_centers.shape[1]):\n", - " mask_1 = project_sphere_to_image(sphere_centers[:, i], 0.02, K_1, world2master)\n", - " mask_2 = project_sphere_to_image(sphere_centers[:, i], 0.02, K_2, world2second)\n", + " if i % 20 == 0:\n", + " print(i)\n", + " mask_1 = project_sphere_to_image(\n", + " sphere_centers[:, i : (i + 1)], 0.02, K_1, world2master\n", + " )\n", + " mask_2 = project_sphere_to_image(\n", + " sphere_centers[:, i : (i + 1)], 0.02, K_2, world2second\n", + " )\n", " points.append((center_extranctor(mask_1), center_extranctor(mask_2)))\n", " triangulated_points = triangulate_position(\n", - " points, world2master, master2second, camera_matrix_1, camera_matrix_2\n", + " np.array(points), world2master, master2second, camera_matrix_1, camera_matrix_2\n", " )\n", " print(f\"triangulated_points shape is {triangulated_points.shape}\")\n", "\n", " evaluate_precision(sphere_centers, triangulated_points)\n", " print(\"evalution complete\")\n", "\n", + "\n", "# main test: opposite camera placement\n", - "world2master = Transformation(Rotation.from_euler(\"xyz\", [], degrees=True).as_matrix(), np.array([0, 0, 0]))\n", - "master2second = Transformation(Rotation.from_euler(\"xyz\", [], degrees=True).as_matrix(), np.array([0, 0, 0]))\n", - "evaluate_camera_position(world2master, master2second, K_1, K_2)\n" + "# world2master = Transformation(Rotation.from_euler(\"xyz\", [0, -45, -90], degrees=True).as_matrix(), np.array([-TABLE_LENGTH/4, TABLE_WIDTH, 0.5]))\n", + "# master2second = Transformation(Rotation.from_euler(\"xyz\", [], degrees=True).as_matrix(), np.array([0, 0, 0]))\n", + "\n", + "world2master = Transformation(\n", + " Rotation.from_euler(\"xyz\", [0, -45, -90], degrees=True).as_matrix(),\n", + " np.array([-TABLE_LENGTH / 4, TABLE_WIDTH, 0.5]),\n", + ")\n", + "master2second = Transformation(\n", + " Rotation.from_euler(\n", + " \"xyz\", [0.01175419895518242, 2.170836441913732, 2.19333242876324], degrees=False\n", + " ).as_matrix(),\n", + " np.array([-0.06677747450343367, -1.174973690204363, 1.140354306665756]),\n", + ")\n", + "evaluate_camera_position(world2master, master2second, get_mask_center, K_1, K_2)" ] } ], From 20c5dcba732fe0d4cdf4927282d027eea71d0a7e Mon Sep 17 00:00:00 2001 From: dfbakin Date: Mon, 3 Feb 2025 03:47:30 +0300 Subject: [PATCH 5/9] added plotly and dash framework for interacive visual fixed math regarding projections to image plane --- research.ipynb | 4582 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 4577 insertions(+), 5 deletions(-) diff --git a/research.ipynb b/research.ipynb index e60338b..d1cb8c3 100644 --- a/research.ipynb +++ b/research.ipynb @@ -17,7 +17,7 @@ }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -32,13 +32,13 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# let us introduce intrinsic parameters and assume that they provide zero reprojection error for an arbitrary image-world correlation\n", "\n", - "IMAGE_SIZE = (1920, 1200)\n", + "IMAGE_SIZE = (1200, 1920)\n", "K_1 = np.array(\n", " [\n", " 672.2824725267757,\n", @@ -83,6 +83,10 @@ " -0.003645360450147547,\n", " ]\n", ")\n", + "# reference:\n", + "# https://github.com/robotics-laboratory/handy/blob/table-dataset-1/datasets/TableOrange2msRecord_22_04/camera_params.yaml\n", + "stereo_cam_rotation = [0.01175419895518242, 2.170836441913732, 2.19333242876324]\n", + "stereo_cam_translation = [-0.06677747450343367, -1.174973690204363, 1.140354306665756]\n", "\n", "# other constant and measurements\n", "TABLE_LENGTH = 2.74\n", @@ -91,7 +95,7 @@ }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -118,7 +122,439 @@ "\n", " # right transformation is applied first\n", " def __mult__(self, other):\n", - " return Transformation(self.R @ other.R, self.t + other.t)" + " return Transformation(self.R @ other.R, self.t + other.t)\n", + " \n", + "\n", + "class Image:\n", + " def __init__(self, camera_matrix, camera_transformation, distortion_coefs, image_size=IMAGE_SIZE):\n", + " self.camera_matrix = camera_matrix\n", + " self.camera_transformation = camera_transformation\n", + " self.distortion_coefs = distortion_coefs\n", + " self.image_size = image_size\n", + "\n", + " def normilise_image_point(self, point):\n", + " x_normalised = (point[0] - self.camera_matrix[0, 2]) / self.camera_matrix[0, 0]\n", + " y_normalised = (point[1] - self.camera_matrix[1, 2]) / self.camera_matrix[1, 1]\n", + " return np.array([x_normalised, y_normalised, 1]).reshape(3, 1)\n", + "\n", + " # in world coordinates\n", + " def project_point_to_image(self, point):\n", + " transformed_point = self.camera_transformation(point)\n", + " transformed_point = transformed_point / transformed_point[2]\n", + " projected_point = self.camera_matrix @ transformed_point\n", + " return projected_point\n", + " \n", + " def project_points_to_image(self, points):\n", + " return np.array([self.project_point_to_image(point) for point in points])\n", + "\n", + " def project_ball_to_image(self, \n", + " center, radius: int) -> np.ndarray:\n", + " center = center.reshape((3, 1))\n", + " camera_matrix_inv = np.linalg.inv(self.camera_matrix)\n", + "\n", + " # projecting center and some edge point to approximate radius after projection\n", + " projected_center = self.project_point_to_image(center)\n", + " projected_center /= projected_center[2]\n", + " edge_point = center + np.array([radius, 0, 0]).reshape((3, 1))\n", + " projected_edge_point = self.project_point_to_image(edge_point)\n", + " projected_edge_point /= projected_edge_point[2]\n", + " approx_projected_radius = np.linalg.norm(\n", + " projected_edge_point - projected_center, ord=2\n", + " )\n", + "\n", + " # calculating bounding box for calculations with 1.5 margin\n", + " x_start = int(max(0, projected_center[0].item() - 1.5 * approx_projected_radius))\n", + " y_start = int(max(0, projected_center[1].item() - 1.5 * approx_projected_radius))\n", + " x_stop = int(\n", + " min(IMAGE_SIZE[1], projected_center[0].item() + 1.5 * approx_projected_radius)\n", + " )\n", + " y_stop = int(\n", + " min(IMAGE_SIZE[0], projected_center[1].item() + 1.5 * approx_projected_radius)\n", + " )\n", + "\n", + " image = np.zeros(self.image_size)\n", + " for x in range(x_start, x_stop):\n", + " for y in range(y_start, y_stop):\n", + " # back project image point\n", + " world_ray = camera_matrix_inv @ np.array([x, y, 1]).reshape((3, 1))\n", + " # measure distance from the sphere center\n", + " distance = np.linalg.norm(\n", + " np.cross(world_ray.flatten(), center.flatten()), ord=2\n", + " ) / np.linalg.norm(world_ray, ord=2)\n", + " # if back-projected ray intersects with the sphere, paint the pixel in the mask\n", + " if distance <= radius:\n", + " image[y, x] = 1\n", + " return image\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Visually testing `Image` class" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "\n", + "def display_plane(normal, size, origin):\n", + " \"\"\"Display a plane in the shape of a rectangle.\n", + " \n", + " Args:\n", + " normal (tuple): Normal vector of the plane (nx, ny, nz).\n", + " size (tuple): Size of the rectangle (width, height).\n", + " origin (tuple): Origin of the rectangle (x, y, z).\n", + " \"\"\"\n", + " normal = np.array(normal) / np.linalg.norm(normal) # Normalize normal vector\n", + " u = np.array([1, 0, 0]) if abs(normal[0]) < 0.9 else np.array([0, 1, 0])\n", + " v = np.cross(normal, u)\n", + " u = np.cross(v, normal)\n", + " u, v = u / np.linalg.norm(u), v / np.linalg.norm(v)\n", + " \n", + " w, h = size\n", + " corners = np.array([\n", + " origin + w/2 * u + h/2 * v,\n", + " origin - w/2 * u + h/2 * v,\n", + " origin - w/2 * u - h/2 * v,\n", + " origin + w/2 * u - h/2 * v,\n", + " ])\n", + " \n", + " x, y, z = corners[:, 0], corners[:, 1], corners[:, 2]\n", + " \n", + " return go.Mesh3d(\n", + " x=[*x, x[0]], y=[*y, y[0]], z=[*z, z[0]],\n", + " color='lightblue', opacity=0.5, alphahull=0\n", + " )\n", + "\n", + "def display_ball(position, radius, color):\n", + " \"\"\"Display a ball (sphere).\n", + " \n", + " Args:\n", + " position (tuple): (x, y, z) coordinates of the center.\n", + " radius (float): Radius of the sphere.\n", + " color (str): Color of the sphere.\n", + " \"\"\"\n", + " u, v = np.mgrid[0:2*np.pi:20j, 0:np.pi:10j]\n", + " x = position[0] + radius * np.cos(u) * np.sin(v)\n", + " y = position[1] + radius * np.sin(u) * np.sin(v)\n", + " z = position[2] + radius * np.cos(v)\n", + " \n", + " return go.Surface(x=x, y=y, z=z, colorscale=[[0, color], [1, color]], showscale=False)\n", + "\n", + "def display_parallelepiped(size, direction, origin=(0,0,0)):\n", + " \"\"\"Display a parallelepiped.\n", + " \n", + " Args:\n", + " size (tuple): (width, height, depth).\n", + " direction (tuple): A vector representing the \"facing\" direction.\n", + " origin (tuple): Bottom-left corner of the parallelepiped.\n", + " \"\"\"\n", + " direction = np.array(direction) / np.linalg.norm(direction)\n", + " u = np.array([1, 0, 0]) if abs(direction[0]) < 0.9 else np.array([0, 1, 0])\n", + " v = np.cross(direction, u)\n", + " u = np.cross(v, direction)\n", + " u, v = u / np.linalg.norm(u), v / np.linalg.norm(v)\n", + "\n", + " w, h, d = size\n", + " corners = np.array([\n", + " origin,\n", + " origin + w * u,\n", + " origin + h * v,\n", + " origin + d * direction,\n", + " origin + w * u + h * v,\n", + " origin + w * u + d * direction,\n", + " origin + h * v + d * direction,\n", + " origin + w * u + h * v + d * direction\n", + " ])\n", + " \n", + " x, y, z = corners[:, 0], corners[:, 1], corners[:, 2]\n", + "\n", + " faces = [\n", + " [0, 1, 4, 2], # Bottom\n", + " [0, 1, 5, 3], # Side\n", + " [1, 4, 7, 5], # Side\n", + " [2, 4, 7, 6], # Side\n", + " [0, 2, 6, 3], # Side\n", + " [3, 5, 7, 6] # Top\n", + " ]\n", + "\n", + " return go.Mesh3d(\n", + " x=x, y=y, z=z,\n", + " i=[face[0] for face in faces],\n", + " j=[face[1] for face in faces],\n", + " k=[face[2] for face in faces],\n", + " color=\"orange\", opacity=0.7\n", + " )\n", + "\n", + "def display_camera(position, direction, fov=60, aspect_ratio=1.5, depth=2.0, color=\"blue\"):\n", + " \"\"\"\n", + " Display a camera as a 3D pyramid.\n", + " \n", + " Args:\n", + " position (tuple): (x, y, z) world position of the camera.\n", + " direction (tuple): (dx, dy, dz) unit vector pointing in the direction of the camera.\n", + " fov (float): Field of view in degrees (controls the width of the base).\n", + " aspect_ratio (float): Aspect ratio (width/height of base).\n", + " depth (float): Depth of the pyramid (distance from camera to base).\n", + " color (str): Color of the camera pyramid.\n", + " \"\"\"\n", + " # Normalize the direction vector\n", + " direction = np.array(direction) / np.linalg.norm(direction)\n", + "\n", + " # Compute right and up vectors to construct the base\n", + " up = np.array([0, 0, 1]) if abs(direction[2]) < 0.9 else np.array([1, 0, 0])\n", + " right = np.cross(direction, up)\n", + " up = np.cross(right, direction)\n", + " \n", + " right = right / np.linalg.norm(right)\n", + " up = up / np.linalg.norm(up)\n", + "\n", + " # Compute base size using FOV\n", + " angle = np.radians(fov / 2)\n", + " base_half_width = np.tan(angle) * depth\n", + " base_half_height = base_half_width / aspect_ratio\n", + "\n", + " # Compute base corners\n", + " base_center = position + depth * direction\n", + " corners = np.array([\n", + " base_center + base_half_width * right + base_half_height * up,\n", + " base_center - base_half_width * right + base_half_height * up,\n", + " base_center - base_half_width * right - base_half_height * up,\n", + " base_center + base_half_width * right - base_half_height * up,\n", + " ])\n", + " \n", + " # Pyramid vertices\n", + " vertices = np.vstack([position, corners])\n", + "\n", + " # Pyramid faces (indices)\n", + " faces = [\n", + " [0, 1, 2], # Side 1\n", + " [0, 2, 3], # Side 2\n", + " [0, 3, 4], # Side 3\n", + " [0, 4, 1], # Side 4\n", + " [1, 2, 3, 4] # Base\n", + " ]\n", + "\n", + " # Create Mesh3D\n", + " camera_mesh = go.Mesh3d(\n", + " x=vertices[:, 0], y=vertices[:, 1], z=vertices[:, 2],\n", + " i=[face[0] for face in faces],\n", + " j=[face[1] for face in faces],\n", + " k=[face[2] for face in faces],\n", + " color=color, opacity=0.5\n", + " )\n", + "\n", + " # Create direction line\n", + " direction_line = go.Scatter3d(\n", + " x=[position[0], base_center[0]],\n", + " y=[position[1], base_center[1]],\n", + " z=[position[2], base_center[2]],\n", + " mode=\"lines\",\n", + " line=dict(color=color, width=4),\n", + " name=\"Camera Direction\"\n", + " )\n", + "\n", + " return [camera_mesh, direction_line]\n", + "\n", + "# # Create figure\n", + "# fig = go.Figure()\n", + "\n", + "# # Add two cameras\n", + "# fig.add_traces(display_camera(position=(0, 0, 1), direction=(1, 1, -0.2), fov=60, color=\"red\"))\n", + "# fig.add_traces(display_camera(position=(2, 2, 1), direction=(-1, -1, -0.2), fov=45, color=\"blue\"))\n", + "\n", + "# fig.update_layout(scene=dict(aspectmode=\"data\"))\n", + "# fig.show()\n", + "\n", + "# # Create figure\n", + "# fig = go.Figure()\n", + "\n", + "# # Add plane\n", + "# fig.add_trace(display_plane((0, 0, 1), (2, 1), (0, 0, 0)))\n", + "\n", + "# # Add ball\n", + "# fig.add_trace(display_ball((0.5, 0.5, 0.5), 0.2, \"red\"))\n", + "\n", + "# # Add parallelepiped\n", + "# fig.add_trace(display_parallelepiped((1, 0.5, 0.3), (1, 1, 1), (1, 1, 0)))\n", + "\n", + "# fig.update_layout(scene=dict(aspectmode=\"data\"))\n", + "# fig.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [], + "source": [ + "import dash\n", + "from dash import dcc, html\n", + "from dash.dependencies import Input, Output\n", + "import plotly.graph_objects as go\n", + "import plotly.express as px\n", + "\n", + "class StereoScene:\n", + " def __init__(self, left_camera: Image, right_camera: Image):\n", + " self.left_camera = left_camera\n", + " self.right_camera = right_camera\n", + "\n", + " def project_ball_to_images(self, center, radius):\n", + " left_image = self.left_camera.project_ball_to_image(center, radius)\n", + " right_image = self.right_camera.project_ball_to_image(center, radius)\n", + " return left_image, right_image\n", + " \n", + " def launch_3D_scene(self):\n", + " app = dash.Dash(__name__)\n", + "\n", + " app.layout = html.Div([\n", + " dcc.Graph(id='3d-scene'),\n", + " dcc.Graph(id='left-image'),\n", + " dcc.Graph(id='right-image'),\n", + " html.Label('Ball X Position'),\n", + " dcc.Slider(id='ball-x', min=-2, max=2, step=0.05, value=0),\n", + " html.Label('Ball Y Position'),\n", + " dcc.Slider(id='ball-y', min=-2, max=2, step=0.05, value=0),\n", + " html.Label('Ball Z Position'),\n", + " dcc.Slider(id='ball-z', min=0, max=3, step=0.05, value=1),\n", + " ])\n", + "\n", + " @app.callback(\n", + " [Output('3d-scene', 'figure'),\n", + " Output('left-image', 'figure'),\n", + " Output('right-image', 'figure')],\n", + " [Input('ball-x', 'value'),\n", + " Input('ball-y', 'value'),\n", + " Input('ball-z', 'value')]\n", + " )\n", + " def update_scene(ball_x, ball_y, ball_z):\n", + " ball_center = np.array([ball_x, ball_y, ball_z]).reshape((3, 1))\n", + " left_image, right_image = self.project_ball_to_images(ball_center, 0.04)\n", + " \n", + " fig_3d = go.Figure()\n", + " fig_3d.add_trace(display_plane((0, 0, 1), (TABLE_LENGTH, TABLE_WIDTH), (0, 0, 0)))\n", + " fig_3d.add_trace(display_ball((ball_x, ball_y, ball_z), 0.04, \"green\"))\n", + "\n", + " fig_3d.add_trace(go.Cone(x=[0], y=[0], z=[0], u=[0], v=[0], w=[1], showscale=False, \n", + " sizemode=\"absolute\", sizeref=0.1, anchor=\"tip\", colorscale=[[0, \"red\"], [1, \"red\"]]))\n", + "\n", + " second_camera_direction = self.right_camera.camera_transformation(np.array([0, 0, 1])).flatten().tolist()\n", + " fig_3d.add_trace(go.Cone(x=[0], y=[0], z=[0], u=[second_camera_direction[0]], v=[second_camera_direction[1]], w=[second_camera_direction[2]], \n", + " sizemode=\"absolute\", sizeref=0.1, anchor=\"tip\",\n", + " colorscale=[[0, \"blue\"], [1, \"blue\"]]))\n", + " \n", + " fig_left = go.Figure(data=go.Heatmap(z=left_image))\n", + " fig_right = go.Figure(data=go.Heatmap(z=right_image))\n", + " \n", + " return fig_3d, fig_left, fig_right\n", + "\n", + " return app\n", + "\n", + " # def project_points_to_image(self, points):\n", + " # return self.left_camera.project_points_to_image(points), self.right_camera.project_points_to_image(points)\n", + "\n", + " # def project_point_to_image(self, point):\n", + " # return self.left_camera.project_point_to_image(point), self.right_camera.project_point_to_image(point)\n", + "\n", + " # def project_point_to_world(self, left_point, right_point):\n", + " # left_point = self.left_camera.camera_transformation.inverse_transform(left_point)\n", + " # right_point = self.right_camera.camera_transformation.inverse_transform(right_point)\n", + " # return left_point, right_point\n", + "\n", + " # def project_points_to_world(self, left_points, right_points):\n", + " # return self" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "camera_1_transform = Transformation(np.eye(3), np.array([0, 0, 0]))\n", + "image_1 = Image(K_1, camera_1_transform, distortion_coefs_1)\n", + "camera_2_transform = Transformation(cv2.Rodrigues(np.array(stereo_cam_rotation))[0], \n", + " np.array(stereo_cam_translation))\n", + "image_2 = Image(K_2, camera_2_transform, distortion_coefs_2)\n", + "\n", + "scene = StereoScene(image_1, image_2)\n", + "app = scene.launch_3D_scene()\n", + "app.run_server(host=\"localhost\", port=\"8060\",\n", + " debug=True, use_reloader=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from IPython.display import display, clear_output\n", + "\n", + "camera_1_transform = Transformation(np.eye(3), np.array([0, 0, 0]))\n", + "image_1 = Image(K_1, camera_1_transform, distortion_coefs_1)\n", + "camera_2_transform = Transformation(np.eye(3), np.array([0, 0, 0]))\n", + "# Transformation(cv2.Rodrigues(np.array(stereo_cam_rotation))[0], \n", + "# np.array(stereo_cam_translation))\n", + "image_2 = Image(K_2, camera_2_transform, distortion_coefs_2)\n", + "\n", + "\n", + "\n", + "ball_center = np.array([0, 0, 5]).reshape((3, 1))\n", + "image_with_ball = image_1.project_ball_to_image(ball_center, 0.25)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1],\n", + " [0],\n", + " [0]])" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.array([1, 0, 0]).reshape((3, 1))" ] }, { @@ -367,6 +803,4142 @@ ")\n", "evaluate_camera_position(world2master, master2second, get_mask_center, K_1, K_2)" ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "color": "lightblue", + "opacity": 0.5, + "type": "mesh3d", + "x": [ + 0, + 1, + 1, + 0 + ], + "y": [ + 0, + 0, + 1, + 1 + ], + "z": [ + 0, + 0, + 0, + 0 + ] + }, + { + "colorscale": [ + [ + 0, + "red" + ], + [ + 1, + "red" + ] + ], + "type": "surface", + "x": [ + [ + 0.5, + 0.5342020143325669, + 0.5642787609686539, + 0.5866025403784438, + 0.5984807753012208, + 0.5984807753012208, + 0.5866025403784438, + 0.5642787609686539, + 0.5342020143325669, + 0.5 + ], + [ + 0.5, + 0.532348854856634, + 0.5607959603993067, + 0.5819101758650076, + 0.5931448152559406, + 0.5931448152559406, + 0.5819101758650076, + 0.5607959603993067, + 0.532348854856634, + 0.5 + ], + [ + 0.5, + 0.5269901950127845, + 0.5507249741741725, + 0.5683415728292669, + 0.5777151691869572, + 0.5777151691869572, + 0.5683415728292669, + 0.5507249741741727, + 0.5269901950127845, + 0.5 + ], + [ + 0.5, + 0.5187067287432743, + 0.5351571499181971, + 0.547367099948713, + 0.5538638786614714, + 0.5538638786614714, + 0.547367099948713, + 0.5351571499181971, + 0.5187067287432743, + 0.5 + ], + [ + 0.5, + 0.5083960981496268, + 0.5157795029491969, + 0.5212596668124331, + 0.5241756010988238, + 0.5241756010988238, + 0.5212596668124331, + 0.515779502949197, + 0.5083960981496268, + 0.5 + ], + [ + 0.5, + 0.4971756200425813, + 0.49469190199143603, + 0.49284841889930686, + 0.49186752203401735, + 0.49186752203401735, + 0.49284841889930686, + 0.49469190199143603, + 0.4971756200425813, + 0.5 + ], + [ + 0.5, + 0.4862612073286926, + 0.47417951581652984, + 0.4652121557666551, + 0.46044072314522244, + 0.46044072314522244, + 0.4652121557666551, + 0.47417951581652984, + 0.4862612073286926, + 0.5 + ], + [ + 0.5, + 0.476835605980073, + 0.45646517974899475, + 0.44134569534570584, + 0.4333007857290677, + 0.4333007857290677, + 0.44134569534570584, + 0.4564651797489947, + 0.47683560598007296, + 0.5 + ], + [ + 0.5, + 0.46992022615611934, + 0.44346851696799267, + 0.42383533894935854, + 0.413388743124112, + 0.413388743124112, + 0.42383533894935854, + 0.44346851696799267, + 0.4699202261561193, + 0.5 + ], + [ + 0.5, + 0.46626445656393073, + 0.43659791754984645, + 0.4145786053943312, + 0.40286237411377723, + 0.40286237411377723, + 0.4145786053943312, + 0.43659791754984645, + 0.46626445656393073, + 0.5 + ], + [ + 0.5, + 0.46626445656393073, + 0.43659791754984645, + 0.4145786053943312, + 0.40286237411377723, + 0.40286237411377723, + 0.4145786053943312, + 0.43659791754984645, + 0.46626445656393073, + 0.5 + ], + [ + 0.5, + 0.4699202261561193, + 0.44346851696799267, + 0.42383533894935854, + 0.413388743124112, + 0.413388743124112, + 0.42383533894935854, + 0.44346851696799267, + 0.4699202261561193, + 0.5 + ], + [ + 0.5, + 0.47683560598007296, + 0.4564651797489947, + 0.4413456953457058, + 0.43330078572906766, + 0.43330078572906766, + 0.4413456953457058, + 0.4564651797489947, + 0.47683560598007296, + 0.5 + ], + [ + 0.5, + 0.4862612073286926, + 0.47417951581652984, + 0.46521215576665503, + 0.4604407231452224, + 0.4604407231452224, + 0.46521215576665503, + 0.4741795158165298, + 0.4862612073286926, + 0.5 + ], + [ + 0.5, + 0.4971756200425813, + 0.49469190199143603, + 0.49284841889930686, + 0.4918675220340173, + 0.4918675220340173, + 0.49284841889930686, + 0.49469190199143603, + 0.49717562004258126, + 0.5 + ], + [ + 0.5, + 0.5083960981496267, + 0.5157795029491969, + 0.521259666812433, + 0.5241756010988238, + 0.5241756010988238, + 0.521259666812433, + 0.5157795029491969, + 0.5083960981496267, + 0.5 + ], + [ + 0.5, + 0.5187067287432743, + 0.535157149918197, + 0.5473670999487129, + 0.5538638786614712, + 0.5538638786614712, + 0.5473670999487129, + 0.535157149918197, + 0.5187067287432743, + 0.5 + ], + [ + 0.5, + 0.5269901950127845, + 0.5507249741741725, + 0.5683415728292669, + 0.5777151691869571, + 0.5777151691869571, + 0.5683415728292669, + 0.5507249741741725, + 0.5269901950127845, + 0.5 + ], + [ + 0.5, + 0.532348854856634, + 0.5607959603993067, + 0.5819101758650076, + 0.5931448152559406, + 0.5931448152559406, + 0.5819101758650076, + 0.5607959603993067, + 0.532348854856634, + 0.5 + ], + [ + 0.5, + 0.5342020143325669, + 0.5642787609686539, + 0.5866025403784438, + 0.5984807753012208, + 0.5984807753012208, + 0.5866025403784438, + 0.5642787609686539, + 0.5342020143325669, + 0.5 + ] + ], + "y": [ + [ + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5 + ], + [ + 0.5, + 0.5111053758995154, + 0.5208712795676567, + 0.5281197988926579, + 0.5319766554671721, + 0.5319766554671721, + 0.5281197988926579, + 0.5208712795676567, + 0.5111053758995154, + 0.5 + ], + [ + 0.5, + 0.5210073120026568, + 0.5394808321428877, + 0.5531923812516605, + 0.5604881441455445, + 0.5604881441455445, + 0.5531923812516605, + 0.5394808321428877, + 0.5210073120026568, + 0.5 + ], + [ + 0.5, + 0.5286327798882795, + 0.5538120239472069, + 0.5725007437372103, + 0.5824448038354865, + 0.5824448038354865, + 0.5725007437372103, + 0.5538120239472069, + 0.5286327798882795, + 0.5 + ], + [ + 0.5, + 0.5331554417896511, + 0.5623118479772637, + 0.5839525256738851, + 0.5954672897669149, + 0.5954672897669149, + 0.5839525256738851, + 0.5623118479772637, + 0.5331554417896511, + 0.5 + ], + [ + 0.5, + 0.5340851971134281, + 0.564059216411043, + 0.5863067487961411, + 0.5981444135244709, + 0.5981444135244709, + 0.5863067487961411, + 0.564059216411043, + 0.5340851971134281, + 0.5 + ], + [ + 0.5, + 0.5313212924436387, + 0.5588647747655294, + 0.5793082964991465, + 0.5901860672091682, + 0.5901860672091682, + 0.5793082964991465, + 0.5588647747655295, + 0.5313212924436388, + 0.5 + ], + [ + 0.5, + 0.5251632397376546, + 0.5472914213930815, + 0.5637155596814565, + 0.5724546611307362, + 0.5724546611307362, + 0.5637155596814565, + 0.5472914213930815, + 0.5251632397376547, + 0.5 + ], + [ + 0.5, + 0.5162783595582018, + 0.530593308710684, + 0.5412182533235083, + 0.5468716682688859, + 0.5468716682688859, + 0.5412182533235083, + 0.530593308710684, + 0.5162783595582019, + 0.5 + ], + [ + 0.5, + 0.5056294665358446, + 0.5105799363253888, + 0.5142543096508607, + 0.5162094028612335, + 0.5162094028612335, + 0.5142543096508607, + 0.5105799363253888, + 0.5056294665358446, + 0.5 + ], + [ + 0.5, + 0.4943705334641554, + 0.48942006367461116, + 0.48574569034913934, + 0.48379059713876654, + 0.48379059713876654, + 0.48574569034913934, + 0.48942006367461116, + 0.4943705334641554, + 0.5 + ], + [ + 0.5, + 0.48372164044179816, + 0.46940669128931595, + 0.45878174667649174, + 0.4531283317311141, + 0.4531283317311141, + 0.45878174667649174, + 0.46940669128931595, + 0.48372164044179816, + 0.5 + ], + [ + 0.5, + 0.4748367602623454, + 0.4527085786069185, + 0.4362844403185435, + 0.42754533886926394, + 0.42754533886926394, + 0.4362844403185435, + 0.4527085786069185, + 0.4748367602623454, + 0.5 + ], + [ + 0.5, + 0.46867870755636126, + 0.4411352252344705, + 0.42069170350085355, + 0.40981393279083184, + 0.40981393279083184, + 0.4206917035008535, + 0.4411352252344705, + 0.46867870755636126, + 0.5 + ], + [ + 0.5, + 0.465914802886572, + 0.4359407835889571, + 0.41369325120385886, + 0.4018555864755291, + 0.4018555864755291, + 0.41369325120385886, + 0.4359407835889571, + 0.46591480288657194, + 0.5 + ], + [ + 0.5, + 0.46684455821034887, + 0.43768815202273625, + 0.4160474743261149, + 0.4045327102330851, + 0.4045327102330851, + 0.4160474743261149, + 0.4376881520227362, + 0.46684455821034887, + 0.5 + ], + [ + 0.5, + 0.47136722011172044, + 0.4461879760527931, + 0.42749925626278973, + 0.41755519616451353, + 0.41755519616451353, + 0.42749925626278973, + 0.4461879760527931, + 0.47136722011172044, + 0.5 + ], + [ + 0.5, + 0.4789926879973432, + 0.46051916785711233, + 0.44680761874833946, + 0.4395118558544555, + 0.4395118558544555, + 0.44680761874833946, + 0.46051916785711233, + 0.4789926879973432, + 0.5 + ], + [ + 0.5, + 0.48889462410048456, + 0.47912872043234334, + 0.4718802011073421, + 0.4680233445328279, + 0.4680233445328279, + 0.4718802011073421, + 0.47912872043234334, + 0.48889462410048456, + 0.5 + ], + [ + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5 + ] + ], + "z": [ + [ + 0.6, + 0.5939692620785908, + 0.5766044443118978, + 0.55, + 0.5173648177666931, + 0.482635182233307, + 0.45, + 0.4233955556881022, + 0.40603073792140915, + 0.4 + ], + [ + 0.6, + 0.5939692620785908, + 0.5766044443118978, + 0.55, + 0.5173648177666931, + 0.482635182233307, + 0.45, + 0.4233955556881022, + 0.40603073792140915, + 0.4 + ], + [ + 0.6, + 0.5939692620785908, + 0.5766044443118978, + 0.55, + 0.5173648177666931, + 0.482635182233307, + 0.45, + 0.4233955556881022, + 0.40603073792140915, + 0.4 + ], + [ + 0.6, + 0.5939692620785908, + 0.5766044443118978, + 0.55, + 0.5173648177666931, + 0.482635182233307, + 0.45, + 0.4233955556881022, + 0.40603073792140915, + 0.4 + ], + [ + 0.6, + 0.5939692620785908, + 0.5766044443118978, + 0.55, + 0.5173648177666931, + 0.482635182233307, + 0.45, + 0.4233955556881022, + 0.40603073792140915, + 0.4 + ], + [ + 0.6, + 0.5939692620785908, + 0.5766044443118978, + 0.55, + 0.5173648177666931, + 0.482635182233307, + 0.45, + 0.4233955556881022, + 0.40603073792140915, + 0.4 + ], + [ + 0.6, + 0.5939692620785908, + 0.5766044443118978, + 0.55, + 0.5173648177666931, + 0.482635182233307, + 0.45, + 0.4233955556881022, + 0.40603073792140915, + 0.4 + ], + [ + 0.6, + 0.5939692620785908, + 0.5766044443118978, + 0.55, + 0.5173648177666931, + 0.482635182233307, + 0.45, + 0.4233955556881022, + 0.40603073792140915, + 0.4 + ], + [ + 0.6, + 0.5939692620785908, + 0.5766044443118978, + 0.55, + 0.5173648177666931, + 0.482635182233307, + 0.45, + 0.4233955556881022, + 0.40603073792140915, + 0.4 + ], + [ + 0.6, + 0.5939692620785908, + 0.5766044443118978, + 0.55, + 0.5173648177666931, + 0.482635182233307, + 0.45, + 0.4233955556881022, + 0.40603073792140915, + 0.4 + ], + [ + 0.6, + 0.5939692620785908, + 0.5766044443118978, + 0.55, + 0.5173648177666931, + 0.482635182233307, + 0.45, + 0.4233955556881022, + 0.40603073792140915, + 0.4 + ], + [ + 0.6, + 0.5939692620785908, + 0.5766044443118978, + 0.55, + 0.5173648177666931, + 0.482635182233307, + 0.45, + 0.4233955556881022, + 0.40603073792140915, + 0.4 + ], + [ + 0.6, + 0.5939692620785908, + 0.5766044443118978, + 0.55, + 0.5173648177666931, + 0.482635182233307, + 0.45, + 0.4233955556881022, + 0.40603073792140915, + 0.4 + ], + [ + 0.6, + 0.5939692620785908, + 0.5766044443118978, + 0.55, + 0.5173648177666931, + 0.482635182233307, + 0.45, + 0.4233955556881022, + 0.40603073792140915, + 0.4 + ], + [ + 0.6, + 0.5939692620785908, + 0.5766044443118978, + 0.55, + 0.5173648177666931, + 0.482635182233307, + 0.45, + 0.4233955556881022, + 0.40603073792140915, + 0.4 + ], + [ + 0.6, + 0.5939692620785908, + 0.5766044443118978, + 0.55, + 0.5173648177666931, + 0.482635182233307, + 0.45, + 0.4233955556881022, + 0.40603073792140915, + 0.4 + ], + [ + 0.6, + 0.5939692620785908, + 0.5766044443118978, + 0.55, + 0.5173648177666931, + 0.482635182233307, + 0.45, + 0.4233955556881022, + 0.40603073792140915, + 0.4 + ], + [ + 0.6, + 0.5939692620785908, + 0.5766044443118978, + 0.55, + 0.5173648177666931, + 0.482635182233307, + 0.45, + 0.4233955556881022, + 0.40603073792140915, + 0.4 + ], + [ + 0.6, + 0.5939692620785908, + 0.5766044443118978, + 0.55, + 0.5173648177666931, + 0.482635182233307, + 0.45, + 0.4233955556881022, + 0.40603073792140915, + 0.4 + ], + [ + 0.6, + 0.5939692620785908, + 0.5766044443118978, + 0.55, + 0.5173648177666931, + 0.482635182233307, + 0.45, + 0.4233955556881022, + 0.40603073792140915, + 0.4 + ] + ] + }, + { + "colorscale": [ + [ + 0, + "blue" + ], + [ + 1, + "blue" + ] + ], + "type": "surface", + "x": [ + [ + 0.7, + 0.7342020143325668, + 0.7642787609686539, + 0.7866025403784438, + 0.7984807753012207, + 0.7984807753012207, + 0.7866025403784438, + 0.7642787609686539, + 0.7342020143325668, + 0.7 + ], + [ + 0.7, + 0.7323488548566339, + 0.7607959603993066, + 0.7819101758650076, + 0.7931448152559406, + 0.7931448152559406, + 0.7819101758650076, + 0.7607959603993066, + 0.7323488548566339, + 0.7 + ], + [ + 0.7, + 0.7269901950127845, + 0.7507249741741725, + 0.7683415728292669, + 0.7777151691869572, + 0.7777151691869572, + 0.7683415728292669, + 0.7507249741741726, + 0.7269901950127845, + 0.7 + ], + [ + 0.7, + 0.7187067287432742, + 0.735157149918197, + 0.747367099948713, + 0.7538638786614713, + 0.7538638786614713, + 0.747367099948713, + 0.735157149918197, + 0.7187067287432742, + 0.7 + ], + [ + 0.7, + 0.7083960981496268, + 0.7157795029491969, + 0.721259666812433, + 0.7241756010988237, + 0.7241756010988237, + 0.721259666812433, + 0.715779502949197, + 0.7083960981496268, + 0.7 + ], + [ + 0.7, + 0.6971756200425813, + 0.694691901991436, + 0.6928484188993068, + 0.6918675220340174, + 0.6918675220340174, + 0.6928484188993068, + 0.694691901991436, + 0.6971756200425813, + 0.7 + ], + [ + 0.7, + 0.6862612073286926, + 0.6741795158165298, + 0.665212155766655, + 0.6604407231452224, + 0.6604407231452224, + 0.665212155766655, + 0.6741795158165298, + 0.6862612073286926, + 0.7 + ], + [ + 0.7, + 0.676835605980073, + 0.6564651797489947, + 0.6413456953457058, + 0.6333007857290677, + 0.6333007857290677, + 0.6413456953457058, + 0.6564651797489947, + 0.676835605980073, + 0.7 + ], + [ + 0.7, + 0.6699202261561192, + 0.6434685169679927, + 0.6238353389493585, + 0.613388743124112, + 0.613388743124112, + 0.6238353389493585, + 0.6434685169679926, + 0.6699202261561192, + 0.7 + ], + [ + 0.7, + 0.6662644565639307, + 0.6365979175498464, + 0.6145786053943312, + 0.6028623741137772, + 0.6028623741137772, + 0.6145786053943312, + 0.6365979175498464, + 0.6662644565639306, + 0.7 + ], + [ + 0.7, + 0.6662644565639307, + 0.6365979175498464, + 0.6145786053943312, + 0.6028623741137772, + 0.6028623741137772, + 0.6145786053943312, + 0.6365979175498464, + 0.6662644565639306, + 0.7 + ], + [ + 0.7, + 0.6699202261561192, + 0.6434685169679927, + 0.6238353389493585, + 0.613388743124112, + 0.613388743124112, + 0.6238353389493585, + 0.6434685169679926, + 0.6699202261561192, + 0.7 + ], + [ + 0.7, + 0.676835605980073, + 0.6564651797489947, + 0.6413456953457057, + 0.6333007857290677, + 0.6333007857290677, + 0.6413456953457057, + 0.6564651797489947, + 0.6768356059800729, + 0.7 + ], + [ + 0.7, + 0.6862612073286926, + 0.6741795158165298, + 0.6652121557666549, + 0.6604407231452224, + 0.6604407231452224, + 0.6652121557666549, + 0.6741795158165298, + 0.6862612073286926, + 0.7 + ], + [ + 0.7, + 0.6971756200425813, + 0.6946919019914359, + 0.6928484188993068, + 0.6918675220340172, + 0.6918675220340172, + 0.6928484188993068, + 0.6946919019914359, + 0.6971756200425813, + 0.7 + ], + [ + 0.7, + 0.7083960981496267, + 0.7157795029491969, + 0.7212596668124329, + 0.7241756010988237, + 0.7241756010988237, + 0.7212596668124329, + 0.7157795029491969, + 0.7083960981496267, + 0.7 + ], + [ + 0.7, + 0.7187067287432742, + 0.7351571499181969, + 0.7473670999487129, + 0.7538638786614712, + 0.7538638786614712, + 0.7473670999487129, + 0.7351571499181969, + 0.7187067287432742, + 0.7 + ], + [ + 0.7, + 0.7269901950127845, + 0.7507249741741725, + 0.7683415728292669, + 0.777715169186957, + 0.777715169186957, + 0.7683415728292669, + 0.7507249741741725, + 0.7269901950127845, + 0.7 + ], + [ + 0.7, + 0.7323488548566339, + 0.7607959603993066, + 0.7819101758650076, + 0.7931448152559406, + 0.7931448152559406, + 0.7819101758650076, + 0.7607959603993066, + 0.7323488548566339, + 0.7 + ], + [ + 0.7, + 0.7342020143325668, + 0.7642787609686539, + 0.7866025403784438, + 0.7984807753012207, + 0.7984807753012207, + 0.7866025403784438, + 0.7642787609686539, + 0.7342020143325668, + 0.7 + ] + ], + "y": [ + [ + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3 + ], + [ + 0.3, + 0.3111053758995154, + 0.32087127956765665, + 0.32811979889265785, + 0.3319766554671721, + 0.3319766554671721, + 0.32811979889265785, + 0.32087127956765665, + 0.3111053758995154, + 0.3 + ], + [ + 0.3, + 0.3210073120026568, + 0.33948083214288766, + 0.35319238125166047, + 0.3604881441455445, + 0.3604881441455445, + 0.35319238125166047, + 0.33948083214288766, + 0.3210073120026568, + 0.3 + ], + [ + 0.3, + 0.32863277988827955, + 0.3538120239472069, + 0.37250074373721026, + 0.3824448038354864, + 0.3824448038354864, + 0.37250074373721026, + 0.3538120239472069, + 0.32863277988827955, + 0.3 + ], + [ + 0.3, + 0.3331554417896511, + 0.36231184797726373, + 0.38395252567388505, + 0.39546728976691486, + 0.39546728976691486, + 0.38395252567388505, + 0.3623118479772638, + 0.3331554417896511, + 0.3 + ], + [ + 0.3, + 0.334085197113428, + 0.3640592164110429, + 0.38630674879614113, + 0.3981444135244709, + 0.3981444135244709, + 0.38630674879614113, + 0.3640592164110429, + 0.334085197113428, + 0.3 + ], + [ + 0.3, + 0.3313212924436387, + 0.35886477476552947, + 0.3793082964991465, + 0.3901860672091682, + 0.3901860672091682, + 0.3793082964991465, + 0.35886477476552947, + 0.3313212924436387, + 0.3 + ], + [ + 0.3, + 0.3251632397376546, + 0.3472914213930815, + 0.3637155596814565, + 0.3724546611307361, + 0.3724546611307361, + 0.3637155596814565, + 0.34729142139308156, + 0.3251632397376546, + 0.3 + ], + [ + 0.3, + 0.3162783595582018, + 0.33059330871068404, + 0.34121825332350825, + 0.3468716682688859, + 0.3468716682688859, + 0.34121825332350825, + 0.33059330871068404, + 0.3162783595582018, + 0.3 + ], + [ + 0.3, + 0.3056294665358446, + 0.31057993632538883, + 0.3142543096508607, + 0.31620940286123345, + 0.31620940286123345, + 0.3142543096508607, + 0.31057993632538883, + 0.3056294665358446, + 0.3 + ], + [ + 0.3, + 0.29437053346415537, + 0.28942006367461115, + 0.2857456903491393, + 0.28379059713876653, + 0.28379059713876653, + 0.2857456903491393, + 0.28942006367461115, + 0.29437053346415537, + 0.3 + ], + [ + 0.3, + 0.28372164044179815, + 0.26940669128931594, + 0.25878174667649173, + 0.25312833173111415, + 0.25312833173111415, + 0.25878174667649173, + 0.26940669128931594, + 0.28372164044179815, + 0.3 + ], + [ + 0.3, + 0.2748367602623454, + 0.2527085786069185, + 0.2362844403185435, + 0.2275453388692639, + 0.2275453388692639, + 0.2362844403185435, + 0.2527085786069185, + 0.2748367602623454, + 0.3 + ], + [ + 0.3, + 0.26867870755636125, + 0.24113522523447053, + 0.22069170350085354, + 0.20981393279083183, + 0.20981393279083183, + 0.22069170350085351, + 0.2411352252344705, + 0.26867870755636125, + 0.3 + ], + [ + 0.3, + 0.265914802886572, + 0.2359407835889571, + 0.21369325120385885, + 0.2018555864755291, + 0.2018555864755291, + 0.21369325120385885, + 0.2359407835889571, + 0.265914802886572, + 0.3 + ], + [ + 0.3, + 0.26684455821034886, + 0.23768815202273622, + 0.2160474743261149, + 0.20453271023308509, + 0.20453271023308509, + 0.21604747432611487, + 0.2376881520227362, + 0.26684455821034886, + 0.3 + ], + [ + 0.3, + 0.27136722011172043, + 0.24618797605279308, + 0.22749925626278972, + 0.21755519616451355, + 0.21755519616451355, + 0.2274992562627897, + 0.24618797605279308, + 0.27136722011172043, + 0.3 + ], + [ + 0.3, + 0.27899268799734317, + 0.2605191678571123, + 0.24680761874833948, + 0.2395118558544555, + 0.2395118558544555, + 0.24680761874833945, + 0.26051916785711227, + 0.27899268799734317, + 0.3 + ], + [ + 0.3, + 0.28889462410048455, + 0.2791287204323433, + 0.27188020110734207, + 0.2680233445328279, + 0.2680233445328279, + 0.27188020110734207, + 0.2791287204323433, + 0.28889462410048455, + 0.3 + ], + [ + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3 + ] + ], + "z": [ + [ + 0.30000000000000004, + 0.29396926207859086, + 0.2766044443118978, + 0.25, + 0.21736481776669306, + 0.182635182233307, + 0.15000000000000002, + 0.12339555568810222, + 0.10603073792140917, + 0.1 + ], + [ + 0.30000000000000004, + 0.29396926207859086, + 0.2766044443118978, + 0.25, + 0.21736481776669306, + 0.182635182233307, + 0.15000000000000002, + 0.12339555568810222, + 0.10603073792140917, + 0.1 + ], + [ + 0.30000000000000004, + 0.29396926207859086, + 0.2766044443118978, + 0.25, + 0.21736481776669306, + 0.182635182233307, + 0.15000000000000002, + 0.12339555568810222, + 0.10603073792140917, + 0.1 + ], + [ + 0.30000000000000004, + 0.29396926207859086, + 0.2766044443118978, + 0.25, + 0.21736481776669306, + 0.182635182233307, + 0.15000000000000002, + 0.12339555568810222, + 0.10603073792140917, + 0.1 + ], + [ + 0.30000000000000004, + 0.29396926207859086, + 0.2766044443118978, + 0.25, + 0.21736481776669306, + 0.182635182233307, + 0.15000000000000002, + 0.12339555568810222, + 0.10603073792140917, + 0.1 + ], + [ + 0.30000000000000004, + 0.29396926207859086, + 0.2766044443118978, + 0.25, + 0.21736481776669306, + 0.182635182233307, + 0.15000000000000002, + 0.12339555568810222, + 0.10603073792140917, + 0.1 + ], + [ + 0.30000000000000004, + 0.29396926207859086, + 0.2766044443118978, + 0.25, + 0.21736481776669306, + 0.182635182233307, + 0.15000000000000002, + 0.12339555568810222, + 0.10603073792140917, + 0.1 + ], + [ + 0.30000000000000004, + 0.29396926207859086, + 0.2766044443118978, + 0.25, + 0.21736481776669306, + 0.182635182233307, + 0.15000000000000002, + 0.12339555568810222, + 0.10603073792140917, + 0.1 + ], + [ + 0.30000000000000004, + 0.29396926207859086, + 0.2766044443118978, + 0.25, + 0.21736481776669306, + 0.182635182233307, + 0.15000000000000002, + 0.12339555568810222, + 0.10603073792140917, + 0.1 + ], + [ + 0.30000000000000004, + 0.29396926207859086, + 0.2766044443118978, + 0.25, + 0.21736481776669306, + 0.182635182233307, + 0.15000000000000002, + 0.12339555568810222, + 0.10603073792140917, + 0.1 + ], + [ + 0.30000000000000004, + 0.29396926207859086, + 0.2766044443118978, + 0.25, + 0.21736481776669306, + 0.182635182233307, + 0.15000000000000002, + 0.12339555568810222, + 0.10603073792140917, + 0.1 + ], + [ + 0.30000000000000004, + 0.29396926207859086, + 0.2766044443118978, + 0.25, + 0.21736481776669306, + 0.182635182233307, + 0.15000000000000002, + 0.12339555568810222, + 0.10603073792140917, + 0.1 + ], + [ + 0.30000000000000004, + 0.29396926207859086, + 0.2766044443118978, + 0.25, + 0.21736481776669306, + 0.182635182233307, + 0.15000000000000002, + 0.12339555568810222, + 0.10603073792140917, + 0.1 + ], + [ + 0.30000000000000004, + 0.29396926207859086, + 0.2766044443118978, + 0.25, + 0.21736481776669306, + 0.182635182233307, + 0.15000000000000002, + 0.12339555568810222, + 0.10603073792140917, + 0.1 + ], + [ + 0.30000000000000004, + 0.29396926207859086, + 0.2766044443118978, + 0.25, + 0.21736481776669306, + 0.182635182233307, + 0.15000000000000002, + 0.12339555568810222, + 0.10603073792140917, + 0.1 + ], + [ + 0.30000000000000004, + 0.29396926207859086, + 0.2766044443118978, + 0.25, + 0.21736481776669306, + 0.182635182233307, + 0.15000000000000002, + 0.12339555568810222, + 0.10603073792140917, + 0.1 + ], + [ + 0.30000000000000004, + 0.29396926207859086, + 0.2766044443118978, + 0.25, + 0.21736481776669306, + 0.182635182233307, + 0.15000000000000002, + 0.12339555568810222, + 0.10603073792140917, + 0.1 + ], + [ + 0.30000000000000004, + 0.29396926207859086, + 0.2766044443118978, + 0.25, + 0.21736481776669306, + 0.182635182233307, + 0.15000000000000002, + 0.12339555568810222, + 0.10603073792140917, + 0.1 + ], + [ + 0.30000000000000004, + 0.29396926207859086, + 0.2766044443118978, + 0.25, + 0.21736481776669306, + 0.182635182233307, + 0.15000000000000002, + 0.12339555568810222, + 0.10603073792140917, + 0.1 + ], + [ + 0.30000000000000004, + 0.29396926207859086, + 0.2766044443118978, + 0.25, + 0.21736481776669306, + 0.182635182233307, + 0.15000000000000002, + 0.12339555568810222, + 0.10603073792140917, + 0.1 + ] + ] + } + ], + "layout": { + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmapgl" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "# Add a table (as a plane)\n", + "fig.add_trace(go.Mesh3d(x=[0, 1, 1, 0], y=[0, 0, 1, 1], z=[0, 0, 0, 0], color='lightblue', opacity=0.50))\n", + "\n", + "# Add some spheres (balls)\n", + "import numpy as np\n", + "def create_sphere(center, radius, color):\n", + " u, v = np.mgrid[0:2*np.pi:20j, 0:np.pi:10j]\n", + " x = center[0] + radius * np.cos(u) * np.sin(v)\n", + " y = center[1] + radius * np.sin(u) * np.sin(v)\n", + " z = center[2] + radius * np.cos(v)\n", + " return go.Surface(x=x, y=y, z=z, colorscale=[[0, color], [1, color]])\n", + "\n", + "fig.add_trace(create_sphere([0.5, 0.5, 0.5], 0.1, 'red'))\n", + "fig.add_trace(create_sphere([0.7, 0.3, 0.2], 0.1, 'blue'))\n", + "\n", + "fig.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "alphahull": 0, + "color": "lightblue", + "opacity": 0.5, + "type": "mesh3d", + "x": [ + 1, + -1, + -1, + 1, + 1 + ], + "y": [ + 0.5, + 0.5, + -0.5, + -0.5, + 0.5 + ], + "z": [ + 0, + 0, + 0, + 0, + 0 + ] + }, + { + "colorscale": [ + [ + 0, + "red" + ], + [ + 1, + "red" + ] + ], + "showscale": false, + "type": "surface", + "x": [ + [ + 0.5, + 0.5684040286651337, + 0.6285575219373079, + 0.6732050807568877, + 0.6969615506024416, + 0.6969615506024416, + 0.6732050807568878, + 0.6285575219373079, + 0.5684040286651337, + 0.5 + ], + [ + 0.5, + 0.5646977097132679, + 0.6215919207986134, + 0.6638203517300152, + 0.6862896305118813, + 0.6862896305118813, + 0.6638203517300152, + 0.6215919207986134, + 0.5646977097132679, + 0.5 + ], + [ + 0.5, + 0.5539803900255692, + 0.6014499483483452, + 0.6366831456585339, + 0.6554303383739144, + 0.6554303383739144, + 0.6366831456585339, + 0.6014499483483452, + 0.5539803900255692, + 0.5 + ], + [ + 0.5, + 0.5374134574865486, + 0.570314299836394, + 0.5947341998974259, + 0.6077277573229427, + 0.6077277573229427, + 0.594734199897426, + 0.570314299836394, + 0.5374134574865486, + 0.5 + ], + [ + 0.5, + 0.5167921962992535, + 0.531559005898394, + 0.542519333624866, + 0.5483512021976475, + 0.5483512021976475, + 0.542519333624866, + 0.531559005898394, + 0.5167921962992535, + 0.5 + ], + [ + 0.5, + 0.4943512400851626, + 0.4893838039828721, + 0.4856968377986138, + 0.4837350440680347, + 0.4837350440680347, + 0.4856968377986138, + 0.4893838039828721, + 0.4943512400851626, + 0.5 + ], + [ + 0.5, + 0.4725224146573852, + 0.4483590316330597, + 0.4304243115333102, + 0.42088144629044494, + 0.42088144629044494, + 0.4304243115333101, + 0.4483590316330597, + 0.4725224146573852, + 0.5 + ], + [ + 0.5, + 0.453671211960146, + 0.4129303594979895, + 0.3826913906914117, + 0.36660157145813543, + 0.36660157145813543, + 0.38269139069141167, + 0.41293035949798945, + 0.453671211960146, + 0.5 + ], + [ + 0.5, + 0.4398404523122386, + 0.38693703393598533, + 0.3476706778987171, + 0.326777486248224, + 0.326777486248224, + 0.3476706778987171, + 0.38693703393598533, + 0.4398404523122386, + 0.5 + ], + [ + 0.5, + 0.4325289131278615, + 0.37319583509969295, + 0.32915721078866245, + 0.30572474822755447, + 0.30572474822755447, + 0.32915721078866245, + 0.3731958350996929, + 0.43252891312786146, + 0.5 + ], + [ + 0.5, + 0.4325289131278615, + 0.37319583509969295, + 0.32915721078866245, + 0.30572474822755447, + 0.30572474822755447, + 0.32915721078866245, + 0.3731958350996929, + 0.43252891312786146, + 0.5 + ], + [ + 0.5, + 0.4398404523122386, + 0.38693703393598533, + 0.3476706778987171, + 0.32677748624822395, + 0.32677748624822395, + 0.3476706778987171, + 0.38693703393598533, + 0.43984045231223856, + 0.5 + ], + [ + 0.5, + 0.4536712119601459, + 0.4129303594979894, + 0.3826913906914116, + 0.3666015714581353, + 0.3666015714581353, + 0.3826913906914116, + 0.4129303594979894, + 0.4536712119601459, + 0.5 + ], + [ + 0.5, + 0.4725224146573852, + 0.4483590316330596, + 0.43042431153331007, + 0.42088144629044477, + 0.42088144629044477, + 0.43042431153331, + 0.4483590316330596, + 0.47252241465738515, + 0.5 + ], + [ + 0.5, + 0.49435124008516257, + 0.48938380398287207, + 0.48569683779861367, + 0.48373504406803464, + 0.48373504406803464, + 0.48569683779861367, + 0.48938380398287207, + 0.49435124008516257, + 0.5 + ], + [ + 0.5, + 0.5167921962992535, + 0.531559005898394, + 0.5425193336248659, + 0.5483512021976474, + 0.5483512021976474, + 0.5425193336248659, + 0.531559005898394, + 0.5167921962992535, + 0.5 + ], + [ + 0.5, + 0.5374134574865486, + 0.5703142998363939, + 0.5947341998974259, + 0.6077277573229426, + 0.6077277573229426, + 0.5947341998974259, + 0.570314299836394, + 0.5374134574865486, + 0.5 + ], + [ + 0.5, + 0.5539803900255691, + 0.6014499483483451, + 0.6366831456585338, + 0.6554303383739143, + 0.6554303383739143, + 0.6366831456585338, + 0.6014499483483452, + 0.5539803900255692, + 0.5 + ], + [ + 0.5, + 0.5646977097132679, + 0.6215919207986134, + 0.6638203517300152, + 0.6862896305118813, + 0.6862896305118813, + 0.6638203517300152, + 0.6215919207986134, + 0.5646977097132679, + 0.5 + ], + [ + 0.5, + 0.5684040286651337, + 0.6285575219373079, + 0.6732050807568877, + 0.6969615506024416, + 0.6969615506024416, + 0.6732050807568878, + 0.6285575219373079, + 0.5684040286651337, + 0.5 + ] + ], + "y": [ + [ + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5 + ], + [ + 0.5, + 0.5222107517990309, + 0.5417425591353133, + 0.5562395977853157, + 0.5639533109343442, + 0.5639533109343442, + 0.5562395977853158, + 0.5417425591353133, + 0.5222107517990309, + 0.5 + ], + [ + 0.5, + 0.5420146240053136, + 0.5789616642857753, + 0.606384762503321, + 0.620976288291089, + 0.620976288291089, + 0.606384762503321, + 0.5789616642857753, + 0.5420146240053136, + 0.5 + ], + [ + 0.5, + 0.5572655597765591, + 0.6076240478944138, + 0.6450014874744205, + 0.6648896076709728, + 0.6648896076709728, + 0.6450014874744205, + 0.6076240478944138, + 0.5572655597765591, + 0.5 + ], + [ + 0.5, + 0.5663108835793023, + 0.6246236959545275, + 0.6679050513477701, + 0.6909345795338298, + 0.6909345795338298, + 0.6679050513477702, + 0.6246236959545276, + 0.5663108835793023, + 0.5 + ], + [ + 0.5, + 0.568170394226856, + 0.6281184328220858, + 0.6726134975922823, + 0.6962888270489418, + 0.6962888270489418, + 0.6726134975922823, + 0.6281184328220858, + 0.5681703942268561, + 0.5 + ], + [ + 0.5, + 0.5626425848872775, + 0.617729549531059, + 0.658616592998293, + 0.6803721344183364, + 0.6803721344183364, + 0.658616592998293, + 0.617729549531059, + 0.5626425848872775, + 0.5 + ], + [ + 0.5, + 0.5503264794753092, + 0.594582842786163, + 0.6274311193629131, + 0.6449093222614724, + 0.6449093222614724, + 0.6274311193629131, + 0.5945828427861631, + 0.5503264794753092, + 0.5 + ], + [ + 0.5, + 0.5325567191164037, + 0.5611866174213681, + 0.5824365066470165, + 0.5937433365377718, + 0.5937433365377718, + 0.5824365066470165, + 0.5611866174213681, + 0.5325567191164037, + 0.5 + ], + [ + 0.5, + 0.5112589330716892, + 0.5211598726507777, + 0.5285086193017214, + 0.5324188057224669, + 0.5324188057224669, + 0.5285086193017214, + 0.5211598726507777, + 0.5112589330716892, + 0.5 + ], + [ + 0.5, + 0.48874106692831076, + 0.4788401273492224, + 0.4714913806982787, + 0.4675811942775331, + 0.4675811942775331, + 0.4714913806982787, + 0.4788401273492224, + 0.48874106692831076, + 0.5 + ], + [ + 0.5, + 0.4674432808835963, + 0.43881338257863195, + 0.4175634933529835, + 0.4062566634622283, + 0.4062566634622283, + 0.4175634933529835, + 0.4388133825786319, + 0.4674432808835963, + 0.5 + ], + [ + 0.5, + 0.44967352052469084, + 0.405417157213837, + 0.37256888063708704, + 0.3550906777385278, + 0.3550906777385278, + 0.37256888063708704, + 0.405417157213837, + 0.4496735205246908, + 0.5 + ], + [ + 0.5, + 0.4373574151127226, + 0.3822704504689411, + 0.3413834070017071, + 0.3196278655816637, + 0.3196278655816637, + 0.34138340700170705, + 0.38227045046894104, + 0.4373574151127225, + 0.5 + ], + [ + 0.5, + 0.431829605773144, + 0.3718815671779142, + 0.3273865024077177, + 0.3037111729510582, + 0.3037111729510582, + 0.3273865024077177, + 0.3718815671779142, + 0.43182960577314394, + 0.5 + ], + [ + 0.5, + 0.43368911642069774, + 0.37537630404547245, + 0.3320949486522298, + 0.3090654204661702, + 0.3090654204661702, + 0.33209494865222977, + 0.3753763040454724, + 0.43368911642069774, + 0.5 + ], + [ + 0.5, + 0.44273444022344094, + 0.3923759521055862, + 0.35499851252557946, + 0.3351103923290271, + 0.3351103923290271, + 0.3549985125255794, + 0.3923759521055862, + 0.4427344402234409, + 0.5 + ], + [ + 0.5, + 0.45798537599468636, + 0.42103833571422467, + 0.393615237496679, + 0.379023711708911, + 0.379023711708911, + 0.3936152374966789, + 0.4210383357142246, + 0.45798537599468636, + 0.5 + ], + [ + 0.5, + 0.47778924820096913, + 0.45825744086468667, + 0.44376040221468416, + 0.43604668906565575, + 0.43604668906565575, + 0.44376040221468416, + 0.4582574408646866, + 0.4777892482009691, + 0.5 + ], + [ + 0.5, + 0.5, + 0.49999999999999994, + 0.49999999999999994, + 0.49999999999999994, + 0.49999999999999994, + 0.49999999999999994, + 0.49999999999999994, + 0.5, + 0.5 + ] + ], + "z": [ + [ + 0.7, + 0.6879385241571817, + 0.6532088886237957, + 0.6000000000000001, + 0.534729635533386, + 0.46527036446661396, + 0.4, + 0.34679111137620444, + 0.3120614758428183, + 0.3 + ], + [ + 0.7, + 0.6879385241571817, + 0.6532088886237957, + 0.6000000000000001, + 0.534729635533386, + 0.46527036446661396, + 0.4, + 0.34679111137620444, + 0.3120614758428183, + 0.3 + ], + [ + 0.7, + 0.6879385241571817, + 0.6532088886237957, + 0.6000000000000001, + 0.534729635533386, + 0.46527036446661396, + 0.4, + 0.34679111137620444, + 0.3120614758428183, + 0.3 + ], + [ + 0.7, + 0.6879385241571817, + 0.6532088886237957, + 0.6000000000000001, + 0.534729635533386, + 0.46527036446661396, + 0.4, + 0.34679111137620444, + 0.3120614758428183, + 0.3 + ], + [ + 0.7, + 0.6879385241571817, + 0.6532088886237957, + 0.6000000000000001, + 0.534729635533386, + 0.46527036446661396, + 0.4, + 0.34679111137620444, + 0.3120614758428183, + 0.3 + ], + [ + 0.7, + 0.6879385241571817, + 0.6532088886237957, + 0.6000000000000001, + 0.534729635533386, + 0.46527036446661396, + 0.4, + 0.34679111137620444, + 0.3120614758428183, + 0.3 + ], + [ + 0.7, + 0.6879385241571817, + 0.6532088886237957, + 0.6000000000000001, + 0.534729635533386, + 0.46527036446661396, + 0.4, + 0.34679111137620444, + 0.3120614758428183, + 0.3 + ], + [ + 0.7, + 0.6879385241571817, + 0.6532088886237957, + 0.6000000000000001, + 0.534729635533386, + 0.46527036446661396, + 0.4, + 0.34679111137620444, + 0.3120614758428183, + 0.3 + ], + [ + 0.7, + 0.6879385241571817, + 0.6532088886237957, + 0.6000000000000001, + 0.534729635533386, + 0.46527036446661396, + 0.4, + 0.34679111137620444, + 0.3120614758428183, + 0.3 + ], + [ + 0.7, + 0.6879385241571817, + 0.6532088886237957, + 0.6000000000000001, + 0.534729635533386, + 0.46527036446661396, + 0.4, + 0.34679111137620444, + 0.3120614758428183, + 0.3 + ], + [ + 0.7, + 0.6879385241571817, + 0.6532088886237957, + 0.6000000000000001, + 0.534729635533386, + 0.46527036446661396, + 0.4, + 0.34679111137620444, + 0.3120614758428183, + 0.3 + ], + [ + 0.7, + 0.6879385241571817, + 0.6532088886237957, + 0.6000000000000001, + 0.534729635533386, + 0.46527036446661396, + 0.4, + 0.34679111137620444, + 0.3120614758428183, + 0.3 + ], + [ + 0.7, + 0.6879385241571817, + 0.6532088886237957, + 0.6000000000000001, + 0.534729635533386, + 0.46527036446661396, + 0.4, + 0.34679111137620444, + 0.3120614758428183, + 0.3 + ], + [ + 0.7, + 0.6879385241571817, + 0.6532088886237957, + 0.6000000000000001, + 0.534729635533386, + 0.46527036446661396, + 0.4, + 0.34679111137620444, + 0.3120614758428183, + 0.3 + ], + [ + 0.7, + 0.6879385241571817, + 0.6532088886237957, + 0.6000000000000001, + 0.534729635533386, + 0.46527036446661396, + 0.4, + 0.34679111137620444, + 0.3120614758428183, + 0.3 + ], + [ + 0.7, + 0.6879385241571817, + 0.6532088886237957, + 0.6000000000000001, + 0.534729635533386, + 0.46527036446661396, + 0.4, + 0.34679111137620444, + 0.3120614758428183, + 0.3 + ], + [ + 0.7, + 0.6879385241571817, + 0.6532088886237957, + 0.6000000000000001, + 0.534729635533386, + 0.46527036446661396, + 0.4, + 0.34679111137620444, + 0.3120614758428183, + 0.3 + ], + [ + 0.7, + 0.6879385241571817, + 0.6532088886237957, + 0.6000000000000001, + 0.534729635533386, + 0.46527036446661396, + 0.4, + 0.34679111137620444, + 0.3120614758428183, + 0.3 + ], + [ + 0.7, + 0.6879385241571817, + 0.6532088886237957, + 0.6000000000000001, + 0.534729635533386, + 0.46527036446661396, + 0.4, + 0.34679111137620444, + 0.3120614758428183, + 0.3 + ], + [ + 0.7, + 0.6879385241571817, + 0.6532088886237957, + 0.6000000000000001, + 0.534729635533386, + 0.46527036446661396, + 0.4, + 0.34679111137620444, + 0.3120614758428183, + 0.3 + ] + ] + }, + { + "color": "orange", + "i": [ + 0, + 0, + 1, + 2, + 0, + 3 + ], + "j": [ + 1, + 1, + 4, + 4, + 2, + 5 + ], + "k": [ + 4, + 5, + 7, + 7, + 6, + 7 + ], + "opacity": 0.7, + "type": "mesh3d", + "x": [ + 1, + 1.816496580927726, + 1, + 1.1732050807568877, + 1.816496580927726, + 1.9897016616846137, + 1.1732050807568877, + 1.9897016616846137 + ], + "y": [ + 1, + 0.591751709536137, + 1.3535533905932737, + 1.1732050807568877, + 0.9453051001294108, + 0.7649567902930248, + 1.5267584713501614, + 1.1185101808862985 + ], + "z": [ + 0, + -0.408248290463863, + -0.3535533905932738, + 0.17320508075688776, + -0.7618016810571369, + -0.23504320970697526, + -0.18034830983638603, + -0.5885966003002491 + ] + } + ], + "layout": { + "scene": { + "aspectmode": "data" + }, + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmapgl" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "\n", + "def display_plane(normal, size, origin):\n", + " \"\"\"Display a plane in the shape of a rectangle.\n", + " \n", + " Args:\n", + " normal (tuple): Normal vector of the plane (nx, ny, nz).\n", + " size (tuple): Size of the rectangle (width, height).\n", + " origin (tuple): Origin of the rectangle (x, y, z).\n", + " \"\"\"\n", + " normal = np.array(normal) / np.linalg.norm(normal) # Normalize normal vector\n", + " u = np.array([1, 0, 0]) if abs(normal[0]) < 0.9 else np.array([0, 1, 0])\n", + " v = np.cross(normal, u)\n", + " u = np.cross(v, normal)\n", + " u, v = u / np.linalg.norm(u), v / np.linalg.norm(v)\n", + " \n", + " w, h = size\n", + " corners = np.array([\n", + " origin + w/2 * u + h/2 * v,\n", + " origin - w/2 * u + h/2 * v,\n", + " origin - w/2 * u - h/2 * v,\n", + " origin + w/2 * u - h/2 * v,\n", + " ])\n", + " \n", + " x, y, z = corners[:, 0], corners[:, 1], corners[:, 2]\n", + " \n", + " return go.Mesh3d(\n", + " x=[*x, x[0]], y=[*y, y[0]], z=[*z, z[0]],\n", + " color='lightblue', opacity=0.5, alphahull=0\n", + " )\n", + "\n", + "def display_ball(position, radius, color):\n", + " \"\"\"Display a ball (sphere).\n", + " \n", + " Args:\n", + " position (tuple): (x, y, z) coordinates of the center.\n", + " radius (float): Radius of the sphere.\n", + " color (str): Color of the sphere.\n", + " \"\"\"\n", + " u, v = np.mgrid[0:2*np.pi:20j, 0:np.pi:10j]\n", + " x = position[0] + radius * np.cos(u) * np.sin(v)\n", + " y = position[1] + radius * np.sin(u) * np.sin(v)\n", + " z = position[2] + radius * np.cos(v)\n", + " \n", + " return go.Surface(x=x, y=y, z=z, colorscale=[[0, color], [1, color]], showscale=False)\n", + "\n", + "def display_parallelepiped(size, direction, origin=(0,0,0)):\n", + " \"\"\"Display a parallelepiped.\n", + " \n", + " Args:\n", + " size (tuple): (width, height, depth).\n", + " direction (tuple): A vector representing the \"facing\" direction.\n", + " origin (tuple): Bottom-left corner of the parallelepiped.\n", + " \"\"\"\n", + " direction = np.array(direction) / np.linalg.norm(direction)\n", + " u = np.array([1, 0, 0]) if abs(direction[0]) < 0.9 else np.array([0, 1, 0])\n", + " v = np.cross(direction, u)\n", + " u = np.cross(v, direction)\n", + " u, v = u / np.linalg.norm(u), v / np.linalg.norm(v)\n", + "\n", + " w, h, d = size\n", + " corners = np.array([\n", + " origin,\n", + " origin + w * u,\n", + " origin + h * v,\n", + " origin + d * direction,\n", + " origin + w * u + h * v,\n", + " origin + w * u + d * direction,\n", + " origin + h * v + d * direction,\n", + " origin + w * u + h * v + d * direction\n", + " ])\n", + " \n", + " x, y, z = corners[:, 0], corners[:, 1], corners[:, 2]\n", + "\n", + " faces = [\n", + " [0, 1, 4, 2], # Bottom\n", + " [0, 1, 5, 3], # Side\n", + " [1, 4, 7, 5], # Side\n", + " [2, 4, 7, 6], # Side\n", + " [0, 2, 6, 3], # Side\n", + " [3, 5, 7, 6] # Top\n", + " ]\n", + "\n", + " return go.Mesh3d(\n", + " x=x, y=y, z=z,\n", + " i=[face[0] for face in faces],\n", + " j=[face[1] for face in faces],\n", + " k=[face[2] for face in faces],\n", + " color=\"orange\", opacity=0.7\n", + " )\n", + "\n", + "# Create figure\n", + "fig = go.Figure()\n", + "\n", + "# Add plane\n", + "fig.add_trace(display_plane((0, 0, 1), (2, 1), (0, 0, 0)))\n", + "\n", + "# Add ball\n", + "fig.add_trace(display_ball((0.5, 0.5, 0.5), 0.2, \"red\"))\n", + "\n", + "# Add parallelepiped\n", + "fig.add_trace(display_parallelepiped((1, 0.5, 0.3), (1, 1, 1), (1, 1, 0)))\n", + "\n", + "fig.update_layout(scene=dict(aspectmode=\"data\"))\n", + "fig.show()\n" + ] } ], "metadata": { From 3f04c9bda1bc24d47a18c06eff45fc2e67195a6b Mon Sep 17 00:00:00 2001 From: dfbakin Date: Tue, 4 Feb 2025 00:25:08 +0300 Subject: [PATCH 6/9] fixed camera placement --- research.ipynb | 4213 ++---------------------------------------------- 1 file changed, 123 insertions(+), 4090 deletions(-) diff --git a/research.ipynb b/research.ipynb index d1cb8c3..62e9319 100644 --- a/research.ipynb +++ b/research.ipynb @@ -32,7 +32,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -95,7 +95,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 83, "metadata": {}, "outputs": [], "source": [ @@ -139,10 +139,15 @@ "\n", " # in world coordinates\n", " def project_point_to_image(self, point):\n", - " transformed_point = self.camera_transformation(point)\n", - " transformed_point = transformed_point / transformed_point[2]\n", - " projected_point = self.camera_matrix @ transformed_point\n", - " return projected_point\n", + " if point.shape != (3, 1):\n", + " point = point.reshape((3, 1))\n", + " print(self.camera_transformation(point))\n", + " return self.camera_matrix @ self.camera_transformation(point)\n", + "\n", + " # transformed_point = self.camera_transformation(point)\n", + " # # transformed_point = transformed_point / transformed_point[2]\n", + " # projected_point = self.camera_matrix @ transformed_point\n", + " # return projected_point\n", " \n", " def project_points_to_image(self, points):\n", " return np.array([self.project_point_to_image(point) for point in points])\n", @@ -153,6 +158,9 @@ " camera_matrix_inv = np.linalg.inv(self.camera_matrix)\n", "\n", " # projecting center and some edge point to approximate radius after projection\n", + "\n", + " print(f\"projecting center\")\n", + "\n", " projected_center = self.project_point_to_image(center)\n", " projected_center /= projected_center[2]\n", " edge_point = center + np.array([radius, 0, 0]).reshape((3, 1))\n", @@ -173,10 +181,13 @@ " )\n", "\n", " image = np.zeros(self.image_size)\n", - " for x in range(x_start, x_stop):\n", - " for y in range(y_start, y_stop):\n", + " # for x in range(x_start, x_stop):\n", + " # for y in range(y_start, y_stop):\n", + " for x in range(IMAGE_SIZE[1]):\n", + " for y in range(IMAGE_SIZE[0]):\n", " # back project image point\n", - " world_ray = camera_matrix_inv @ np.array([x, y, 1]).reshape((3, 1))\n", + " cam_ray = camera_matrix_inv @ np.array([x, y, 1]).reshape((3, 1))\n", + " world_ray = self.camera_transformation.inverse_transform(cam_ray)\n", " # measure distance from the sphere center\n", " distance = np.linalg.norm(\n", " np.cross(world_ray.flatten(), center.flatten()), ord=2\n", @@ -197,7 +208,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -391,7 +402,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 81, "metadata": {}, "outputs": [], "source": [ @@ -401,6 +412,7 @@ "import plotly.graph_objects as go\n", "import plotly.express as px\n", "\n", + "\n", "class StereoScene:\n", " def __init__(self, left_camera: Image, right_camera: Image):\n", " self.left_camera = left_camera\n", @@ -419,7 +431,7 @@ " dcc.Graph(id='left-image'),\n", " dcc.Graph(id='right-image'),\n", " html.Label('Ball X Position'),\n", - " dcc.Slider(id='ball-x', min=-2, max=2, step=0.05, value=0),\n", + " dcc.Slider(id='ball-x', min=-2, max=2, step=0.05, value=0.13),\n", " html.Label('Ball Y Position'),\n", " dcc.Slider(id='ball-y', min=-2, max=2, step=0.05, value=0),\n", " html.Label('Ball Z Position'),\n", @@ -438,20 +450,32 @@ " ball_center = np.array([ball_x, ball_y, ball_z]).reshape((3, 1))\n", " left_image, right_image = self.project_ball_to_images(ball_center, 0.04)\n", " \n", - " fig_3d = go.Figure()\n", - " fig_3d.add_trace(display_plane((0, 0, 1), (TABLE_LENGTH, TABLE_WIDTH), (0, 0, 0)))\n", + " fig_3d = go.Figure(layout=go.Layout(\n", + " scene=dict(\n", + " aspectmode='data')))\n", + " # fig_3d.add_trace(display_plane((0, 0, 1), (TABLE_LENGTH, TABLE_WIDTH), (0, 0, 0)))\n", " fig_3d.add_trace(display_ball((ball_x, ball_y, ball_z), 0.04, \"green\"))\n", "\n", - " fig_3d.add_trace(go.Cone(x=[0], y=[0], z=[0], u=[0], v=[0], w=[1], showscale=False, \n", - " sizemode=\"absolute\", sizeref=0.1, anchor=\"tip\", colorscale=[[0, \"red\"], [1, \"red\"]]))\n", + " fig_3d.add_trace(go.Cone(x=[0], y=[0], z=[0], u=[0], v=[0], w=[1], colorscale=[[0, \"red\"], [1, \"red\"]]))\n", "\n", - " second_camera_direction = self.right_camera.camera_transformation(np.array([0, 0, 1])).flatten().tolist()\n", - " fig_3d.add_trace(go.Cone(x=[0], y=[0], z=[0], u=[second_camera_direction[0]], v=[second_camera_direction[1]], w=[second_camera_direction[2]], \n", - " sizemode=\"absolute\", sizeref=0.1, anchor=\"tip\",\n", + " second_camera_direction = self.right_camera.camera_transformation.R @ np.array([0, 0, 1]).reshape((3, 1))\n", + " # second_camera_direction = second_camera_direction / np.linalg.norm(second_camera_direction, ord=2) * 0.04\n", + " # print(second_camera_direction.shape)\n", + " # print(self.right_camera.camera_transformation.t.shape)\n", + " # print([*self.right_camera.camera_transformation.t.flatten().tolist()] + [*second_camera_direction.tolist()])\n", + " # print()\n", + " second_cam_cone_position_direction = dict(zip([\"x\", \"y\", \"z\", \"u\", \"v\", \"w\"], [*self.right_camera.camera_transformation.t.tolist()] + [*second_camera_direction.tolist()]))\n", + " fig_3d.add_trace(go.Cone(**second_cam_cone_position_direction,\n", " colorscale=[[0, \"blue\"], [1, \"blue\"]]))\n", - " \n", - " fig_left = go.Figure(data=go.Heatmap(z=left_image))\n", - " fig_right = go.Figure(data=go.Heatmap(z=right_image))\n", + "\n", + " fig_left = go.Figure(data=go.Heatmap(z=left_image),\n", + " layout=go.Layout(\n", + " scene=dict(\n", + " aspectmode='data')))\n", + " fig_right = go.Figure(data=go.Heatmap(z=right_image),\n", + " layout=go.Layout(\n", + " scene=dict(\n", + " aspectmode='data')))\n", " \n", " return fig_3d, fig_left, fig_right\n", "\n", @@ -474,7 +498,24 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1.638728271710918\n" + ] + } + ], + "source": [ + "print(np.linalg.norm(stereo_cam_translation, ord=2))" + ] + }, + { + "cell_type": "code", + "execution_count": 84, "metadata": {}, "outputs": [ { @@ -492,11 +533,45 @@ " " ], "text/plain": [ - "" + "" ] }, "metadata": {}, "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "projecting center\n", + "[[0.13]\n", + " [0. ]\n", + " [1. ]]\n", + "[[0.17]\n", + " [0. ]\n", + " [1. ]]\n", + "projecting center\n", + "[[0.13]\n", + " [0. ]\n", + " [1. ]]\n", + "[[0.17]\n", + " [0. ]\n", + " [1. ]]\n", + "projecting center\n", + "[[-0.15207418]\n", + " [-0.17019519]\n", + " [ 1.14703514]]\n", + "[[-0.19201122]\n", + " [-0.16840126]\n", + " [ 1.14568799]]\n", + "projecting center\n", + "[[-0.15207418]\n", + " [-0.17019519]\n", + " [ 1.14703514]]\n", + "[[-0.19201122]\n", + " [-0.16840126]\n", + " [ 1.14568799]]\n" + ] } ], "source": [ @@ -514,9 +589,23 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 78, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "projecting center\n", + "[[0.]\n", + " [0.]\n", + " [5.]]\n", + "[[0.25]\n", + " [0. ]\n", + " [5. ]]\n" + ] + } + ], "source": [ "import plotly.graph_objects as go\n", "from IPython.display import display, clear_output\n", @@ -535,28 +624,6 @@ "\n" ] }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[1],\n", - " [0],\n", - " [0]])" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.array([1, 0, 0]).reshape((3, 1))" - ] - }, { "cell_type": "code", "execution_count": 97, @@ -708,49 +775,9 @@ }, { "cell_type": "code", - "execution_count": 114, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0\n", - "20\n", - "40\n", - "60\n", - "80\n", - "100\n", - "120\n", - "triangulated_points shape is (3, 125)\n", - "[[-1.37 -0.7625 0. -1.37 -0.7625 ]\n", - " [ 0.25 -0.685 0.38125 0.5 -0.685 ]\n", - " [-0.38125 0.75 0.685 -0.38125 1. ]]\n", - "[[ 1.29940542 1.29940542 1.29940542 1.29940542 1.29940542]\n", - " [ 1.03681671 1.03681671 1.03681671 1.03681671 1.03681671]\n", - " [-1.40207606 -1.40207606 -1.40207606 -1.40207606 -1.40207606]]\n", - "precision of triangulated points:\n", - "2.744184139499578\n" - ] - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "evalution complete\n" - ] - } - ], + "outputs": [], "source": [ "def evaluate_camera_position(\n", " world2master: Transformation,\n", @@ -806,2342 +833,9 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.plotly.v1+json": { - "config": { - "plotlyServerURL": "https://plot.ly" - }, - "data": [ - { - "color": "lightblue", - "opacity": 0.5, - "type": "mesh3d", - "x": [ - 0, - 1, - 1, - 0 - ], - "y": [ - 0, - 0, - 1, - 1 - ], - "z": [ - 0, - 0, - 0, - 0 - ] - }, - { - "colorscale": [ - [ - 0, - "red" - ], - [ - 1, - "red" - ] - ], - "type": "surface", - "x": [ - [ - 0.5, - 0.5342020143325669, - 0.5642787609686539, - 0.5866025403784438, - 0.5984807753012208, - 0.5984807753012208, - 0.5866025403784438, - 0.5642787609686539, - 0.5342020143325669, - 0.5 - ], - [ - 0.5, - 0.532348854856634, - 0.5607959603993067, - 0.5819101758650076, - 0.5931448152559406, - 0.5931448152559406, - 0.5819101758650076, - 0.5607959603993067, - 0.532348854856634, - 0.5 - ], - [ - 0.5, - 0.5269901950127845, - 0.5507249741741725, - 0.5683415728292669, - 0.5777151691869572, - 0.5777151691869572, - 0.5683415728292669, - 0.5507249741741727, - 0.5269901950127845, - 0.5 - ], - [ - 0.5, - 0.5187067287432743, - 0.5351571499181971, - 0.547367099948713, - 0.5538638786614714, - 0.5538638786614714, - 0.547367099948713, - 0.5351571499181971, - 0.5187067287432743, - 0.5 - ], - [ - 0.5, - 0.5083960981496268, - 0.5157795029491969, - 0.5212596668124331, - 0.5241756010988238, - 0.5241756010988238, - 0.5212596668124331, - 0.515779502949197, - 0.5083960981496268, - 0.5 - ], - [ - 0.5, - 0.4971756200425813, - 0.49469190199143603, - 0.49284841889930686, - 0.49186752203401735, - 0.49186752203401735, - 0.49284841889930686, - 0.49469190199143603, - 0.4971756200425813, - 0.5 - ], - [ - 0.5, - 0.4862612073286926, - 0.47417951581652984, - 0.4652121557666551, - 0.46044072314522244, - 0.46044072314522244, - 0.4652121557666551, - 0.47417951581652984, - 0.4862612073286926, - 0.5 - ], - [ - 0.5, - 0.476835605980073, - 0.45646517974899475, - 0.44134569534570584, - 0.4333007857290677, - 0.4333007857290677, - 0.44134569534570584, - 0.4564651797489947, - 0.47683560598007296, - 0.5 - ], - [ - 0.5, - 0.46992022615611934, - 0.44346851696799267, - 0.42383533894935854, - 0.413388743124112, - 0.413388743124112, - 0.42383533894935854, - 0.44346851696799267, - 0.4699202261561193, - 0.5 - ], - [ - 0.5, - 0.46626445656393073, - 0.43659791754984645, - 0.4145786053943312, - 0.40286237411377723, - 0.40286237411377723, - 0.4145786053943312, - 0.43659791754984645, - 0.46626445656393073, - 0.5 - ], - [ - 0.5, - 0.46626445656393073, - 0.43659791754984645, - 0.4145786053943312, - 0.40286237411377723, - 0.40286237411377723, - 0.4145786053943312, - 0.43659791754984645, - 0.46626445656393073, - 0.5 - ], - [ - 0.5, - 0.4699202261561193, - 0.44346851696799267, - 0.42383533894935854, - 0.413388743124112, - 0.413388743124112, - 0.42383533894935854, - 0.44346851696799267, - 0.4699202261561193, - 0.5 - ], - [ - 0.5, - 0.47683560598007296, - 0.4564651797489947, - 0.4413456953457058, - 0.43330078572906766, - 0.43330078572906766, - 0.4413456953457058, - 0.4564651797489947, - 0.47683560598007296, - 0.5 - ], - [ - 0.5, - 0.4862612073286926, - 0.47417951581652984, - 0.46521215576665503, - 0.4604407231452224, - 0.4604407231452224, - 0.46521215576665503, - 0.4741795158165298, - 0.4862612073286926, - 0.5 - ], - [ - 0.5, - 0.4971756200425813, - 0.49469190199143603, - 0.49284841889930686, - 0.4918675220340173, - 0.4918675220340173, - 0.49284841889930686, - 0.49469190199143603, - 0.49717562004258126, - 0.5 - ], - [ - 0.5, - 0.5083960981496267, - 0.5157795029491969, - 0.521259666812433, - 0.5241756010988238, - 0.5241756010988238, - 0.521259666812433, - 0.5157795029491969, - 0.5083960981496267, - 0.5 - ], - [ - 0.5, - 0.5187067287432743, - 0.535157149918197, - 0.5473670999487129, - 0.5538638786614712, - 0.5538638786614712, - 0.5473670999487129, - 0.535157149918197, - 0.5187067287432743, - 0.5 - ], - [ - 0.5, - 0.5269901950127845, - 0.5507249741741725, - 0.5683415728292669, - 0.5777151691869571, - 0.5777151691869571, - 0.5683415728292669, - 0.5507249741741725, - 0.5269901950127845, - 0.5 - ], - [ - 0.5, - 0.532348854856634, - 0.5607959603993067, - 0.5819101758650076, - 0.5931448152559406, - 0.5931448152559406, - 0.5819101758650076, - 0.5607959603993067, - 0.532348854856634, - 0.5 - ], - [ - 0.5, - 0.5342020143325669, - 0.5642787609686539, - 0.5866025403784438, - 0.5984807753012208, - 0.5984807753012208, - 0.5866025403784438, - 0.5642787609686539, - 0.5342020143325669, - 0.5 - ] - ], - "y": [ - [ - 0.5, - 0.5, - 0.5, - 0.5, - 0.5, - 0.5, - 0.5, - 0.5, - 0.5, - 0.5 - ], - [ - 0.5, - 0.5111053758995154, - 0.5208712795676567, - 0.5281197988926579, - 0.5319766554671721, - 0.5319766554671721, - 0.5281197988926579, - 0.5208712795676567, - 0.5111053758995154, - 0.5 - ], - [ - 0.5, - 0.5210073120026568, - 0.5394808321428877, - 0.5531923812516605, - 0.5604881441455445, - 0.5604881441455445, - 0.5531923812516605, - 0.5394808321428877, - 0.5210073120026568, - 0.5 - ], - [ - 0.5, - 0.5286327798882795, - 0.5538120239472069, - 0.5725007437372103, - 0.5824448038354865, - 0.5824448038354865, - 0.5725007437372103, - 0.5538120239472069, - 0.5286327798882795, - 0.5 - ], - [ - 0.5, - 0.5331554417896511, - 0.5623118479772637, - 0.5839525256738851, - 0.5954672897669149, - 0.5954672897669149, - 0.5839525256738851, - 0.5623118479772637, - 0.5331554417896511, - 0.5 - ], - [ - 0.5, - 0.5340851971134281, - 0.564059216411043, - 0.5863067487961411, - 0.5981444135244709, - 0.5981444135244709, - 0.5863067487961411, - 0.564059216411043, - 0.5340851971134281, - 0.5 - ], - [ - 0.5, - 0.5313212924436387, - 0.5588647747655294, - 0.5793082964991465, - 0.5901860672091682, - 0.5901860672091682, - 0.5793082964991465, - 0.5588647747655295, - 0.5313212924436388, - 0.5 - ], - [ - 0.5, - 0.5251632397376546, - 0.5472914213930815, - 0.5637155596814565, - 0.5724546611307362, - 0.5724546611307362, - 0.5637155596814565, - 0.5472914213930815, - 0.5251632397376547, - 0.5 - ], - [ - 0.5, - 0.5162783595582018, - 0.530593308710684, - 0.5412182533235083, - 0.5468716682688859, - 0.5468716682688859, - 0.5412182533235083, - 0.530593308710684, - 0.5162783595582019, - 0.5 - ], - [ - 0.5, - 0.5056294665358446, - 0.5105799363253888, - 0.5142543096508607, - 0.5162094028612335, - 0.5162094028612335, - 0.5142543096508607, - 0.5105799363253888, - 0.5056294665358446, - 0.5 - ], - [ - 0.5, - 0.4943705334641554, - 0.48942006367461116, - 0.48574569034913934, - 0.48379059713876654, - 0.48379059713876654, - 0.48574569034913934, - 0.48942006367461116, - 0.4943705334641554, - 0.5 - ], - [ - 0.5, - 0.48372164044179816, - 0.46940669128931595, - 0.45878174667649174, - 0.4531283317311141, - 0.4531283317311141, - 0.45878174667649174, - 0.46940669128931595, - 0.48372164044179816, - 0.5 - ], - [ - 0.5, - 0.4748367602623454, - 0.4527085786069185, - 0.4362844403185435, - 0.42754533886926394, - 0.42754533886926394, - 0.4362844403185435, - 0.4527085786069185, - 0.4748367602623454, - 0.5 - ], - [ - 0.5, - 0.46867870755636126, - 0.4411352252344705, - 0.42069170350085355, - 0.40981393279083184, - 0.40981393279083184, - 0.4206917035008535, - 0.4411352252344705, - 0.46867870755636126, - 0.5 - ], - [ - 0.5, - 0.465914802886572, - 0.4359407835889571, - 0.41369325120385886, - 0.4018555864755291, - 0.4018555864755291, - 0.41369325120385886, - 0.4359407835889571, - 0.46591480288657194, - 0.5 - ], - [ - 0.5, - 0.46684455821034887, - 0.43768815202273625, - 0.4160474743261149, - 0.4045327102330851, - 0.4045327102330851, - 0.4160474743261149, - 0.4376881520227362, - 0.46684455821034887, - 0.5 - ], - [ - 0.5, - 0.47136722011172044, - 0.4461879760527931, - 0.42749925626278973, - 0.41755519616451353, - 0.41755519616451353, - 0.42749925626278973, - 0.4461879760527931, - 0.47136722011172044, - 0.5 - ], - [ - 0.5, - 0.4789926879973432, - 0.46051916785711233, - 0.44680761874833946, - 0.4395118558544555, - 0.4395118558544555, - 0.44680761874833946, - 0.46051916785711233, - 0.4789926879973432, - 0.5 - ], - [ - 0.5, - 0.48889462410048456, - 0.47912872043234334, - 0.4718802011073421, - 0.4680233445328279, - 0.4680233445328279, - 0.4718802011073421, - 0.47912872043234334, - 0.48889462410048456, - 0.5 - ], - [ - 0.5, - 0.5, - 0.5, - 0.5, - 0.5, - 0.5, - 0.5, - 0.5, - 0.5, - 0.5 - ] - ], - "z": [ - [ - 0.6, - 0.5939692620785908, - 0.5766044443118978, - 0.55, - 0.5173648177666931, - 0.482635182233307, - 0.45, - 0.4233955556881022, - 0.40603073792140915, - 0.4 - ], - [ - 0.6, - 0.5939692620785908, - 0.5766044443118978, - 0.55, - 0.5173648177666931, - 0.482635182233307, - 0.45, - 0.4233955556881022, - 0.40603073792140915, - 0.4 - ], - [ - 0.6, - 0.5939692620785908, - 0.5766044443118978, - 0.55, - 0.5173648177666931, - 0.482635182233307, - 0.45, - 0.4233955556881022, - 0.40603073792140915, - 0.4 - ], - [ - 0.6, - 0.5939692620785908, - 0.5766044443118978, - 0.55, - 0.5173648177666931, - 0.482635182233307, - 0.45, - 0.4233955556881022, - 0.40603073792140915, - 0.4 - ], - [ - 0.6, - 0.5939692620785908, - 0.5766044443118978, - 0.55, - 0.5173648177666931, - 0.482635182233307, - 0.45, - 0.4233955556881022, - 0.40603073792140915, - 0.4 - ], - [ - 0.6, - 0.5939692620785908, - 0.5766044443118978, - 0.55, - 0.5173648177666931, - 0.482635182233307, - 0.45, - 0.4233955556881022, - 0.40603073792140915, - 0.4 - ], - [ - 0.6, - 0.5939692620785908, - 0.5766044443118978, - 0.55, - 0.5173648177666931, - 0.482635182233307, - 0.45, - 0.4233955556881022, - 0.40603073792140915, - 0.4 - ], - [ - 0.6, - 0.5939692620785908, - 0.5766044443118978, - 0.55, - 0.5173648177666931, - 0.482635182233307, - 0.45, - 0.4233955556881022, - 0.40603073792140915, - 0.4 - ], - [ - 0.6, - 0.5939692620785908, - 0.5766044443118978, - 0.55, - 0.5173648177666931, - 0.482635182233307, - 0.45, - 0.4233955556881022, - 0.40603073792140915, - 0.4 - ], - [ - 0.6, - 0.5939692620785908, - 0.5766044443118978, - 0.55, - 0.5173648177666931, - 0.482635182233307, - 0.45, - 0.4233955556881022, - 0.40603073792140915, - 0.4 - ], - [ - 0.6, - 0.5939692620785908, - 0.5766044443118978, - 0.55, - 0.5173648177666931, - 0.482635182233307, - 0.45, - 0.4233955556881022, - 0.40603073792140915, - 0.4 - ], - [ - 0.6, - 0.5939692620785908, - 0.5766044443118978, - 0.55, - 0.5173648177666931, - 0.482635182233307, - 0.45, - 0.4233955556881022, - 0.40603073792140915, - 0.4 - ], - [ - 0.6, - 0.5939692620785908, - 0.5766044443118978, - 0.55, - 0.5173648177666931, - 0.482635182233307, - 0.45, - 0.4233955556881022, - 0.40603073792140915, - 0.4 - ], - [ - 0.6, - 0.5939692620785908, - 0.5766044443118978, - 0.55, - 0.5173648177666931, - 0.482635182233307, - 0.45, - 0.4233955556881022, - 0.40603073792140915, - 0.4 - ], - [ - 0.6, - 0.5939692620785908, - 0.5766044443118978, - 0.55, - 0.5173648177666931, - 0.482635182233307, - 0.45, - 0.4233955556881022, - 0.40603073792140915, - 0.4 - ], - [ - 0.6, - 0.5939692620785908, - 0.5766044443118978, - 0.55, - 0.5173648177666931, - 0.482635182233307, - 0.45, - 0.4233955556881022, - 0.40603073792140915, - 0.4 - ], - [ - 0.6, - 0.5939692620785908, - 0.5766044443118978, - 0.55, - 0.5173648177666931, - 0.482635182233307, - 0.45, - 0.4233955556881022, - 0.40603073792140915, - 0.4 - ], - [ - 0.6, - 0.5939692620785908, - 0.5766044443118978, - 0.55, - 0.5173648177666931, - 0.482635182233307, - 0.45, - 0.4233955556881022, - 0.40603073792140915, - 0.4 - ], - [ - 0.6, - 0.5939692620785908, - 0.5766044443118978, - 0.55, - 0.5173648177666931, - 0.482635182233307, - 0.45, - 0.4233955556881022, - 0.40603073792140915, - 0.4 - ], - [ - 0.6, - 0.5939692620785908, - 0.5766044443118978, - 0.55, - 0.5173648177666931, - 0.482635182233307, - 0.45, - 0.4233955556881022, - 0.40603073792140915, - 0.4 - ] - ] - }, - { - "colorscale": [ - [ - 0, - "blue" - ], - [ - 1, - "blue" - ] - ], - "type": "surface", - "x": [ - [ - 0.7, - 0.7342020143325668, - 0.7642787609686539, - 0.7866025403784438, - 0.7984807753012207, - 0.7984807753012207, - 0.7866025403784438, - 0.7642787609686539, - 0.7342020143325668, - 0.7 - ], - [ - 0.7, - 0.7323488548566339, - 0.7607959603993066, - 0.7819101758650076, - 0.7931448152559406, - 0.7931448152559406, - 0.7819101758650076, - 0.7607959603993066, - 0.7323488548566339, - 0.7 - ], - [ - 0.7, - 0.7269901950127845, - 0.7507249741741725, - 0.7683415728292669, - 0.7777151691869572, - 0.7777151691869572, - 0.7683415728292669, - 0.7507249741741726, - 0.7269901950127845, - 0.7 - ], - [ - 0.7, - 0.7187067287432742, - 0.735157149918197, - 0.747367099948713, - 0.7538638786614713, - 0.7538638786614713, - 0.747367099948713, - 0.735157149918197, - 0.7187067287432742, - 0.7 - ], - [ - 0.7, - 0.7083960981496268, - 0.7157795029491969, - 0.721259666812433, - 0.7241756010988237, - 0.7241756010988237, - 0.721259666812433, - 0.715779502949197, - 0.7083960981496268, - 0.7 - ], - [ - 0.7, - 0.6971756200425813, - 0.694691901991436, - 0.6928484188993068, - 0.6918675220340174, - 0.6918675220340174, - 0.6928484188993068, - 0.694691901991436, - 0.6971756200425813, - 0.7 - ], - [ - 0.7, - 0.6862612073286926, - 0.6741795158165298, - 0.665212155766655, - 0.6604407231452224, - 0.6604407231452224, - 0.665212155766655, - 0.6741795158165298, - 0.6862612073286926, - 0.7 - ], - [ - 0.7, - 0.676835605980073, - 0.6564651797489947, - 0.6413456953457058, - 0.6333007857290677, - 0.6333007857290677, - 0.6413456953457058, - 0.6564651797489947, - 0.676835605980073, - 0.7 - ], - [ - 0.7, - 0.6699202261561192, - 0.6434685169679927, - 0.6238353389493585, - 0.613388743124112, - 0.613388743124112, - 0.6238353389493585, - 0.6434685169679926, - 0.6699202261561192, - 0.7 - ], - [ - 0.7, - 0.6662644565639307, - 0.6365979175498464, - 0.6145786053943312, - 0.6028623741137772, - 0.6028623741137772, - 0.6145786053943312, - 0.6365979175498464, - 0.6662644565639306, - 0.7 - ], - [ - 0.7, - 0.6662644565639307, - 0.6365979175498464, - 0.6145786053943312, - 0.6028623741137772, - 0.6028623741137772, - 0.6145786053943312, - 0.6365979175498464, - 0.6662644565639306, - 0.7 - ], - [ - 0.7, - 0.6699202261561192, - 0.6434685169679927, - 0.6238353389493585, - 0.613388743124112, - 0.613388743124112, - 0.6238353389493585, - 0.6434685169679926, - 0.6699202261561192, - 0.7 - ], - [ - 0.7, - 0.676835605980073, - 0.6564651797489947, - 0.6413456953457057, - 0.6333007857290677, - 0.6333007857290677, - 0.6413456953457057, - 0.6564651797489947, - 0.6768356059800729, - 0.7 - ], - [ - 0.7, - 0.6862612073286926, - 0.6741795158165298, - 0.6652121557666549, - 0.6604407231452224, - 0.6604407231452224, - 0.6652121557666549, - 0.6741795158165298, - 0.6862612073286926, - 0.7 - ], - [ - 0.7, - 0.6971756200425813, - 0.6946919019914359, - 0.6928484188993068, - 0.6918675220340172, - 0.6918675220340172, - 0.6928484188993068, - 0.6946919019914359, - 0.6971756200425813, - 0.7 - ], - [ - 0.7, - 0.7083960981496267, - 0.7157795029491969, - 0.7212596668124329, - 0.7241756010988237, - 0.7241756010988237, - 0.7212596668124329, - 0.7157795029491969, - 0.7083960981496267, - 0.7 - ], - [ - 0.7, - 0.7187067287432742, - 0.7351571499181969, - 0.7473670999487129, - 0.7538638786614712, - 0.7538638786614712, - 0.7473670999487129, - 0.7351571499181969, - 0.7187067287432742, - 0.7 - ], - [ - 0.7, - 0.7269901950127845, - 0.7507249741741725, - 0.7683415728292669, - 0.777715169186957, - 0.777715169186957, - 0.7683415728292669, - 0.7507249741741725, - 0.7269901950127845, - 0.7 - ], - [ - 0.7, - 0.7323488548566339, - 0.7607959603993066, - 0.7819101758650076, - 0.7931448152559406, - 0.7931448152559406, - 0.7819101758650076, - 0.7607959603993066, - 0.7323488548566339, - 0.7 - ], - [ - 0.7, - 0.7342020143325668, - 0.7642787609686539, - 0.7866025403784438, - 0.7984807753012207, - 0.7984807753012207, - 0.7866025403784438, - 0.7642787609686539, - 0.7342020143325668, - 0.7 - ] - ], - "y": [ - [ - 0.3, - 0.3, - 0.3, - 0.3, - 0.3, - 0.3, - 0.3, - 0.3, - 0.3, - 0.3 - ], - [ - 0.3, - 0.3111053758995154, - 0.32087127956765665, - 0.32811979889265785, - 0.3319766554671721, - 0.3319766554671721, - 0.32811979889265785, - 0.32087127956765665, - 0.3111053758995154, - 0.3 - ], - [ - 0.3, - 0.3210073120026568, - 0.33948083214288766, - 0.35319238125166047, - 0.3604881441455445, - 0.3604881441455445, - 0.35319238125166047, - 0.33948083214288766, - 0.3210073120026568, - 0.3 - ], - [ - 0.3, - 0.32863277988827955, - 0.3538120239472069, - 0.37250074373721026, - 0.3824448038354864, - 0.3824448038354864, - 0.37250074373721026, - 0.3538120239472069, - 0.32863277988827955, - 0.3 - ], - [ - 0.3, - 0.3331554417896511, - 0.36231184797726373, - 0.38395252567388505, - 0.39546728976691486, - 0.39546728976691486, - 0.38395252567388505, - 0.3623118479772638, - 0.3331554417896511, - 0.3 - ], - [ - 0.3, - 0.334085197113428, - 0.3640592164110429, - 0.38630674879614113, - 0.3981444135244709, - 0.3981444135244709, - 0.38630674879614113, - 0.3640592164110429, - 0.334085197113428, - 0.3 - ], - [ - 0.3, - 0.3313212924436387, - 0.35886477476552947, - 0.3793082964991465, - 0.3901860672091682, - 0.3901860672091682, - 0.3793082964991465, - 0.35886477476552947, - 0.3313212924436387, - 0.3 - ], - [ - 0.3, - 0.3251632397376546, - 0.3472914213930815, - 0.3637155596814565, - 0.3724546611307361, - 0.3724546611307361, - 0.3637155596814565, - 0.34729142139308156, - 0.3251632397376546, - 0.3 - ], - [ - 0.3, - 0.3162783595582018, - 0.33059330871068404, - 0.34121825332350825, - 0.3468716682688859, - 0.3468716682688859, - 0.34121825332350825, - 0.33059330871068404, - 0.3162783595582018, - 0.3 - ], - [ - 0.3, - 0.3056294665358446, - 0.31057993632538883, - 0.3142543096508607, - 0.31620940286123345, - 0.31620940286123345, - 0.3142543096508607, - 0.31057993632538883, - 0.3056294665358446, - 0.3 - ], - [ - 0.3, - 0.29437053346415537, - 0.28942006367461115, - 0.2857456903491393, - 0.28379059713876653, - 0.28379059713876653, - 0.2857456903491393, - 0.28942006367461115, - 0.29437053346415537, - 0.3 - ], - [ - 0.3, - 0.28372164044179815, - 0.26940669128931594, - 0.25878174667649173, - 0.25312833173111415, - 0.25312833173111415, - 0.25878174667649173, - 0.26940669128931594, - 0.28372164044179815, - 0.3 - ], - [ - 0.3, - 0.2748367602623454, - 0.2527085786069185, - 0.2362844403185435, - 0.2275453388692639, - 0.2275453388692639, - 0.2362844403185435, - 0.2527085786069185, - 0.2748367602623454, - 0.3 - ], - [ - 0.3, - 0.26867870755636125, - 0.24113522523447053, - 0.22069170350085354, - 0.20981393279083183, - 0.20981393279083183, - 0.22069170350085351, - 0.2411352252344705, - 0.26867870755636125, - 0.3 - ], - [ - 0.3, - 0.265914802886572, - 0.2359407835889571, - 0.21369325120385885, - 0.2018555864755291, - 0.2018555864755291, - 0.21369325120385885, - 0.2359407835889571, - 0.265914802886572, - 0.3 - ], - [ - 0.3, - 0.26684455821034886, - 0.23768815202273622, - 0.2160474743261149, - 0.20453271023308509, - 0.20453271023308509, - 0.21604747432611487, - 0.2376881520227362, - 0.26684455821034886, - 0.3 - ], - [ - 0.3, - 0.27136722011172043, - 0.24618797605279308, - 0.22749925626278972, - 0.21755519616451355, - 0.21755519616451355, - 0.2274992562627897, - 0.24618797605279308, - 0.27136722011172043, - 0.3 - ], - [ - 0.3, - 0.27899268799734317, - 0.2605191678571123, - 0.24680761874833948, - 0.2395118558544555, - 0.2395118558544555, - 0.24680761874833945, - 0.26051916785711227, - 0.27899268799734317, - 0.3 - ], - [ - 0.3, - 0.28889462410048455, - 0.2791287204323433, - 0.27188020110734207, - 0.2680233445328279, - 0.2680233445328279, - 0.27188020110734207, - 0.2791287204323433, - 0.28889462410048455, - 0.3 - ], - [ - 0.3, - 0.3, - 0.3, - 0.3, - 0.3, - 0.3, - 0.3, - 0.3, - 0.3, - 0.3 - ] - ], - "z": [ - [ - 0.30000000000000004, - 0.29396926207859086, - 0.2766044443118978, - 0.25, - 0.21736481776669306, - 0.182635182233307, - 0.15000000000000002, - 0.12339555568810222, - 0.10603073792140917, - 0.1 - ], - [ - 0.30000000000000004, - 0.29396926207859086, - 0.2766044443118978, - 0.25, - 0.21736481776669306, - 0.182635182233307, - 0.15000000000000002, - 0.12339555568810222, - 0.10603073792140917, - 0.1 - ], - [ - 0.30000000000000004, - 0.29396926207859086, - 0.2766044443118978, - 0.25, - 0.21736481776669306, - 0.182635182233307, - 0.15000000000000002, - 0.12339555568810222, - 0.10603073792140917, - 0.1 - ], - [ - 0.30000000000000004, - 0.29396926207859086, - 0.2766044443118978, - 0.25, - 0.21736481776669306, - 0.182635182233307, - 0.15000000000000002, - 0.12339555568810222, - 0.10603073792140917, - 0.1 - ], - [ - 0.30000000000000004, - 0.29396926207859086, - 0.2766044443118978, - 0.25, - 0.21736481776669306, - 0.182635182233307, - 0.15000000000000002, - 0.12339555568810222, - 0.10603073792140917, - 0.1 - ], - [ - 0.30000000000000004, - 0.29396926207859086, - 0.2766044443118978, - 0.25, - 0.21736481776669306, - 0.182635182233307, - 0.15000000000000002, - 0.12339555568810222, - 0.10603073792140917, - 0.1 - ], - [ - 0.30000000000000004, - 0.29396926207859086, - 0.2766044443118978, - 0.25, - 0.21736481776669306, - 0.182635182233307, - 0.15000000000000002, - 0.12339555568810222, - 0.10603073792140917, - 0.1 - ], - [ - 0.30000000000000004, - 0.29396926207859086, - 0.2766044443118978, - 0.25, - 0.21736481776669306, - 0.182635182233307, - 0.15000000000000002, - 0.12339555568810222, - 0.10603073792140917, - 0.1 - ], - [ - 0.30000000000000004, - 0.29396926207859086, - 0.2766044443118978, - 0.25, - 0.21736481776669306, - 0.182635182233307, - 0.15000000000000002, - 0.12339555568810222, - 0.10603073792140917, - 0.1 - ], - [ - 0.30000000000000004, - 0.29396926207859086, - 0.2766044443118978, - 0.25, - 0.21736481776669306, - 0.182635182233307, - 0.15000000000000002, - 0.12339555568810222, - 0.10603073792140917, - 0.1 - ], - [ - 0.30000000000000004, - 0.29396926207859086, - 0.2766044443118978, - 0.25, - 0.21736481776669306, - 0.182635182233307, - 0.15000000000000002, - 0.12339555568810222, - 0.10603073792140917, - 0.1 - ], - [ - 0.30000000000000004, - 0.29396926207859086, - 0.2766044443118978, - 0.25, - 0.21736481776669306, - 0.182635182233307, - 0.15000000000000002, - 0.12339555568810222, - 0.10603073792140917, - 0.1 - ], - [ - 0.30000000000000004, - 0.29396926207859086, - 0.2766044443118978, - 0.25, - 0.21736481776669306, - 0.182635182233307, - 0.15000000000000002, - 0.12339555568810222, - 0.10603073792140917, - 0.1 - ], - [ - 0.30000000000000004, - 0.29396926207859086, - 0.2766044443118978, - 0.25, - 0.21736481776669306, - 0.182635182233307, - 0.15000000000000002, - 0.12339555568810222, - 0.10603073792140917, - 0.1 - ], - [ - 0.30000000000000004, - 0.29396926207859086, - 0.2766044443118978, - 0.25, - 0.21736481776669306, - 0.182635182233307, - 0.15000000000000002, - 0.12339555568810222, - 0.10603073792140917, - 0.1 - ], - [ - 0.30000000000000004, - 0.29396926207859086, - 0.2766044443118978, - 0.25, - 0.21736481776669306, - 0.182635182233307, - 0.15000000000000002, - 0.12339555568810222, - 0.10603073792140917, - 0.1 - ], - [ - 0.30000000000000004, - 0.29396926207859086, - 0.2766044443118978, - 0.25, - 0.21736481776669306, - 0.182635182233307, - 0.15000000000000002, - 0.12339555568810222, - 0.10603073792140917, - 0.1 - ], - [ - 0.30000000000000004, - 0.29396926207859086, - 0.2766044443118978, - 0.25, - 0.21736481776669306, - 0.182635182233307, - 0.15000000000000002, - 0.12339555568810222, - 0.10603073792140917, - 0.1 - ], - [ - 0.30000000000000004, - 0.29396926207859086, - 0.2766044443118978, - 0.25, - 0.21736481776669306, - 0.182635182233307, - 0.15000000000000002, - 0.12339555568810222, - 0.10603073792140917, - 0.1 - ], - [ - 0.30000000000000004, - 0.29396926207859086, - 0.2766044443118978, - 0.25, - 0.21736481776669306, - 0.182635182233307, - 0.15000000000000002, - 0.12339555568810222, - 0.10603073792140917, - 0.1 - ] - ] - } - ], - "layout": { - "template": { - "data": { - "bar": [ - { - "error_x": { - "color": "#2a3f5f" - }, - "error_y": { - "color": "#2a3f5f" - }, - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "bar" - } - ], - "barpolar": [ - { - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "barpolar" - } - ], - "carpet": [ - { - "aaxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "baxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "type": "carpet" - } - ], - "choropleth": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "choropleth" - } - ], - "contour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "contour" - } - ], - "contourcarpet": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "contourcarpet" - } - ], - "heatmap": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmap" - } - ], - "heatmapgl": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmapgl" - } - ], - "histogram": [ - { - "marker": { - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "histogram" - } - ], - "histogram2d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2d" - } - ], - "histogram2dcontour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2dcontour" - } - ], - "mesh3d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "mesh3d" - } - ], - "parcoords": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "parcoords" - } - ], - "pie": [ - { - "automargin": true, - "type": "pie" - } - ], - "scatter": [ - { - "fillpattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - }, - "type": "scatter" - } - ], - "scatter3d": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatter3d" - } - ], - "scattercarpet": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattercarpet" - } - ], - "scattergeo": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergeo" - } - ], - "scattergl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergl" - } - ], - "scattermapbox": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattermapbox" - } - ], - "scatterpolar": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolar" - } - ], - "scatterpolargl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolargl" - } - ], - "scatterternary": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterternary" - } - ], - "surface": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "surface" - } - ], - "table": [ - { - "cells": { - "fill": { - "color": "#EBF0F8" - }, - "line": { - "color": "white" - } - }, - "header": { - "fill": { - "color": "#C8D4E3" - }, - "line": { - "color": "white" - } - }, - "type": "table" - } - ] - }, - "layout": { - "annotationdefaults": { - "arrowcolor": "#2a3f5f", - "arrowhead": 0, - "arrowwidth": 1 - }, - "autotypenumbers": "strict", - "coloraxis": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "colorscale": { - "diverging": [ - [ - 0, - "#8e0152" - ], - [ - 0.1, - "#c51b7d" - ], - [ - 0.2, - "#de77ae" - ], - [ - 0.3, - "#f1b6da" - ], - [ - 0.4, - "#fde0ef" - ], - [ - 0.5, - "#f7f7f7" - ], - [ - 0.6, - "#e6f5d0" - ], - [ - 0.7, - "#b8e186" - ], - [ - 0.8, - "#7fbc41" - ], - [ - 0.9, - "#4d9221" - ], - [ - 1, - "#276419" - ] - ], - "sequential": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "sequentialminus": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ] - }, - "colorway": [ - "#636efa", - "#EF553B", - "#00cc96", - "#ab63fa", - "#FFA15A", - "#19d3f3", - "#FF6692", - "#B6E880", - "#FF97FF", - "#FECB52" - ], - "font": { - "color": "#2a3f5f" - }, - "geo": { - "bgcolor": "white", - "lakecolor": "white", - "landcolor": "#E5ECF6", - "showlakes": true, - "showland": true, - "subunitcolor": "white" - }, - "hoverlabel": { - "align": "left" - }, - "hovermode": "closest", - "mapbox": { - "style": "light" - }, - "paper_bgcolor": "white", - "plot_bgcolor": "#E5ECF6", - "polar": { - "angularaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "radialaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "scene": { - "xaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "yaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "zaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - } - }, - "shapedefaults": { - "line": { - "color": "#2a3f5f" - } - }, - "ternary": { - "aaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "baxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "caxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "title": { - "x": 0.05 - }, - "xaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - }, - "yaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - } - } - } - } - } - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "import plotly.graph_objects as go\n", "\n", @@ -3167,1670 +861,9 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.plotly.v1+json": { - "config": { - "plotlyServerURL": "https://plot.ly" - }, - "data": [ - { - "alphahull": 0, - "color": "lightblue", - "opacity": 0.5, - "type": "mesh3d", - "x": [ - 1, - -1, - -1, - 1, - 1 - ], - "y": [ - 0.5, - 0.5, - -0.5, - -0.5, - 0.5 - ], - "z": [ - 0, - 0, - 0, - 0, - 0 - ] - }, - { - "colorscale": [ - [ - 0, - "red" - ], - [ - 1, - "red" - ] - ], - "showscale": false, - "type": "surface", - "x": [ - [ - 0.5, - 0.5684040286651337, - 0.6285575219373079, - 0.6732050807568877, - 0.6969615506024416, - 0.6969615506024416, - 0.6732050807568878, - 0.6285575219373079, - 0.5684040286651337, - 0.5 - ], - [ - 0.5, - 0.5646977097132679, - 0.6215919207986134, - 0.6638203517300152, - 0.6862896305118813, - 0.6862896305118813, - 0.6638203517300152, - 0.6215919207986134, - 0.5646977097132679, - 0.5 - ], - [ - 0.5, - 0.5539803900255692, - 0.6014499483483452, - 0.6366831456585339, - 0.6554303383739144, - 0.6554303383739144, - 0.6366831456585339, - 0.6014499483483452, - 0.5539803900255692, - 0.5 - ], - [ - 0.5, - 0.5374134574865486, - 0.570314299836394, - 0.5947341998974259, - 0.6077277573229427, - 0.6077277573229427, - 0.594734199897426, - 0.570314299836394, - 0.5374134574865486, - 0.5 - ], - [ - 0.5, - 0.5167921962992535, - 0.531559005898394, - 0.542519333624866, - 0.5483512021976475, - 0.5483512021976475, - 0.542519333624866, - 0.531559005898394, - 0.5167921962992535, - 0.5 - ], - [ - 0.5, - 0.4943512400851626, - 0.4893838039828721, - 0.4856968377986138, - 0.4837350440680347, - 0.4837350440680347, - 0.4856968377986138, - 0.4893838039828721, - 0.4943512400851626, - 0.5 - ], - [ - 0.5, - 0.4725224146573852, - 0.4483590316330597, - 0.4304243115333102, - 0.42088144629044494, - 0.42088144629044494, - 0.4304243115333101, - 0.4483590316330597, - 0.4725224146573852, - 0.5 - ], - [ - 0.5, - 0.453671211960146, - 0.4129303594979895, - 0.3826913906914117, - 0.36660157145813543, - 0.36660157145813543, - 0.38269139069141167, - 0.41293035949798945, - 0.453671211960146, - 0.5 - ], - [ - 0.5, - 0.4398404523122386, - 0.38693703393598533, - 0.3476706778987171, - 0.326777486248224, - 0.326777486248224, - 0.3476706778987171, - 0.38693703393598533, - 0.4398404523122386, - 0.5 - ], - [ - 0.5, - 0.4325289131278615, - 0.37319583509969295, - 0.32915721078866245, - 0.30572474822755447, - 0.30572474822755447, - 0.32915721078866245, - 0.3731958350996929, - 0.43252891312786146, - 0.5 - ], - [ - 0.5, - 0.4325289131278615, - 0.37319583509969295, - 0.32915721078866245, - 0.30572474822755447, - 0.30572474822755447, - 0.32915721078866245, - 0.3731958350996929, - 0.43252891312786146, - 0.5 - ], - [ - 0.5, - 0.4398404523122386, - 0.38693703393598533, - 0.3476706778987171, - 0.32677748624822395, - 0.32677748624822395, - 0.3476706778987171, - 0.38693703393598533, - 0.43984045231223856, - 0.5 - ], - [ - 0.5, - 0.4536712119601459, - 0.4129303594979894, - 0.3826913906914116, - 0.3666015714581353, - 0.3666015714581353, - 0.3826913906914116, - 0.4129303594979894, - 0.4536712119601459, - 0.5 - ], - [ - 0.5, - 0.4725224146573852, - 0.4483590316330596, - 0.43042431153331007, - 0.42088144629044477, - 0.42088144629044477, - 0.43042431153331, - 0.4483590316330596, - 0.47252241465738515, - 0.5 - ], - [ - 0.5, - 0.49435124008516257, - 0.48938380398287207, - 0.48569683779861367, - 0.48373504406803464, - 0.48373504406803464, - 0.48569683779861367, - 0.48938380398287207, - 0.49435124008516257, - 0.5 - ], - [ - 0.5, - 0.5167921962992535, - 0.531559005898394, - 0.5425193336248659, - 0.5483512021976474, - 0.5483512021976474, - 0.5425193336248659, - 0.531559005898394, - 0.5167921962992535, - 0.5 - ], - [ - 0.5, - 0.5374134574865486, - 0.5703142998363939, - 0.5947341998974259, - 0.6077277573229426, - 0.6077277573229426, - 0.5947341998974259, - 0.570314299836394, - 0.5374134574865486, - 0.5 - ], - [ - 0.5, - 0.5539803900255691, - 0.6014499483483451, - 0.6366831456585338, - 0.6554303383739143, - 0.6554303383739143, - 0.6366831456585338, - 0.6014499483483452, - 0.5539803900255692, - 0.5 - ], - [ - 0.5, - 0.5646977097132679, - 0.6215919207986134, - 0.6638203517300152, - 0.6862896305118813, - 0.6862896305118813, - 0.6638203517300152, - 0.6215919207986134, - 0.5646977097132679, - 0.5 - ], - [ - 0.5, - 0.5684040286651337, - 0.6285575219373079, - 0.6732050807568877, - 0.6969615506024416, - 0.6969615506024416, - 0.6732050807568878, - 0.6285575219373079, - 0.5684040286651337, - 0.5 - ] - ], - "y": [ - [ - 0.5, - 0.5, - 0.5, - 0.5, - 0.5, - 0.5, - 0.5, - 0.5, - 0.5, - 0.5 - ], - [ - 0.5, - 0.5222107517990309, - 0.5417425591353133, - 0.5562395977853157, - 0.5639533109343442, - 0.5639533109343442, - 0.5562395977853158, - 0.5417425591353133, - 0.5222107517990309, - 0.5 - ], - [ - 0.5, - 0.5420146240053136, - 0.5789616642857753, - 0.606384762503321, - 0.620976288291089, - 0.620976288291089, - 0.606384762503321, - 0.5789616642857753, - 0.5420146240053136, - 0.5 - ], - [ - 0.5, - 0.5572655597765591, - 0.6076240478944138, - 0.6450014874744205, - 0.6648896076709728, - 0.6648896076709728, - 0.6450014874744205, - 0.6076240478944138, - 0.5572655597765591, - 0.5 - ], - [ - 0.5, - 0.5663108835793023, - 0.6246236959545275, - 0.6679050513477701, - 0.6909345795338298, - 0.6909345795338298, - 0.6679050513477702, - 0.6246236959545276, - 0.5663108835793023, - 0.5 - ], - [ - 0.5, - 0.568170394226856, - 0.6281184328220858, - 0.6726134975922823, - 0.6962888270489418, - 0.6962888270489418, - 0.6726134975922823, - 0.6281184328220858, - 0.5681703942268561, - 0.5 - ], - [ - 0.5, - 0.5626425848872775, - 0.617729549531059, - 0.658616592998293, - 0.6803721344183364, - 0.6803721344183364, - 0.658616592998293, - 0.617729549531059, - 0.5626425848872775, - 0.5 - ], - [ - 0.5, - 0.5503264794753092, - 0.594582842786163, - 0.6274311193629131, - 0.6449093222614724, - 0.6449093222614724, - 0.6274311193629131, - 0.5945828427861631, - 0.5503264794753092, - 0.5 - ], - [ - 0.5, - 0.5325567191164037, - 0.5611866174213681, - 0.5824365066470165, - 0.5937433365377718, - 0.5937433365377718, - 0.5824365066470165, - 0.5611866174213681, - 0.5325567191164037, - 0.5 - ], - [ - 0.5, - 0.5112589330716892, - 0.5211598726507777, - 0.5285086193017214, - 0.5324188057224669, - 0.5324188057224669, - 0.5285086193017214, - 0.5211598726507777, - 0.5112589330716892, - 0.5 - ], - [ - 0.5, - 0.48874106692831076, - 0.4788401273492224, - 0.4714913806982787, - 0.4675811942775331, - 0.4675811942775331, - 0.4714913806982787, - 0.4788401273492224, - 0.48874106692831076, - 0.5 - ], - [ - 0.5, - 0.4674432808835963, - 0.43881338257863195, - 0.4175634933529835, - 0.4062566634622283, - 0.4062566634622283, - 0.4175634933529835, - 0.4388133825786319, - 0.4674432808835963, - 0.5 - ], - [ - 0.5, - 0.44967352052469084, - 0.405417157213837, - 0.37256888063708704, - 0.3550906777385278, - 0.3550906777385278, - 0.37256888063708704, - 0.405417157213837, - 0.4496735205246908, - 0.5 - ], - [ - 0.5, - 0.4373574151127226, - 0.3822704504689411, - 0.3413834070017071, - 0.3196278655816637, - 0.3196278655816637, - 0.34138340700170705, - 0.38227045046894104, - 0.4373574151127225, - 0.5 - ], - [ - 0.5, - 0.431829605773144, - 0.3718815671779142, - 0.3273865024077177, - 0.3037111729510582, - 0.3037111729510582, - 0.3273865024077177, - 0.3718815671779142, - 0.43182960577314394, - 0.5 - ], - [ - 0.5, - 0.43368911642069774, - 0.37537630404547245, - 0.3320949486522298, - 0.3090654204661702, - 0.3090654204661702, - 0.33209494865222977, - 0.3753763040454724, - 0.43368911642069774, - 0.5 - ], - [ - 0.5, - 0.44273444022344094, - 0.3923759521055862, - 0.35499851252557946, - 0.3351103923290271, - 0.3351103923290271, - 0.3549985125255794, - 0.3923759521055862, - 0.4427344402234409, - 0.5 - ], - [ - 0.5, - 0.45798537599468636, - 0.42103833571422467, - 0.393615237496679, - 0.379023711708911, - 0.379023711708911, - 0.3936152374966789, - 0.4210383357142246, - 0.45798537599468636, - 0.5 - ], - [ - 0.5, - 0.47778924820096913, - 0.45825744086468667, - 0.44376040221468416, - 0.43604668906565575, - 0.43604668906565575, - 0.44376040221468416, - 0.4582574408646866, - 0.4777892482009691, - 0.5 - ], - [ - 0.5, - 0.5, - 0.49999999999999994, - 0.49999999999999994, - 0.49999999999999994, - 0.49999999999999994, - 0.49999999999999994, - 0.49999999999999994, - 0.5, - 0.5 - ] - ], - "z": [ - [ - 0.7, - 0.6879385241571817, - 0.6532088886237957, - 0.6000000000000001, - 0.534729635533386, - 0.46527036446661396, - 0.4, - 0.34679111137620444, - 0.3120614758428183, - 0.3 - ], - [ - 0.7, - 0.6879385241571817, - 0.6532088886237957, - 0.6000000000000001, - 0.534729635533386, - 0.46527036446661396, - 0.4, - 0.34679111137620444, - 0.3120614758428183, - 0.3 - ], - [ - 0.7, - 0.6879385241571817, - 0.6532088886237957, - 0.6000000000000001, - 0.534729635533386, - 0.46527036446661396, - 0.4, - 0.34679111137620444, - 0.3120614758428183, - 0.3 - ], - [ - 0.7, - 0.6879385241571817, - 0.6532088886237957, - 0.6000000000000001, - 0.534729635533386, - 0.46527036446661396, - 0.4, - 0.34679111137620444, - 0.3120614758428183, - 0.3 - ], - [ - 0.7, - 0.6879385241571817, - 0.6532088886237957, - 0.6000000000000001, - 0.534729635533386, - 0.46527036446661396, - 0.4, - 0.34679111137620444, - 0.3120614758428183, - 0.3 - ], - [ - 0.7, - 0.6879385241571817, - 0.6532088886237957, - 0.6000000000000001, - 0.534729635533386, - 0.46527036446661396, - 0.4, - 0.34679111137620444, - 0.3120614758428183, - 0.3 - ], - [ - 0.7, - 0.6879385241571817, - 0.6532088886237957, - 0.6000000000000001, - 0.534729635533386, - 0.46527036446661396, - 0.4, - 0.34679111137620444, - 0.3120614758428183, - 0.3 - ], - [ - 0.7, - 0.6879385241571817, - 0.6532088886237957, - 0.6000000000000001, - 0.534729635533386, - 0.46527036446661396, - 0.4, - 0.34679111137620444, - 0.3120614758428183, - 0.3 - ], - [ - 0.7, - 0.6879385241571817, - 0.6532088886237957, - 0.6000000000000001, - 0.534729635533386, - 0.46527036446661396, - 0.4, - 0.34679111137620444, - 0.3120614758428183, - 0.3 - ], - [ - 0.7, - 0.6879385241571817, - 0.6532088886237957, - 0.6000000000000001, - 0.534729635533386, - 0.46527036446661396, - 0.4, - 0.34679111137620444, - 0.3120614758428183, - 0.3 - ], - [ - 0.7, - 0.6879385241571817, - 0.6532088886237957, - 0.6000000000000001, - 0.534729635533386, - 0.46527036446661396, - 0.4, - 0.34679111137620444, - 0.3120614758428183, - 0.3 - ], - [ - 0.7, - 0.6879385241571817, - 0.6532088886237957, - 0.6000000000000001, - 0.534729635533386, - 0.46527036446661396, - 0.4, - 0.34679111137620444, - 0.3120614758428183, - 0.3 - ], - [ - 0.7, - 0.6879385241571817, - 0.6532088886237957, - 0.6000000000000001, - 0.534729635533386, - 0.46527036446661396, - 0.4, - 0.34679111137620444, - 0.3120614758428183, - 0.3 - ], - [ - 0.7, - 0.6879385241571817, - 0.6532088886237957, - 0.6000000000000001, - 0.534729635533386, - 0.46527036446661396, - 0.4, - 0.34679111137620444, - 0.3120614758428183, - 0.3 - ], - [ - 0.7, - 0.6879385241571817, - 0.6532088886237957, - 0.6000000000000001, - 0.534729635533386, - 0.46527036446661396, - 0.4, - 0.34679111137620444, - 0.3120614758428183, - 0.3 - ], - [ - 0.7, - 0.6879385241571817, - 0.6532088886237957, - 0.6000000000000001, - 0.534729635533386, - 0.46527036446661396, - 0.4, - 0.34679111137620444, - 0.3120614758428183, - 0.3 - ], - [ - 0.7, - 0.6879385241571817, - 0.6532088886237957, - 0.6000000000000001, - 0.534729635533386, - 0.46527036446661396, - 0.4, - 0.34679111137620444, - 0.3120614758428183, - 0.3 - ], - [ - 0.7, - 0.6879385241571817, - 0.6532088886237957, - 0.6000000000000001, - 0.534729635533386, - 0.46527036446661396, - 0.4, - 0.34679111137620444, - 0.3120614758428183, - 0.3 - ], - [ - 0.7, - 0.6879385241571817, - 0.6532088886237957, - 0.6000000000000001, - 0.534729635533386, - 0.46527036446661396, - 0.4, - 0.34679111137620444, - 0.3120614758428183, - 0.3 - ], - [ - 0.7, - 0.6879385241571817, - 0.6532088886237957, - 0.6000000000000001, - 0.534729635533386, - 0.46527036446661396, - 0.4, - 0.34679111137620444, - 0.3120614758428183, - 0.3 - ] - ] - }, - { - "color": "orange", - "i": [ - 0, - 0, - 1, - 2, - 0, - 3 - ], - "j": [ - 1, - 1, - 4, - 4, - 2, - 5 - ], - "k": [ - 4, - 5, - 7, - 7, - 6, - 7 - ], - "opacity": 0.7, - "type": "mesh3d", - "x": [ - 1, - 1.816496580927726, - 1, - 1.1732050807568877, - 1.816496580927726, - 1.9897016616846137, - 1.1732050807568877, - 1.9897016616846137 - ], - "y": [ - 1, - 0.591751709536137, - 1.3535533905932737, - 1.1732050807568877, - 0.9453051001294108, - 0.7649567902930248, - 1.5267584713501614, - 1.1185101808862985 - ], - "z": [ - 0, - -0.408248290463863, - -0.3535533905932738, - 0.17320508075688776, - -0.7618016810571369, - -0.23504320970697526, - -0.18034830983638603, - -0.5885966003002491 - ] - } - ], - "layout": { - "scene": { - "aspectmode": "data" - }, - "template": { - "data": { - "bar": [ - { - "error_x": { - "color": "#2a3f5f" - }, - "error_y": { - "color": "#2a3f5f" - }, - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "bar" - } - ], - "barpolar": [ - { - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "barpolar" - } - ], - "carpet": [ - { - "aaxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "baxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "type": "carpet" - } - ], - "choropleth": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "choropleth" - } - ], - "contour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "contour" - } - ], - "contourcarpet": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "contourcarpet" - } - ], - "heatmap": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmap" - } - ], - "heatmapgl": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmapgl" - } - ], - "histogram": [ - { - "marker": { - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "histogram" - } - ], - "histogram2d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2d" - } - ], - "histogram2dcontour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2dcontour" - } - ], - "mesh3d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "mesh3d" - } - ], - "parcoords": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "parcoords" - } - ], - "pie": [ - { - "automargin": true, - "type": "pie" - } - ], - "scatter": [ - { - "fillpattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - }, - "type": "scatter" - } - ], - "scatter3d": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatter3d" - } - ], - "scattercarpet": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattercarpet" - } - ], - "scattergeo": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergeo" - } - ], - "scattergl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergl" - } - ], - "scattermapbox": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattermapbox" - } - ], - "scatterpolar": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolar" - } - ], - "scatterpolargl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolargl" - } - ], - "scatterternary": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterternary" - } - ], - "surface": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "surface" - } - ], - "table": [ - { - "cells": { - "fill": { - "color": "#EBF0F8" - }, - "line": { - "color": "white" - } - }, - "header": { - "fill": { - "color": "#C8D4E3" - }, - "line": { - "color": "white" - } - }, - "type": "table" - } - ] - }, - "layout": { - "annotationdefaults": { - "arrowcolor": "#2a3f5f", - "arrowhead": 0, - "arrowwidth": 1 - }, - "autotypenumbers": "strict", - "coloraxis": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "colorscale": { - "diverging": [ - [ - 0, - "#8e0152" - ], - [ - 0.1, - "#c51b7d" - ], - [ - 0.2, - "#de77ae" - ], - [ - 0.3, - "#f1b6da" - ], - [ - 0.4, - "#fde0ef" - ], - [ - 0.5, - "#f7f7f7" - ], - [ - 0.6, - "#e6f5d0" - ], - [ - 0.7, - "#b8e186" - ], - [ - 0.8, - "#7fbc41" - ], - [ - 0.9, - "#4d9221" - ], - [ - 1, - "#276419" - ] - ], - "sequential": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "sequentialminus": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ] - }, - "colorway": [ - "#636efa", - "#EF553B", - "#00cc96", - "#ab63fa", - "#FFA15A", - "#19d3f3", - "#FF6692", - "#B6E880", - "#FF97FF", - "#FECB52" - ], - "font": { - "color": "#2a3f5f" - }, - "geo": { - "bgcolor": "white", - "lakecolor": "white", - "landcolor": "#E5ECF6", - "showlakes": true, - "showland": true, - "subunitcolor": "white" - }, - "hoverlabel": { - "align": "left" - }, - "hovermode": "closest", - "mapbox": { - "style": "light" - }, - "paper_bgcolor": "white", - "plot_bgcolor": "#E5ECF6", - "polar": { - "angularaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "radialaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "scene": { - "xaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "yaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "zaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - } - }, - "shapedefaults": { - "line": { - "color": "#2a3f5f" - } - }, - "ternary": { - "aaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "baxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "caxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "title": { - "x": 0.05 - }, - "xaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - }, - "yaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - } - } - } - } - } - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "import plotly.graph_objects as go\n", "import numpy as np\n", @@ -4943,7 +976,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": ".venv", "language": "python", "name": "python3" }, @@ -4957,7 +990,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.12" + "version": "3.12.3" } }, "nbformat": 4, From 3d1a1631d9dacba637fc903ef0fe13d95718dd67 Mon Sep 17 00:00:00 2001 From: dfbakin Date: Tue, 4 Feb 2025 05:22:27 +0300 Subject: [PATCH 7/9] MVP done: triangulation and camera evaluation tested --- research.ipynb | 890 +++++++++++++++++-------------------------------- 1 file changed, 298 insertions(+), 592 deletions(-) diff --git a/research.ipynb b/research.ipynb index 62e9319..35680d4 100644 --- a/research.ipynb +++ b/research.ipynb @@ -17,7 +17,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -95,7 +95,36 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[-0.03576951]\n", + " [-0.00441021]\n", + " [ 1. ]]\n", + "[[1.65632969e+03]\n", + " [6.02966699e+02]\n", + " [1.00000000e+00]]\n" + ] + } + ], + "source": [ + "inv = np.linalg.inv(K_1)\n", + "# x, y = \n", + "# point = np.array([IMAGE_SIZE[0], IMAGE_SIZE[1], 1])\n", + "# point[0]\n", + "print(inv @ np.array([IMAGE_SIZE[1]/2, IMAGE_SIZE[0]/2, 1]).reshape((3, 1)))\n", + "print(K_1 @ np.array([1, 0, 1]).reshape((3, 1)))\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ @@ -141,7 +170,6 @@ " def project_point_to_image(self, point):\n", " if point.shape != (3, 1):\n", " point = point.reshape((3, 1))\n", - " print(self.camera_transformation(point))\n", " return self.camera_matrix @ self.camera_transformation(point)\n", "\n", " # transformed_point = self.camera_transformation(point)\n", @@ -151,54 +179,90 @@ " \n", " def project_points_to_image(self, points):\n", " return np.array([self.project_point_to_image(point) for point in points])\n", + " \n", + " def normilize_image_point(self, image_point):\n", + " x_normalised = (image_point[0] - self.camera_matrix[0, 2]) / self.camera_matrix[0, 0]\n", + " y_normalised = (image_point[1] - self.camera_matrix[1, 2]) / self.camera_matrix[1, 1]\n", + " return np.array([x_normalised, y_normalised, 1]).reshape(3, 1)\n", + " \n", "\n", " def project_ball_to_image(self, \n", - " center, radius: int) -> np.ndarray:\n", - " center = center.reshape((3, 1))\n", - " camera_matrix_inv = np.linalg.inv(self.camera_matrix)\n", + " center, radius: float) -> np.ndarray:\n", "\n", - " # projecting center and some edge point to approximate radius after projection\n", + " def valid_coords(x, y):\n", + " return x >= 0 and x < self.image_size[1] and y >= 0 and y < self.image_size[0]\n", "\n", - " print(f\"projecting center\")\n", + " center = center.reshape((3, 1))\n", + " camera_matrix_inv = np.linalg.inv(self.camera_matrix)\n", "\n", - " projected_center = self.project_point_to_image(center)\n", + " transformed_center = self.camera_transformation(center)\n", + " projected_center = self.camera_matrix @ transformed_center\n", " projected_center /= projected_center[2]\n", - " edge_point = center + np.array([radius, 0, 0]).reshape((3, 1))\n", - " projected_edge_point = self.project_point_to_image(edge_point)\n", - " projected_edge_point /= projected_edge_point[2]\n", - " approx_projected_radius = np.linalg.norm(\n", - " projected_edge_point - projected_center, ord=2\n", - " )\n", "\n", - " # calculating bounding box for calculations with 1.5 margin\n", - " x_start = int(max(0, projected_center[0].item() - 1.5 * approx_projected_radius))\n", - " y_start = int(max(0, projected_center[1].item() - 1.5 * approx_projected_radius))\n", - " x_stop = int(\n", - " min(IMAGE_SIZE[1], projected_center[0].item() + 1.5 * approx_projected_radius)\n", - " )\n", - " y_stop = int(\n", - " min(IMAGE_SIZE[0], projected_center[1].item() + 1.5 * approx_projected_radius)\n", - " )\n", + " if np.linalg.norm(projected_center.flatten() - np.array([IMAGE_SIZE[1]/2, IMAGE_SIZE[0]/2, 1])) > 2000:\n", + " return np.zeros(self.image_size)\n", "\n", " image = np.zeros(self.image_size)\n", - " # for x in range(x_start, x_stop):\n", - " # for y in range(y_start, y_stop):\n", - " for x in range(IMAGE_SIZE[1]):\n", - " for y in range(IMAGE_SIZE[0]):\n", - " # back project image point\n", - " cam_ray = camera_matrix_inv @ np.array([x, y, 1]).reshape((3, 1))\n", - " world_ray = self.camera_transformation.inverse_transform(cam_ray)\n", - " # measure distance from the sphere center\n", - " distance = np.linalg.norm(\n", - " np.cross(world_ray.flatten(), center.flatten()), ord=2\n", - " ) / np.linalg.norm(world_ray, ord=2)\n", - " # if back-projected ray intersects with the sphere, paint the pixel in the mask\n", - " if distance <= radius:\n", + " checked_pixels = set()\n", + "\n", + " pixels_to_check = {(int(projected_center[0][0]), int(projected_center[1][0]))}\n", + " while pixels_to_check:\n", + " x, y = pixels_to_check.pop()\n", + "\n", + " image_point_camera_ray = camera_matrix_inv @ np.array([x, y, 1]).reshape((3, 1))\n", + " image_point_world_ray = self.camera_transformation.inverse_transform(image_point_camera_ray) - \\\n", + " self.camera_transformation.inverse_transform(np.array([0, 0, 0]).reshape((3, 1)))\n", + " ball_center_world_ray = center - self.camera_transformation.inverse_transform(np.array([0, 0, 0]).reshape((3, 1)))\n", + "\n", + " distance = np.linalg.norm(\n", + " np.cross(ball_center_world_ray.flatten(), image_point_world_ray.flatten()), ord=2) / \\\n", + " np.linalg.norm(image_point_world_ray, ord=2)\n", + " if distance <= radius:\n", + " if valid_coords(x, y):\n", " image[y, x] = 1\n", + " # adding all 8 neighbours to the queue\n", + " for dx in range(-1, 2):\n", + " for dy in range(-1, 2):\n", + " if (x + dx, y + dy) not in checked_pixels:\n", + " pixels_to_check.add((x + dx, y + dy))\n", + " checked_pixels.add((x + dx, y + dy))\n", + "\n", " return image\n", "\n" ] }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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ka3Ys9bx5JNPKicnRwkJCRHl7W0OtKuvkLp3764OHTqcdtdJRUWF/H5/K/Uq+vLy8rRmzRq98sor6tmz51nbZmRkSJLeffddSZLf72/2+DTV2SYpKUnf/va39e6778rv96uurk7V1dURbT79/ren8R88eFAvv/yyfv7zn5+1XXueA039Pdt/836/X5WVlRH1DQ0Nqqqqalfzoim8HDx4UIWFhRFnX5qTkZGhhoYGvffee5LaxzFoct5556l79+4Rc/5cmAOS9Oqrr6qsrOxzPxck++dAuwow8fHxGjp0qIqKipyyxsZGFRUVKTMzsxV7Fh3GGOXl5WnlypVat27daaf6mrN9+3ZJUmpqqiQpMzNTO3fujPiPuenDbuDAgS3S75Z0/Phx7d+/X6mpqRo6dKji4uIi3v+ysjKVl5c77397Gv+SJUuUkpKisWPHnrVde54D6enp8vv9Ee95OBxWSUlJxHteXV2t0tJSp826devU2NjohLvMzExt3LhR9fX1TpvCwkL169evzZ02b05TeNm3b59efvlldevW7XPX2b59u2JiYpyvVmw/Bp/2/vvv6+jRoxFzvr3PgSZPPPGEhg4dqiFDhnxuW+vnQGtfRRxtK1asMG632yxdutS8/fbbZurUqSYpKSnijgtbTZs2zXi9XrN+/fqI2+BOnjxpjDHm3XffNXfffbfZunWrOXDggFm9erU577zzzIgRI5xtNN1CO2rUKLN9+3azdu1a06NHjzZ7C+1nzZw506xfv94cOHDAvP766yYrK8t0797dVFZWGmM+uY26V69eZt26dWbr1q0mMzPTZGZmOuvbPv4mp06dMr169TKzZ8+OKG+Pc+DYsWNm27ZtZtu2bUaSuf/++822bducO2zmz59vkpKSzOrVq81bb71lxo0b1+xt1BdffLEpKSkxr732munbt2/ELbTV1dXG5/OZG2+80ezatcusWLHCdOzYsc3cPnq2Y1BXV2d++MMfmp49e5rt27dHfDY03U2yadMm88ADD5jt27eb/fv3m6eeesr06NHD3HTTTc4+2vIxONv4jx07Zm6//XZTXFxsDhw4YF5++WVzySWXmL59+5qamhpnG+15DjQJhUKmY8eOZtGiRaetb/scaE67CzDGGPPggw+aXr16mfj4eHPppZeaN954o7W7FBWSml2WLFlijDGmvLzcjBgxwiQnJxu3220uuOACM2vWrIhngBhjzHvvvWfGjBljEhMTTffu3c3MmTNNfX19K4zoy7v++utNamqqiY+PN9/61rfM9ddfb959912n/uOPPza/+tWvTNeuXU3Hjh3Nv/3bv5nDhw9HbMPm8Tf55z//aSSZsrKyiPL2OAdeeeWVZuf9pEmTjDGf3Eo9d+5c4/P5jNvtNiNHjjztuBw9etTccMMNpnPnzsbj8Zif/vSn5tixYxFtduzYYa688krjdrvNt771LTN//vxvaoif62zH4MCBA2f8bGh6NlBpaanJyMgwXq/XJCQkmAEDBph777034h94Y9ruMTjb+E+ePGlGjRplevToYeLi4kzv3r3NlClTTvuf1vY8B5o8+uijJjEx0VRXV5+2vu1zoDkuY4xp0VM8AAAAUdauroEBAADnBgIMAACwDgEGAABYhwADAACsQ4ABAADWIcAAAADrEGAAAIB1CDAAAMA6BBgAAGAdAgwAALAOAQYAAFiHAAMAAKzz/wGDrl4sxpaV7wAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "image = Image(K_1, Transformation(np.eye(3), np.zeros((3, 1))), distortion_coefs_1)\n", + "projected_ball_mask = image.project_ball_to_image(np.array([2.3, 1.5, 2]).reshape((3, 1)), 0.1)\n", + "plt.imshow(projected_ball_mask)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -208,41 +272,13 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "import plotly.graph_objects as go\n", "import numpy as np\n", "\n", - "def display_plane(normal, size, origin):\n", - " \"\"\"Display a plane in the shape of a rectangle.\n", - " \n", - " Args:\n", - " normal (tuple): Normal vector of the plane (nx, ny, nz).\n", - " size (tuple): Size of the rectangle (width, height).\n", - " origin (tuple): Origin of the rectangle (x, y, z).\n", - " \"\"\"\n", - " normal = np.array(normal) / np.linalg.norm(normal) # Normalize normal vector\n", - " u = np.array([1, 0, 0]) if abs(normal[0]) < 0.9 else np.array([0, 1, 0])\n", - " v = np.cross(normal, u)\n", - " u = np.cross(v, normal)\n", - " u, v = u / np.linalg.norm(u), v / np.linalg.norm(v)\n", - " \n", - " w, h = size\n", - " corners = np.array([\n", - " origin + w/2 * u + h/2 * v,\n", - " origin - w/2 * u + h/2 * v,\n", - " origin - w/2 * u - h/2 * v,\n", - " origin + w/2 * u - h/2 * v,\n", - " ])\n", - " \n", - " x, y, z = corners[:, 0], corners[:, 1], corners[:, 2]\n", - " \n", - " return go.Mesh3d(\n", - " x=[*x, x[0]], y=[*y, y[0]], z=[*z, z[0]],\n", - " color='lightblue', opacity=0.5, alphahull=0\n", - " )\n", "\n", "def display_ball(position, radius, color):\n", " \"\"\"Display a ball (sphere).\n", @@ -259,150 +295,27 @@ " \n", " return go.Surface(x=x, y=y, z=z, colorscale=[[0, color], [1, color]], showscale=False)\n", "\n", - "def display_parallelepiped(size, direction, origin=(0,0,0)):\n", - " \"\"\"Display a parallelepiped.\n", - " \n", - " Args:\n", - " size (tuple): (width, height, depth).\n", - " direction (tuple): A vector representing the \"facing\" direction.\n", - " origin (tuple): Bottom-left corner of the parallelepiped.\n", - " \"\"\"\n", - " direction = np.array(direction) / np.linalg.norm(direction)\n", - " u = np.array([1, 0, 0]) if abs(direction[0]) < 0.9 else np.array([0, 1, 0])\n", - " v = np.cross(direction, u)\n", - " u = np.cross(v, direction)\n", - " u, v = u / np.linalg.norm(u), v / np.linalg.norm(v)\n", - "\n", - " w, h, d = size\n", - " corners = np.array([\n", - " origin,\n", - " origin + w * u,\n", - " origin + h * v,\n", - " origin + d * direction,\n", - " origin + w * u + h * v,\n", - " origin + w * u + d * direction,\n", - " origin + h * v + d * direction,\n", - " origin + w * u + h * v + d * direction\n", - " ])\n", - " \n", - " x, y, z = corners[:, 0], corners[:, 1], corners[:, 2]\n", - "\n", - " faces = [\n", - " [0, 1, 4, 2], # Bottom\n", - " [0, 1, 5, 3], # Side\n", - " [1, 4, 7, 5], # Side\n", - " [2, 4, 7, 6], # Side\n", - " [0, 2, 6, 3], # Side\n", - " [3, 5, 7, 6] # Top\n", - " ]\n", - "\n", - " return go.Mesh3d(\n", - " x=x, y=y, z=z,\n", - " i=[face[0] for face in faces],\n", - " j=[face[1] for face in faces],\n", - " k=[face[2] for face in faces],\n", - " color=\"orange\", opacity=0.7\n", - " )\n", - "\n", - "def display_camera(position, direction, fov=60, aspect_ratio=1.5, depth=2.0, color=\"blue\"):\n", - " \"\"\"\n", - " Display a camera as a 3D pyramid.\n", - " \n", - " Args:\n", - " position (tuple): (x, y, z) world position of the camera.\n", - " direction (tuple): (dx, dy, dz) unit vector pointing in the direction of the camera.\n", - " fov (float): Field of view in degrees (controls the width of the base).\n", - " aspect_ratio (float): Aspect ratio (width/height of base).\n", - " depth (float): Depth of the pyramid (distance from camera to base).\n", - " color (str): Color of the camera pyramid.\n", - " \"\"\"\n", - " # Normalize the direction vector\n", - " direction = np.array(direction) / np.linalg.norm(direction)\n", - "\n", - " # Compute right and up vectors to construct the base\n", - " up = np.array([0, 0, 1]) if abs(direction[2]) < 0.9 else np.array([1, 0, 0])\n", - " right = np.cross(direction, up)\n", - " up = np.cross(right, direction)\n", - " \n", - " right = right / np.linalg.norm(right)\n", - " up = up / np.linalg.norm(up)\n", - "\n", - " # Compute base size using FOV\n", - " angle = np.radians(fov / 2)\n", - " base_half_width = np.tan(angle) * depth\n", - " base_half_height = base_half_width / aspect_ratio\n", - "\n", - " # Compute base corners\n", - " base_center = position + depth * direction\n", + "def display_table(length, width):\n", " corners = np.array([\n", - " base_center + base_half_width * right + base_half_height * up,\n", - " base_center - base_half_width * right + base_half_height * up,\n", - " base_center - base_half_width * right - base_half_height * up,\n", - " base_center + base_half_width * right - base_half_height * up,\n", - " ])\n", - " \n", - " # Pyramid vertices\n", - " vertices = np.vstack([position, corners])\n", - "\n", - " # Pyramid faces (indices)\n", - " faces = [\n", - " [0, 1, 2], # Side 1\n", - " [0, 2, 3], # Side 2\n", - " [0, 3, 4], # Side 3\n", - " [0, 4, 1], # Side 4\n", - " [1, 2, 3, 4] # Base\n", - " ]\n", - "\n", - " # Create Mesh3D\n", - " camera_mesh = go.Mesh3d(\n", - " x=vertices[:, 0], y=vertices[:, 1], z=vertices[:, 2],\n", - " i=[face[0] for face in faces],\n", - " j=[face[1] for face in faces],\n", - " k=[face[2] for face in faces],\n", - " color=color, opacity=0.5\n", - " )\n", - "\n", - " # Create direction line\n", - " direction_line = go.Scatter3d(\n", - " x=[position[0], base_center[0]],\n", - " y=[position[1], base_center[1]],\n", - " z=[position[2], base_center[2]],\n", - " mode=\"lines\",\n", - " line=dict(color=color, width=4),\n", - " name=\"Camera Direction\"\n", - " )\n", - "\n", - " return [camera_mesh, direction_line]\n", - "\n", - "# # Create figure\n", - "# fig = go.Figure()\n", - "\n", - "# # Add two cameras\n", - "# fig.add_traces(display_camera(position=(0, 0, 1), direction=(1, 1, -0.2), fov=60, color=\"red\"))\n", - "# fig.add_traces(display_camera(position=(2, 2, 1), direction=(-1, -1, -0.2), fov=45, color=\"blue\"))\n", - "\n", - "# fig.update_layout(scene=dict(aspectmode=\"data\"))\n", - "# fig.show()\n", - "\n", - "# # Create figure\n", - "# fig = go.Figure()\n", - "\n", - "# # Add plane\n", - "# fig.add_trace(display_plane((0, 0, 1), (2, 1), (0, 0, 0)))\n", - "\n", - "# # Add ball\n", - "# fig.add_trace(display_ball((0.5, 0.5, 0.5), 0.2, \"red\"))\n", - "\n", - "# # Add parallelepiped\n", - "# fig.add_trace(display_parallelepiped((1, 0.5, 0.3), (1, 1, 1), (1, 1, 0)))\n", - "\n", - "# fig.update_layout(scene=dict(aspectmode=\"data\"))\n", - "# fig.show()\n" + " [-length / 2, -width / 2],\n", + " [length / 2, -width / 2],\n", + " [length / 2, width / 2],\n", + " [-length / 2, width / 2],\n", + " [-length / 2, -width / 2]])\n", + "\n", + "\n", + " return go.Surface(\n", + " x=[[0, length, length, 0]],\n", + " y=[[0, 0, width, width]],\n", + " z=[[0, 0, 0, 0]],\n", + " colorscale=[[0, 'brown'], [1, 'brown']],\n", + " showscale=False\n", + " )\n" ] }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 60, "metadata": {}, "outputs": [], "source": [ @@ -414,9 +327,11 @@ "\n", "\n", "class StereoScene:\n", - " def __init__(self, left_camera: Image, right_camera: Image):\n", + " def __init__(self, left_camera: Image, right_camera: Image, table_middle_normal):\n", " self.left_camera = left_camera\n", " self.right_camera = right_camera\n", + " self.table_middle_normal = table_middle_normal\n", + " self.last_n_clicks = 0\n", "\n", " def project_ball_to_images(self, center, radius):\n", " left_image = self.left_camera.project_ball_to_image(center, radius)\n", @@ -431,11 +346,24 @@ " dcc.Graph(id='left-image'),\n", " dcc.Graph(id='right-image'),\n", " html.Label('Ball X Position'),\n", - " dcc.Slider(id='ball-x', min=-2, max=2, step=0.05, value=0.13),\n", + " dcc.Slider(id='ball-x', min=-1.5, max=1.5, step=0.1, value=0.13),\n", " html.Label('Ball Y Position'),\n", - " dcc.Slider(id='ball-y', min=-2, max=2, step=0.05, value=0),\n", + " dcc.Slider(id='ball-y', min=-1.5, max=1.5, step=0.1, value=0),\n", " html.Label('Ball Z Position'),\n", - " dcc.Slider(id='ball-z', min=0, max=3, step=0.05, value=1),\n", + " dcc.Slider(id='ball-z', min=0, max=2, step=0.1, value=1),\n", + " html.Label('Master camera pitch'),\n", + " dcc.Input(id='pitch_1', type='range'),\n", + " html.Label('Master camera yaw'),\n", + " dcc.Input(id='yaw_1', type='range'),\n", + " html.Label('Master camera roll'),\n", + " dcc.Input(id='roll_1', type='range'),\n", + " html.Label('Slave camera pitch'),\n", + " dcc.Input(id='pitch_2', type='range'),\n", + " html.Label('Slave camera pitch'),\n", + " dcc.Input(id='yaw_2', type='range'),\n", + " html.Label('Slave camera pitch'),\n", + " dcc.Input(id='roll_2', type='range'),\n", + " html.Button('Evaluate camera placement', id='evaluate-placement'),\n", " ])\n", "\n", " @app.callback(\n", @@ -444,9 +372,10 @@ " Output('right-image', 'figure')],\n", " [Input('ball-x', 'value'),\n", " Input('ball-y', 'value'),\n", - " Input('ball-z', 'value')]\n", + " Input('ball-z', 'value'),\n", + " Input('evaluate-placement', 'n_clicks')]\n", " )\n", - " def update_scene(ball_x, ball_y, ball_z):\n", + " def update_scene(ball_x, ball_y, ball_z, n_clicks):\n", " ball_center = np.array([ball_x, ball_y, ball_z]).reshape((3, 1))\n", " left_image, right_image = self.project_ball_to_images(ball_center, 0.04)\n", " \n", @@ -456,15 +385,12 @@ " # fig_3d.add_trace(display_plane((0, 0, 1), (TABLE_LENGTH, TABLE_WIDTH), (0, 0, 0)))\n", " fig_3d.add_trace(display_ball((ball_x, ball_y, ball_z), 0.04, \"green\"))\n", "\n", - " fig_3d.add_trace(go.Cone(x=[0], y=[0], z=[0], u=[0], v=[0], w=[1], colorscale=[[0, \"red\"], [1, \"red\"]]))\n", + " fig_3d.add_trace(go.Cone(x=[0], y=[0], z=[0], u=[0], v=[0], w=[-1], colorscale=[[0, \"red\"], [1, \"red\"]]))\n", "\n", " second_camera_direction = self.right_camera.camera_transformation.R @ np.array([0, 0, 1]).reshape((3, 1))\n", - " # second_camera_direction = second_camera_direction / np.linalg.norm(second_camera_direction, ord=2) * 0.04\n", - " # print(second_camera_direction.shape)\n", - " # print(self.right_camera.camera_transformation.t.shape)\n", - " # print([*self.right_camera.camera_transformation.t.flatten().tolist()] + [*second_camera_direction.tolist()])\n", - " # print()\n", - " second_cam_cone_position_direction = dict(zip([\"x\", \"y\", \"z\", \"u\", \"v\", \"w\"], [*self.right_camera.camera_transformation.t.tolist()] + [*second_camera_direction.tolist()]))\n", + " second_cam_cone_position_direction = dict(zip([\"x\", \"y\", \"z\", \"u\", \"v\", \"w\"], \n", + " [*self.right_camera.camera_transformation.t.tolist()] + \n", + " [*(-second_camera_direction).tolist()]))\n", " fig_3d.add_trace(go.Cone(**second_cam_cone_position_direction,\n", " colorscale=[[0, \"blue\"], [1, \"blue\"]]))\n", "\n", @@ -476,46 +402,45 @@ " layout=go.Layout(\n", " scene=dict(\n", " aspectmode='data')))\n", + "\n", + " center_1 = get_mask_center(left_image)\n", + " center_2 = get_mask_center(right_image)\n", + " points_1 = np.array([[center_1[0]], [center_1[1]]])\n", + " points_2 = np.array([[center_2[0]], [center_2[1]]])\n", + " print(self.triangulate_position(points_1, points_2,\n", + " self.left_camera.camera_transformation, self.right_camera.camera_transformation))\n", + " \n", + " if self.last_n_clicks != n_clicks:\n", + " self.last_n_clicks = n_clicks\n", + " print(\"Evaluating camera placement\")\n", " \n", " return fig_3d, fig_left, fig_right\n", "\n", " return app\n", "\n", - " # def project_points_to_image(self, points):\n", - " # return self.left_camera.project_points_to_image(points), self.right_camera.project_points_to_image(points)\n", - "\n", - " # def project_point_to_image(self, point):\n", - " # return self.left_camera.project_point_to_image(point), self.right_camera.project_point_to_image(point)\n", - "\n", - " # def project_point_to_world(self, left_point, right_point):\n", - " # left_point = self.left_camera.camera_transformation.inverse_transform(left_point)\n", - " # right_point = self.right_camera.camera_transformation.inverse_transform(right_point)\n", - " # return left_point, right_point\n", + " def triangulate_position(self, \n", + " points_by_view_1, points_by_view_2, world2cam, cam2cam\n", + "):\n", + " print(\"triangulating\")\n", + " # print(points_by_view)\n", + " world2cam_Rt = np.column_stack((world2cam.R, world2cam.t))\n", + " world2second_cam = cam2cam * world2cam\n", + " world2second_cam_Rt = np.column_stack((world2second_cam.R, world2second_cam.t))\n", + " proj_1 = self.left_camera.camera_matrix @ world2cam_Rt\n", + " proj_2 = self.right_camera.camera_matrix @ world2second_cam_Rt\n", + "\n", + " res = cv2.triangulatePoints(\n", + " proj_1, proj_2, points_by_view_1, points_by_view_2\n", + " )\n", + " res /= res[3, :] # normalizing\n", "\n", - " # def project_points_to_world(self, left_points, right_points):\n", - " # return self" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1.638728271710918\n" - ] - } - ], - "source": [ - "print(np.linalg.norm(stereo_cam_translation, ord=2))" + " # TODO preserve 4D points?\n", + " return res[:3, :]" ] }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 61, "metadata": {}, "outputs": [ { @@ -533,7 +458,7 @@ " " ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -543,34 +468,55 @@ "name": "stdout", "output_type": "stream", "text": [ - "projecting center\n", - "[[0.13]\n", - " [0. ]\n", - " [1. ]]\n", - "[[0.17]\n", - " [0. ]\n", - " [1. ]]\n", - "projecting center\n", - "[[0.13]\n", - " [0. ]\n", - " [1. ]]\n", - "[[0.17]\n", - " [0. ]\n", - " [1. ]]\n", - "projecting center\n", - "[[-0.15207418]\n", - " [-0.17019519]\n", - " [ 1.14703514]]\n", - "[[-0.19201122]\n", - " [-0.16840126]\n", - " [ 1.14568799]]\n", - "projecting center\n", - "[[-0.15207418]\n", - " [-0.17019519]\n", - " [ 1.14703514]]\n", - "[[-0.19201122]\n", - " [-0.16840126]\n", - " [ 1.14568799]]\n" + "triangulating\n", + "[[1.30151015e-01]\n", + " [4.40582071e-05]\n", + " [1.00013910e+00]]\n", + "Evaluating camera placement\n", + "triangulating\n", + "[[1.30151015e-01]\n", + " [4.40582071e-05]\n", + " [1.00013910e+00]]\n", + "triangulating\n", + "[[2.00392703e-01]\n", + " [1.04118132e-04]\n", + " [9.99784938e-01]]\n", + "triangulating\n", + "[[4.00798827e-01]\n", + " [6.75168854e-05]\n", + " [1.00010712e+00]]\n", + "triangulating\n", + "[[4.01002641e-01]\n", + " [2.83106675e-05]\n", + " [6.99383788e-01]]\n", + "triangulating\n", + "[[6.01252088e-01]\n", + " [1.49496649e-04]\n", + " [6.99542618e-01]]\n", + "triangulating\n", + "[[ 0.60087384]\n", + " [-0.30055477]\n", + " [ 0.69919094]]\n", + "triangulating\n", + "[[ 0.80179156]\n", + " [-0.3006406 ]\n", + " [ 0.69929381]]\n", + "triangulating\n", + "[[ 0.80168179]\n", + " [-0.50115822]\n", + " [ 0.69900768]]\n", + "triangulating\n", + "[[ 0.8032505 ]\n", + " [-0.49923985]\n", + " [ 0.61401023]]\n", + "triangulating\n", + "[[ 0.71034747]\n", + " [-0.49231924]\n", + " [ 0.60753432]]\n", + "triangulating\n", + "[[ 0.70135693]\n", + " [-0.40093755]\n", + " [ 0.59878874]]\n" ] } ], @@ -581,147 +527,22 @@ " np.array(stereo_cam_translation))\n", "image_2 = Image(K_2, camera_2_transform, distortion_coefs_2)\n", "\n", - "scene = StereoScene(image_1, image_2)\n", + "scene = StereoScene(image_1, image_2, None)\n", "app = scene.launch_3D_scene()\n", "app.run_server(host=\"localhost\", port=\"8060\",\n", " debug=True, use_reloader=False)" ] }, { - "cell_type": "code", - "execution_count": 78, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "projecting center\n", - "[[0.]\n", - " [0.]\n", - " [5.]]\n", - "[[0.25]\n", - " [0. ]\n", - " [5. ]]\n" - ] - } - ], - "source": [ - "import plotly.graph_objects as go\n", - "from IPython.display import display, clear_output\n", - "\n", - "camera_1_transform = Transformation(np.eye(3), np.array([0, 0, 0]))\n", - "image_1 = Image(K_1, camera_1_transform, distortion_coefs_1)\n", - "camera_2_transform = Transformation(np.eye(3), np.array([0, 0, 0]))\n", - "# Transformation(cv2.Rodrigues(np.array(stereo_cam_rotation))[0], \n", - "# np.array(stereo_cam_translation))\n", - "image_2 = Image(K_2, camera_2_transform, distortion_coefs_2)\n", - "\n", - "\n", - "\n", - "ball_center = np.array([0, 0, 5]).reshape((3, 1))\n", - "image_with_ball = image_1.project_ball_to_image(ball_center, 0.25)\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 97, - "metadata": {}, - "outputs": [], - "source": [ - "def validate_image_point(point):\n", - " return (\n", - " point[0] < 0\n", - " or point[0] >= IMAGE_SIZE[0]\n", - " or point[1] < 0\n", - " or point[1] >= IMAGE_SIZE[1]\n", - " )\n", - " # raise ValueError(f\"Invalid image point coordinates: out of FOV?\")\n", - "\n", - "\n", - "def project_point_to_image(point, transformation, camera_matrix):\n", - " return camera_matrix @ transformation(point)\n", - "\n", - "\n", - "def normilise_image_point(point, camera_matrix):\n", - " x_normalised = (point[0] - camera_matrix[0, 2]) / camera_matrix[0, 0]\n", - " y_normalised = (point[1] - camera_matrix[1, 2]) / camera_matrix[1, 1]\n", - " return np.array([x_normalised, y_normalised, 1]).reshape(3, 1)\n", - "\n", - "\n", - "def project_sphere_to_image(\n", - " center, radius: int, camera_matrix: np.ndarray, world2cam\n", - ") -> np.ndarray:\n", - " image = np.zeros(IMAGE_SIZE[::-1])\n", - " center = center.reshape((3, 1))\n", - " camera_matrix_inv = np.linalg.inv(camera_matrix)\n", - "\n", - " # projecting center and some edge point to approximate radius after projection\n", - " projected_center = project_point_to_image(center, world2cam, camera_matrix)\n", - " projected_center /= projected_center[2]\n", - " edge_point = center + np.array([radius, 0, 0]).reshape((3, 1))\n", - " projected_edge_point = project_point_to_image(edge_point, world2cam, camera_matrix)\n", - " projected_edge_point /= projected_edge_point[2]\n", - " approx_projected_radius = np.linalg.norm(\n", - " projected_edge_point - projected_center, ord=2\n", - " )\n", - "\n", - " # calculating bounding box for calculations with 1.5 margin\n", - " x_start = int(max(0, projected_center[0].item() - 1.5 * approx_projected_radius))\n", - " y_start = int(max(0, projected_center[1].item() - 1.5 * approx_projected_radius))\n", - " x_stop = int(\n", - " min(IMAGE_SIZE[0], projected_center[0].item() + 1.5 * approx_projected_radius)\n", - " )\n", - " y_stop = int(\n", - " min(IMAGE_SIZE[1], projected_center[1].item() + 1.5 * approx_projected_radius)\n", - " )\n", - "\n", - " for x in range(x_start, x_stop):\n", - " for y in range(y_start, y_stop):\n", - " # back project image point\n", - " world_ray = camera_matrix_inv @ np.array([x, y, 1]).reshape((3, 1))\n", - " # measure distance from the sphere center\n", - " distance = np.linalg.norm(\n", - " np.cross(world_ray.flatten(), center.flatten()), ord=2\n", - " ) / np.linalg.norm(world_ray, ord=2)\n", - " # if back-projected ray intersects with the sphere, paint the pixel in the mask\n", - " if distance <= radius:\n", - " image[y, x] = 1\n", - " return image\n", - "\n", - "\n", - "# image = project_sphere_to_image((-0.25, 0.25, 0.5), 0.02, K_1, None)\n", - "# plt.imshow(image, cmap=\"gray\")\n", - "# plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": 76, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "def triangulate_position(\n", - " points_by_view, world2cam, cam2cam, camera_matrix_1, camera_matrix_2\n", - "):\n", - " world2cam_Rt = np.column_stack((world2cam.R, world2cam.t))\n", - " world2second_cam = cam2cam * world2cam\n", - " world2second_cam_Rt = np.column_stack((world2second_cam.R, world2second_cam.t))\n", - " proj_1 = camera_matrix_1 @ world2cam_Rt\n", - " proj_2 = camera_matrix_2 @ world2second_cam_Rt\n", - " # TODO preserve 4D points?\n", - " res = cv2.triangulatePoints(\n", - " proj_1, proj_2, points_by_view[:, :, 0].T, points_by_view[:, :, 1].T\n", - " )\n", - " for i in range(res.shape[1]):\n", - " res[:, i] /= res[3, i]\n", - " return res[:3, :]" + "[Ссылка на веб интерфейс для визуального контроля](http://localhost:8060)" ] }, { "cell_type": "code", - "execution_count": 95, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -745,75 +566,91 @@ "\n", "\n", "def get_mask_centroid(mask):\n", - " return np.array(center_of_mass(mask))" - ] - }, - { - "cell_type": "code", - "execution_count": 107, - "metadata": {}, - "outputs": [], - "source": [ - "def evaluate_precision(sphere_centers, triangulated_points):\n", - " print(sphere_centers[:, :5])\n", - " print(triangulated_points[:, :5])\n", - " errors = sphere_centers - triangulated_points\n", - " print(\"precision of triangulated points:\")\n", - " print(np.mean(errors * errors))\n", - "\n", - " fig = plt.figure()\n", - " ax = fig.add_subplot(projection='3d')\n", - " ax.scatter(sphere_centers[0, :], sphere_centers[1, :], sphere_centers[2, :], marker='o')\n", - " ax.scatter(triangulated_points[0, :], triangulated_points[1, :], triangulated_points[2, :], marker='o')\n", - "\n", - " ax.set_xlabel('X Label')\n", - " ax.set_ylabel('Y Label')\n", - " ax.set_zlabel('Z Label')\n", - "\n", - " plt.show()" + " return np.array(center_of_mass(mask))\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 51, "metadata": {}, "outputs": [], "source": [ "def evaluate_camera_position(\n", " world2master: Transformation,\n", " master2second: Transformation,\n", - " center_extranctor,\n", + " center_extractor,\n", " camera_matrix_1,\n", " camera_matrix_2,\n", "):\n", - " NUMBER_OF_SPHERES = 5\n", + " NUMBER_OF_SPHERES = 6\n", + "\n", + " image_1 = Image(K_1, world2master, distortion_coefs_1)\n", + " image_2 = Image(K_2, master2second * world2master, distortion_coefs_2)\n", + " stereo_scene = StereoScene(image_1, image_2, None)\n", + "\n", " sphere_centers = []\n", " for x in np.linspace(-TABLE_LENGTH / 2, TABLE_LENGTH / 2, NUMBER_OF_SPHERES):\n", " for y in np.linspace(-TABLE_WIDTH / 2, TABLE_WIDTH / 2, NUMBER_OF_SPHERES):\n", " for z in np.linspace(0, 1, NUMBER_OF_SPHERES):\n", " sphere_centers.append((x, y, z))\n", - " sphere_centers = np.array(sphere_centers).reshape((3, NUMBER_OF_SPHERES**3))\n", - " points = []\n", + "\n", + " sphere_centers = np.array(sphere_centers).T\n", + " points_1 = []\n", + " points_2 = []\n", + " valid_sphere_centers = []\n", " world2second = master2second * world2master\n", - " for i in range(sphere_centers.shape[1]):\n", - " if i % 20 == 0:\n", - " print(i)\n", - " mask_1 = project_sphere_to_image(\n", - " sphere_centers[:, i : (i + 1)], 0.02, K_1, world2master\n", - " )\n", - " mask_2 = project_sphere_to_image(\n", - " sphere_centers[:, i : (i + 1)], 0.02, K_2, world2second\n", + "\n", + " for i in range(sphere_centers.shape[1]): \n", + " mask_1, mask_2 = stereo_scene.project_ball_to_images(\n", + " sphere_centers[:, i : (i + 1)], 0.02\n", " )\n", - " points.append((center_extranctor(mask_1), center_extranctor(mask_2)))\n", - " triangulated_points = triangulate_position(\n", - " np.array(points), world2master, master2second, camera_matrix_1, camera_matrix_2\n", - " )\n", - " print(f\"triangulated_points shape is {triangulated_points.shape}\")\n", "\n", - " evaluate_precision(sphere_centers, triangulated_points)\n", - " print(\"evalution complete\")\n", + " if np.sum(mask_1) == 0 or np.sum(mask_2) == 0:\n", + " continue\n", + "\n", + " points_1.append(center_extractor(mask_1))\n", + " points_2.append(center_extractor(mask_2))\n", + " valid_sphere_centers.append(sphere_centers[:, i])\n", + "\n", + " points_1 = np.array(points_1).T\n", + " points_2 = np.array(points_2).T\n", + "\n", + " sphere_centers = np.array(valid_sphere_centers).T\n", "\n", + " triangulated_points = stereo_scene.triangulate_position(points_1, points_2, world2master, master2second)\n", "\n", + "\n", + " # Calculate the Euclidean distance between the true and triangulated points\n", + " distances = np.linalg.norm(sphere_centers - triangulated_points, axis=0)\n", + " \n", + " # Calculate mean and standard deviation of the distances\n", + " mean_distance = np.mean(distances)\n", + " std_distance = np.std(distances)\n", + " \n", + " print(f\"Mean distance error: {mean_distance}\")\n", + " print(f\"Standard deviation of distance error: {std_distance}\")\n", + " print(\"evalution complete\")\n", + " print()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "triangulating\n", + "Mean distance error: 0.005709064422212816\n", + "Standard deviation of distance error: 0.008145795684027528\n", + "evalution complete\n", + "\n" + ] + } + ], + "source": [ "# main test: opposite camera placement\n", "# world2master = Transformation(Rotation.from_euler(\"xyz\", [0, -45, -90], degrees=True).as_matrix(), np.array([-TABLE_LENGTH/4, TABLE_WIDTH, 0.5]))\n", "# master2second = Transformation(Rotation.from_euler(\"xyz\", [], degrees=True).as_matrix(), np.array([0, 0, 0]))\n", @@ -823,11 +660,7 @@ " np.array([-TABLE_LENGTH / 4, TABLE_WIDTH, 0.5]),\n", ")\n", "master2second = Transformation(\n", - " Rotation.from_euler(\n", - " \"xyz\", [0.01175419895518242, 2.170836441913732, 2.19333242876324], degrees=False\n", - " ).as_matrix(),\n", - " np.array([-0.06677747450343367, -1.174973690204363, 1.140354306665756]),\n", - ")\n", + " cv2.Rodrigues(np.array(stereo_cam_rotation))[0], np.array(stereo_cam_translation))\n", "evaluate_camera_position(world2master, master2second, get_mask_center, K_1, K_2)" ] }, @@ -837,140 +670,13 @@ "metadata": {}, "outputs": [], "source": [ - "import plotly.graph_objects as go\n", - "\n", - "fig = go.Figure()\n", - "\n", - "# Add a table (as a plane)\n", - "fig.add_trace(go.Mesh3d(x=[0, 1, 1, 0], y=[0, 0, 1, 1], z=[0, 0, 0, 0], color='lightblue', opacity=0.50))\n", - "\n", - "# Add some spheres (balls)\n", - "import numpy as np\n", - "def create_sphere(center, radius, color):\n", - " u, v = np.mgrid[0:2*np.pi:20j, 0:np.pi:10j]\n", - " x = center[0] + radius * np.cos(u) * np.sin(v)\n", - " y = center[1] + radius * np.sin(u) * np.sin(v)\n", - " z = center[2] + radius * np.cos(v)\n", - " return go.Surface(x=x, y=y, z=z, colorscale=[[0, color], [1, color]])\n", - "\n", - "fig.add_trace(create_sphere([0.5, 0.5, 0.5], 0.1, 'red'))\n", - "fig.add_trace(create_sphere([0.7, 0.3, 0.2], 0.1, 'blue'))\n", - "\n", - "fig.show()\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import plotly.graph_objects as go\n", - "import numpy as np\n", - "\n", - "def display_plane(normal, size, origin):\n", - " \"\"\"Display a plane in the shape of a rectangle.\n", - " \n", - " Args:\n", - " normal (tuple): Normal vector of the plane (nx, ny, nz).\n", - " size (tuple): Size of the rectangle (width, height).\n", - " origin (tuple): Origin of the rectangle (x, y, z).\n", - " \"\"\"\n", - " normal = np.array(normal) / np.linalg.norm(normal) # Normalize normal vector\n", - " u = np.array([1, 0, 0]) if abs(normal[0]) < 0.9 else np.array([0, 1, 0])\n", - " v = np.cross(normal, u)\n", - " u = np.cross(v, normal)\n", - " u, v = u / np.linalg.norm(u), v / np.linalg.norm(v)\n", - " \n", - " w, h = size\n", - " corners = np.array([\n", - " origin + w/2 * u + h/2 * v,\n", - " origin - w/2 * u + h/2 * v,\n", - " origin - w/2 * u - h/2 * v,\n", - " origin + w/2 * u - h/2 * v,\n", - " ])\n", - " \n", - " x, y, z = corners[:, 0], corners[:, 1], corners[:, 2]\n", - " \n", - " return go.Mesh3d(\n", - " x=[*x, x[0]], y=[*y, y[0]], z=[*z, z[0]],\n", - " color='lightblue', opacity=0.5, alphahull=0\n", - " )\n", - "\n", - "def display_ball(position, radius, color):\n", - " \"\"\"Display a ball (sphere).\n", - " \n", - " Args:\n", - " position (tuple): (x, y, z) coordinates of the center.\n", - " radius (float): Radius of the sphere.\n", - " color (str): Color of the sphere.\n", - " \"\"\"\n", - " u, v = np.mgrid[0:2*np.pi:20j, 0:np.pi:10j]\n", - " x = position[0] + radius * np.cos(u) * np.sin(v)\n", - " y = position[1] + radius * np.sin(u) * np.sin(v)\n", - " z = position[2] + radius * np.cos(v)\n", - " \n", - " return go.Surface(x=x, y=y, z=z, colorscale=[[0, color], [1, color]], showscale=False)\n", - "\n", - "def display_parallelepiped(size, direction, origin=(0,0,0)):\n", - " \"\"\"Display a parallelepiped.\n", - " \n", - " Args:\n", - " size (tuple): (width, height, depth).\n", - " direction (tuple): A vector representing the \"facing\" direction.\n", - " origin (tuple): Bottom-left corner of the parallelepiped.\n", - " \"\"\"\n", - " direction = np.array(direction) / np.linalg.norm(direction)\n", - " u = np.array([1, 0, 0]) if abs(direction[0]) < 0.9 else np.array([0, 1, 0])\n", - " v = np.cross(direction, u)\n", - " u = np.cross(v, direction)\n", - " u, v = u / np.linalg.norm(u), v / np.linalg.norm(v)\n", - "\n", - " w, h, d = size\n", - " corners = np.array([\n", - " origin,\n", - " origin + w * u,\n", - " origin + h * v,\n", - " origin + d * direction,\n", - " origin + w * u + h * v,\n", - " origin + w * u + d * direction,\n", - " origin + h * v + d * direction,\n", - " origin + w * u + h * v + d * direction\n", - " ])\n", - " \n", - " x, y, z = corners[:, 0], corners[:, 1], corners[:, 2]\n", - "\n", - " faces = [\n", - " [0, 1, 4, 2], # Bottom\n", - " [0, 1, 5, 3], # Side\n", - " [1, 4, 7, 5], # Side\n", - " [2, 4, 7, 6], # Side\n", - " [0, 2, 6, 3], # Side\n", - " [3, 5, 7, 6] # Top\n", - " ]\n", - "\n", - " return go.Mesh3d(\n", - " x=x, y=y, z=z,\n", - " i=[face[0] for face in faces],\n", - " j=[face[1] for face in faces],\n", - " k=[face[2] for face in faces],\n", - " color=\"orange\", opacity=0.7\n", - " )\n", - "\n", - "# Create figure\n", - "fig = go.Figure()\n", - "\n", - "# Add plane\n", - "fig.add_trace(display_plane((0, 0, 1), (2, 1), (0, 0, 0)))\n", - "\n", - "# Add ball\n", - "fig.add_trace(display_ball((0.5, 0.5, 0.5), 0.2, \"red\"))\n", - "\n", - "# Add parallelepiped\n", - "fig.add_trace(display_parallelepiped((1, 0.5, 0.3), (1, 1, 1), (1, 1, 0)))\n", - "\n", - "fig.update_layout(scene=dict(aspectmode=\"data\"))\n", - "fig.show()\n" + "world2master = Transformation(\n", + " Rotation.from_euler(\"xyz\", [0, -45, -90], degrees=True).as_matrix(),\n", + " np.array([-TABLE_LENGTH / 4, TABLE_WIDTH, 0.5]),\n", + ")\n", + "master2second = Transformation(\n", + " cv2.Rodrigues(np.array(stereo_cam_rotation))[0], np.array(stereo_cam_translation))\n", + "evaluate_camera_position(world2master, master2second, get_mask_center, K_1, K_2)" ] } ], @@ -990,7 +696,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.3" + "version": "3.10.12" } }, "nbformat": 4, From c2395a46952c5c35bacc05acee03075bb693be72 Mon Sep 17 00:00:00 2001 From: dfbakin Date: Sat, 8 Feb 2025 04:08:09 +0300 Subject: [PATCH 8/9] tmp --- .../scripts/camera_placement_params.yaml | 25 + .../camera/scripts/camera_placement_visual.py | 586 ++++++++++++++++++ research.ipynb | 139 ++--- 3 files changed, 658 insertions(+), 92 deletions(-) create mode 100644 packages/camera/scripts/camera_placement_params.yaml create mode 100644 packages/camera/scripts/camera_placement_visual.py diff --git a/packages/camera/scripts/camera_placement_params.yaml b/packages/camera/scripts/camera_placement_params.yaml new file mode 100644 index 0000000..09a7696 --- /dev/null +++ b/packages/camera/scripts/camera_placement_params.yaml @@ -0,0 +1,25 @@ +intrinsics: + image_size: [1200, 1920] + 1: + camera_matrix: [672.2824725267757, 0, 984.0472159818853, 0, 672.6886411532304, 602.96669930345, 0, 0, 1] + distortion_coefs: [-0.09715103386082896, 0.06788948036532018, -0.0007157453506997161, 0.0003048354358359307, -0.003636308978789861] + 2: + camera_matrix: [685.7143789189881, 0, 991.0247637161314, 0, 686.3020333004097, 601.2442243349392, 0, 0, 1] + distortion_coefs: [-0.09781628655937251, 0.07153618281495966, -0.001066517414175782, 0.0004679942401339674, -0.003645360450147547] + +camera_positions: + 0: + 1: + rotation: [0, 0, 90] + translation: [0.5, 1, 1] + 2: + rotation: [0, 0, -90] + translation: [0.5, -1, -1] + + 1: + 1: + rotation: [0, 90, 45] + translation: [0.5, 1, 1] + 2: + rotation: [0, 90, 45] + translation: [0.5, -1, -1] diff --git a/packages/camera/scripts/camera_placement_visual.py b/packages/camera/scripts/camera_placement_visual.py new file mode 100644 index 0000000..79b49c9 --- /dev/null +++ b/packages/camera/scripts/camera_placement_visual.py @@ -0,0 +1,586 @@ +import argparse +import json +import os +from typing import Dict, List, Tuple + +import cv2 +import numpy as np +import rosbag2_py +import yaml +from cv_bridge import CvBridge +from geometry_msgs.msg import Point, TransformStamped +from rclpy.serialization import serialize_message +from scipy.spatial.transform import Rotation +from sensor_msgs.msg import CameraInfo +from visualization_msgs.msg import ImageMarker, Marker + +SEC_MULTIPLIER = 10**9 +MS_MULTIPLIER = 10**6 +MCS_MULTIPLIER = 10**3 +NANO_MULTIPLIER = 1 + +TABLE_LENGTH = 2.74 +TABLE_WIDTH = 1.525 +FPS_LATENCY_MS = 15 + +last_marker_id = 0 +last_table_marker_id = None + + +def get_new_marker_id() -> int: + global last_marker_id + last_marker_id += 1 + return last_marker_id + + +class CameraParameters: + def __init__( + self, + image_size, + camera_matrix, + dist_coefs, + camera_id, + rotation_vector, + translation_vector, + ): + self.camera_matrix = np.array(camera_matrix, dtype=float).reshape((3, 3)) + self.dist_coefs = np.array(dist_coefs, dtype=float) + self.camera_id = camera_id + self.image_size = image_size + + self.mapx, self.mapy = cv2.initUndistortRectifyMap( + self.camera_matrix, + self.dist_coefs, + None, + self.camera_matrix, + self.image_size, + cv2.CV_32FC1, + ) + + self.rotation_vector = np.array(rotation_vector, dtype=float) + self.translation_vector = np.array(translation_vector, dtype=float) + self.rotation_matrix = cv2.Rodrigues(self.rotation_vector.reshape((3, 1)))[0] + + self.static_transformation = TransformStamped() + self.static_transformation.child_frame_id = f"camera_{self.camera_id}" + self.static_transformation.header.frame_id = "table" + + self.static_transformation.transform.translation.x = self.translation_vector[0] + self.static_transformation.transform.translation.y = self.translation_vector[1] + self.static_transformation.transform.translation.z = self.translation_vector[2] + + qx, qy, qz, qw = Rotation.from_matrix(self.rotation_matrix).as_quat().tolist() + + self.static_transformation.transform.rotation.x = qx + self.static_transformation.transform.rotation.y = qy + self.static_transformation.transform.rotation.z = qz + self.static_transformation.transform.rotation.w = qw + + def undistort(self, image: cv2.Mat) -> cv2.Mat: + return cv2.remap(image, self.mapx, self.mapy, cv2.INTER_NEAREST) + + def publish_camera_info(self, writer: rosbag2_py.SequentialWriter, current_time: int) -> None: + camera_info_msg = CameraInfo() + camera_info_msg.header.frame_id = f"camera_{self.camera_id}" + camera_info_msg.header.stamp.sec = current_time % SEC_MULTIPLIER + camera_info_msg.header.stamp.nanosec = current_time // SEC_MULTIPLIER + camera_info_msg.height = self.image_size[1] + camera_info_msg.width = self.image_size[0] + + camera_info_msg.distortion_model = "plumb_bob" + camera_info_msg.d = self.dist_coefs.flatten().tolist() + + camera_info_msg.k = self.camera_matrix.flatten().tolist() + camera_info_msg.p = self.camera_matrix.flatten().tolist() + + writer.write( + f"/camera_{self.camera_id}/info", + serialize_message(camera_info_msg), + current_time, + ) + + def publish_transform(self, writer: rosbag2_py.SequentialWriter, current_time: int) -> None: + self.static_transformation.header.stamp.sec = current_time % SEC_MULTIPLIER + self.static_transformation.header.stamp.nanosec = current_time // SEC_MULTIPLIER + + writer.write("/tf", serialize_message(self.static_transformation), current_time) + + +def init_writer(export_file: str) -> rosbag2_py.SequentialWriter: + writer = rosbag2_py.SequentialWriter() + writer.open( + rosbag2_py.StorageOptions(uri=export_file, storage_id="mcap"), + rosbag2_py.ConverterOptions( + input_serialization_format="cdr", output_serialization_format="cdr" + ), + ) + + writer.create_topic( + rosbag2_py.TopicMetadata( + name="/triangulation/ball_marker", + type="visualization_msgs/msg/Marker", + serialization_format="cdr", + ) + ) + writer.create_topic( + rosbag2_py.TopicMetadata( + name="/triangulation/ball_table_projection", + type="visualization_msgs/msg/Marker", + serialization_format="cdr", + ) + ) + writer.create_topic( + rosbag2_py.TopicMetadata( + name="/triangulation/trajectory", + type="visualization_msgs/msg/Marker", + serialization_format="cdr", + ) + ) + writer.create_topic( + rosbag2_py.TopicMetadata( + name="/triangulation/intersection_points", + type="visualization_msgs/msg/Marker", + serialization_format="cdr", + ) + ) + writer.create_topic( + rosbag2_py.TopicMetadata( + name="/triangulation/table_plane", + type="visualization_msgs/msg/Marker", + serialization_format="cdr", + ) + ) + writer.create_topic( + rosbag2_py.TopicMetadata( + name="/tf", + type="geometry_msgs/msg/TransformStamped", + serialization_format="cdr", + ) + ) + + for i in range(2): + writer.create_topic( + rosbag2_py.TopicMetadata( + name=f"/camera_{i + 1}/image", + type="sensor_msgs/msg/CompressedImage", + serialization_format="cdr", + ) + ) + writer.create_topic( + rosbag2_py.TopicMetadata( + name=f"/camera_{i + 1}/ball_center", + type="visualization_msgs/msg/ImageMarker", + serialization_format="cdr", + ) + ) + writer.create_topic( + rosbag2_py.TopicMetadata( + name=f"/camera_{i + 1}/info", + type="sensor_msgs/msg/CameraInfo", + serialization_format="cdr", + ) + ) + + return writer + + +def init_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser() + + parser.add_argument( + "--params-file", help="yaml file with intrinsic parameters and cameras' positions", required=True + ) + parser.add_argument("--export", help="some_file.mcap", required=True) + + return parser + + +def init_ball_marker( + marker_id: int, current_time: int, position: List[int], camera_id: int, ttl=100 +) -> Marker: + msg = Marker() + msg.header.frame_id = f"camera_{camera_id}" + msg.header.stamp.sec = current_time // SEC_MULTIPLIER + msg.header.stamp.nanosec = current_time % SEC_MULTIPLIER + msg.ns = "ball_markers" + msg.id = marker_id + msg.type = Marker.SPHERE + msg.action = Marker.ADD + msg.pose.position.x, msg.pose.position.y, msg.pose.position.z = position + + msg.pose.orientation.x = 0.0 + msg.pose.orientation.y = 0.0 + msg.pose.orientation.z = 0.0 + msg.pose.orientation.w = 1.0 + + msg.scale.x = 0.04 + msg.scale.y = 0.04 + msg.scale.z = 0.04 + + msg.color.r = 1.0 + msg.color.g = 0.5 + msg.color.b = 0.0 + msg.color.a = 1.0 + + msg.lifetime.nanosec = ttl * 10**6 # ttl in milliseconds + + return msg + + +def init_detection_center_marker( + marker_id: int, current_time: int, position: List[int], camera_id: int, ttl=100 +) -> ImageMarker: + msg = ImageMarker() + msg.header.frame_id = f"camera_{camera_id}" + msg.header.stamp.sec = current_time // SEC_MULTIPLIER + msg.header.stamp.nanosec = current_time % SEC_MULTIPLIER + msg.ns = "ball_markers" + msg.id = marker_id + msg.type = ImageMarker.CIRCLE + msg.action = ImageMarker.ADD + + msg.position.x = float(position[0]) + msg.position.y = float(position[1]) + msg.position.z = 0.0 + + msg.scale = 3.0 + msg.filled = 255 + + msg.fill_color.r = 1.0 # orange color + msg.fill_color.g = 0.0 + msg.fill_color.b = 0.0 + msg.fill_color.a = 1.0 # alpha (1.0 = opaque, 0.0 = transparent) + + msg.outline_color.r = 1.0 # orange color + msg.outline_color.g = 0.0 + msg.outline_color.b = 0.0 + msg.outline_color.a = 1.0 # alpha (1.0 = opaque, 0.0 = transparent) + + msg.lifetime.nanosec = ttl * 10**6 # ttl = 10ms + + return msg + + +def simulate( + writer: rosbag2_py.SequentialWriter, + rgb_sources, + filename_to_info, + trajectory_predictions, + intrinsics, + R: np.ndarray, + T: np.ndarray, +) -> None: + filenames_to_publish = sorted(filename_to_info.keys()) + cv_bridge_instance = CvBridge() + + current_simulation_time = 0 # in nanoseconds + for i in range(len(filenames_to_publish)): + if i % 10 == 0: + publish_table_plain(writer, current_simulation_time) + filename = filenames_to_publish[i] + for camera_idx in range(2): + # load and segmentate image + image = cv2.imread(os.path.join(rgb_sources[camera_idx], filename)) + + image = intrinsics[camera_idx].undistort(image) + + # prepare CompressedImages + camera_feed_msg = cv_bridge_instance.cv2_to_compressed_imgmsg(image, dst_format="jpeg") + camera_feed_msg.header.frame_id = f"camera_{camera_idx + 1}" + camera_feed_msg.header.stamp.sec = current_simulation_time // SEC_MULTIPLIER + camera_feed_msg.header.stamp.nanosec = current_simulation_time % SEC_MULTIPLIER + + # publish images + writer.write( + f"/camera_{camera_idx + 1}/image", + serialize_message(camera_feed_msg), + current_simulation_time, + ) + # publish camera info and transformations + intrinsics[camera_idx].publish_camera_info(writer, current_simulation_time) + intrinsics[camera_idx].publish_transform(writer, current_simulation_time) + + center_detection = init_detection_center_marker( + get_new_marker_id(), + current_simulation_time, + filename_to_info[filename]["image_points"][camera_idx], + camera_idx + 1, + ttl=50, + ) + writer.write( + f"/camera_{camera_idx + 1}/ball_center", + serialize_message(center_detection), + current_simulation_time, + ) + + current_point = np.array( + filename_to_info[filename]["triangulated_point"], dtype=float + ).reshape((3, 1)) + ball_marker = init_ball_marker( + get_new_marker_id(), + current_simulation_time, + (R @ current_point - T).flatten().tolist(), + current_point.flatten().tolist(), + ttl=FPS_LATENCY_MS, + ) + ball_marker.header.frame_id = "table" + writer.write( + "/triangulation/ball_marker", + serialize_message(ball_marker), + current_simulation_time, + ) + + projection_marker = ball_marker + projection_marker.id = get_new_marker_id() + projection_marker.scale.z = 0.001 + projection_marker.pose.position.z = 0.0 + projection_marker.color.r = 1.0 + writer.write( + "/triangulation/ball_table_projection", + serialize_message(projection_marker), + current_simulation_time, + ) + + if trajectory_predictions: + publish_predicted_trajectory( + writer, trajectory_predictions, filename, current_simulation_time + ) + current_simulation_time += FPS_LATENCY_MS * MS_MULTIPLIER # 15 ms between the frames + + +def init_camera_info( + writer: rosbag2_py.SequentialWriter, params_path: str, camera_ids=[1, 2] +) -> Tuple[List[CameraParameters], np.ndarray]: + intrinsics = [] + for camera_id in camera_ids: + with open(params_path, mode="r", encoding="utf-8") as file: + data = yaml.safe_load(file) + intrinsics.append( + CameraParameters( + data["parameters"][camera_id]["image_size"], + data["parameters"][camera_id]["camera_matrix"], + data["parameters"][camera_id]["distortion_coefs"], + camera_id, + data["parameters"][camera_id]["rotation"], + data["parameters"][camera_id]["translation"], + ) + ) + complanar_aruco_points = np.array(data["triangulated_common_points"], dtype=np.float64) + + centroid = np.mean(complanar_aruco_points, axis=0) + _, _, VT = np.linalg.svd(complanar_aruco_points[:10] - centroid, full_matrices=False) + print("normal is", VT[-1, :]) + return intrinsics, VT[-1, :] + + +def publish_table_plain(writer: rosbag2_py.SequentialWriter, simulation_time: int) -> None: + global last_table_marker_id + if last_table_marker_id: + marker = Marker() + marker.id = last_table_marker_id + marker.action = 2 # DELETE + writer.write("/triangulation/table_plane", serialize_message(marker), simulation_time) + + # publish blue table plain + marker = Marker() + marker.header.frame_id = "table" + marker.id = get_new_marker_id() + last_table_marker_id = marker.id + marker.type = marker.CUBE + marker.action = marker.ADD + marker.scale.x = TABLE_LENGTH + marker.scale.y = TABLE_WIDTH + marker.scale.z = 0.01 + + marker.color.r = 0.0 + marker.color.g = 0.0 + marker.color.b = 0.8 + marker.color.a = 0.3 + + marker.pose.position.x = 0.0 + marker.pose.position.y = 0.0 + marker.pose.position.z = 0.0 + ( + marker.pose.orientation.x, + marker.pose.orientation.y, + marker.pose.orientation.z, + marker.pose.orientation.w, + ) = Rotation.from_euler("xyz", [0, 0, 90], degrees=True).as_quat().tolist() + writer.write("/triangulation/table_plane", serialize_message(marker), simulation_time) + + # publish white border + marker = Marker() + marker.id = get_new_marker_id() + marker.header.frame_id = "table" + marker.type = marker.LINE_STRIP + marker.action = marker.ADD + marker.scale.x = 0.01 + + marker.color.r = 1.0 + marker.color.g = 1.0 + marker.color.b = 1.0 + marker.color.a = 0.9 + + ( + marker.pose.orientation.x, + marker.pose.orientation.y, + marker.pose.orientation.z, + marker.pose.orientation.w, + ) = Rotation.from_euler("xyz", [0, 0, 90], degrees=True).as_quat().tolist() + + coords = [ + (-TABLE_WIDTH / 2, -TABLE_LENGTH / 2), + (-TABLE_WIDTH / 2, TABLE_LENGTH / 2), + (TABLE_WIDTH / 2, TABLE_LENGTH / 2), + (TABLE_WIDTH / 2, -TABLE_LENGTH / 2), + (-TABLE_WIDTH / 2, -TABLE_LENGTH / 2), + ] + + for cur_y, cur_x in coords: + new_point = Point() + new_point.x = cur_x + new_point.y = cur_y + marker.points.append(new_point) + + writer.write("/triangulation/table_plane", serialize_message(marker), simulation_time) + + # publish length line + marker.id = get_new_marker_id() + marker.points = [] + + coords = [ + (0.0, -TABLE_LENGTH / 2), + (0.0, TABLE_LENGTH / 2), + ] + + for cur_y, cur_x in coords: + new_point = Point() + new_point.x = cur_x + new_point.y = cur_y + marker.points.append(new_point) + + writer.write("/triangulation/table_plane", serialize_message(marker), simulation_time) + + # publish width line + marker.id = get_new_marker_id() + marker.points = [] + + coords = [ + (-TABLE_WIDTH / 2, 0.0), + (TABLE_WIDTH / 2, 0.0), + ] + + for cur_y, cur_x in coords: + new_point = Point() + new_point.x = cur_x + new_point.y = cur_y + marker.points.append(new_point) + + writer.write("/triangulation/table_plane", serialize_message(marker), simulation_time) + + +def get_cam2world_transform( + table_plane_normal: np.ndarray, table_orientation_points: List[List[float]] +) -> Tuple[np.ndarray]: + edge_table_orient_point = np.array(table_orientation_points[0], dtype=float) + middle_table_orient_point = np.array(table_orientation_points[1], dtype=float) + x_vector = middle_table_orient_point - edge_table_orient_point + z_vector = ( + table_plane_normal + - (table_plane_normal.dot(x_vector) / np.linalg.norm(x_vector, ord=2)) * x_vector + ) + # z_vector = table_plane_normal + y_vector = np.cross(x_vector, z_vector) + + x_vector /= np.linalg.norm(x_vector, ord=2) + y_vector /= np.linalg.norm(y_vector, ord=2) + z_vector /= np.linalg.norm(z_vector, ord=2) + + R = np.concatenate( + (y_vector.reshape((3, 1)), x_vector.reshape((3, 1)), z_vector.reshape((3, 1))), + axis=1, + ) + table_line_middle = (edge_table_orient_point + middle_table_orient_point) / 2 + T = table_line_middle.reshape((3, 1)) + R_inv = np.linalg.inv(R) + + return R, T, R_inv, R_inv @ T, table_line_middle + + +def publish_cam2table_transform( + writer: rosbag2_py.SequentialWriter, R: np.ndarray, T: np.ndarray +) -> None: + static_transformation = TransformStamped() + static_transformation.child_frame_id = "table" + static_transformation.header.frame_id = "world" + + static_transformation.transform.translation.x = T[0, 0] + static_transformation.transform.translation.y = T[1, 0] + static_transformation.transform.translation.z = T[2, 0] + static_transformation.header.stamp.sec = 0 + static_transformation.header.stamp.nanosec = 0 + + qx, qy, qz, qw = Rotation.from_matrix(R).as_quat().tolist() + + static_transformation.transform.rotation.x = qx + static_transformation.transform.rotation.y = qy + static_transformation.transform.rotation.z = qz + static_transformation.transform.rotation.w = qw + + writer.write("/tf", serialize_message(static_transformation), 0) + + +if __name__ == "__main__": + # init all modules + parser = init_parser() + args = parser.parse_args() + writer = init_writer(args.export) + + table_plane_normal = np.array([0, 0, 1]) + publish_table_plain(writer, 100) + + + with open(args.params_file, mode="r", encoding="utf-8") as yaml_params_file: + params = yaml.safe_load(yaml_params_file) + yaml_intrinsics_dics = params["intrinsics"] + + + for position_idx in params["camera_positions"].keys(): + R_1, T_1 = params["camera_positions"][position_idx][1]["rotation"], params["camera_positions"][position_idx][1]["translation"] + R_1 = np.array(R_1) / 180 * np.pi + T_1 = np.array(T_1) + + R_2, T_2 = params["camera_positions"][position_idx][2]["rotation"], params["camera_positions"][position_idx][2]["translation"] + R_2 = np.array(R_2) / 180 * np.pi + T_2 = np.array(T_2) + + intrinsics = [CameraParameters(yaml_intrinsics_dics["image_size"], yaml_intrinsics_dics[1]["camera_matrix"], yaml_intrinsics_dics[1]["distortion_coefs"], 1, R_1, T_1), + CameraParameters(yaml_intrinsics_dics["image_size"], yaml_intrinsics_dics[2]["camera_matrix"], yaml_intrinsics_dics[2]["distortion_coefs"], 2, R_2, T_2)] + for cam_params in intrinsics: + cam_params.publish_camera_info(writer, 10000 * position_idx) + + + + + + + # intrinsics, table_plane_normal = init_camera_info( + # writer, args.intrinsic_params, [1, 2] + # ) + + # R, T, R2table, T2table, table_center = get_cam2world_transform( + # table_plane_normal, data["table_orientation_points"] + # ) + # publish_cam2table_transform(writer, R, T) + # publish_cam2table_transform(writer, np.eye(3), np.zeros((3, 1))) + + # simulate( + # writer, + # args.rgb_sources, + # data["triangulated_points"], + # trajectory_predictions, + # intrinsics, + # R2table, + # T2table, + # ) + del writer diff --git a/research.ipynb b/research.ipynb index 35680d4..6aace41 100644 --- a/research.ipynb +++ b/research.ipynb @@ -17,7 +17,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -32,7 +32,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -95,7 +95,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -124,7 +124,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -233,16 +233,16 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 35, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" }, @@ -272,7 +272,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -281,13 +281,6 @@ "\n", "\n", "def display_ball(position, radius, color):\n", - " \"\"\"Display a ball (sphere).\n", - " \n", - " Args:\n", - " position (tuple): (x, y, z) coordinates of the center.\n", - " radius (float): Radius of the sphere.\n", - " color (str): Color of the sphere.\n", - " \"\"\"\n", " u, v = np.mgrid[0:2*np.pi:20j, 0:np.pi:10j]\n", " x = position[0] + radius * np.cos(u) * np.sin(v)\n", " y = position[1] + radius * np.sin(u) * np.sin(v)\n", @@ -295,6 +288,10 @@ " \n", " return go.Surface(x=x, y=y, z=z, colorscale=[[0, color], [1, color]], showscale=False)\n", "\n", + "def display_camera(position, 3d_rotation_matrix, color=\"red\", length=0.1):\n", + " pass\n", + "\n", + "\n", "def display_table(length, width):\n", " corners = np.array([\n", " [-length / 2, -width / 2],\n", @@ -310,12 +307,36 @@ " z=[[0, 0, 0, 0]],\n", " colorscale=[[0, 'brown'], [1, 'brown']],\n", " showscale=False\n", - " )\n" + " )\n", + "\n", + "\n", + "def get_bbox(mask: np.ndarray) -> List[float]:\n", + " if not np.any(mask):\n", + " return [0., 0., 0., 0.]\n", + " # x_min, y_min, x_max, y_max\n", + " horizontal_indicies = np.where(np.any(mask, axis=0))[0]\n", + " vertical_indicies = np.where(np.any(mask, axis=1))[0]\n", + " x1, x2 = horizontal_indicies[[0, -1]]\n", + " y1, y2 = vertical_indicies[[0, -1]]\n", + " bbox = list(map(float, [x1, y1, x2, y2]))\n", + " return bbox\n", + "\n", + "\n", + "def get_mask_center(mask):\n", + " bbox = get_bbox(mask)\n", + " centroid_x = (bbox[0] + bbox[2]) / 2\n", + " centroid_y = (bbox[1] + bbox[3]) / 2\n", + " return np.array([centroid_x, centroid_y])\n", + "\n", + "\n", + "def get_mask_centroid(mask):\n", + " return np.array(center_of_mass(mask))\n", + "\n" ] }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -393,6 +414,8 @@ " [*(-second_camera_direction).tolist()]))\n", " fig_3d.add_trace(go.Cone(**second_cam_cone_position_direction,\n", " colorscale=[[0, \"blue\"], [1, \"blue\"]]))\n", + " \n", + " display_arrow([0, 0, 0], [1, 0, 0], \"red\", figure=fig_3d, length=10)\n", "\n", " fig_left = go.Figure(data=go.Heatmap(z=left_image),\n", " layout=go.Layout(\n", @@ -440,7 +463,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -458,7 +481,7 @@ " " ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -476,47 +499,7 @@ "triangulating\n", "[[1.30151015e-01]\n", " [4.40582071e-05]\n", - " [1.00013910e+00]]\n", - "triangulating\n", - "[[2.00392703e-01]\n", - " [1.04118132e-04]\n", - " [9.99784938e-01]]\n", - "triangulating\n", - "[[4.00798827e-01]\n", - " [6.75168854e-05]\n", - " [1.00010712e+00]]\n", - "triangulating\n", - "[[4.01002641e-01]\n", - " [2.83106675e-05]\n", - " [6.99383788e-01]]\n", - "triangulating\n", - "[[6.01252088e-01]\n", - " [1.49496649e-04]\n", - " [6.99542618e-01]]\n", - "triangulating\n", - "[[ 0.60087384]\n", - " [-0.30055477]\n", - " [ 0.69919094]]\n", - "triangulating\n", - "[[ 0.80179156]\n", - " [-0.3006406 ]\n", - " [ 0.69929381]]\n", - "triangulating\n", - "[[ 0.80168179]\n", - " [-0.50115822]\n", - " [ 0.69900768]]\n", - "triangulating\n", - "[[ 0.8032505 ]\n", - " [-0.49923985]\n", - " [ 0.61401023]]\n", - "triangulating\n", - "[[ 0.71034747]\n", - " [-0.49231924]\n", - " [ 0.60753432]]\n", - "triangulating\n", - "[[ 0.70135693]\n", - " [-0.40093755]\n", - " [ 0.59878874]]\n" + " [1.00013910e+00]]\n" ] } ], @@ -542,36 +525,7 @@ }, { "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def get_bbox(mask: np.ndarray) -> List[float]:\n", - " if not np.any(mask):\n", - " return [0., 0., 0., 0.]\n", - " # x_min, y_min, x_max, y_max\n", - " horizontal_indicies = np.where(np.any(mask, axis=0))[0]\n", - " vertical_indicies = np.where(np.any(mask, axis=1))[0]\n", - " x1, x2 = horizontal_indicies[[0, -1]]\n", - " y1, y2 = vertical_indicies[[0, -1]]\n", - " bbox = list(map(float, [x1, y1, x2, y2]))\n", - " return bbox\n", - "\n", - "\n", - "def get_mask_center(mask):\n", - " bbox = get_bbox(mask)\n", - " centroid_x = (bbox[0] + bbox[2]) / 2\n", - " centroid_y = (bbox[1] + bbox[3]) / 2\n", - " return np.array([centroid_x, centroid_y])\n", - "\n", - "\n", - "def get_mask_centroid(mask):\n", - " return np.array(center_of_mass(mask))\n" - ] - }, - { - "cell_type": "code", - "execution_count": 51, + "execution_count": 62, "metadata": {}, "outputs": [], "source": [ @@ -614,8 +568,8 @@ "\n", " points_1 = np.array(points_1).T\n", " points_2 = np.array(points_2).T\n", - "\n", " sphere_centers = np.array(valid_sphere_centers).T\n", + " print(sphere_centers.shape)\n", "\n", " triangulated_points = stereo_scene.triangulate_position(points_1, points_2, world2master, master2second)\n", "\n", @@ -635,13 +589,14 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 65, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "(3, 54)\n", "triangulating\n", "Mean distance error: 0.005709064422212816\n", "Standard deviation of distance error: 0.008145795684027528\n", From 289b37b5568ae6dae1cfa9cb0a92cb914c828308 Mon Sep 17 00:00:00 2001 From: dfbakin Date: Sun, 9 Feb 2025 13:03:42 +0000 Subject: [PATCH 9/9] feat: done main placement --- .gitignore | 1 + .../scripts/camera_placement_params.yaml | 99 ++++- .../camera/scripts/camera_placement_visual.py | 414 +++++++++++++----- 3 files changed, 384 insertions(+), 130 deletions(-) diff --git a/.gitignore b/.gitignore index 06f7000..8948850 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .vscode +.venv *.png *.bin *.db3 diff --git a/packages/camera/scripts/camera_placement_params.yaml b/packages/camera/scripts/camera_placement_params.yaml index 09a7696..712878a 100644 --- a/packages/camera/scripts/camera_placement_params.yaml +++ b/packages/camera/scripts/camera_placement_params.yaml @@ -1,5 +1,5 @@ intrinsics: - image_size: [1200, 1920] + image_size: [1920, 1200] 1: camera_matrix: [672.2824725267757, 0, 984.0472159818853, 0, 672.6886411532304, 602.96669930345, 0, 0, 1] distortion_coefs: [-0.09715103386082896, 0.06788948036532018, -0.0007157453506997161, 0.0003048354358359307, -0.003636308978789861] @@ -10,16 +10,97 @@ intrinsics: camera_positions: 0: 1: - rotation: [0, 0, 90] - translation: [0.5, 1, 1] + rotation: [-135, 0, 90] + translation: [1.3, -0.6, 1] 2: - rotation: [0, 0, -90] - translation: [0.5, -1, -1] + rotation: [-135, 0, -90] + translation: [-1.3, -0.6, 1] 1: 1: - rotation: [0, 90, 45] - translation: [0.5, 1, 1] + rotation: [-110, 0, 90] + translation: [1.3, -0.6, 1] 2: - rotation: [0, 90, 45] - translation: [0.5, -1, -1] + rotation: [-110, 0, -90] + translation: [-1.3, -0.6, 1] + + 2: + 1: + rotation: [-110, 0, 90] + translation: [1, -0.6, 1] + 2: + rotation: [-110, 0, -90] + translation: [-1, -0.6, 1] + + 3: + 1: + rotation: [-100, 0, 60] + translation: [1.6, -0.6, 0.7] + 2: + rotation: [-100, 0, -60] + translation: [-1.6, -0.6, 0.7] + + 4: + 1: + rotation: [-110, 0, 45] + translation: [1.3, -1.3, 1] + 2: + rotation: [-110, 0, -45] + translation: [-1.3, -1.3, 1] + + + 5: + 1: + rotation: [-110, 0, 45] + translation: [1.3, -1.3, 1] + 2: + rotation: [-110, 0, -45] + translation: [-1.3, -1.3, 1] + + 6: + 1: + rotation: [-110, 0, 45] + translation: [1, -1.3, 1] + 2: + rotation: [-110, 0, -45] + translation: [-1, -1.3, 1] + + 7: + 1: + rotation: [-110, 0, 90] + translation: [1.3, 0, 1] + 2: + rotation: [-100, 0, -30] + translation: [-0.8, -1.6, 0.7] + + 8: # stereopair with 20cm gap + 1: + rotation: [-90, 0, 0] + translation: [0.1, -1.7, 0.2] + 2: + rotation: [-90, 0, 0] + translation: [-0.1, -1.7, 0.2] + + 9: # stereopair with 10cm gap + 1: + rotation: [-90, 0, 0] + translation: [0.05, -1.7, 0.2] + 2: + rotation: [-90, 0, 0] + translation: [-0.05, -1.7, 0.2] + + 10: # stereopair with 20cm gap facing slightly up + 1: + rotation: [-80, 0, 0] + translation: [0.1, -1.9, 0.2] + 2: + rotation: [-80, 0, 0] + translation: [-0.1, -1.9, 0.2] + + 11: # stereopair with 10cm gap facing slightly up + 1: + rotation: [-80, 0, 0] + translation: [0.05, -1.9, 0.2] + 2: + rotation: [-80, 0, 0] + translation: [-0.05, -1.9, 0.2] diff --git a/packages/camera/scripts/camera_placement_visual.py b/packages/camera/scripts/camera_placement_visual.py index 79b49c9..02e8057 100644 --- a/packages/camera/scripts/camera_placement_visual.py +++ b/packages/camera/scripts/camera_placement_visual.py @@ -14,6 +14,9 @@ from sensor_msgs.msg import CameraInfo from visualization_msgs.msg import ImageMarker, Marker +from scipy.ndimage import center_of_mass +from typing import Tuple, List + SEC_MULTIPLIER = 10**9 MS_MULTIPLIER = 10**6 MCS_MULTIPLIER = 10**3 @@ -42,6 +45,7 @@ def __init__( camera_id, rotation_vector, translation_vector, + yaw_pitch_roll_order=False, ): self.camera_matrix = np.array(camera_matrix, dtype=float).reshape((3, 3)) self.dist_coefs = np.array(dist_coefs, dtype=float) @@ -59,11 +63,17 @@ def __init__( self.rotation_vector = np.array(rotation_vector, dtype=float) self.translation_vector = np.array(translation_vector, dtype=float) - self.rotation_matrix = cv2.Rodrigues(self.rotation_vector.reshape((3, 1)))[0] + if yaw_pitch_roll_order: + self.rotation_matrix = Rotation.from_euler( + "xyz", rotation_vector, degrees=True + ).as_matrix() + else: + self.rotation_matrix = cv2.Rodrigues(self.rotation_vector.reshape((3, 1)))[0] + self.static_transformation = TransformStamped() self.static_transformation.child_frame_id = f"camera_{self.camera_id}" - self.static_transformation.header.frame_id = "table" + self.static_transformation.header.frame_id = "world" self.static_transformation.transform.translation.x = self.translation_vector[0] self.static_transformation.transform.translation.y = self.translation_vector[1] @@ -188,7 +198,9 @@ def init_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser() parser.add_argument( - "--params-file", help="yaml file with intrinsic parameters and cameras' positions", required=True + "--params-file", + help="yaml file with intrinsic parameters and cameras' positions", + required=True, ) parser.add_argument("--export", help="some_file.mcap", required=True) @@ -261,93 +273,6 @@ def init_detection_center_marker( return msg -def simulate( - writer: rosbag2_py.SequentialWriter, - rgb_sources, - filename_to_info, - trajectory_predictions, - intrinsics, - R: np.ndarray, - T: np.ndarray, -) -> None: - filenames_to_publish = sorted(filename_to_info.keys()) - cv_bridge_instance = CvBridge() - - current_simulation_time = 0 # in nanoseconds - for i in range(len(filenames_to_publish)): - if i % 10 == 0: - publish_table_plain(writer, current_simulation_time) - filename = filenames_to_publish[i] - for camera_idx in range(2): - # load and segmentate image - image = cv2.imread(os.path.join(rgb_sources[camera_idx], filename)) - - image = intrinsics[camera_idx].undistort(image) - - # prepare CompressedImages - camera_feed_msg = cv_bridge_instance.cv2_to_compressed_imgmsg(image, dst_format="jpeg") - camera_feed_msg.header.frame_id = f"camera_{camera_idx + 1}" - camera_feed_msg.header.stamp.sec = current_simulation_time // SEC_MULTIPLIER - camera_feed_msg.header.stamp.nanosec = current_simulation_time % SEC_MULTIPLIER - - # publish images - writer.write( - f"/camera_{camera_idx + 1}/image", - serialize_message(camera_feed_msg), - current_simulation_time, - ) - # publish camera info and transformations - intrinsics[camera_idx].publish_camera_info(writer, current_simulation_time) - intrinsics[camera_idx].publish_transform(writer, current_simulation_time) - - center_detection = init_detection_center_marker( - get_new_marker_id(), - current_simulation_time, - filename_to_info[filename]["image_points"][camera_idx], - camera_idx + 1, - ttl=50, - ) - writer.write( - f"/camera_{camera_idx + 1}/ball_center", - serialize_message(center_detection), - current_simulation_time, - ) - - current_point = np.array( - filename_to_info[filename]["triangulated_point"], dtype=float - ).reshape((3, 1)) - ball_marker = init_ball_marker( - get_new_marker_id(), - current_simulation_time, - (R @ current_point - T).flatten().tolist(), - current_point.flatten().tolist(), - ttl=FPS_LATENCY_MS, - ) - ball_marker.header.frame_id = "table" - writer.write( - "/triangulation/ball_marker", - serialize_message(ball_marker), - current_simulation_time, - ) - - projection_marker = ball_marker - projection_marker.id = get_new_marker_id() - projection_marker.scale.z = 0.001 - projection_marker.pose.position.z = 0.0 - projection_marker.color.r = 1.0 - writer.write( - "/triangulation/ball_table_projection", - serialize_message(projection_marker), - current_simulation_time, - ) - - if trajectory_predictions: - publish_predicted_trajectory( - writer, trajectory_predictions, filename, current_simulation_time - ) - current_simulation_time += FPS_LATENCY_MS * MS_MULTIPLIER # 15 ms between the frames - - def init_camera_info( writer: rosbag2_py.SequentialWriter, params_path: str, camera_ids=[1, 2] ) -> Tuple[List[CameraParameters], np.ndarray]: @@ -530,6 +455,233 @@ def publish_cam2table_transform( writer.write("/tf", serialize_message(static_transformation), 0) +from typing import Any + + +class Transformation: + def __init__(self, R, t): + self.R = R + self.t = t.reshape((3, 1)) + self.R_inv = np.linalg.inv(self.R) + + def __call__(self, point): + return self.R @ point + self.t + + def __mul__(self, other): + return Transformation(self.R @ other.R, self.R @ other.t + self.t) + + def transform(self, point): + return self(point) + + def inverse_transform(self, point): + return self.R_inv @ (point - self.t) + + # right transformation is applied first + def __mult__(self, other): + return Transformation(self.R @ other.R, self.t + other.t) + + +class Image: + def __init__( + self, camera_matrix, camera_transformation, distortion_coefs, image_size=(1200, 1920) + ): + self.camera_matrix = camera_matrix + self.camera_transformation = camera_transformation + self.distortion_coefs = distortion_coefs + self.image_size = image_size + + def normilise_image_point(self, point): + x_normalised = (point[0] - self.camera_matrix[0, 2]) / self.camera_matrix[0, 0] + y_normalised = (point[1] - self.camera_matrix[1, 2]) / self.camera_matrix[1, 1] + return np.array([x_normalised, y_normalised, 1]).reshape(3, 1) + + # in world coordinates + def project_point_to_image(self, point): + if point.shape != (3, 1): + point = point.reshape((3, 1)) + return self.camera_matrix @ self.camera_transformation(point) + + # transformed_point = self.camera_transformation(point) + # # transformed_point = transformed_point / transformed_point[2] + # projected_point = self.camera_matrix @ transformed_point + # return projected_point + + def project_points_to_image(self, points): + return np.array([self.project_point_to_image(point) for point in points]) + + def normilize_image_point(self, image_point): + x_normalised = (image_point[0] - self.camera_matrix[0, 2]) / self.camera_matrix[0, 0] + y_normalised = (image_point[1] - self.camera_matrix[1, 2]) / self.camera_matrix[1, 1] + return np.array([x_normalised, y_normalised, 1]).reshape(3, 1) + + def project_ball_to_image(self, center, radius: float) -> np.ndarray: + def valid_coords(x, y): + return x >= 0 and x < self.image_size[1] and y >= 0 and y < self.image_size[0] + + center = center.reshape((3, 1)) + camera_matrix_inv = np.linalg.inv(self.camera_matrix) + + transformed_center = self.camera_transformation(center) + projected_center = self.camera_matrix @ transformed_center + projected_center /= projected_center[2] + + if ( + np.linalg.norm( + projected_center.flatten() + - np.array([self.image_size[1] / 2, self.image_size[0] / 2, 1]) + ) + > 2000 + ): + return np.zeros(self.image_size) + + image = np.zeros(self.image_size) + checked_pixels = set() + + pixels_to_check = {(int(projected_center[0][0]), int(projected_center[1][0]))} + while pixels_to_check: + x, y = pixels_to_check.pop() + + image_point_camera_ray = camera_matrix_inv @ np.array([x, y, 1]).reshape((3, 1)) + image_point_world_ray = self.camera_transformation.inverse_transform( + image_point_camera_ray + ) - self.camera_transformation.inverse_transform(np.array([0, 0, 0]).reshape((3, 1))) + ball_center_world_ray = center - self.camera_transformation.inverse_transform( + np.array([0, 0, 0]).reshape((3, 1)) + ) + + distance = np.linalg.norm( + np.cross(ball_center_world_ray.flatten(), image_point_world_ray.flatten()), ord=2 + ) / np.linalg.norm(image_point_world_ray, ord=2) + if distance <= radius: + if valid_coords(x, y): + image[y, x] = 1 + # adding all 8 neighbours to the queue + for dx in range(-1, 2): + for dy in range(-1, 2): + if (x + dx, y + dy) not in checked_pixels: + pixels_to_check.add((x + dx, y + dy)) + checked_pixels.add((x + dx, y + dy)) + + return image + + +def get_bbox(mask: np.ndarray) -> List[float]: + if not np.any(mask): + return [0.0, 0.0, 0.0, 0.0] + # x_min, y_min, x_max, y_max + horizontal_indicies = np.where(np.any(mask, axis=0))[0] + vertical_indicies = np.where(np.any(mask, axis=1))[0] + x1, x2 = horizontal_indicies[[0, -1]] + y1, y2 = vertical_indicies[[0, -1]] + bbox = list(map(float, [x1, y1, x2, y2])) + return bbox + + +def get_mask_center(mask): + bbox = get_bbox(mask) + centroid_x = (bbox[0] + bbox[2]) / 2 + centroid_y = (bbox[1] + bbox[3]) / 2 + return np.array([centroid_x, centroid_y]) + + +def get_mask_centroid(mask): + return np.array(center_of_mass(mask)) + + +class StereoScene: + def __init__(self, left_camera: Image, right_camera: Image, table_middle_normal): + self.left_camera = left_camera + self.right_camera = right_camera + self.table_middle_normal = table_middle_normal + self.last_n_clicks = 0 + + def project_ball_to_images(self, center, radius): + left_image = self.left_camera.project_ball_to_image(center, radius) + right_image = self.right_camera.project_ball_to_image(center, radius) + return left_image, right_image + + def triangulate_position(self, points_by_view_1, points_by_view_2, world2cam, cam2cam): + print("triangulating") + # print(points_by_view) + world2cam_Rt = np.column_stack((world2cam.R, world2cam.t)) + world2second_cam = cam2cam * world2cam + world2second_cam_Rt = np.column_stack((world2second_cam.R, world2second_cam.t)) + proj_1 = self.left_camera.camera_matrix @ world2cam_Rt + proj_2 = self.right_camera.camera_matrix @ world2second_cam_Rt + + res = cv2.triangulatePoints(proj_1, proj_2, points_by_view_1, points_by_view_2) + res /= res[3, :] # normalizing + + # TODO preserve 4D points? + return res[:3, :] + + +def evaluate_camera_position( + world2master: Transformation, + master2second: Transformation, + center_extractor, + camera_params_1: CameraParameters, + camera_params_2: CameraParameters, + simulation_time +): + NUMBER_OF_SPHERES = 6 + image_1 = Image(camera_params_1.camera_matrix, world2master, camera_params_1.dist_coefs) + image_2 = Image( + camera_params_2.camera_matrix, master2second * world2master, camera_params_2.dist_coefs + ) + stereo_scene = StereoScene(image_1, image_2, None) + + sphere_centers = [] + for y in np.linspace(-TABLE_LENGTH / 2, TABLE_LENGTH / 2, NUMBER_OF_SPHERES): + for x in np.linspace(-TABLE_WIDTH / 2, TABLE_WIDTH / 2, NUMBER_OF_SPHERES): + for z in np.linspace(0, 1, NUMBER_OF_SPHERES): + sphere_centers.append((x, y, z)) + + sphere_centers = np.array(sphere_centers).T + points_1 = [] + points_2 = [] + valid_sphere_centers = [] + # world2second = master2second * world2master + + + for i in range(sphere_centers.shape[1]): + mask_1, mask_2 = stereo_scene.project_ball_to_images(sphere_centers[:, i : (i + 1)], 0.02) + ball_marker = init_ball_marker(get_new_marker_id(), simulation_time, sphere_centers[:, i : (i + 1)].flatten(), 1, ttl=SEC_MULTIPLIER) + ball_marker.header.frame_id = "table" + writer.write( + "/triangulation/ball_marker", + serialize_message(ball_marker), + simulation_time, + ) + + if np.sum(mask_1) == 0 or np.sum(mask_2) == 0: + continue + + points_1.append(center_extractor(mask_1)) + points_2.append(center_extractor(mask_2)) + valid_sphere_centers.append(sphere_centers[:, i]) + + points_1 = np.array(points_1).T + points_2 = np.array(points_2).T + sphere_centers = np.array(valid_sphere_centers).T + + triangulated_points = stereo_scene.triangulate_position( + points_1, points_2, world2master, master2second + ) + + # Calculate the Euclidean distance between the true and triangulated points + distances = np.linalg.norm(sphere_centers - triangulated_points, axis=0) + + # Calculate mean and standard deviation of the distances + mean_distance = np.mean(distances) + std_distance = np.std(distances) + + print(f"Mean distance error: {mean_distance}") + print(f"Standard deviation of distance error: {std_distance}") + print("evalution complete") + print() + + if __name__ == "__main__": # init all modules parser = init_parser() @@ -539,48 +691,68 @@ def publish_cam2table_transform( table_plane_normal = np.array([0, 0, 1]) publish_table_plain(writer, 100) - with open(args.params_file, mode="r", encoding="utf-8") as yaml_params_file: params = yaml.safe_load(yaml_params_file) yaml_intrinsics_dics = params["intrinsics"] - + publish_cam2table_transform(writer, np.eye(3), np.zeros((3, 1))) for position_idx in params["camera_positions"].keys(): - R_1, T_1 = params["camera_positions"][position_idx][1]["rotation"], params["camera_positions"][position_idx][1]["translation"] - R_1 = np.array(R_1) / 180 * np.pi + print(position_idx) + R_1, T_1 = ( + params["camera_positions"][position_idx][1]["rotation"], + params["camera_positions"][position_idx][1]["translation"], + ) + R_1 = np.array(R_1) T_1 = np.array(T_1) - R_2, T_2 = params["camera_positions"][position_idx][2]["rotation"], params["camera_positions"][position_idx][2]["translation"] - R_2 = np.array(R_2) / 180 * np.pi + R_2, T_2 = ( + params["camera_positions"][position_idx][2]["rotation"], + params["camera_positions"][position_idx][2]["translation"], + ) + R_2 = np.array(R_2) T_2 = np.array(T_2) - intrinsics = [CameraParameters(yaml_intrinsics_dics["image_size"], yaml_intrinsics_dics[1]["camera_matrix"], yaml_intrinsics_dics[1]["distortion_coefs"], 1, R_1, T_1), - CameraParameters(yaml_intrinsics_dics["image_size"], yaml_intrinsics_dics[2]["camera_matrix"], yaml_intrinsics_dics[2]["distortion_coefs"], 2, R_2, T_2)] + intrinsics = [ + CameraParameters( + yaml_intrinsics_dics["image_size"], + yaml_intrinsics_dics[1]["camera_matrix"], + yaml_intrinsics_dics[1]["distortion_coefs"], + 1, + R_1, + T_1, + yaw_pitch_roll_order=True, + ), + CameraParameters( + yaml_intrinsics_dics["image_size"], + yaml_intrinsics_dics[2]["camera_matrix"], + yaml_intrinsics_dics[2]["distortion_coefs"], + 2, + R_2, + T_2, + yaw_pitch_roll_order=True, + ), + ] for cam_params in intrinsics: - cam_params.publish_camera_info(writer, 10000 * position_idx) - - - + cam_params.publish_transform(writer, SEC_MULTIPLIER * position_idx) + cam_params.publish_camera_info(writer, SEC_MULTIPLIER * position_idx) + publish_table_plain(writer, SEC_MULTIPLIER * position_idx) + world2master = Transformation( + intrinsics[0].rotation_matrix, intrinsics[1].translation_vector + ) + rotation_master2slave = world2master.R_inv @ world2master.R + translation_master2slave = intrinsics[1].translation_vector + # print(rotation_master2slave.shape) + master2slave = Transformation(rotation_master2slave, translation_master2slave) + + evaluate_camera_position( + world2master, + master2slave, + get_mask_center, + intrinsics[0], + intrinsics[1], + SEC_MULTIPLIER * position_idx + ) - # intrinsics, table_plane_normal = init_camera_info( - # writer, args.intrinsic_params, [1, 2] - # ) - - # R, T, R2table, T2table, table_center = get_cam2world_transform( - # table_plane_normal, data["table_orientation_points"] - # ) - # publish_cam2table_transform(writer, R, T) - # publish_cam2table_transform(writer, np.eye(3), np.zeros((3, 1))) - - # simulate( - # writer, - # args.rgb_sources, - # data["triangulated_points"], - # trajectory_predictions, - # intrinsics, - # R2table, - # T2table, - # ) del writer