From dce9e11502ab99eb2b187f5a8fe595dde036a37c Mon Sep 17 00:00:00 2001 From: crosstyan Date: Mon, 21 Apr 2025 16:26:21 +0800 Subject: [PATCH] x --- .gitignore | 3 +- cali.py | 7 +- compute_3d_maybe.ipynb | 157 ++++++++++++++++++++++++++++++++++++ dump_and_play.sh | 58 +++++++++++++ find_cute_box.py | 16 ++-- get_ext.ipynb | 179 ++++++++++++++++++----------------------- output/b-af_03.parquet | 3 - output/params.parquet | 4 +- pose/a.parquet | 3 + pose/b.parquet | 3 + run_capture.py | 132 ++++++++++++++++++++++++++++++ 11 files changed, 446 insertions(+), 119 deletions(-) create mode 100644 compute_3d_maybe.ipynb create mode 100755 dump_and_play.sh delete mode 100644 output/b-af_03.parquet create mode 100644 pose/a.parquet create mode 100644 pose/b.parquet create mode 100644 run_capture.py diff --git a/.gitignore b/.gitignore index b475da3..40ff91e 100644 --- a/.gitignore +++ b/.gitignore @@ -166,4 +166,5 @@ cython_debug/ #.idea/ .DS_Store output/svg -*.mp4 \ No newline at end of file +*.mp4 +output \ No newline at end of file diff --git a/cali.py b/cali.py index f067b4c..e17400e 100644 --- a/cali.py +++ b/cali.py @@ -9,9 +9,6 @@ from itertools import chain from typing import Optional, Sequence, TypedDict, cast import awkward as ak -from matplotlib.pyplot import stem -from numpy import ndarray - class ArucoDictionary(Enum): Dict_4X4_50 = aruco.DICT_4X4_50 @@ -37,10 +34,10 @@ class ArucoDictionary(Enum): Dict_ArUco_ORIGINAL = aruco.DICT_ARUCO_ORIGINAL -IMAGE_FOLDER = Path("dumped/batch_two/b") +IMAGE_FOLDER = Path("dumped/batch_three/c") OUTPUT_FOLDER = Path("output") DICTIONARY = ArucoDictionary.Dict_4X4_50 -CALIBRATION_PARQUET: Optional[Path] = OUTPUT_FOLDER / "af_03.parquet" +CALIBRATION_PARQUET: Optional[Path] = OUTPUT_FOLDER / "c-af_03.parquet" class CameraParams(TypedDict): diff --git a/compute_3d_maybe.ipynb b/compute_3d_maybe.ipynb new file mode 100644 index 0000000..dbcd9a7 --- /dev/null +++ b/compute_3d_maybe.ipynb @@ -0,0 +1,157 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [], + "source": [ + "import awkward as ak\n", + "from awkward import Array as AwakwardArray, Record as AwkwardRecord\n", + "from typing import cast\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
[{prediction: None, trackings: [], frame_num: 0, ...},\n",
+       " {prediction: None, trackings: [], frame_num: 1, ...},\n",
+       " {prediction: None, trackings: [], frame_num: 2, ...},\n",
+       " {prediction: None, trackings: [], frame_num: 3, ...},\n",
+       " {prediction: None, trackings: [], frame_num: 4, ...},\n",
+       " {prediction: None, trackings: [], frame_num: 5, ...},\n",
+       " {prediction: None, trackings: [], frame_num: 6, ...},\n",
+       " {prediction: None, trackings: [], frame_num: 7, ...},\n",
+       " {prediction: None, trackings: [], frame_num: 8, ...},\n",
+       " {prediction: {Akeypoints: [[...]], ...}, trackings: [{...}], ...},\n",
+       " ...,\n",
+       " {prediction: {Akeypoints: [[...]], ...}, trackings: [{...}], ...},\n",
+       " {prediction: {Akeypoints: [[...]], ...}, trackings: [{...}], ...},\n",
+       " {prediction: {Akeypoints: [[...]], ...}, trackings: [{...}], ...},\n",
+       " {prediction: {Akeypoints: [[...]], ...}, trackings: [{...}], ...},\n",
+       " {prediction: {Akeypoints: [[...]], ...}, trackings: [{...}], ...},\n",
+       " {prediction: {Akeypoints: [[...]], ...}, trackings: [{...}], ...},\n",
+       " {prediction: {Akeypoints: [[...]], ...}, trackings: [{...}], ...},\n",
+       " {prediction: {Akeypoints: [[...]], ...}, trackings: [{...}], ...},\n",
+       " {prediction: {Akeypoints: [[...]], ...}, trackings: [{...}], ...}]\n",
+       "-------------------------------------------------------------------\n",
+       "type: 808 * {\n",
+       "    prediction: ?{\n",
+       "        Akeypoints: var * var * var * float64,\n",
+       "        bboxes: var * var * float64,\n",
+       "        scores: var * var * var * float64,\n",
+       "        frame_number: int64,\n",
+       "        reference_frame_size: {\n",
+       "            "0": int64,\n",
+       "            "1": int64\n",
+       "        }\n",
+       "    },\n",
+       "    trackings: var * {\n",
+       "        id: int64,\n",
+       "        bounding_boxes: var * var * float64\n",
+       "    },\n",
+       "    frame_num: int64,\n",
+       "    reference_frame_size: {\n",
+       "        height: int64,\n",
+       "        width: int64\n",
+       "    }\n",
+       "}
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "a_ak = ak.from_parquet(\"pose/a.parquet\")\n", + "b_ak = ak.from_parquet(\"pose/b.parquet\")\n", + "# display(a_ak)\n", + "display(b_ak)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
{Akeypoints: [[[893, 417], [898, 408], [...], ..., [782, 596], [785, 599]]],\n",
+       " bboxes: [[756, 341, 940, 597]],\n",
+       " scores: [[[0.907], [0.896], [0.916], [0.341], ..., [0.811], [0.835], [0.802]]],\n",
+       " frame_number: 5,\n",
+       " reference_frame_size: {'0': 1080, '1': 1920}}\n",
+       "--------------------------------------------------------------------------------\n",
+       "type: {\n",
+       "    Akeypoints: var * var * var * float64,\n",
+       "    bboxes: var * var * float64,\n",
+       "    scores: var * var * var * float64,\n",
+       "    frame_number: int64,\n",
+       "    reference_frame_size: {\n",
+       "        "0": int64,\n",
+       "        "1": int64\n",
+       "    }\n",
+       "}
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a_ak[\"prediction\"][5]" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [], + "source": [ + "unique_tracking_ids_a = np.unique(ak.to_numpy(ak.flatten(cast(AwakwardArray, a_ak[\"trackings\"][\"id\"]))))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.8" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/dump_and_play.sh b/dump_and_play.sh new file mode 100755 index 0000000..85813a9 --- /dev/null +++ b/dump_and_play.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +if [ -z $1 ]; then + echo "Usage: $0 " + exit 1 +else + echo "dumping video from port $1" +fi + +TARGET_PORT=$1; + +if ! [[ $TARGET_PORT =~ ^[0-9]+$ ]] ; then + echo "error: expect a number, got $TARGET_PORT" >&2 + exit 1 +fi + +# See also majestic.yaml +# Get the current date and time in the format YYYYMMDD-HHMMSS +DATE=$(date +"%Y%m%d-%H%M%S") + +# use mts as MPEG transport stream +FILENAME="output/video-${DATE}-${TARGET_PORT}.mts" +# SINK="autovideosink" +SINK="glimagesink" + + +# Run the GStreamer pipeline with the dynamic filename +# gst-launch-1.0 -e udpsrc port=$TARGET_PORT \ +# ! 'application/x-rtp,encoding-name=H265,payload=96' \ +# ! rtph265depay \ +# ! h265parse \ +# ! tee name=t \ +# t. ! queue ! $DECODER ! videoconvert ! $SINK \ +# t. ! queue ! mp4mux ! filesink location=$FILENAME + + +# DECODER="nvh265dec" +# DECODER="vulkanh265dec" +# DECODER="avdec_h265" +DECODER="vtdec_hw" +# DECODER="vtdec" +# gst-launch-1.0 -e udpsrc port=$TARGET_PORT auto-multicast=true multicast-group=224.0.0.123 \ +# ! 'application/x-rtp,encoding-name=H265,payload=96' \ +# ! rtph265depay \ +# ! tee name=t \ +# ! h265parse \ +# t. ! queue ! $DECODER ! videoconvert ! $SINK \ +# t. ! queue ! mpegtsmux ! filesink location=$FILENAME + +# hvc1 +# hev1 +gst-launch-1.0 -e udpsrc port=$TARGET_PORT auto-multicast=true multicast-group=224.0.0.123 \ +! 'application/x-rtp,encoding-name=H265,payload=96' \ +! rtph265depay \ +! tee name=t \ +t. ! queue ! h265parse ! "video/x-h265,stream-format=hvc1" ! $DECODER ! videoconvert ! $SINK \ +t. ! queue ! h265parse ! mpegtsmux ! filesink location=$FILENAME + diff --git a/find_cute_box.py b/find_cute_box.py index 46d21c4..e357170 100644 --- a/find_cute_box.py +++ b/find_cute_box.py @@ -10,9 +10,14 @@ import numpy as np NDArray = np.ndarray CALIBRATION_PARQUET = Path("output") / "usbcam_cal.parquet" -DICTIONARY: Final[int] = aruco.DICT_4X4_50 +# 7x7 +DICTIONARY: Final[int] = aruco.DICT_7X7_1000 # 400mm MARKER_LENGTH: Final[float] = 0.4 +RED = (0, 0, 255) +GREEN = (0, 255, 0) +BLUE = (255, 0, 0) +YELLOW = (0, 255, 255) def gen(): @@ -47,23 +52,18 @@ def main(): # logger.info("markers={}, ids={}", np.array(markers).shape, np.array(ids).shape) for m, i in zip(markers, ids): center = np.mean(m, axis=0).astype(int) - GREY = (128, 128, 128) logger.info("id={}, center={}", i, center) - cv2.circle(frame, tuple(center), 5, GREY, -1) + cv2.circle(frame, tuple(center), 5, RED, -1) cv2.putText( frame, str(i), tuple(center), cv2.FONT_HERSHEY_SIMPLEX, 1, - GREY, + RED, 2, ) # BGR - RED = (0, 0, 255) - GREEN = (0, 255, 0) - BLUE = (255, 0, 0) - YELLOW = (0, 255, 255) color_map = [RED, GREEN, BLUE, YELLOW] for color, corners in zip(color_map, m): corners = corners.astype(int) diff --git a/get_ext.ipynb b/get_ext.ipynb index 50f68b0..581e0a2 100644 --- a/get_ext.ipynb +++ b/get_ext.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 38, "metadata": {}, "outputs": [], "source": [ @@ -22,7 +22,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 39, "metadata": {}, "outputs": [], "source": [ @@ -33,80 +33,15 @@ "MARKER_LENGTH: Final[float] = 0.4\n", "\n", "A_CALIBRATION_PARQUET = Path(\"output\") / \"a-ae_08.parquet\"\n", - "B_CALIBRATION_PARQUET = Path(\"output\") / \"b-af_03.parquet\"" + "B_CALIBRATION_PARQUET = Path(\"output\") / \"b-ae_09.parquet\"\n", + "C_CALIBRATION_PARQUET = Path(\"output\") / \"c-af_03.parquet\"" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 40, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'ops_map'" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "{16: array([[0.152, 0.025, 0. ],\n", - " [0.249, 0.025, 0. ],\n", - " [0.249, 0.122, 0. ],\n", - " [0.152, 0.122, 0. ]]),\n", - " 17: array([[0.025, 0.152, 0. ],\n", - " [0.122, 0.152, 0. ],\n", - " [0.122, 0.249, 0. ],\n", - " [0.025, 0.249, 0. ]]),\n", - " 18: array([[0.27900001, 0.152 , 0. ],\n", - " [0.37599999, 0.152 , 0. ],\n", - " [0.37599999, 0.249 , 0. ],\n", - " [0.27900001, 0.249 , 0. ]]),\n", - " 19: array([[0.152 , 0.27900001, 0. ],\n", - " [0.249 , 0.27900001, 0. ],\n", - " [0.249 , 0.37599999, 0. ],\n", - " [0.152 , 0.37599999, 0. ]]),\n", - " 20: array([[1.51999995e-01, 1.70838222e-17, 3.86000000e-01],\n", - " [2.48999998e-01, 2.89628965e-17, 3.86000000e-01],\n", - " [2.48999998e-01, 2.30233595e-17, 2.88999999e-01],\n", - " [1.51999995e-01, 1.11442852e-17, 2.88999999e-01]]),\n", - " 21: array([[ 2.50000004e-02, -6.24569833e-18, 2.59000005e-01],\n", - " [ 1.22000001e-01, 5.63337574e-18, 2.59000005e-01],\n", - " [ 1.22000001e-01, -3.06161408e-19, 1.62000002e-01],\n", - " [ 2.50000004e-02, -1.21852355e-17, 1.62000002e-01]]),\n", - " 22: array([[2.79000014e-01, 2.48603320e-17, 2.59000005e-01],\n", - " [3.75999987e-01, 3.67394027e-17, 2.59000005e-01],\n", - " [3.75999987e-01, 3.07998655e-17, 1.62000002e-01],\n", - " [2.79000014e-01, 1.89207949e-17, 1.62000002e-01]]),\n", - " 23: array([[ 1.51999995e-01, 1.53080704e-18, 1.31999986e-01],\n", - " [ 2.48999998e-01, 1.34098813e-17, 1.31999986e-01],\n", - " [ 2.48999998e-01, 7.47034601e-18, 3.50000129e-02],\n", - " [ 1.51999995e-01, -4.40872829e-18, 3.50000129e-02]]),\n", - " 24: array([[1.53080852e-18, 2.49000005e-01, 3.86000000e-01],\n", - " [1.53080852e-18, 1.52000002e-01, 3.86000000e-01],\n", - " [7.47034556e-18, 1.52000002e-01, 2.88999999e-01],\n", - " [7.47034556e-18, 2.49000005e-01, 2.88999999e-01]]),\n", - " 25: array([[9.30731537e-18, 3.76000000e-01, 2.59000005e-01],\n", - " [9.30731537e-18, 2.78999999e-01, 2.59000005e-01],\n", - " [1.52468525e-17, 2.78999999e-01, 1.62000002e-01],\n", - " [1.52468525e-17, 3.76000000e-01, 1.62000002e-01]]),\n", - " 26: array([[9.30731537e-18, 1.21999986e-01, 2.59000005e-01],\n", - " [9.30731537e-18, 2.50000129e-02, 2.59000005e-01],\n", - " [1.52468525e-17, 2.50000129e-02, 1.62000002e-01],\n", - " [1.52468525e-17, 1.21999986e-01, 1.62000002e-01]]),\n", - " 27: array([[1.70838237e-17, 2.49000005e-01, 1.31999986e-01],\n", - " [1.70838237e-17, 1.52000002e-01, 1.31999986e-01],\n", - " [2.30233590e-17, 1.52000002e-01, 3.50000129e-02],\n", - " [2.30233590e-17, 2.49000005e-01, 3.50000129e-02]])}" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "aruco_dict = aruco.getPredefinedDictionary(DICTIONARY)\n", "def read_camera_calibration(path: Path) -> tuple[MatLike, MatLike]:\n", @@ -115,8 +50,6 @@ " distortion_coefficients = cast(MatLike, ak.to_numpy(cal[\"distortion_coefficients\"]))\n", " return camera_matrix, distortion_coefficients\n", "\n", - "a_mtx, a_dist = read_camera_calibration(A_CALIBRATION_PARQUET)\n", - "b_camera_matrix, b_distortion_coefficients = read_camera_calibration(B_CALIBRATION_PARQUET)\n", "ops = ak.from_parquet(OBJECT_POINTS_PARQUET)\n", "detector = aruco.ArucoDetector(\n", " dictionary=aruco_dict, detectorParams=aruco.DetectorParameters()\n", @@ -125,12 +58,12 @@ "total_ids = cast(NDArray, ak.to_numpy(ops[\"ids\"])).flatten()\n", "total_corners = cast(NDArray, ak.to_numpy(ops[\"corners\"])).reshape(-1, 4, 3)\n", "ops_map: dict[int, NDArray] = dict(zip(total_ids, total_corners))\n", - "display(\"ops_map\", ops_map)" + "# display(\"ops_map\", ops_map)" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 41, "metadata": {}, "outputs": [], "source": [ @@ -227,26 +160,35 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 42, "metadata": {}, "outputs": [], "source": [ - "A_IMG = Path(\"dumped/batch_two/op/video-20241219-142434-op-a.png\")\n", - "B_IMG = Path(\"dumped/batch_two/op/video-20241219-142439-op-b.png\")\n", + "A_IMG = Path(\"dumped/batch_three/video-20241224-154256-a.png\")\n", + "B_IMG = Path(\"dumped/batch_three/video-20241224-154302-b.png\")\n", + "C_IMG = Path(\"dumped/batch_three/video-20241224-154252-c.png\")\n", + "C_PRIME_IMG = Path(\"dumped/batch_three/video-20241224-153926-c-prime.png\")\n", + "\n", "a_img = cv2.imread(str(A_IMG))\n", - "b_img = cv2.imread(str(B_IMG))" + "b_img = cv2.imread(str(B_IMG))\n", + "c_img = cv2.imread(str(C_IMG))\n", + "c_prime_img = cv2.imread(str(C_PRIME_IMG))\n", + "\n", + "a_mtx, a_dist = read_camera_calibration(A_CALIBRATION_PARQUET)\n", + "b_mtx, b_dist = read_camera_calibration(B_CALIBRATION_PARQUET)\n", + "c_mtx, c_dist = read_camera_calibration(C_CALIBRATION_PARQUET)" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 43, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/var/folders/cj/0zmvpygn7m72m42lh6x_hcgw0000gn/T/ipykernel_28395/542219436.py:22: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n", + "/var/folders/cj/0zmvpygn7m72m42lh6x_hcgw0000gn/T/ipykernel_79393/542219436.py:22: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n", " id = int(id)\n" ] } @@ -258,26 +200,45 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 44, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/var/folders/cj/0zmvpygn7m72m42lh6x_hcgw0000gn/T/ipykernel_28395/542219436.py:22: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n", + "/var/folders/cj/0zmvpygn7m72m42lh6x_hcgw0000gn/T/ipykernel_79393/542219436.py:22: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n", " id = int(id)\n" ] } ], "source": [ - "b_result_img, b_rvec, b_tvec = process(b_img, b_camera_matrix, b_distortion_coefficients)\n", + "b_result_img, b_rvec, b_tvec = process(b_img, b_mtx, b_dist)\n", "# plt.imshow(cv2.cvtColor(b_result_img, cv2.COLOR_BGR2RGB))" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 45, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/var/folders/cj/0zmvpygn7m72m42lh6x_hcgw0000gn/T/ipykernel_79393/542219436.py:22: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n", + " id = int(id)\n" + ] + } + ], + "source": [ + "c_result_img, c_rvec, c_tvec = process(c_img, c_mtx, c_dist)\n", + "c_prime_result_img, c_prime_rvec, c_prime_tvec = process(c_prime_img, c_mtx, c_dist)" + ] + }, + { + "cell_type": "code", + "execution_count": 46, "metadata": {}, "outputs": [ { @@ -292,10 +253,12 @@ { "data": { "text/html": [ - "
[{name: 'a', rvec: [[-1.04], ..., [-2.95]], tvec: [...], ...},\n",
-       " {name: 'b', rvec: [[0.948], ..., [2.97]], tvec: [...], ...}]\n",
-       "--------------------------------------------------------------\n",
-       "type: 2 * {\n",
+       "
[{name: 'a-ae_08', rvec: [[-0.602], ..., [-3.05]], tvec: [...], ...},\n",
+       " {name: 'b-ae_09', rvec: [[0.572], ..., [3.02]], tvec: [...], ...},\n",
+       " {name: 'c-af_03', rvec: [[-1.98], ..., [-2.4]], tvec: [...], ...},\n",
+       " {name: 'c-prime-af_03', rvec: [[-1.99], ...], tvec: [...], ...}]\n",
+       "---------------------------------------------------------------------\n",
+       "type: 4 * {\n",
        "    name: string,\n",
        "    rvec: var * var * float64,\n",
        "    tvec: var * var * float64,\n",
@@ -304,7 +267,7 @@
        "}
" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -313,16 +276,16 @@ { "data": { "text/plain": [ - "\n", + "\n", " created_by: parquet-cpp-arrow version 14.0.1\n", " num_columns: 5\n", - " num_rows: 2\n", + " num_rows: 4\n", " num_row_groups: 1\n", " format_version: 2.6\n", " serialized_size: 0" ] }, - "execution_count": 8, + "execution_count": 46, "metadata": {}, "output_type": "execute_result" } @@ -331,19 +294,33 @@ "params = AwkwardArray(\n", " [\n", " {\n", - " \"name\": \"a\",\n", + " \"name\": \"a-ae_08\",\n", " \"rvec\": a_rvec,\n", " \"tvec\": a_tvec,\n", " \"camera_matrix\": a_mtx,\n", " \"distortion_coefficients\": a_dist,\n", " },\n", " {\n", - " \"name\": \"b\",\n", + " \"name\": \"b-ae_09\",\n", " \"rvec\": b_rvec,\n", " \"tvec\": b_tvec,\n", - " \"camera_matrix\": b_camera_matrix,\n", - " \"distortion_coefficients\": b_distortion_coefficients,\n", + " \"camera_matrix\": b_mtx,\n", + " \"distortion_coefficients\": b_dist,\n", " },\n", + " {\n", + " \"name\": \"c-af_03\",\n", + " \"rvec\": c_rvec,\n", + " \"tvec\": c_tvec,\n", + " \"camera_matrix\": c_mtx,\n", + " \"distortion_coefficients\": c_dist\n", + " },\n", + " {\n", + " \"name\": \"c-prime-af_03\",\n", + " \"rvec\": c_prime_rvec,\n", + " \"tvec\": c_prime_tvec,\n", + " \"camera_matrix\": c_mtx,\n", + " \"distortion_coefficients\": c_dist\n", + " }\n", " ]\n", ")\n", "display(\"params\", params)\n", @@ -352,7 +329,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 47, "metadata": {}, "outputs": [ { @@ -361,14 +338,16 @@ "True" ] }, - "execution_count": 9, + "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cv2.imwrite(\"output/a_result_img.png\", a_result_img)\n", - "cv2.imwrite(\"output/b_result_img.png\", b_result_img)" + "cv2.imwrite(\"output/b_result_img.png\", b_result_img)\n", + "cv2.imwrite(\"output/c_result_img.png\", c_result_img)\n", + "cv2.imwrite(\"output/c_prime_result_img.png\", c_prime_result_img)" ] } ], diff --git a/output/b-af_03.parquet b/output/b-af_03.parquet deleted file mode 100644 index 4807c1f..0000000 --- a/output/b-af_03.parquet +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1ba58c40d4594ad0fe79e5365b10a4fac5f91c0851a7b4f07f18075aa66aa5c3 -size 57927 diff --git a/output/params.parquet b/output/params.parquet index 3a900f1..db8e83c 100644 --- a/output/params.parquet +++ b/output/params.parquet @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:09ff6b97fac9c1e99fb617032d7af878630fa7dd491bc452b94e353aeeaf2842 -size 7955 +oid sha256:ccd7afa8e7527afb54419a3a00a4c61d42827037e71c6811505398d569a475f9 +size 8255 diff --git a/pose/a.parquet b/pose/a.parquet new file mode 100644 index 0000000..7bde762 --- /dev/null +++ b/pose/a.parquet @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9bb05c507a4562bcc5221d0bb8c99c7c7f626b11a40076bf0d669682e3a7a35e +size 1560075 diff --git a/pose/b.parquet b/pose/b.parquet new file mode 100644 index 0000000..9fa0645 --- /dev/null +++ b/pose/b.parquet @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:de75d374f57d0ce505a4d74bd6de94cabdc2b074d6a507312bb06f1d08163068 +size 1885737 diff --git a/run_capture.py b/run_capture.py new file mode 100644 index 0000000..1d0ed40 --- /dev/null +++ b/run_capture.py @@ -0,0 +1,132 @@ +from datetime import datetime +from os import PathLike +from pathlib import Path +import signal +from subprocess import Popen, TimeoutExpired +from typing import Any, Literal +from loguru import logger +import click +import loguru + +# pacman -S python-loguru +# pacman -S python-click + +Mode = Literal["preview", "save", "save_preview"] +MODE_LIST: list[Mode] = ["preview", "save", "save_preview"] +MULTICAST_ADDR = "224.0.0.123" + + +class DumpCommand: + port: int + output_path: str + + def __init__(self, port: int, output_path: PathLike | str): + self.port = port + self.output_path = str(output_path) + + def save_and_decode_nv_pipeline(self): + # note that capabilties SHOULD NOT have spaces in between + # `gst-launch-1.0` could tolerate that, but not the API itself + return f"""gst-launch-1.0 -e udpsrc port={self.port} \ + ! 'application/x-rtp,encoding-name=H265,payload=96' \ + ! rtph265depay \ + ! h265parse \ + ! tee name=t \ + t. ! queue ! nvh265dec ! videoconvert ! autovideosink \ + t. ! queue ! mp4mux ! filesink location={self.output_path} + """ + + def save_and_decode_nv_pipeline_multicast(self): + return f"""gst-launch-1.0 -e udpsrc port={self.port} \ + auto-multicast=true \ + multicast-group={MULTICAST_ADDR} \ + ! 'application/x-rtp,encoding-name=H265,payload=96' \ + ! rtph265depay \ + ! h265parse \ + ! tee name=t \ + t. ! queue ! vtdec_hw ! videoconvert ! autovideosink \ + t. ! queue ! mp4mux ! filesink location={self.output_path} + """ + # `vtdec_hw` for macos + # `nvh265dec` for nv + + def save_pipeline(self): + return f"""gst-launch-1.0 -e udpsrc port={self.port} \ + ! 'application/x-rtp, encoding-name=H265, payload=96' \ + ! rtph265depay \ + ! queue ! h265parse ! mp4mux ! filesink location={self.output_path} + """ + + def decode_cv_only(self): + return f"""gst-launch-1.0 -e udpsrc port={self.port} \ + ! 'application/x-rtp,encoding-name=H265,payload=96' \ + ! rtph265depay \ + ! h265parse \ + ! nvh265dec \ + ! videoconvert \ + ! autovideosink + """ + + def get_pipeline_from_mode(self, mode: Mode): + if mode == "save": + return self.save_pipeline() + elif mode == "save_preview": + return self.save_and_decode_nv_pipeline_multicast() + elif mode == "preview": + return self.decode_cv_only() + raise ValueError(f"Unknown mode: {mode}") + + +def test_filename( + port: int, + output_dir: PathLike | str, + date: datetime, + prefix="video_", + suffix=".mp4", +): + date_str = date.strftime("%Y-%m-%d_%H-%M-%S") + assert suffix.startswith("."), "suffix should start with a dot" + file_name = f"{prefix}{date_str}_{port}{suffix}" + return Path(output_dir) / file_name + + +# nmap -sS --open -p 22 192.168.2.0/24 + + +@click.command() +@click.option("-o", "--output", type=click.Path(exists=True), default="output") +@click.option("-m", "--mode", type=click.Choice(MODE_LIST), default="save_preview") +def main(output: str, mode: Mode): + ports = [5601, 5602, 5603, 5604, 5605, 5606] + output_dir = Path(output) + now = datetime.now() + commands = [ + DumpCommand(port, test_filename(port, output_dir, now)) for port in ports + ] + ps: list[Popen] = [] + run_flag: bool = True + + def handle_sigint(signum: int, frame: Any): + nonlocal run_flag + run_flag = False + logger.info("Received SIGINT, stopping all processes") + + for command in commands: + p = Popen(command.get_pipeline_from_mode(mode), shell=True) + ps.append(p) + + signal.signal(signal.SIGINT, handle_sigint) + while run_flag: + pass + + for p in ps: + p.send_signal(signal.SIGINT) + for p in ps: + try: + p.wait(3) + except TimeoutExpired: + logger.warning("Command `{}` timeout", p.args) + + +if __name__ == "__main__": + main() # pylint: disable=no-value-for-parameter