Skip to content

Commit 7dc8384

Browse files
Huang Yibinchristianrauch
authored andcommitted
add test_tag_pose_estimation for both c and py
1 parent dc6316d commit 7dc8384

File tree

3 files changed

+340
-0
lines changed

3 files changed

+340
-0
lines changed

test/CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ add_library(getline OBJECT getline.c)
33
add_executable(test_detection test_detection.c)
44
target_link_libraries(test_detection ${PROJECT_NAME} getline)
55

6+
# Add the pose estimation test executable
7+
add_executable(test_tag_pose_estimation test_tag_pose_estimation.c)
8+
target_link_libraries(test_tag_pose_estimation ${PROJECT_NAME} getline)
9+
610
# test images with true detection
711
set(TEST_IMAGE_NAMES
812
"33369213973_9d9bb4cc96_c"
@@ -16,3 +20,13 @@ foreach(IMG IN LISTS TEST_IMAGE_NAMES)
1620
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
1721
)
1822
endforeach()
23+
24+
# Add pose estimation tests for each image
25+
foreach(IMG IN LISTS TEST_IMAGE_NAMES)
26+
add_test(NAME test_tag_pose_estimation_${IMG}
27+
COMMAND $<TARGET_FILE:test_tag_pose_estimation>
28+
data/${IMG}.jpg data/${IMG}.txt
29+
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
30+
)
31+
endforeach()
32+

