
    *i:                         d Z ddlZddlmZ ddlmZ ddlmZmZm	Z	m
Z
 ddlZddlZddlmZ e G d d                      Z G d	 d
          Z G d d          ZdedefdZdedefdZdS )z.Camera calibration engine using ChArUco boards    N)	dataclass)Path)DictListOptionalTuple)Rotationc                   z    e Zd ZU dZdZeed<   dZeed<   dZe	ed<   dZ
e	ed<   d	Zeed
<   ededd fd            ZdS )ChArUcoBoardConfigz+Configuration for ChArUco calibration board	   	squares_x	squares_ygQ?square_lengthgZd;O?marker_lengthDICT_6X6_250
dictionarydatareturnc                 f    t          | d         | d         | d         | d         | d                   S )Nr   r   r   r   r   )r   r   r   r   r   )r   )r   s    5/home/robot-lab/raiden_cmu/raiden/calibration/core.py	from_dictzChArUcoBoardConfig.from_dict   sA    !;';'//L)
 
 
 	
    N)__name__
__module____qualname____doc__r   int__annotations__r   r   floatr   r   strstaticmethoddictr    r   r   r   r      s         55IsIsM5 M5   $J$$$
 
!5 
 
 
 \
 
 
r   r   c                   p   e Zd ZdZdefdZdej        dee	ej                 e	ej                 f         fdZ
dej        dee	ej                 e	ej                 e	e         e	ej                 f         fdZdej        d	ej        d
ej        dej        dee	ej                 e	ej                 f         f
dZdS )ChArUcoDetectorz Detects ChArUco boards in imagesboard_configc                 "   || _         |j        }t          t          j        |          r?t          t          j        |          }t          j                            |          | _        nt          d|           t          j                            |j	        |j
        f|j        |j        | j                  | _        t          j                                        | _        t          j                            | j        | j                  | _        d S )NzUnknown ArUco dictionary: )sizesquareLengthmarkerLengthr   )r&   r   hasattrcv2arucogetattrgetPredefinedDictionary
ValueErrorCharucoBoardr   r   r   r   boardDetectorParametersdetector_paramsArucoDetectordetector)selfr&   	dict_name
aruco_dicts       r   __init__zChArUcoDetector.__init__%   s    ( !+	39i(( 	G I66J!i??
KKDOOE)EEFFF Y++(,*@A%3%3	 , 
 

  #y;;==	//AUVVr   imager   c                 P   t          |j                  dk    r t          j        |t          j                  }n|}| j                            |          \  }}}|t          |          dk    rdS t          j                            |||| j	                  \  }}}|dk    rdS ||fS )a#  Detect ChArUco board in image

        Args:
            image: Input image (grayscale or color)

        Returns:
            Tuple of (corners, ids) or (None, None) if detection failed
            - corners: Nx2 array of corner positions
            - ids: Nx1 array of corner IDs
           Nr   NNmarkerCorners	markerIdsr;   r2   
lenshaper,   cvtColorCOLOR_BGR2GRAYr6   detectMarkersr-   interpolateCornersCharucor2   	r7   r;   graymarker_corners
marker_ids_num_cornerscharuco_cornerscharuco_idss	            r   detectzChArUcoDetector.detect=   s     u{q  <s'9::DDD )-(C(CD(I(I%
AZA!5!5: 58I4W4W( *	 5X 5
 5
1_k !:++r   c                 \   t          |j                  dk    r t          j        |t          j                  }n|}| j                            |          \  }}}|t          |          dk    rdS t          j                            |||| j	                  \  }}}|dk    rdd||fS ||||fS )a  Detect ChArUco board and return ArUco marker info for debugging

        Args:
            image: Input image (grayscale or color)

        Returns:
            Tuple of (charuco_corners, charuco_ids, marker_corners, marker_ids)
            - charuco_corners: Nx2 array of ChArUco corner positions (or None)
            - charuco_ids: Nx1 array of ChArUco corner IDs (or None)
            - marker_corners: List of detected ArUco marker corners (or None)
            - marker_ids: Array of detected ArUco marker IDs (or None)
        r=   Nr   )NNNNr?   rB   rI   s	            r   detect_with_markersz#ChArUcoDetector.detect_with_markersc   s    $ u{q  <s'9::DDD )-(C(CD(I(I%
AZA!5!5)) 58I4W4W( *	 5X 5
 5
1_k !~z99^ZGGr   cornersidscamera_matrixdist_coeffsc           	      p    t           j                            ||| j        ||dd          \  }}}|sdS ||fS )a  Estimate board pose from detected corners

        Args:
            corners: Detected corner positions
            ids: Detected corner IDs
            camera_matrix: Camera intrinsic matrix
            dist_coeffs: Camera distortion coefficients

        Returns:
            Tuple of (rvec, tvec) or (None, None) if estimation failed
            - rvec: Rotation vector (3x1)
            - tvec: Translation vector (3x1)
        N)charucoCorners
