{ "cells": [ { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [], "source": [ "from copy import deepcopy\n", "from datetime import datetime, timedelta\n", "from pathlib import Path\n", "from typing import (Any, Generator, Optional, Sequence, TypeAlias, TypedDict,\n", " cast, overload)\n", "\n", "import awkward as ak\n", "import jax\n", "import jax.numpy as jnp\n", "import numpy as np\n", "import orjson\n", "from beartype import beartype\n", "from cv2 import undistortPoints\n", "from jaxtyping import Array, Float, Num, jaxtyped\n", "from matplotlib import pyplot as plt\n", "from numpy.typing import ArrayLike\n", "from scipy.spatial.transform import Rotation as R\n", "\n", "from app.camera import Camera, CameraParams, Detection\n", "from app.visualize.whole_body import visualize_whole_body\n", "\n", "NDArray: TypeAlias = np.ndarray" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
[{name: 'AE_01', port: 5602, intrinsic: {...}, extrinsic: {...}, ...},\n",
       " {name: 'AE_1A', port: 5601, intrinsic: {...}, extrinsic: {...}, ...},\n",
       " {name: 'AE_08', port: 5600, intrinsic: {...}, extrinsic: {...}, ...}]\n",
       "------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n",
       "backend: cpu\n",
       "nbytes: 823 B\n",
       "type: 3 * {\n",
       "    name: string,\n",
       "    port: int64,\n",
       "    intrinsic: {\n",
       "        camera_matrix: var * var * var * float64,\n",
       "        distortion_coefficients: var * float64\n",
       "    },\n",
       "    extrinsic: {\n",
       "        rvec: var * float64,\n",
       "        tvec: var * float64\n",
       "    },\n",
       "    resolution: {\n",
       "        width: int64,\n",
       "        height: int64\n",
       "    }\n",
       "}
" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "DATASET_PATH = Path(\"samples\") / \"04_02\" \n", "AK_CAMERA_DATASET: ak.Array = ak.from_parquet(DATASET_PATH / \"camera_params.parquet\")\n", "display(AK_CAMERA_DATASET)" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "class Resolution(TypedDict):\n", " width: int\n", " height: int\n", "\n", "class Intrinsic(TypedDict):\n", " camera_matrix: Num[Array, \"3 3\"]\n", " \"\"\"\n", " K\n", " \"\"\"\n", " distortion_coefficients: Num[Array, \"N\"]\n", " \"\"\"\n", " distortion coefficients; usually 5\n", " \"\"\"\n", "\n", "class Extrinsic(TypedDict):\n", " rvec: Num[NDArray, \"3\"]\n", " tvec: Num[NDArray, \"3\"]\n", "\n", "class ExternalCameraParams(TypedDict):\n", " name: str\n", " port: int\n", " intrinsic: Intrinsic\n", " extrinsic: Extrinsic\n", " resolution: Resolution\n" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [], "source": [ "def read_dataset_by_port(port: int) -> ak.Array:\n", " P = DATASET_PATH / f\"{port}.parquet\"\n", " return ak.from_parquet(P)\n", "\n", "KEYPOINT_DATASET = {int(p): read_dataset_by_port(p) for p in ak.to_numpy(AK_CAMERA_DATASET[\"port\"])}" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
[{frame_index: 0, boxes: [[599, ...], [...]], kps: [...], kps_scores: ..., ...},\n",
       " {frame_index: 1, boxes: [[599, ...], [...]], kps: [...], kps_scores: ..., ...},\n",
       " {frame_index: 2, boxes: [[599, ...], [...]], kps: [...], kps_scores: ..., ...},\n",
       " {frame_index: 3, boxes: [[599, ...], [...]], kps: [...], kps_scores: ..., ...},\n",
       " {frame_index: 4, boxes: [[598, ...], [...]], kps: [...], kps_scores: ..., ...},\n",
       " {frame_index: 5, boxes: [[596, ...], [...]], kps: [...], kps_scores: ..., ...},\n",
       " {frame_index: 6, boxes: [[594, ...], [...]], kps: [...], kps_scores: ..., ...},\n",
       " {frame_index: 7, boxes: [[595, ...], [...]], kps: [...], kps_scores: ..., ...},\n",
       " {frame_index: 8, boxes: [[595, ...], [...]], kps: [...], kps_scores: ..., ...},\n",
       " {frame_index: 9, boxes: [[595, ...], [...]], kps: [...], kps_scores: ..., ...},\n",
       " ...,\n",
       " {frame_index: 520, boxes: [[1.09e+03, ...], ...], kps: [...], ...},\n",
       " {frame_index: 521, boxes: [[1.09e+03, ...], ...], kps: [...], ...},\n",
       " {frame_index: 522, boxes: [[1.09e+03, ...], ...], kps: [...], ...},\n",
       " {frame_index: 523, boxes: [[1.09e+03, ...], ...], kps: [...], ...},\n",
       " {frame_index: 524, boxes: [[1.09e+03, ...], ...], kps: [...], ...},\n",
       " {frame_index: 525, boxes: [[1.09e+03, ...], ...], kps: [...], ...},\n",
       " {frame_index: 526, boxes: [[1.09e+03, ...], ...], kps: [...], ...},\n",
       " {frame_index: 527, boxes: [[1.09e+03, ...], ...], kps: [...], ...},\n",
       " {frame_index: 528, boxes: [[1.09e+03, ...], ...], kps: [...], ...}]\n",
       "-----------------------------------------------------------------------------------------------------------------------------------------------\n",
       "backend: cpu\n",
       "nbytes: 4.6 MB\n",
       "type: 529 * {\n",
       "    frame_index: int64,\n",
       "    boxes: var * var * float64,\n",
       "    kps: var * var * var * float64,\n",
       "    kps_scores: var * var * float64\n",
       "}
" ], "text/plain": [ "" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "KEYPOINT_DATASET[5601]" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [], "source": [ "class KeypointDataset(TypedDict):\n", " frame_index: int\n", " boxes: Num[NDArray, \"N 4\"]\n", " kps: Num[NDArray, \"N J 2\"]\n", " kps_scores: Num[NDArray, \"N J\"]\n", "\n", "\n", "@jaxtyped(typechecker=beartype)\n", "def to_transformation_matrix(\n", " rvec: Num[NDArray, \"3\"], tvec: Num[NDArray, \"3\"]\n", ") -> Num[NDArray, \"4 4\"]:\n", " res = np.eye(4)\n", " res[:3, :3] = R.from_rotvec(rvec).as_matrix()\n", " res[:3, 3] = tvec\n", " return res\n", "\n", "\n", "@jaxtyped(typechecker=beartype)\n", "def undistort_points(\n", " points: Num[NDArray, \"M 2\"],\n", " camera_matrix: Num[NDArray, \"3 3\"],\n", " dist_coeffs: Num[NDArray, \"N\"],\n", ") -> Num[NDArray, \"M 2\"]:\n", " K = camera_matrix\n", " dist = dist_coeffs\n", " res = undistortPoints(points, K, dist, P=K) # type: ignore\n", " return res.reshape(-1, 2)\n", "\n", "\n", "def from_camera_params(camera: ExternalCameraParams) -> Camera:\n", " rt = jnp.array(\n", " to_transformation_matrix(\n", " ak.to_numpy(camera[\"extrinsic\"][\"rvec\"]),\n", " ak.to_numpy(camera[\"extrinsic\"][\"tvec\"]),\n", " )\n", " )\n", " K = jnp.array(camera[\"intrinsic\"][\"camera_matrix\"]).reshape(3, 3)\n", " dist_coeffs = jnp.array(camera[\"intrinsic\"][\"distortion_coefficients\"])\n", " image_size = jnp.array(\n", " (camera[\"resolution\"][\"width\"], camera[\"resolution\"][\"height\"])\n", " )\n", " return Camera(\n", " id=camera[\"name\"],\n", " params=CameraParams(\n", " K=K,\n", " Rt=rt,\n", " dist_coeffs=dist_coeffs,\n", " image_size=image_size,\n", " ),\n", " )\n", "\n", "\n", "def preprocess_keypoint_dataset(\n", " dataset: Sequence[KeypointDataset],\n", " camera: Camera,\n", " fps: float,\n", " start_timestamp: datetime,\n", ") -> Generator[Detection, None, None]:\n", " frame_interval_s = 1 / fps\n", " for el in dataset:\n", " frame_index = el[\"frame_index\"]\n", " timestamp = start_timestamp + timedelta(seconds=frame_index * frame_interval_s)\n", " for kp, kp_score in zip(el[\"kps\"], el[\"kps_scores\"]):\n", " kp = undistort_points(\n", " np.asarray(kp), np.asarray(camera.params.K), np.asarray(camera.params.dist_coeffs)\n", " )\n", " yield Detection(\n", " keypoints=jnp.array(kp),\n", " confidences=jnp.array(kp_score),\n", " camera=camera,\n", " timestamp=timestamp,\n", " )" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [], "source": [ "DetectionGenerator: TypeAlias = Generator[Detection, None, None]\n", "\n", "\n", "def sync_batch_gen(gens: list[DetectionGenerator], diff: timedelta):\n", " \"\"\"\n", " given a list of detection generators, return a generator that yields a batch of detections\n", "\n", " Args:\n", " gens: list of detection generators\n", " diff: maximum timestamp difference between detections to consider them part of the same batch\n", " \"\"\"\n", " N = len(gens)\n", " last_batch_timestamp: Optional[datetime] = None\n", " next_batch_timestamp: Optional[datetime] = None\n", " current_batch: list[Detection] = []\n", " next_batch: list[Detection] = []\n", " paused: list[bool] = [False] * N\n", " finished: list[bool] = [False] * N\n", "\n", " def reset_paused():\n", " \"\"\"\n", " reset paused list based on finished list\n", " \"\"\"\n", " for i in range(N):\n", " if not finished[i]:\n", " paused[i] = False\n", " else:\n", " paused[i] = True\n", "\n", " EPS = 1e-6\n", " # a small epsilon to avoid floating point precision issues\n", " diff_esp = diff - timedelta(seconds=EPS)\n", " while True:\n", " for i, gen in enumerate(gens):\n", " try:\n", " if finished[i] or paused[i]:\n", " continue\n", " val = next(gen)\n", " if last_batch_timestamp is None:\n", " last_batch_timestamp = val.timestamp\n", " current_batch.append(val)\n", " else:\n", " if abs(val.timestamp - last_batch_timestamp) >= diff_esp:\n", " next_batch.append(val)\n", " if next_batch_timestamp is None:\n", " next_batch_timestamp = val.timestamp\n", " paused[i] = True\n", " if all(paused):\n", " yield current_batch\n", " current_batch = next_batch\n", " next_batch = []\n", " last_batch_timestamp = next_batch_timestamp\n", " next_batch_timestamp = None\n", " reset_paused()\n", " else:\n", " current_batch.append(val)\n", " except StopIteration:\n", " finished[i] = True\n", " paused[i] = True\n", " if all(finished):\n", " if len(current_batch) > 0:\n", " # All generators exhausted, flush remaining batch and exit\n", " yield current_batch\n", " break" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [], "source": [ "@overload\n", "def to_projection_matrix(\n", " transformation_matrix: Num[NDArray, \"4 4\"], camera_matrix: Num[NDArray, \"3 3\"]\n", ") -> Num[NDArray, \"3 4\"]: ...\n", "\n", "\n", "@overload\n", "def to_projection_matrix(\n", " transformation_matrix: Num[Array, \"4 4\"], camera_matrix: Num[Array, \"3 3\"]\n", ") -> Num[Array, \"3 4\"]: ...\n", "\n", "\n", "@jaxtyped(typechecker=beartype)\n", "def to_projection_matrix(\n", " transformation_matrix: Num[Any, \"4 4\"],\n", " camera_matrix: Num[Any, \"3 3\"],\n", ") -> Num[Any, \"3 4\"]:\n", " return camera_matrix @ transformation_matrix[:3, :]\n", "\n", "to_projection_matrix_jit = jax.jit(to_projection_matrix)\n", "\n", "\n", "@jaxtyped(typechecker=beartype)\n", "def dlt(\n", " H1: Num[NDArray, \"3 4\"],\n", " H2: Num[NDArray, \"3 4\"],\n", " p1: Num[NDArray, \"2\"],\n", " p2: Num[NDArray, \"2\"],\n", ") -> Num[NDArray, \"3\"]:\n", " \"\"\"\n", " Direct Linear Transformation\n", " \"\"\"\n", " A = [\n", " p1[1] * H1[2, :] - H1[1, :],\n", " H1[0, :] - p1[0] * H1[2, :],\n", " p2[1] * H2[2, :] - H2[1, :],\n", " H2[0, :] - p2[0] * H2[2, :],\n", " ]\n", " A = np.array(A).reshape((4, 4))\n", "\n", " B = A.transpose() @ A\n", " from scipy import linalg\n", "\n", " U, s, Vh = linalg.svd(B, full_matrices=False)\n", " return Vh[3, 0:3] / Vh[3, 3]\n", "\n", "\n", "@overload\n", "def homogeneous_to_euclidean(points: Num[NDArray, \"N 4\"]) -> Num[NDArray, \"N 3\"]: ...\n", "\n", "\n", "@overload\n", "def homogeneous_to_euclidean(points: Num[Array, \"N 4\"]) -> Num[Array, \"N 3\"]: ...\n", "\n", "\n", "@jaxtyped(typechecker=beartype)\n", "def homogeneous_to_euclidean(\n", " points: Num[Any, \"N 4\"],\n", ") -> Num[Any, \"N 3\"]:\n", " \"\"\"\n", " 将齐次坐标转换为欧几里得坐标\n", "\n", " Args:\n", " points: homogeneous coordinates (x, y, z, w) in numpy array or jax array\n", "\n", " Returns:\n", " euclidean coordinates (x, y, z) in numpy array or jax array\n", " \"\"\"\n", " return points[..., :-1] / points[..., -1:]\n", "\n" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.041666666666666664" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "FPS = 24\n", "image_gen_5600 = preprocess_keypoint_dataset(KEYPOINT_DATASET[5600], from_camera_params(AK_CAMERA_DATASET[AK_CAMERA_DATASET[\"port\"] == 5600][0]), FPS, datetime(2024, 4, 2, 12, 0, 0)) # type: ignore\n", "image_gen_5601 = preprocess_keypoint_dataset(KEYPOINT_DATASET[5601], from_camera_params(AK_CAMERA_DATASET[AK_CAMERA_DATASET[\"port\"] == 5601][0]), FPS, datetime(2024, 4, 2, 12, 0, 0)) # type: ignore\n", "image_gen_5602 = preprocess_keypoint_dataset(KEYPOINT_DATASET[5602], from_camera_params(AK_CAMERA_DATASET[AK_CAMERA_DATASET[\"port\"] == 5602][0]), FPS, datetime(2024, 4, 2, 12, 0, 0)) # type: ignore\n", "\n", "display(1/FPS)\n", "sync_gen = sync_batch_gen([image_gen_5600, image_gen_5601, image_gen_5602], timedelta(seconds=1/FPS))" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [], "source": [ "detections = next(sync_gen)" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [], "source": [ "from app.camera import calculate_affinity_matrix_by_epipolar_constraint\n", "\n", "sorted_detections, affinity_matrix = calculate_affinity_matrix_by_epipolar_constraint(detections, \n", " alpha_2d=2000)" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'timestamp': '2024-04-02 12:00:00', 'camera': 'AE_08'},\n", " {'timestamp': '2024-04-02 12:00:00', 'camera': 'AE_08'},\n", " {'timestamp': '2024-04-02 12:00:00', 'camera': 'AE_1A'},\n", " {'timestamp': '2024-04-02 12:00:00', 'camera': 'AE_1A'},\n", " {'timestamp': '2024-04-02 12:00:00', 'camera': 'AE_01'},\n", " {'timestamp': '2024-04-02 12:00:00', 'camera': 'AE_01'}]" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "Array([[ -inf, -inf, 0.697, 0.451, -0.009, 0.2 ],\n", " [ -inf, -inf, 0.919, 0.835, 0.43 , 0.65 ],\n", " [ 0.697, 0.919, -inf, -inf, 0.362, 0.487],\n", " [ 0.451, 0.835, -inf, -inf, 0.744, 0.827],\n", " [-0.009, 0.43 , 0.362, 0.744, -inf, -inf],\n", " [ 0.2 , 0.65 , 0.487, 0.827, -inf, -inf]], dtype=float32)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "display(list(map(lambda x: {\"timestamp\": str(x.timestamp), \"camera\": x.camera.id}, sorted_detections)))\n", "with jnp.printoptions(precision=3, suppress=True):\n", " display(affinity_matrix)" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[[0, 2, 5], [1, 3, 4]]" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "array([[0, 0, 1, 0, 0, 1],\n", " [0, 0, 0, 1, 1, 0],\n", " [1, 0, 0, 0, 0, 1],\n", " [0, 1, 0, 0, 1, 0],\n", " [0, 1, 0, 1, 0, 0],\n", " [1, 0, 1, 0, 0, 0]])" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from app.solver._old import GLPKSolver\n", "\n", "def clusters_to_detections(clusters: list[list[int]], sorted_detections: list[Detection]) -> list[list[Detection]]:\n", " \"\"\"\n", " given a list of clusters (which is the indices of the detections in the sorted_detections list),\n", " extract the detections from the sorted_detections list\n", "\n", " Args:\n", " clusters: list of clusters, each cluster is a list of indices of the detections in the `sorted_detections` list\n", " sorted_detections: list of SORTED detections\n", "\n", " Returns:\n", " list of clusters, each cluster is a list of detections\n", " \"\"\"\n", " return [[sorted_detections[i] for i in cluster] for cluster in clusters]\n", "\n", "solver = GLPKSolver()\n", "aff_np = np.asarray(affinity_matrix).astype(np.float64)\n", "clusters, sol_matrix = solver.solve(aff_np)\n", "display(clusters)\n", "display(sol_matrix)" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/png": "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/vEf/xFzwJ46xhjuueceJkyYQE5ODrW1tWzZsiXlOm1tbSxatIhAIEBRURE33XQTXV3DH88gmS2G20NzM+6qw18HngTCabm6pT2hREROEGkPNj/96U957LHH+MUvfsHmzZv56U9/yv3338/Pf/7zZJ3777+fhx9+mMcff5xVq1aRl5fH/Pnz6evrS9ZZtGgRGzduZNmyZbz00kusXLmSW265Jd3NlQzThRtyekbyTYzjPo7Kzsc4w9gnSkRERo5Js8svv9x89atfTTl29dVXm0WLFhljjHEcx1RUVJgHHnggeb6jo8P4/X7z7LPPGmOM2bRpkwHMu+++m6zzyiuvGMuyzM6dO4+oHaFQaO8ugyoqg5e8i683dsE492uvzxTM+7oBK6WO/9Qa4zvprJRj+X/2VVN0zT0G2zPs9849/8vGUzJp1L8HKioqKsdjCYVCw8ohae+x+exnP8vy5cv58MMPAVi3bh1vvvkml112GQBbt24lGAxSW1ubfE1hYSFz586lrq4OgLq6OoqKipgzZ06yTm1tLbZts2rVqgHfNxKJEA6HU4pIOtj5pTh9Bz0GdRJwuK0YRETkmEv7rKjvfe97hMNhZsyYgcfjIZFI8JOf/IRFixYBEAwGASgvL095XXl5efJcMBikrKwstaFeLyUlJck6B1u6dCk/+tGP0n07kuFMbyd2TgFO59D2fEqE9+CrPpujGXtj5xRg+jqH/XoREekv7T02v/71r3nmmWf41a9+xerVq3n66af52c9+xtNPP53ut0qxZMkSQqFQsmzfvn1E308yg9N7BGvJWAMMHjYOibadYIY/xsby+jDx6OEriojIEUt7j82dd97J9773Pa677joAZs2axbZt21i6dCk33HADFRUVADQ1NTFhwoTk65qamjjrrLMAqKiooLm5OeW68Xictra25OsP5vf78fv96b4dETwFpUS62lKOOX3dGCcxSi0SEZHBpL3HpqenB9tOvazH48HZO3ukurqaiooKli9fnjwfDodZtWoVNTU1ANTU1NDR0UF9fX2yzooVK3Ach7lz56a7ySKHZnv7hRgn3AzqbREROe6kvcfmiiuu4Cc/+QmTJ0/m9NNPZ82aNTz44IN89atfBcCyLG6//XZ+/OMfM23aNKqrq7n77ruprKzkqquuAuAzn/kMCxYs4Oabb+bxxx8nFotx2223cd11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nPH7/eapp54ymzZtMrfccospKipKGYUtw/ed73zHvP7662br1q3mrbfeMrW1tWbcuHGmubnZGONOuZw8ebJZsWKFee+990xNTY2pqalJvn7fdL958+aZtWvXmldffdWMHz9e070PobOz06xZs8asWbPGAObBBx80a9asMdu2bTPGuNO9i4qKzG9/+1uzfv16c+WVVw443Xv27Nlm1apV5s033zTTpk1LmWrc0dFhysvLzfXXX282bNhgnnvuOZObm6upxnsd6jPo7Ow0f/d3f2fq6urM1q1bzR/+8Adz9tlnm2nTppm+vr7kNfQZDN+tt95qCgsLzeuvv54ylbinpydZJx2/e/ZNNb7zzjvN5s2bzSOPPKLp3nsd7jP46KOPzL333mvee+89s3XrVvPb3/7WTJ061Vx88cXJaxyrzyDjgo0xxvz85z83kydPNj6fz5x33nnm7bffHu0mZYxrr73WTJgwwfh8PjNx4kRz7bXXmo8++ih5vre313z96183xcXFJjc31/zFX/yF2b17d8o1Pv30U3PZZZeZnJwcM27cOPOd73zHxGKxY30rJ4zXXnvNAP3KDTfcYIxxp3zffffdpry83Pj9fnPppZeahoaGlGu0traahQsXmvz8fBMIBMyNN95oOjs7U+qsW7fOXHjhhcbv95uJEyea++6771jd4nHvUJ9BT0+PmTdvnhk/frzJysoyU6ZMMTfffHO//5nSZzB8A33vAfPLX/4yWSddv3tee+01c9ZZZxmfz2emTp2a8h5j2eE+g8bGRnPxxRebkpIS4/f7zSmnnGLuvPPOlHVsjDk2n4G1t8EiIiIiJ7yMGmMjIiIiY5uCjYiIiGQMBRsRERHJGAo2IiIikjEUbERERCRjKNiIiIhIxlCwERERkYyhYCMiIiIZQ8FGREREMoaCjYiIiGQMBRsRERHJGAo2IiIikjH+f71FqjArEj+oAAAAAElFTkSuQmCC", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "WIDTH = 2560\n", "HEIGHT = 1440\n", "\n", "clusters_detections = clusters_to_detections(clusters, sorted_detections)\n", "im = np.zeros((HEIGHT, WIDTH, 3), dtype=np.uint8)\n", "for el in clusters_detections[0]:\n", " im = visualize_whole_body(np.asarray(el.keypoints), im)\n", "\n", "p = plt.imshow(im)\n", "display(p)" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "image/png": "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9z6DQCAg1dXVUl1dLQDkySeflOrqajl48KCI6NO9nU6nvP3227J9+3a54YYbup3uPX36dKmsrJSPPvpIJkyYEDfV2Ov1isvlksWLF8vOnTvltddeE5vNxqnGJ5zpMwgEAvLP//zPUlFRIQcOHJC//OUvcumll8qECRMkGAzGrsHPoP/uuusucTgc8sEHH8RNJW5ra4vVScR3T+dU4wceeED27Nkja9as4XTvE872GXz++efy2GOPyebNm+XAgQPy9ttvy/jx42X27NmxawzVZ5BywUZE5JlnnpGioiIxm81yxRVXyCeffJLsJqWMW265RfLz88VsNsvo0aPllltukc8//zx2vr29Xb73ve9JVlaW2Gw2+frXvy51dXVx1/jyyy9lwYIFkpaWJrm5uXL//fdLOBwe6rcyYmzYsEEAdCm33XabiOhTvh988EFxuVxisVhkzpw5UlNTE3eNxsZGWbRokWRkZIjdbpclS5ZIIBCIq7Nt2zaZOXOmWCwWGT16tDz++OND9RaHvTN9Bm1tbTJ37lwZNWqUmEwmGTt2rNxxxx1d/pjiZ9B/3f3sAchvfvObWJ1Effds2LBBLrnkEjGbzTJ+/Pi41ziXne0zqK2tldmzZ0t2drZYLBY5//zz5YEHHohbx0ZkaD4D5USDiYiIiEa8lBpjQ0REROc2BhsiIiJKGQw2RERElDIYbIiIiChlMNgQERFRymCwISIiopTBYENEREQpg8GGiIiIUgaDDREREaUMBhsiIiJKGQw2RERElDIYbIiIiChl/P/0tpGwXucEQAAAAABJRU5ErkJggg==", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "im_prime = np.zeros((HEIGHT, WIDTH, 3), dtype=np.uint8)\n", "for el in clusters_detections[1]:\n", " im_prime = visualize_whole_body(np.asarray(el.keypoints), im_prime)\n", "\n", "p_prime = plt.imshow(im_prime)\n", "display(p_prime)" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [], "source": [ "@jaxtyped(typechecker=beartype)\n", "def triangulate_one_point_from_multiple_views_linear(\n", " proj_matrices: Float[Array, \"N 3 4\"],\n", " points: Num[Array, \"N 2\"],\n", " confidences: Optional[Float[Array, \"N\"]] = None,\n", ") -> Float[Array, \"3\"]:\n", " \"\"\"\n", " Args:\n", " proj_matrices: 形状为(N, 3, 4)的投影矩阵序列\n", " points: 形状为(N, 2)的点坐标序列\n", " confidences: 形状为(N,)的置信度序列,范围[0.0, 1.0]\n", "\n", " Returns:\n", " point_3d: 形状为(3,)的三角测量得到的3D点\n", " \"\"\"\n", " assert len(proj_matrices) == len(points)\n", "\n", " N = len(proj_matrices)\n", " confi: Float[Array, \"N\"]\n", " if confidences is None:\n", " confi = jnp.ones(N, dtype=np.float32)\n", " else:\n", " # Use square root of confidences for weighting - more balanced approach\n", " confi = jnp.sqrt(jnp.clip(confidences, 0, 1))\n", "\n", " A = jnp.zeros((N * 2, 4), dtype=np.float32)\n", " for i in range(N):\n", " x, y = points[i]\n", " A = A.at[2 * i].set(proj_matrices[i, 2] * x - proj_matrices[i, 0])\n", " A = A.at[2 * i + 1].set(proj_matrices[i, 2] * y - proj_matrices[i, 1])\n", " A = A.at[2 * i].mul(confi[i])\n", " A = A.at[2 * i + 1].mul(confi[i])\n", "\n", " # https://docs.jax.dev/en/latest/_autosummary/jax.numpy.linalg.svd.html\n", " _, _, vh = jnp.linalg.svd(A, full_matrices=False)\n", " point_3d_homo = vh[-1] # shape (4,)\n", "\n", " # replace the Python `if` with a jnp.where\n", " point_3d_homo = jnp.where(\n", " point_3d_homo[3] < 0, # predicate (scalar bool tracer)\n", " -point_3d_homo, # if True\n", " point_3d_homo, # if False\n", " )\n", "\n", " point_3d = point_3d_homo[:3] / point_3d_homo[3]\n", " return point_3d\n", "\n", "\n", "@jaxtyped(typechecker=beartype)\n", "def triangulate_points_from_multiple_views_linear(\n", " proj_matrices: Float[Array, \"N 3 4\"],\n", " points: Num[Array, \"N P 2\"],\n", " confidences: Optional[Float[Array, \"N P\"]] = None,\n", ") -> Float[Array, \"P 3\"]:\n", " \"\"\"\n", " Batch‐triangulate P points observed by N cameras, linearly via SVD.\n", "\n", " Args:\n", " proj_matrices: (N, 3, 4) projection matrices\n", " points: (N, P, 2) image-coordinates per view\n", " confidences: (N, P, 1) optional per-view confidences in [0,1]\n", "\n", " Returns:\n", " (P, 3) 3D point for each of the P tracks\n", " \"\"\"\n", " N, P, _ = points.shape\n", " assert proj_matrices.shape[0] == N\n", " if confidences is None:\n", " conf = jnp.ones((N, P), dtype=jnp.float32)\n", " else:\n", " conf = jnp.sqrt(jnp.clip(confidences, 0.0, 1.0))\n", "\n", " # vectorize your one‐point routine over P\n", " vmap_triangulate = jax.vmap(\n", " triangulate_one_point_from_multiple_views_linear,\n", " in_axes=(None, 1, 1), # proj_matrices static, map over points[:,p,:], conf[:,p]\n", " out_axes=0,\n", " )\n", "\n", " # returns (P, 3)\n", " return vmap_triangulate(proj_matrices, points, conf)" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [], "source": [ "def triangle_from_cluster(cluster: list[Detection]) -> Float[Array, \"3\"]:\n", " proj_matrices = jnp.array(\n", " [\n", " el.camera.params.projection_matrix\n", " for el in cluster\n", " ]\n", " )\n", " points = jnp.array([el.keypoints for el in cluster])\n", " confidences = jnp.array([el.confidences for el in cluster])\n", " return triangulate_points_from_multiple_views_linear(proj_matrices, points, confidences=confidences)\n", "\n", "\n", "res = {\n", " \"a\": triangle_from_cluster(clusters_detections[0]).tolist(),\n", " \"b\": triangle_from_cluster(clusters_detections[1]).tolist(),\n", "} \n", "with open(\"samples/res.json\", \"wb\") as f:\n", " f.write(orjson.dumps(res))" ] } ], "metadata": { "kernelspec": { "display_name": ".venv", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.9" } }, "nbformat": 4, "nbformat_minor": 2 }