test/test_tag_pose_estimation.c

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <string.h>
4+
#include <math.h>
5+
#include <assert.h>
6+
#include <stdarg.h> // Added this header file to support variable arguments
7+
8+
#include "apriltag.h"
9+
#include "apriltag_pose.h"
10+
#include "common/matd.h"
11+
#include "common/pjpeg.h"
12+
#include "test/getline.h"
13+
14+
// Declare tag family creation functions - use tag36h11 instead of tagStandard41h12
15+
apriltag_family_t *tag36h11_create(void);
16+
void tag36h11_destroy(apriltag_family_t *tf);
17+
18+
// format function copied from test_detection.c
19+
char* format(const char *fmt, ...) {
20+
va_list args;
21+
va_start(args, fmt);
22+
int required = vsnprintf(NULL, 0, fmt, args);
23+
va_end(args);
24+
25+
if (required < 0) {
26+
return NULL;
27+
}
28+
29+
char *buffer = malloc(required + 1);
30+
if (!buffer) {
31+
return NULL;
32+
}
33+
34+
va_start(args, fmt);
35+
int result = vsnprintf(buffer, required + 1, fmt, args);
36+
va_end(args);
37+
38+
if (result < 0) {
39+
free(buffer);
40+
return NULL;
41+
}
42+
43+
return buffer;
44+
}
45+
46+
// Parse expected detection results
47+
int parse_expected_detection(const char* line, int* id, double corners[4][2]) {
48+
char* line_copy = strdup(line);
49+
if (!line_copy) {
50+
return 0;
51+
}
52+
53+
// Split ID and corners using comma
54+
char* token = strtok(line_copy, ",");
55+
if (!token) {
56+
free(line_copy);
57+
return 0;
58+
}
59+
60+
*id = atoi(token);
61+
62+
// Parse four corners
63+
for (int i = 0; i < 4; i++) {
64+
token = strtok(NULL, " ()");
65+
if (!token) {
66+
free(line_copy);
67+
return 0;
68+
}
69+
corners[i][0] = atof(token);
70+
71+
token = strtok(NULL, " ()");
72+
if (!token) {
73+
free(line_copy);
74+
return 0;
75+
}
76+
corners[i][1] = atof(token);
77+
}
78+
79+
free(line_copy);
80+
return 1;
81+
}
82+
83+
int main(int argc, char *argv[]) {
84+
if (argc < 3) {
85+
printf("Usage: %s <image_path> <expected_data_path>\n", argv[0]);
86+
printf("Example: %s test/data/33369213973_9d9bb4cc96_c.jpg test/data/33369213973_9d9bb4cc96_c.txt\n", argv[0]);
87+
return 1;
88+
}
89+
90+
const char *image_path = argv[1];
91+
const char *expected_data_path = argv[2];
92+
93+
printf("Testing tag pose estimation with image: %s\n", image_path);
94+
printf("Expected data file: %s\n", expected_data_path);
95+
96+
// Create AprilTag detector - use tag36h11 family
97+
apriltag_detector_t *td = apriltag_detector_create();
98+
apriltag_family_t *tf = tag36h11_create();
99+
apriltag_detector_add_family(td, tf);
100+
101+
// Set detector parameters - use same parameters as test_detection.c
102+
td->quad_decimate = 1.0; // Consistent with test_detection.c
103+
td->quad_sigma = 0.0;
104+
td->nthreads = 4;
105+
td->debug = 0;
106+
td->refine_edges = 0; // Consistent with test_detection.c
107+
// Remove non-existent parameters
108+
// td->refine_decode = 0;
109+
// td->refine_pose = 0;
110+
111+
// Load image
112+
int pjpeg_error;
113+
pjpeg_t *pjpeg = pjpeg_create_from_file(image_path, 0, &pjpeg_error); // Fix function call, add error parameter
114+
if (!pjpeg || pjpeg_error) {
115+
printf("Failed to load image: %s, error code: %d\n", image_path, pjpeg_error);
116+
return 1;
117+
}
118+
119+
image_u8_t *im = pjpeg_to_u8_baseline(pjpeg);
120+
pjpeg_destroy(pjpeg);
121+
122+
if (!im) {
123+
printf("Failed to decode image: %s\n", image_path);
124+
return 1;
125+
}
126+
127+
// Perform detection
128+
zarray_t *detections = apriltag_detector_detect(td, im);
129+
130+
// Read expected data file
131+
FILE *expected_file = fopen(expected_data_path, "r");
132+
if (!expected_file) {
133+
printf("Failed to open expected data file: %s\n", expected_data_path);
134+
image_u8_destroy(im);
135+
apriltag_detections_destroy(detections);
136+
apriltag_detector_destroy(td);
137+
tag36h11_destroy(tf);
138+
return 1;
139+
}
140+
141+
printf("Found %d detections in image\n", zarray_size(detections));
142+
143+
// Perform pose estimation test for each detection
144+
int num_detections = zarray_size(detections);
145+
int pose_estimation_success = 0;
146+
147+
for (int i = 0; i < num_detections; i++) {
148+
apriltag_detection_t *det;
149+
zarray_get(detections, i, &det);
150+
151+
printf("Processing detection %d: tag ID %d\n", i, det->id);
152+
153+
// Set camera parameters and tag size
154+
apriltag_detection_info_t info;
155+
info.det = det;
156+
info.tagsize = 0.16; // Assume tag size is 16cm
157+
info.fx = 600.0; // Assume focal length
158+
info.fy = 600.0;
159+
info.cx = im->width / 2.0; // Assume optical center is at image center
160+
info.cy = im->height / 2.0;
161+
162+
// Perform pose estimation
163+
apriltag_pose_t pose;
164+
double err = estimate_tag_pose(&info, &pose);
165+
166+
printf(" Estimated pose for tag %d:\n", det->id);
167+
printf(" Translation: [%f, %f, %f]\n",
168+
MATD_EL(pose.t, 0, 0),
169+
MATD_EL(pose.t, 1, 0),
170+
MATD_EL(pose.t, 2, 0));
171+
printf(" Rotation matrix:\n");
172+
printf(" [%f, %f, %f]\n",
173+
MATD_EL(pose.R, 0, 0), MATD_EL(pose.R, 0, 1), MATD_EL(pose.R, 0, 2));
174+
printf(" [%f, %f, %f]\n",
175+
MATD_EL(pose.R, 1, 0), MATD_EL(pose.R, 1, 1), MATD_EL(pose.R, 1, 2));
176+
printf(" [%f, %f, %f]\n",
177+
MATD_EL(pose.R, 2, 0), MATD_EL(pose.R, 2, 1), MATD_EL(pose.R, 2, 2));
178+
printf(" Reprojection error: %f\n", err);
179+
180+
// Check if pose estimation was successful
181+
if (pose.R != NULL && pose.t != NULL && err < 1.0) { // Assume error less than 1.0 means success
182+
pose_estimation_success++;
183+
}
184+
185+
// Free pose memory
186+
if (pose.R) matd_destroy(pose.R);
187+
if (pose.t) matd_destroy(pose.t);
188+
}
189+
190+
// Output test results
191+
printf("\nPose estimation test results:\n");
192+
printf(" Successful pose estimations: %d/%d\n", pose_estimation_success, num_detections);
193+
194+
if (pose_estimation_success == num_detections && num_detections > 0) {
195+
printf(" All pose estimations successful!\n");
196+
} else if (num_detections > 0) {
197+
printf(" Some pose estimations failed.\n");
198+
} else {
199+
printf(" No detections found in image.\n");
200+
}
201+
202+
// Clean up resources
203+
image_u8_destroy(im);
204+
apriltag_detections_destroy(detections);
205+
apriltag_detector_destroy(td);
206+
tag36h11_destroy(tf);
207+
208+
return (pose_estimation_success == num_detections && num_detections > 0) ? 0 : 1;
209+
}