charucoIdsr2   cameraMatrix
distCoeffsrvectvecr>   )r,   r-   estimatePoseCharucoBoardr2   )r7   rT   rU   rV   rW   successr]   r^   s           r   estimate_posezChArUcoDetector.estimate_pose   sX    ( "i@@"*&" A 
 
t  	:Tzr   N)r   r   r   r   r   r:   npndarrayr   r   rQ   listrS   ra   r#   r   r   r%   r%   "   s5       **W%7 W W W W0$,Z$,	x
#Xbj%99	:$, $, $, $,L)HZ)H	hrz2HTNHRZDXX
)H )H )H )HV!! Z! z	!
 Z! 
x
#Xbj%99	:! ! ! ! ! !r   r%   c                      e Zd ZdZdefdZdeej                 deej                 de	e
e
f         defdZej        fd	eej                 d
ee	ej        ej        f                  de
defdZd
ee	ej        ej        f                  defdZede
defd            Zdej        dej        dej        dej        dej        dej        defdZdS )CameraCalibratorz'Calibrates cameras using ChArUco boardsr&   c                 <    || _         t          |          | _        d S )N)r&   r%   r6   )r7   r&   s     r   r:   zCameraCalibrator.__init__   s    ('55r   all_cornersall_ids
image_sizer   c                 B   t          |          dk     rdddS t          j                            ||| j        j        |dd          \  }}}}}d|                                |                                                                |d |D             d	 |D             d
S )a  Calibrate camera intrinsics from multiple views

        Args:
            all_corners: List of detected corners from each view
            all_ids: List of detected IDs from each view
            image_size: Image size (width, height)

        Returns:
            Dictionary with calibration results:
            - camera_matrix: 3x3 intrinsic matrix
            - distortion_coeffs: Distortion coefficients
            - reprojection_error: RMS reprojection error
            - success: Whether calibration succeeded
        r=   Fz%Need at least 3 views for calibrationr`   errorN)rY   rZ   r2   	imageSizer[   r\   Tc                 6    g | ]}|                                 S r#   tolist).0rs     r   
<listcomp>z9CameraCalibrator.calibrate_intrinsics.<locals>.<listcomp>        000Qahhjj000r   c                 6    g | ]}|                                 S r#   rp   )rr   ts     r   rt   z9CameraCalibrator.calibrate_intrinsics.<locals>.<listcomp>   ru   r   )r`   rV   distortion_coeffsreprojection_errorrvecstvecs)rC   r,   r-   calibrateCameraCharucor6   r2   rq   flatten)	r7   rh   ri   rj   retrV   rW   rz   r{   s	            r   calibrate_intrinsicsz%CameraCalibrator.calibrate_intrinsics   s    ( {a$/VWWW I,,*"m)$! -   	6]K *1133!,!4!4!6!6!=!=!?!?"%00%00000%000
 
 	
r   robot_posescamera_posesmethodc                 "   t          |          dk     st          |          dk     rdddS t          |          t          |          k    rdddS g }g }|D ]D}|                    |ddddf                    |                    |ddddf                    Eg }g }|D ]F\  }	}
t          j        |	          \  }}|                    |           |                    |
           Gt          j        |||||          \  }}t          j        |          \  }}d	|                                |                                                                |                                                                |                     |          d
