handle images and movies

This commit is contained in:
2025-11-29 12:21:25 +01:00
parent a9de335b48
commit d4cbab2714
75 changed files with 80 additions and 37 deletions
+66 -24
View File
@@ -1,9 +1,11 @@
import os
import abc import abc
import math import math
import cv2 as cv import cv2 as cv
import numpy as np import numpy as np
from pathlib import Path from pathlib import Path
from ar_tag_pose.utils import to_board_pose_euler from ar_tag_pose.utils import to_board_pose_euler
import mimetypes
RESULTS_FOLDER = "results" RESULTS_FOLDER = "results"
@@ -18,8 +20,24 @@ class BoardPose(abc.ABC):
obj_points_list = [] obj_points_list = []
img_points_list = [] img_points_list = []
def __init__(self, device_id: int): def __init__(self, source: str):
self.device_id = device_id self.device_id = None
self.src_video = None
self.src_images = None
if source.isnumeric():
self.device_id = int(source)
elif isinstance(source, str):
mime_type = mimetypes.guess_type(source)[0]
if "video" in mime_type:
self.src_video = source
if "image" in mime_type:
self.src_images = source
basename = os.path.basename(source)
name = basename.split('_')[0]
numeric_part = basename.split('_')[-1].split('.')[0]
suffix = basename.split('_')[-1].split('.')[1]
self.src_images = f"{os.path.dirname(source)}/{name}_%0{len(numeric_part)}d.{suffix}"
Path.mkdir(Path(RESULTS_FOLDER), exist_ok=True) Path.mkdir(Path(RESULTS_FOLDER), exist_ok=True)
@staticmethod @staticmethod
@@ -62,7 +80,7 @@ class BoardPose(abc.ABC):
rvecs=r_vecs, tvecs=t_vecs, flags=flags) rvecs=r_vecs, tvecs=t_vecs, flags=flags)
print(f"Camera calibration finished: ", end='') print(f"Camera calibration finished: ", end='')
if ret < 2: if ret < 3:
print(f"Success ({ret})!") print(f"Success ({ret})!")
success = True success = True
self.mtx = mtx self.mtx = mtx
@@ -75,29 +93,52 @@ class BoardPose(abc.ABC):
return success return success
def process(self): def process(self):
cap = None
if self.device_id is not None:
# Open camera
cap = cv.VideoCapture(self.device_id)
BoardPose.cap_init(cap)
elif self.src_video is not None:
# Open video file
cap = cv.VideoCapture(self.src_video)
elif self.src_images is not None:
# Open image file sequence
cap = cv.VideoCapture(self.src_images, cv.CAP_IMAGES)
# Open camera if cap is None or not cap.isOpened():
cap = cv.VideoCapture(self.device_id) print("Cannot open media")
BoardPose.cap_init(cap)
if not cap.isOpened():
print("Cannot open camera")
exit() exit()
cal_img_size = None cal_img_size = None
last_rotation = [0, 0, 0] frame_go = True
while True: frame_num = 0
while cap.isOpened():
if self.src_images:
cap.set(cv.CAP_PROP_POS_FRAMES, frame_num)
# Read a new frame # Read a new frame
ok, img = cap.read() ok, img = cap.read()
if not ok: if not ok:
return break
k = cv.waitKey(1) & 0xFF k = cv.waitKey(1) & 0xFF
if k == ord('q'): if k == ord('q'):
break break
elif k == ord('g'):
frame_go = True
frame_num = 0
elif k == ord('s'): elif k == ord('s'):
frame_go = False
frame_num = 0
elif k == ord('-'):
frame_go = False
frame_num = frame_num - 1
elif k == ord('+'):
frame_go = False
frame_num = frame_num + 1
elif k == ord('p'):
success, cal_img_size, obj_points, img_points = self.calibrate_points(img) success, cal_img_size, obj_points, img_points = self.calibrate_points(img)
if success: if success:
print("Frame captured") print("Points captured")
self.obj_points_list.append(obj_points) self.obj_points_list.append(obj_points)
self.img_points_list.append(img_points) self.img_points_list.append(img_points)
else: else:
@@ -111,20 +152,21 @@ class BoardPose(abc.ABC):
self.dist = None self.dist = None
self.obj_points_list = [] self.obj_points_list = []
self.img_points_list = [] self.img_points_list = []
else:
success, r_vec, t_vec = self._process(img) success, r_vec, t_vec = self._process(img)
if success: if success:
r_vec_obj, _ = to_board_pose_euler(r_vec, t_vec) r_vec_obj, _ = to_board_pose_euler(r_vec, t_vec)
rot = [round(180.0 / math.pi * v, 0) for v in r_vec_obj] rot = [round(180.0 / math.pi * v, 1) for v in r_vec_obj]
if rot != last_rotation: print("\r ", end='')
print("\r ", print(f"\rTilt: {rot[0]:.1f}, Roll: {rot[1]:.1f}, Azimuth: {rot[2]:.1f}", end='')
end='')
print(f"\rTilt: {rot[0]:.1f}, Roll: {rot[1]:.1f}, Azimuth: {rot[2]:.1f}", end='')
last_rotation = rot
img_scaled = cv.resize(img, dsize=None, fx=0.5, fy=0.5) img_scaled = cv.resize(img, dsize=None, fx=0.5, fy=0.5)
cv.imshow('img', img_scaled) cv.imshow('img', img_scaled)
cv.destroyAllWindows()
if frame_go:
frame_num += 1
cv.destroyAllWindows()
@abc.abstractmethod @abc.abstractmethod
def get_name(self) -> str: def get_name(self) -> str:
+8 -7
View File
@@ -15,7 +15,7 @@ def factory(dict_type: int, board_size: cv.typing.Size, marker_length: float=100
return detector, board, name return detector, board, name
class BoardPoseAruco(BoardPose): class BoardPoseAruco(BoardPose):
def __init__(self, device_id: int, dict_type: int, board_size: tuple[int, int]): def __init__(self, device_id: str, dict_type: int, board_size: tuple[int, int]):
BoardPose.__init__(self, device_id) BoardPose.__init__(self, device_id)
self.board_size = board_size self.board_size = board_size
self.detector, self.board, self.name = factory(dict_type, board_size) self.detector, self.board, self.name = factory(dict_type, board_size)
@@ -53,8 +53,6 @@ class BoardPoseAruco(BoardPose):
pose = False pose = False
r_vec = None r_vec = None
t_vec = None t_vec = None
if not self.is_calibrated():
return pose, None, None
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY) gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
(corners, ids, rejected) = self.detector.detectMarkers(gray) (corners, ids, rejected) = self.detector.detectMarkers(gray)
@@ -67,6 +65,9 @@ class BoardPoseAruco(BoardPose):
if draw_marker_id: if draw_marker_id:
cv.aruco.drawDetectedMarkers(image, corners, ids) cv.aruco.drawDetectedMarkers(image, corners, ids)
if not self.is_calibrated():
return pose, None, None
# Draw marker axis # Draw marker axis
if draw_marker_axis: if draw_marker_axis:
_r_vecs, _t_vecs, _ = cv.aruco.estimatePoseSingleMarkers(corners, markerLength=0.1, cameraMatrix=self.mtx, distCoeffs=self.dist) _r_vecs, _t_vecs, _ = cv.aruco.estimatePoseSingleMarkers(corners, markerLength=0.1, cameraMatrix=self.mtx, distCoeffs=self.dist)
@@ -87,12 +88,12 @@ class BoardPoseAruco(BoardPose):
def main(): def main():
# construct the argument parser and parse the arguments # construct the argument parser and parse the arguments
ap = argparse.ArgumentParser() ap = argparse.ArgumentParser()
ap.add_argument("-i", "--device_id", type=int, ap.add_argument("-s", "--source", type=str,
default=0, default='0',
help="Camera device index ") help="Camera device index, movie file or first file of image sequence")
args = vars(ap.parse_args()) args = vars(ap.parse_args())
pose = BoardPoseAruco(args["device_id"], 10, (5, 5)) pose = BoardPoseAruco(args["source"], 10, (5, 5))
pose.board_image_generate(margin_length=10) pose.board_image_generate(margin_length=10)
pose.calibrate_load() pose.calibrate_load()
pose.process() pose.process()
+6 -6
View File
@@ -15,7 +15,7 @@ def factory(dict_type: int, board_size: cv.typing.Size, square_length: float = 5
return detector, board, name return detector, board, name
class BoardPoseCharuco(BoardPose): class BoardPoseCharuco(BoardPose):
def __init__(self, device_id: int, dict_type: int, board_size: tuple[int, int]): def __init__(self, device_id: str, dict_type: int, board_size: tuple[int, int]):
BoardPose.__init__(self, device_id) BoardPose.__init__(self, device_id)
self.board_size = board_size self.board_size = board_size
self.detector, self.board, self.name = factory(dict_type, board_size) self.detector, self.board, self.name = factory(dict_type, board_size)
@@ -85,13 +85,13 @@ class BoardPoseCharuco(BoardPose):
def main(): def main():
# construct the argument parser and parse the arguments # construct the argument parser and parse the arguments
ap = argparse.ArgumentParser() ap = argparse.ArgumentParser()
ap.add_argument("-i", "--device_id", type=int, ap.add_argument("-s", "--source", type=str,
default=0, default='0',
help="Camera device index ") help="Camera device index, movie file or first file of image sequence")
args = vars(ap.parse_args()) args = vars(ap.parse_args())
pose = BoardPoseCharuco(args["device_id"], 10, (6, 7)) pose = BoardPoseCharuco(args["source"], 10, (6, 7))
pose.board_image_generate() pose.board_image_generate(margin_length=10)
pose.calibrate_load() pose.calibrate_load()
pose.process() pose.process()

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB