From 356863e1192805b74831c4424854784d29099ca2 Mon Sep 17 00:00:00 2001 From: Théo de la Hogue Date: Wed, 29 Mar 2023 11:23:01 +0200 Subject: Adding calibrate_camera.py script example. --- .../utils/A3_DICT_APRILTAG_16h5_3cm_35cmx25cm.pdf | Bin 0 -> 97542 bytes .../utils/A3_DICT_ARUCO_ORIGINAL_3cm_35cmx25cm.pdf | Bin 53854 -> 0 bytes .../utils/A4_DICT_APRILTAG_16h5_5cm_0-7.pdf | Bin 0 -> 37670 bytes .../utils/A4_DICT_ARUCO_ORIGINAL_3cm_0-9.pdf | Bin 26485 -> 0 bytes src/argaze/utils/README.md | 15 +-- src/argaze/utils/camera_calibrate.py | 116 +++++++++++++++++++++ 6 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 src/argaze/ArUcoMarkers/utils/A3_DICT_APRILTAG_16h5_3cm_35cmx25cm.pdf delete mode 100644 src/argaze/ArUcoMarkers/utils/A3_DICT_ARUCO_ORIGINAL_3cm_35cmx25cm.pdf create mode 100644 src/argaze/ArUcoMarkers/utils/A4_DICT_APRILTAG_16h5_5cm_0-7.pdf delete mode 100644 src/argaze/ArUcoMarkers/utils/A4_DICT_ARUCO_ORIGINAL_3cm_0-9.pdf create mode 100644 src/argaze/utils/camera_calibrate.py (limited to 'src') diff --git a/src/argaze/ArUcoMarkers/utils/A3_DICT_APRILTAG_16h5_3cm_35cmx25cm.pdf b/src/argaze/ArUcoMarkers/utils/A3_DICT_APRILTAG_16h5_3cm_35cmx25cm.pdf new file mode 100644 index 0000000..2adcee1 Binary files /dev/null and b/src/argaze/ArUcoMarkers/utils/A3_DICT_APRILTAG_16h5_3cm_35cmx25cm.pdf differ diff --git a/src/argaze/ArUcoMarkers/utils/A3_DICT_ARUCO_ORIGINAL_3cm_35cmx25cm.pdf b/src/argaze/ArUcoMarkers/utils/A3_DICT_ARUCO_ORIGINAL_3cm_35cmx25cm.pdf deleted file mode 100644 index 455fd4f..0000000 Binary files a/src/argaze/ArUcoMarkers/utils/A3_DICT_ARUCO_ORIGINAL_3cm_35cmx25cm.pdf and /dev/null differ diff --git a/src/argaze/ArUcoMarkers/utils/A4_DICT_APRILTAG_16h5_5cm_0-7.pdf b/src/argaze/ArUcoMarkers/utils/A4_DICT_APRILTAG_16h5_5cm_0-7.pdf new file mode 100644 index 0000000..fcf850d Binary files /dev/null and b/src/argaze/ArUcoMarkers/utils/A4_DICT_APRILTAG_16h5_5cm_0-7.pdf differ diff --git a/src/argaze/ArUcoMarkers/utils/A4_DICT_ARUCO_ORIGINAL_3cm_0-9.pdf b/src/argaze/ArUcoMarkers/utils/A4_DICT_ARUCO_ORIGINAL_3cm_0-9.pdf deleted file mode 100644 index 03085f2..0000000 Binary files a/src/argaze/ArUcoMarkers/utils/A4_DICT_ARUCO_ORIGINAL_3cm_0-9.pdf and /dev/null differ diff --git a/src/argaze/utils/README.md b/src/argaze/utils/README.md index f91438f..719218d 100644 --- a/src/argaze/utils/README.md +++ b/src/argaze/utils/README.md @@ -8,22 +8,25 @@ Collection of ready-to-use commands based on ArGaze toolkit. # ArUco Markers factory -Export all markers from DICT_APRILTAG_16h5 dictionary as 5 cm pictures with 300 dpi resolution into an \_export/markers folder: +Export all markers from *DICT_APRILTAG_16h5* dictionary as 5 cm pictures with 300 dpi resolution into an *\_export/markers* folder: ``` python ./src/argaze/utils/aruco_markers_dictionary_export.py DICT_APRILTAG_16h5 -s 5 -r 300 -o _export/markers ``` -Export a 7 columns and 5 rows calibration board made of 5cm squares with 3cm markers from DICT_APRILTAG_16h5 dictionary at 300 dpi into an \_export folder: +Export a 7 columns and 5 rows calibration board made of 5cm squares with 3cm markers from *DICT_APRILTAG_16h5* dictionary at 300 dpi into an *\_export* folder: ``` python ./src/argaze/utils/aruco_calibration_board_export.py 7 5 5 3 DICT_APRILTAG_16h5 -r 300 -o _export ``` -# TODO: Camera calibration +# Camera calibration -Calibrate a network camera (-t IP_ADDRESS) using a 7 columns and 5 rows calibration board made of 5cm squares with 3cm markers from DICT_APRILTAG_16h5 dictionary. Then, export its optical parameters into an camera.json file: +Calibrate a camera device (-d DEVICE) using a 7 columns and 5 rows calibration board made of 5cm squares with 3cm markers from *DICT_APRILTAG_16h5* dictionary. Then, export its optical parameters into an *camera.json* file: ``` -python ./src/argaze/utils/camera_calibrate.py 7 5 5 3 -t IP_ADDRESS -d DICT_APRILTAG_16h5 -o _export/camera.json -``` \ No newline at end of file +python ./src/argaze/utils/camera_calibrate.py 7 5 5 3 DICT_APRILTAG_16h5 -d DEVICE -o _export +``` + +.. note:: + Use **A3_DICT_APRILTAG_16h5_3cm_35cmx25cm.pdf** file located in ./src/argaze/ArUcoMarkers/utils/ folder ready to be printed on A3 paper sheet. \ No newline at end of file diff --git a/src/argaze/utils/camera_calibrate.py b/src/argaze/utils/camera_calibrate.py new file mode 100644 index 0000000..f55f0ce --- /dev/null +++ b/src/argaze/utils/camera_calibrate.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python + +import argparse +import os +import time + +from argaze.ArUcoMarkers import ArUcoMarkersDictionary, ArUcoBoard, ArUcoDetector, ArUcoCamera + +import cv2 + +def main(): + """ + Captures board pictures and finally outputs camera calibration data into a calibration.json file. + + - Export and print a calibration board using aruco_calibration_board_export.py script. + - Place the calibration board face to the camera. + - Move the calibration board in a manner to view it entirely on screen in various configurations (orientation and distance): + The script will automatically take pictures. Do this step with a good lighting and a clear background. + - Once enough pictures have been captured (~20), press Esc key then, wait for the camera calibration processing. + - Finally, check rms parameter: it should be between 0. and 1. if the calibration succeeded (lower is better). + + ### Reference: + - [Camera calibration using ArUco marker tutorial](https://automaticaddison.com/how-to-perform-camera-calibration-using-opencv/) + """ + + # Manage arguments + parser = argparse.ArgumentParser(description=main.__doc__.split('-')[0]) + parser.add_argument('columns', metavar='COLS_NUMBER', type=int, default=7, help='expected board columns number') + parser.add_argument('rows', metavar='ROWS_NUMBER', type=int, default=5, help='expected board rows number') + parser.add_argument('square_size', metavar='SQUARE_SIZE', type=float, default=5, help='expected square size in cm') + parser.add_argument('marker_size', metavar='MARKER_SIZE', type=float, default=3, help='expected marker size in cm') + parser.add_argument('dictionary', metavar='DICTIONARY', type=ArUcoMarkersDictionary.ArUcoMarkersDictionary, default='DICT_ARUCO_ORIGINAL', help='expected dictionary name: DICT_4X4_50, DICT_4X4_100, DICT_4X4_250, DICT_4X4_1000, DICT_5X5_50, DICT_5X5_100, DICT_5X5_250, DICT_5X5_1000, DICT_6X6_50, DICT_6X6_100, DICT_6X6_250, DICT_6X6_1000, DICT_7X7_50, DICT_7X7_100, DICT_7X7_250, DICT_7X7_1000, DICT_ARUCO_ORIGINAL, DICT_APRILTAG_16h5, DICT_APRILTAG_25h9, DICT_APRILTAG_36h10, DICT_APRILTAG_36h11') + + parser.add_argument('-d', '--device', metavar='DEVICE', type=int, default=0, help='video capture device id') + parser.add_argument('-o', '--output', metavar='OUT', type=str, default='.', help='destination folder filepath') + args = parser.parse_args() + + # Enable camera video capture + video_capture = cv2.VideoCapture(args.device) + + frame_width = int(video_capture.get(cv2.CAP_PROP_FRAME_WIDTH)) + frame_height = int(video_capture.get(cv2.CAP_PROP_FRAME_HEIGHT)) + + # Create aruco camera + aruco_camera = ArUcoCamera.ArUcoCamera(dimensions=(frame_width, frame_height)) + + # Create aruco board + aruco_board = ArUcoBoard.ArUcoBoard(args.columns, args.rows, args.square_size, args.marker_size, args.dictionary) + + # Create aruco detector + aruco_detector = ArUcoDetector.ArUcoDetector(dictionary=args.dictionary, marker_size=args.marker_size) + + print(f'{aruco_camera.dimensions[0]}x{aruco_camera.dimensions[1]} pixels camera calibration starts') + print("Waiting for calibration board...") + + expected_markers_number = aruco_board.markers_number + expected_corners_number = aruco_board.corners_number + + # Capture loop + try: + + # Capture frames with a full displayed board inside + while video_capture.isOpened(): + + success, video_frame = video_capture.read() + + if success: + + # Detect calibration board + aruco_detector.detect_board(video_frame, aruco_board, expected_markers_number) + + # Draw detected markers + aruco_detector.draw_detected_markers(video_frame) + + # Draw current calibration data count + cv2.putText(video_frame, f'Capture: {aruco_camera.calibration_data_count}', (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA) + cv2.imshow('Camera Calibration', video_frame) + + # If all board corners are detected + if aruco_detector.board_corners_number == expected_corners_number: + + # Draw board corners to notify a capture is done + aruco_detector.draw_board(video_frame) + + # Append calibration data + aruco_camera.store_calibration_data(aruco_detector.board_corners, aruco_detector.board_corners_identifier) + + cv2.imshow('Camera Calibration', video_frame) + + # Stop calibration by pressing 'Esc' key + if cv2.waitKey(1) == 27: + break + + # Stop calibration on 'ctrl+C' interruption + except KeyboardInterrupt: + pass + + # Stop frame display + cv2.destroyAllWindows() + + print('\nCalibrating camera...') + aruco_camera.calibrate(aruco_board) + + print('\nCalibration succeeded!') + print(f'\nRMS:\n{aruco_camera.rms}') + print(f'\nDimensions:\n{frame_width}x{frame_height}') + print(f'\nCamera matrix:\n{aruco_camera.K}') + print(f'\nDistortion coefficients:\n{aruco_camera.D}') + + aruco_camera.to_json(f'{args.output}/calibration.json') + + print(f'\ncalibration.json file exported into {args.output} folder') + +if __name__ == '__main__': + + main() -- cgit v1.1