S )a`  Perform hand-eye calibration (eye-in-hand configuration)

        Solves the AX=XB problem where:
        - A: Transformation between robot gripper poses
        - B: Transformation between camera poses
        - X: Transformation from camera to gripper (what we're solving for)

        Args:
            robot_poses: List of 4x4 robot gripper-to-base transformation matrices
            camera_poses: List of (rvec, tvec) tuples for board-to-camera transforms
            method: OpenCV calibration method (default: Tsai)

        Returns:
            Dictionary with calibration results:
            - rotation_matrix: 3x3 rotation matrix (camera to gripper)
            - translation_vector: 3x1 translation vector (camera to gripper)
            - rotation_vector: 3x1 rotation vector (axis-angle)
            - success: Whether calibration succeeded
        r=   FzNeed at least 3 pose pairsrl   z2Robot poses and camera poses must have same lengthN   )R_gripper2baset_gripper2baseR_target2camt_target2camr   T)r`   rotation_matrixtranslation_vectorrotation_vectorr   )rC   appendr,   	RodriguescalibrateHandEyerq   r}   _get_method_name)r7   r   r   r   r   r   poser   r   r]   r^   RrM   R_cam2grippert_cam2gripperrvec_cam2grippers                   r   calibrate_hand_eyez#CameraCalibrator.calibrate_hand_eye   s   2 {a3|#4#4q#8#8$/KLLL{s<0000 M    	1 	1D!!$rr2A2v,///!!$rr1Q3w-0000 & 	& 	&JD$=&&DAq"""%%%% (+';))%%(
 (
 (
$} "mM::! ,3355"/"7"7"9"9"@"@"B"B/7799@@BB++F33
 
 	
r   c                    t          |          dk     rdddS g }g }|D ]j\  }}t          j        |          \  }}|                    t	          j        |                     |                    |                                           kt          |          dk    r't	          j        |                                          }n|d         }|	                                }	t          j        |d          }
|	j        }| |
z  }t          j        |          \  }}d|                                |                                |                                                                dd	S )
a  Calibrate a fixed scene camera

        For a fixed scene camera viewing a fixed calibration board,
        we compute the camera's pose relative to the board frame (world frame).
        We average across multiple observations for robustness.

        Args:
            camera_poses: List of (rvec, tvec) tuples for board-to-camera transforms

        Returns:
            Dictionary with calibration results:
            - rotation_matrix: 3x3 rotation matrix (camera to board/world frame)
            - translation_vector: 3x1 translation vector (camera position in board/world frame)
            - rotation_vector: 3x1 rotation vector
            - reference_frame: "board" (the board frame is used as world frame)
            - success: Whether calibration succeeded
           FzNeed at least 1 poserl   r   axisTr2   )r`   r   r   r   reference_frame)rC   r,   r   r   r	   from_matrixr}   concatenatemean	as_matrixrb   Trq   )r7   r   	rotationstranslationsr]   r^   r   rM   avg_rotationavg_R_board_to_camavg_t_board_to_camavg_R_cam_to_boardavg_t_cam_to_boardavg_rvecs                 r   calibrate_scene_cameraz'CameraCalibrator.calibrate_scene_camera,  sw   ( |q  $/EFFF 	& 	0 	0JD$=&&DAqX1!44555//// y>>A#/	::??AALL$Q<L)3355W\::: 01003EE m$677! 188::"4";";"="='//1188::&
 
 	
r   c           
          t           j        dt           j        dt           j        dt           j        dt           j        di}|                    | d|            S )z.Get human-readable name for calibration methodCALIB_HAND_EYE_TSAICALIB_HAND_EYE_PARKCALIB_HAND_EYE_HORAUDCALIB_HAND_EYE_ANDREFFCALIB_HAND_EYE_DANIILIDISUNKNOWN_)r,   r   r   r   r   r   get)r   method_namess     r   r   z!CameraCalibrator._get_method_namef  sY     #%:#%:%'>&(@)+F
 (;6(;(;<<<r   rT   rU   r]   r^   rV   rW   c                    | j         j                                        }||                                         }t	          j        |||||          \  }	}
|	                    dd          }	|                    dd          }t          j        	                    |	|z
  d          }t          j
        t          j        |dz                      S )a  Compute reprojection error for a single view

        Args:
            corners: Detected corner positions
            ids: Detected corner IDs
            rvec: Rotation vector
            tvec: Translation vector
            camera_matrix: Camera intrinsic matrix
            dist_coeffs: Distortion coefficients

        Returns:
            RMS reprojection error in pixels
           r   r   )r6   r2   getChessboardCornersr}   r,   projectPointsreshaperb   linalgnormsqrtr   )r7   rT   rU   r]   r^   rV   rW   
obj_pointsobj_points_detected	projectedrM   errorss               r   compute_reprojection_errorz+CameraCalibrator.compute_reprojection_errorr  s    . ](==??
(7 (t]K
 
	1
 %%b!,,	//"a((	G 3!<<wrwvqy))***r   N)r   r   r   r   r   r:   r   rb   rc   r   r   r   r   r,   r   r   r   r!   r    r   r   r   r#   r   r   rf   rf      s       116%7 6 6 6 6*
"*%*
 bj!*
 #s(O	*

 
*
 *
 *
 *
` -	E
 E
"*%E
 5RZ!789E
 	E

 
E
 E
 E
 E
N8
 rz2:'=!>?8
	8
 8
 8
 8
t 	= 	= 	= 	= 	= \	=$+$+ Z$+ j	$+
 j$+ z$+ Z$+ 
$+ $+ $+ $+ $+ $+r   rf   
poses_filer   c                 ~    t          | d          5 }t          j        |          cddd           S # 1 swxY w Y   dS )zLoad calibration poses from JSON file

    Args:
        poses_file: Path to calibration poses JSON file

    Returns:
        Dictionary with poses data
    rs   N)openjsonload)r   fs     r   load_calibration_posesr     s     
j#		 !y||                 s   266resultsoutput_filec                     t          |          }|j                            dd           t          |d          5 }t	          j        | |d           ddd           dS # 1 swxY w Y   dS )zSave calibration results to JSON file

    Args:
        results: Calibration results dictionary
        output_file: Path to output JSON file
    T)parentsexist_okwr   )indentN)r   parentmkdirr   r   dump)r   r   output_pathr   s       r   save_calibration_resultsr     s     {##KTD999	k3		 (1	'1Q''''( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (s   A!!A%(A%)r   r   dataclassesr   pathlibr   typingr   r   r   r   r,   numpyrb   scipy.spatial.transformr	   r   r%   rf   r    r   r   r#   r   r   <module>r      sg   4 4  ! ! ! ! ! !       . . . . . . . . . . . . 



     , , , , , , 
 
 
 
 
 
 
 
(M M M M M M M M`d+ d+ d+ d+ d+ d+ d+ d+N
s 
t 
 
 
 
(d ( ( ( ( ( ( (r   