aboutsummaryrefslogtreecommitdiff
path: root/src/argaze/utils/tobii_camera_calibrate.py
blob: 2994c360d9eb8b3fe380e7cbb3197f78190f6f4a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#!/usr/bin/env python

import argparse
import os
import time

from argaze.TobiiGlassesPro2 import TobiiController, TobiiVideo
from argaze.ArUcoMarkers import ArUcoMarkersDictionary, ArUcoBoard, ArUcoTracker, ArUcoCamera

import cv2 as cv

def main():
    """
    Captures board pictures and finally outputs camera calibration data into a .json file.

    - Export and print a calibration board using
    - Place the calibration board in order to view it entirely on screen and move the camera in many 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='number of columns')
    parser.add_argument('rows', metavar='ROWS_NUMBER', type=int, default=5, help='number of rows')
    parser.add_argument('square_size', metavar='SQUARE_SIZE', type=float, default=5, help='square size (cm)')
    parser.add_argument('marker_size', metavar='MARKER_SIZE', type=float, default=3, help='marker size (cm)')
    parser.add_argument('-t', '--tobii_ip', metavar='TOBII_IP', type=str, default=None, help='tobii glasses ip')
    parser.add_argument('-o', '--output', metavar='OUT', type=str, default='camera.json', help='destination filepath')
    parser.add_argument('-d', '--dictionary', metavar='DICT', type=ArUcoMarkersDictionary.ArUcoMarkersDictionary, default='DICT_ARUCO_ORIGINAL', help='aruco marker dictionnary (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)')
    args = parser.parse_args()

    # Create tobii controller (with auto discovery network process if no ip argument is provided)
    print("Looking for a Tobii Glasses Pro 2 device ...")

    try:

        tobii_controller = TobiiController.TobiiController(args.tobii_ip)
        print(f'Tobii Glasses Pro 2 device found at {tobii_controller.address} address.')

    except ConnectionError as e:

        print(e)
        exit()

    # Setup camera at 25 fps to work on Full HD video stream
    tobii_controller.set_video_freq_25()

    # Enable tobii video stream
    tobii_video_stream = tobii_controller.enable_video_stream()

    # Create aruco camera
    aruco_camera = ArUcoCamera.ArUcoCamera()

    # Create aruco board
    aruco_board = ArUcoBoard.ArUcoBoard(args.dictionary, args.columns, args.rows, args.square_size, args.marker_size)

    # Create aruco tracker
    aruco_tracker = ArUcoTracker.ArUcoTracker(args.dictionary, args.marker_size, aruco_camera)

    # Start tobii glasses streaming
    tobii_controller.start_streaming()

    print("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:

        while tobii_video_stream.is_alive():

            # capture frame with a full displayed board
            video_ts, video_frame = tobii_video_stream.read()

            # track all markers in the board
            aruco_tracker.track_board(video_frame.matrix, aruco_board, expected_markers_number)

            # draw only markers
            aruco_tracker.draw_tracked_markers(video_frame.matrix)

            # draw current calibration data count
            cv.putText(video_frame.matrix, f'Capture: {aruco_camera.calibration_data_count}', (50, 50), cv.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv.LINE_AA)
            cv.imshow('Tobii Camera Calibration', video_frame.matrix)

            # if all board corners are detected
            if aruco_tracker.board_corners_number == expected_corners_number:

                # draw board corners to notify a capture is done
                aruco_tracker.draw_board(video_frame.matrix)

                # append data
                aruco_camera.store_calibration_data(aruco_tracker.board_corners, aruco_tracker.board_corners_ids)

                cv.imshow('Tobii Camera Calibration', video_frame.matrix)

            # close window using 'Esc' key
            if cv.waitKey(1) == 27:
                break

    # exit on 'ctrl+C' interruption
    except KeyboardInterrupt:
        pass

    # stop frame display
    cv.destroyAllWindows()

    # Stop tobii glasses streaming
    tobii_controller.stop_streaming()

    print('\nCalibrating camera...')
    aruco_camera.calibrate(aruco_board, video_frame.width, video_frame.height)

    print('\nCalibration succeeded!')
    print(f'\nRMS:\n{aruco_camera.rms}')
    print(f'\nDimensions:\n{video_frame.width}x{video_frame.height}')
    print(f'\nCamera matrix:\n{aruco_camera.K}')
    print(f'\nDistortion coefficients:\n{aruco_camera.D}')

    aruco_camera.save_calibration_file(args.output)

    print(f'\nCalibration data exported into {args.output} file')

if __name__ == '__main__':

    main()