This commit is contained in:
2025-04-21 16:26:21 +08:00
parent 3598defe68
commit dce9e11502
11 changed files with 446 additions and 119 deletions

3
.gitignore vendored
View File

@ -166,4 +166,5 @@ cython_debug/
#.idea/
.DS_Store
output/svg
*.mp4
*.mp4
output

View File

@ -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):

157
compute_3d_maybe.ipynb Normal file
View File

@ -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": [
"<pre>[{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",
" &quot;0&quot;: int64,\n",
" &quot;1&quot;: 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",
"}</pre>"
],
"text/plain": [
"<Array [{prediction: None, ...}, ..., {...}] type='808 * {prediction: ?{Ake...'>"
]
},
"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": [
"<pre>{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: {&#x27;0&#x27;: 1080, &#x27;1&#x27;: 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",
" &quot;0&quot;: int64,\n",
" &quot;1&quot;: int64\n",
" }\n",
"}</pre>"
],
"text/plain": [
"<Record {Akeypoints: [[...]], bboxes: ..., ...} type='{Akeypoints: var * va...'>"
]
},
"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
}

58
dump_and_play.sh Executable file
View File

@ -0,0 +1,58 @@
#!/bin/bash
if [ -z $1 ]; then
echo "Usage: $0 <port>"
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

View File

@ -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)

View File

@ -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": [
"<pre>[{name: &#x27;a&#x27;, rvec: [[-1.04], ..., [-2.95]], tvec: [...], ...},\n",
" {name: &#x27;b&#x27;, rvec: [[0.948], ..., [2.97]], tvec: [...], ...}]\n",
"--------------------------------------------------------------\n",
"type: 2 * {\n",
"<pre>[{name: &#x27;a-ae_08&#x27;, rvec: [[-0.602], ..., [-3.05]], tvec: [...], ...},\n",
" {name: &#x27;b-ae_09&#x27;, rvec: [[0.572], ..., [3.02]], tvec: [...], ...},\n",
" {name: &#x27;c-af_03&#x27;, rvec: [[-1.98], ..., [-2.4]], tvec: [...], ...},\n",
" {name: &#x27;c-prime-af_03&#x27;, 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 @@
"}</pre>"
],
"text/plain": [
"<Array [{name: 'a', rvec: [...], ...}, ...] type='2 * {name: string, rvec: ...'>"
"<Array [{name: 'a-ae_08', rvec: ..., ...}, ...] type='4 * {name: string, rv...'>"
]
},
"metadata": {},
@ -313,16 +276,16 @@
{
"data": {
"text/plain": [
"<pyarrow._parquet.FileMetaData object at 0x17f93f830>\n",
"<pyarrow._parquet.FileMetaData object at 0x311da8900>\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)"
]
}
],

Binary file not shown.

Binary file not shown.

BIN
pose/a.parquet LFS Normal file

Binary file not shown.

BIN
pose/b.parquet LFS Normal file

Binary file not shown.

132
run_capture.py Normal file
View File

@ -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