test/test_tag_pose_estimation.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#!/usr/bin/env python3
2+
3+
import sys
4+
import os
5+
import cv2
6+
import numpy as np
7+
from apriltag import apriltag
8+
9+
def parse_expected_detection(line):
10+
"""
11+
Parse expected detection results from a line in the data file.
12+
Format: ID, (x1 y1), (x2 y2), (x3 y3), (x4 y4)
13+
"""
14+
try:
15+
parts = line.strip().split(',')
16+
if len(parts) != 5:
17+
return None, None
18+
19+
tag_id = int(parts[0].strip())
20+
21+
corners = []
22+
for i in range(1, 5):
23+
corner_str = parts[i].strip().strip('()')
24+
x, y = map(float, corner_str.split())
25+
corners.append([x, y])
26+
27+
return tag_id, np.array(corners)
28+
except (ValueError, IndexError):
29+
return None, None
30+
31+
def main():
32+
if len(sys.argv) < 3:
33+
print(f"Usage: {sys.argv[0]} <image_path> <expected_data_path>")
34+
print(f"Example: {sys.argv[0]} data/33369213973_9d9bb4cc96_c.jpg data/33369213973_9d9bb4cc96_c.txt")
35+
return 1
36+
37+
image_path = sys.argv[1]
38+
expected_data_path = sys.argv[2]
39+
40+
print(f"Testing tag pose estimation with image: {image_path}")
41+
print(f"Expected data file: {expected_data_path}")
42+
43+
# Load image
44+
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
45+
if image is None:
46+
print(f"Failed to load image: {image_path}")
47+
return 1
48+
49+
# Create AprilTag detector - use tag36h11 family to match the C version
50+
detector = apriltag("tag36h11",
51+
threads=4,
52+
decimate=1.0, # Consistent with test_detection.c
53+
refine_edges=False) # Consistent with test_detection.c
54+
55+
# Perform detection
56+
detections = detector.detect(image)
57+
58+
# Read expected data file
59+
try:
60+
with open(expected_data_path, 'r') as expected_file:
61+
expected_lines = expected_file.readlines()
62+
except FileNotFoundError:
63+
print(f"Failed to open expected data file: {expected_data_path}")
64+
return 1
65+
66+
print(f"Found {len(detections)} detections in image")
67+
68+
# Perform pose estimation test for each detection
69+
num_detections = len(detections)
70+
pose_estimation_success = 0
71+
72+
for i, det in enumerate(detections):
73+
print(f"Processing detection {i}: tag ID {det['id']}")
74+
75+
# Set camera parameters and tag size
76+
tagsize = 0.16 # Assume tag size is 16cm
77+
fx = 600.0 # Assume focal length
78+
fy = 600.0
79+
cx = image.shape[1] / 2.0 # Assume optical center is at image center
80+
cy = image.shape[0] / 2.0
81+
82+
# Perform pose estimation
83+
try:
84+
pose = detector.estimate_tag_pose(det, tagsize, fx, fy, cx, cy)
85+
R = pose['R']
86+
t = pose['t']
87+
err = pose['error']
88+
89+
print(f" Estimated pose for tag {det['id']}:")
90+
print(f" Translation: [{t[0][0]:f}, {t[1][0]:f}, {t[2][0]:f}]")
91+
print(f" Rotation matrix:")
92+
print(f" [{R[0,0]:f}, {R[0,1]:f}, {R[0,2]:f}]")
93+
print(f" [{R[1,0]:f}, {R[1,1]:f}, {R[1,2]:f}]")
94+
print(f" [{R[2,0]:f}, {R[2,1]:f}, {R[2,2]:f}]")
95+
print(f" Reprojection error: {err:f}")
96+
97+
# Check if pose estimation was successful
98+
if err < 1.0: # Assume error less than 1.0 means success
99+
pose_estimation_success += 1
100+
except Exception as e:
101+
print(f" Pose estimation failed for tag {det['id']}: {e}")
102+
103+
# Output test results
104+
print(f"\nPose estimation test results:")
105+
print(f" Successful pose estimations: {pose_estimation_success}/{num_detections}")
106+
107+
if pose_estimation_success == num_detections and num_detections > 0:
108+
print(" All pose estimations successful!")
109+
elif num_detections > 0:
110+
print(" Some pose estimations failed.")
111+
else:
112+
print(" No detections found in image.")
113+
114+
return 0 if (pose_estimation_success == num_detections and num_detections > 0) else 1
115+
116+
if __name__ == "__main__":
117+
sys.exit(main())

0 commit comments

Comments
 (0)