{"id":370,"date":"2017-10-29T18:33:29","date_gmt":"2017-10-29T18:33:29","guid":{"rendered":"https:\/\/peterklemperer.com\/blog\/?p=370"},"modified":"2017-10-29T20:08:05","modified_gmt":"2017-10-29T20:08:05","slug":"opencv-charuco-board-pose-estimation","status":"publish","type":"post","link":"https:\/\/peterklemperer.com\/blog\/2017\/10\/29\/opencv-charuco-board-pose-estimation\/","title":{"rendered":"OpenCV Charuco-Board Pose Estimation"},"content":{"rendered":"<p>The OpenCV Charuco-board pose estimation tools can provide localization estimation relative to your camera. The red-green-and blue axes drawn at the upper-right of the charuco-board taped to my wall indicates the pose estimate.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"371\" data-permalink=\"https:\/\/peterklemperer.com\/blog\/2017\/10\/29\/opencv-charuco-board-pose-estimation\/opencv_charuco_board_pose_estimation\/\" data-orig-file=\"https:\/\/peterklemperer.com\/blog\/wp-content\/uploads\/2017\/10\/opencv_charuco_board_pose_estimation.jpeg\" data-orig-size=\"2560,1484\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"opencv_charuco_board_pose_estimation\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/peterklemperer.com\/blog\/wp-content\/uploads\/2017\/10\/opencv_charuco_board_pose_estimation-300x174.jpeg\" data-large-file=\"https:\/\/peterklemperer.com\/blog\/wp-content\/uploads\/2017\/10\/opencv_charuco_board_pose_estimation-1024x594.jpeg\" class=\"aligncenter size-medium wp-image-371\" src=\"https:\/\/peterklemperer.com\/blog\/wp-content\/uploads\/2017\/10\/opencv_charuco_board_pose_estimation-300x174.jpeg\" alt=\"\" width=\"300\" height=\"174\" srcset=\"https:\/\/peterklemperer.com\/blog\/wp-content\/uploads\/2017\/10\/opencv_charuco_board_pose_estimation-300x174.jpeg 300w, https:\/\/peterklemperer.com\/blog\/wp-content\/uploads\/2017\/10\/opencv_charuco_board_pose_estimation-768x445.jpeg 768w, https:\/\/peterklemperer.com\/blog\/wp-content\/uploads\/2017\/10\/opencv_charuco_board_pose_estimation-1024x594.jpeg 1024w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/p>\n<p>These instructions are not particularly user-friendly, but got me started. I&#8217;m not really sure that my distance coefficients are set up correctly. Hopefully I&#8217;ll have a chance to clean that up and post an update if I make improvements.<\/p>\n<p>Please comment if you&#8217;ve got ideas for improvement or if you found this post useful.<\/p>\n<p><!--more--><\/p>\n<p><strong>Overview<\/strong><\/p>\n<ol>\n<li>Obtain accurate configuration settings<\/li>\n<li>Compile the pose estimation program<\/li>\n<li>Run and verify configuration settings<\/li>\n<\/ol>\n<p><strong>Step 1: Obtain accurate configuration settings<\/strong><\/p>\n<p>For my program to work, you should have a board configuration file called See my <a href=\"https:\/\/peterklemperer.com\/blog\/2017\/10\/29\/opencv-charuco-camera-calibration\/\">previous post on opencv charuco calibration<\/a> for details.<\/p>\n<p><strong>Step 2: Compile the included pose estimation program<\/strong><\/p>\n<p>Download the following python3 program and save it into the same folder as the two XML files from step 1.<\/p>\n<pre># charuco_pose.py\r\n#\r\n# Peter F. Klemperer\r\n# October 29, 2017\r\nimport pdb\r\nimport time\r\nimport numpy as np\r\nimport cv2\r\nimport glob\r\n\r\nimport cv2.aruco as aruco\r\n\r\ncap = cv2.VideoCapture(0)\r\n\r\ndef read_node_real( reader, name ):\r\n node = reader.getNode( name )\r\n return node.real()\r\n\r\ndef read_node_string( reader, name ):\r\n node = reader.getNode( name )\r\n return node.string()\r\n\r\ndef read_node_matrix( reader, name ):\r\n node = reader.getNode( name )\r\n return node.mat()\r\n\r\n# read defaultConfig.xml presented to\r\n# opencv_interactive-calibration\r\nconfig_reader = cv2.FileStorage()\r\nconfig_reader.open(\"defaultConfig.xml\",cv2.FileStorage_READ)\r\n\r\naruco_parameters = aruco.DetectorParameters_create()\r\naruco_dict_num = int(read_node_real( config_reader, \"charuco_dict\" ) )\r\naruco_dict = aruco.Dictionary_get(aruco_dict_num)\r\ncharuco_square_length = int(read_node_real( config_reader, \"charuco_square_lenght\" ))\r\ncharuco_marker_size = int(read_node_real( config_reader, \"charuco_marker_size\" ))\r\nconfig_reader.release()\r\n\r\n# TODO clean this up and use config parameters?\r\n# look up definition o charuco_square_lenght, charuco_marker_size\r\n# 5 was width (x) passed to interactive-calibration\r\n# 7 was height (y) passed to interactive-calibration\r\n# .034 is square_length (meters)\r\n# .02 is marker_size (meters)\r\ncharuco_board = aruco.CharucoBoard_create( 5, 7, 0.034, 0.02, aruco_dict )\r\n\r\n# read the cameraParameters.xml file generated by\r\n# opencv_interactive-calibration\r\ncamera_reader = cv2.FileStorage()\r\ncamera_reader.open(\"cameraParameters.xml\",cv2.FileStorage_READ)\r\ncamera_matrix = read_node_matrix( camera_reader, \"cameraMatrix\" )\r\ndist_coeffs = read_node_matrix( camera_reader, \"dist_coeffs\" )\r\ncamera_reader.release()\r\n\r\n\r\nwhile(True):\r\n time.sleep( 0.1 )\r\n # Read frame from Camera\r\n # convert frame to grayscale\r\n ret, frame = cap.read()\r\n gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\r\n\r\n\r\n\r\ncorners, ids, rejectedImgPoints = aruco.detectMarkers(gray, aruco_dict, \r\n parameters=aruco_parameters)\r\n\r\n# if enough markers were detected\r\n # then process the board\r\n if( ids is not None ):\r\n #gray = aruco.drawDetectedMarkers(gray, corners)\r\n ret, ch_corners, ch_ids = aruco.interpolateCornersCharuco(corners, ids, gray, charuco_board )\r\n \r\n # if there are enough corners to get a reasonable result\r\n if( ret &gt; 5 ):\r\n aruco.drawDetectedCornersCharuco(frame,ch_corners,ch_ids,(0,0,255) )\r\n retval, rvec, tvec = aruco.estimatePoseCharucoBoard( ch_corners, ch_ids, charuco_board, camera_matrix, dist_coeffs )\r\n\r\n# if a pose could be estimated\r\n if( retval ) :\r\n frame = aruco.drawAxis(frame,camera_matrix,dist_coeffs,rvec,tvec,0.032)\r\n\r\n# imshow and waitKey are required for the window\r\n # to open on a mac.\r\n cv2.imshow('frame', frame)\r\n\r\nif( cv2.waitKey(1) &amp; 0xFF == ord('q') ):\r\n break\r\n\r\ncap.release()\r\ncv2.destroyAllWindows()<\/pre>\n<p><strong>Step 3: Run and Verify<\/strong><\/p>\n<p>Run the program and verify. I made the axis length the same as the black square edge to try to confirm I&#8217;d chosen the distance coefficients accurately, but I haven&#8217;t done any testing beyond that.<\/p>\n<p><strong>Summary<\/strong><\/p>\n<p>There are many of these tutorials on the web, and this one is mine. I hope that you find my sharing it useful.<\/p>\n<p><strong>Resources<\/strong><\/p>\n<ul>\n<li>https:\/\/dspace.mit.edu\/bitstream\/handle\/1721.1\/83725\/864440992-MIT.pdf?sequence=2<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>The OpenCV Charuco-board pose estimation tools can provide localization estimation relative to your camera. The red-green-and blue axes drawn at the upper-right of the charuco-board taped to my wall indicates the pose estimate. These instructions are not particularly user-friendly, but &hellip; <a href=\"https:\/\/peterklemperer.com\/blog\/2017\/10\/29\/opencv-charuco-board-pose-estimation\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[65,6],"tags":[],"class_list":["post-370","post","type-post","status-publish","format-standard","hentry","category-mobot","category-projects"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1VqWo-5Y","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/peterklemperer.com\/blog\/wp-json\/wp\/v2\/posts\/370","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/peterklemperer.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/peterklemperer.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/peterklemperer.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/peterklemperer.com\/blog\/wp-json\/wp\/v2\/comments?post=370"}],"version-history":[{"count":0,"href":"https:\/\/peterklemperer.com\/blog\/wp-json\/wp\/v2\/posts\/370\/revisions"}],"wp:attachment":[{"href":"https:\/\/peterklemperer.com\/blog\/wp-json\/wp\/v2\/media?parent=370"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/peterklemperer.com\/blog\/wp-json\/wp\/v2\/categories?post=370"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/peterklemperer.com\/blog\/wp-json\/wp\/v2\/tags?post=370"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}