From 86f70372aef6b0b4cd821d3d388eb30ebdd406c3 Mon Sep 17 00:00:00 2001 From: Jens Ahrensfeld Date: Mon, 24 Nov 2025 14:24:19 +0100 Subject: [PATCH] added aruco generate and detect --- .gitignore | 1 + README.md | 6 +++- aruco_detect.py | 85 +++++++++++++++++++++++++++++++++++++++++++++++++ aruco_gen.py | 51 +++++++++++++++++++++++++++++ aruco_types.py | 25 +++++++++++++++ cam_pose.py | 8 ++--- 6 files changed, 171 insertions(+), 5 deletions(-) create mode 100644 aruco_detect.py create mode 100644 aruco_gen.py create mode 100644 aruco_types.py diff --git a/.gitignore b/.gitignore index fbca225..4c036aa 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ results/ +__pycache__/ \ No newline at end of file diff --git a/README.md b/README.md index a56aadb..2b208a5 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,6 @@ +# Checker board https://docs.opencv.org/4.x/d6/d55/tutorial_table_of_content_calib3d.html -https://docs.opencv.org/4.x/d9/db7/tutorial_py_table_of_contents_calib3d.html \ No newline at end of file +https://docs.opencv.org/4.x/d9/db7/tutorial_py_table_of_contents_calib3d.html + +# Aruco +https://docs.opencv.org/4.x/d2/d64/tutorial_table_of_content_objdetect.html \ No newline at end of file diff --git a/aruco_detect.py b/aruco_detect.py new file mode 100644 index 0000000..2f7e574 --- /dev/null +++ b/aruco_detect.py @@ -0,0 +1,85 @@ +import argparse +import cv2 +import sys + +import cv2.aruco + +from aruco_types import ARUCO_DICT + + +def im_resize(image, new_width: int): + # Define new width while maintaining the aspect ratio + aspect_ratio = new_width / image.shape[0] + new_height = int(image.shape[1] * aspect_ratio) # Compute height based on aspect ratio + + image = cv2.resize(image, (new_width, new_height)) + return image + +def factory(aruco_type: int) -> cv2.aruco.ArucoDetector: + aruco_dict = cv2.aruco.getPredefinedDictionary(aruco_type) + aruco_params = cv2.aruco.DetectorParameters() + aruco_detector = cv2.aruco.ArucoDetector(aruco_dict, aruco_params) + + return aruco_detector + +def process_video(aruco_detector: cv2.aruco.ArucoDetector): + # Open camera + video = cv2.VideoCapture(0) + if not video.isOpened(): + print("Cannot open camera") + exit() + + while True: + # Read a new frame + ok, img = video.read() + if not ok: + return + process(aruco_detector, img) + k = cv2.waitKey(1) & 0xFF + if k == ord('q'): + break + +def process_still(aruco_detector, filename: str): + image = cv2.imread(filename) + image = im_resize(image, 600) + process(aruco_detector, image) + k = cv2.waitKey(-1) & 0xFF + +def process(aruco_detector, img): + (corners, ids, rejected) = aruco_detector.detectMarkers(img) + img = cv2.aruco.drawDetectedMarkers(img, corners, ids) + cv2.imshow("Image", img) + +def main(): + # construct the argument parser and parse the arguments + ap = argparse.ArgumentParser() + ap.add_argument("-i", "--image", + help="path to input image containing ArUCo tag") + ap.add_argument("-t", "--type", type=str, + default="DICT_ARUCO_ORIGINAL", + help="type of ArUCo tag to detect") + args = vars(ap.parse_args()) + + # verify that the supplied ArUCo tag exists and is supported by + # OpenCV + if ARUCO_DICT.get(args["type"], None) is None: + print("[INFO] ArUCo tag of '{}' is not supported".format( + args["type"])) + sys.exit(0) + # load the ArUCo dictionary, grab the ArUCo parameters, and detect + # the markers + print("[INFO] detecting '{}' tags...".format(args["type"])) + + aruco_detector = factory(ARUCO_DICT[args["type"]]) + + # load the input image from disk and resize it + print("[INFO] loading image...") + + if args["image"] is not None: + process_still(aruco_detector, args["image"]) + else: + process_video(aruco_detector) + + +if __name__ == '__main__': + main() diff --git a/aruco_gen.py b/aruco_gen.py new file mode 100644 index 0000000..ca8a5ae --- /dev/null +++ b/aruco_gen.py @@ -0,0 +1,51 @@ +import numpy as np +import argparse +import cv2 +import sys +from aruco_types import ARUCO_DICT + +def generate(aruco_dict: cv2.aruco.Dictionary, tag_id: int, side_pixels: int = 300): + tag = np.zeros((side_pixels, side_pixels, 1), dtype="uint8") + cv2.aruco.generateImageMarker(aruco_dict, tag_id, side_pixels, tag, 1) + + return tag + +def main(): + # construct the argument parser and parse the arguments + ap = argparse.ArgumentParser() + ap.add_argument("-f", "--folder", required=True, + help="path to output image containing ArUCo tag") + ap.add_argument("-d", "--id", type=int, required=True, + help="ID of ArUCo tag to generate") + ap.add_argument("-t", "--type", type=str, + default="DICT_ARUCO_ORIGINAL", + help="type of ArUCo tag to generate") + ap.add_argument("-i", "--imagetype", type=str, + default="png", + help="image file type of ArUCo tag image") + args = vars(ap.parse_args()) + + # verify that the supplied ArUCo tag exists and is supported by + # OpenCV + if ARUCO_DICT.get(args["type"], None) is None: + print("[INFO] ArUCo tag of '{}' is not supported".format( + args["type"])) + sys.exit(0) + + # load the ArUCo dictionary + aruco_dict = cv2.aruco.getPredefinedDictionary(ARUCO_DICT[args["type"]]) + + # allocate memory for the output ArUCo tag and then draw the ArUCo + # tag on the output image + print("[INFO] generating ArUCo tag type '{}' with ID '{}'".format( + args["type"], args["id"])) + tag = generate(aruco_dict, args["id"], 300) + # write the generated ArUCo tag to disk and then display it to our + # screen + filename = f"{args["folder"]}/{args["type"]}_ID_{args["id"]:04d}.{args["imagetype"]}" + cv2.imwrite(filename, tag) + cv2.imshow("ArUCo Tag", tag) + cv2.waitKey(0) + +if __name__ == '__main__': + main() diff --git a/aruco_types.py b/aruco_types.py new file mode 100644 index 0000000..bf90cc4 --- /dev/null +++ b/aruco_types.py @@ -0,0 +1,25 @@ +import cv2 + +ARUCO_DICT = { + "DICT_4X4_50": cv2.aruco.DICT_4X4_50, + "DICT_4X4_100": cv2.aruco.DICT_4X4_100, + "DICT_4X4_250": cv2.aruco.DICT_4X4_250, + "DICT_4X4_1000": cv2.aruco.DICT_4X4_1000, + "DICT_5X5_50": cv2.aruco.DICT_5X5_50, + "DICT_5X5_100": cv2.aruco.DICT_5X5_100, + "DICT_5X5_250": cv2.aruco.DICT_5X5_250, + "DICT_5X5_1000": cv2.aruco.DICT_5X5_1000, + "DICT_6X6_50": cv2.aruco.DICT_6X6_50, + "DICT_6X6_100": cv2.aruco.DICT_6X6_100, + "DICT_6X6_250": cv2.aruco.DICT_6X6_250, + "DICT_6X6_1000": cv2.aruco.DICT_6X6_1000, + "DICT_7X7_50": cv2.aruco.DICT_7X7_50, + "DICT_7X7_100": cv2.aruco.DICT_7X7_100, + "DICT_7X7_250": cv2.aruco.DICT_7X7_250, + "DICT_7X7_1000": cv2.aruco.DICT_7X7_1000, + "DICT_ARUCO_ORIGINAL": cv2.aruco.DICT_ARUCO_ORIGINAL, + "DICT_APRILTAG_16h5": cv2.aruco.DICT_APRILTAG_16h5, + "DICT_APRILTAG_25h9": cv2.aruco.DICT_APRILTAG_25h9, + "DICT_APRILTAG_36h10": cv2.aruco.DICT_APRILTAG_36h10, + "DICT_APRILTAG_36h11": cv2.aruco.DICT_APRILTAG_36h11 +} diff --git a/cam_pose.py b/cam_pose.py index 4caabcc..1894a0b 100644 --- a/cam_pose.py +++ b/cam_pose.py @@ -14,7 +14,7 @@ def draw_gizmo(img, corners, imgpts): return img -def draw_cube(img, corners, imgpts): +def draw_cube(img, imgpts): imgpts = np.int32(imgpts).reshape(-1, 2) # draw ground floor in green @@ -29,7 +29,7 @@ def draw_cube(img, corners, imgpts): return img -def init_data(): +def factory(): criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001) objp = np.zeros((PAT_NX * PAT_NY, 3), np.float32) objp[:, :2] = np.mgrid[0:PAT_NX, 0:PAT_NY].T.reshape(-1, 2) @@ -46,7 +46,7 @@ def process_video(): mtx, dist, _, _ = [X[i] for i in ('mtx', 'dist', 'rvecs', 'tvecs')] # Init - criteria, objp, axis = init_data() + criteria, objp, axis = factory() # Open camera video = cv.VideoCapture(0) @@ -69,7 +69,7 @@ def process_still(): mtx, dist, _, _ = [X[i] for i in ('mtx', 'dist', 'rvecs', 'tvecs')] # Init - criteria, objp, axis = init_data() + criteria, objp, axis = factory() for fname in glob.glob('check*.jpg'): img = cv.imread(fname)