1
1
import numpy as np
2
2
import cv2
3
- import sys
4
- import time
5
- from OpenGL .GL import *
6
- from OpenGL .GLUT import *
7
- from OpenGL .GLU import *
8
3
import pygame
9
4
from pygame .locals import *
10
- from objloader import Obj
11
-
12
- ARUCO_DICT = {
13
- "DICT_4X4_50" : cv2 .aruco .DICT_4X4_50 ,
14
- "DICT_4X4_100" : cv2 .aruco .DICT_4X4_100 ,
15
- "DICT_4X4_250" : cv2 .aruco .DICT_4X4_250 ,
16
- "DICT_4X4_1000" : cv2 .aruco .DICT_4X4_1000 ,
17
- "DICT_5X5_50" : cv2 .aruco .DICT_5X5_50 ,
18
- "DICT_5X5_100" : cv2 .aruco .DICT_5X5_100 ,
19
- "DICT_5X5_250" : cv2 .aruco .DICT_5X5_250 ,
20
- "DICT_5X5_1000" : cv2 .aruco .DICT_5X5_1000 ,
21
- "DICT_6X6_50" : cv2 .aruco .DICT_6X6_50 ,
22
- "DICT_6X6_100" : cv2 .aruco .DICT_6X6_100 ,
23
- "DICT_6X6_250" : cv2 .aruco .DICT_6X6_250 ,
24
- "DICT_6X6_1000" : cv2 .aruco .DICT_6X6_1000 ,
25
- "DICT_7X7_50" : cv2 .aruco .DICT_7X7_50 ,
26
- "DICT_7X7_100" : cv2 .aruco .DICT_7X7_100 ,
27
- "DICT_7X7_250" : cv2 .aruco .DICT_7X7_250 ,
28
- "DICT_7X7_1000" : cv2 .aruco .DICT_7X7_1000 ,
29
- "DICT_ARUCO_ORIGINAL" : cv2 .aruco .DICT_ARUCO_ORIGINAL ,
30
- "DICT_APRILTAG_16h5" : cv2 .aruco .DICT_APRILTAG_16h5 ,
31
- "DICT_APRILTAG_25h9" : cv2 .aruco .DICT_APRILTAG_25h9 ,
32
- "DICT_APRILTAG_36h10" : cv2 .aruco .DICT_APRILTAG_36h10 ,
33
- "DICT_APRILTAG_36h11" : cv2 .aruco .DICT_APRILTAG_36h11
34
- }
5
+ from OpenGL .GL import *
6
+ from OpenGL .GLU import *
35
7
36
- def init_gl (width , height ):
37
- glClearColor (0.0 , 0.0 , 0.0 , 0.0 )
38
- glClearDepth (1.0 )
39
- glDepthFunc (GL_LESS )
8
+ class ObjLoader :
9
+ def __init__ (self , filename , swapyz = False ):
10
+ self .vertices = []
11
+ self .normals = []
12
+ self .texcoords = []
13
+ self .faces = []
14
+ self .gl_list = None
15
+
16
+ for line in open (filename , "r" ):
17
+ if line .startswith ('#' ): continue
18
+ values = line .split ()
19
+ if not values : continue
20
+
21
+ if values [0 ] == 'v' :
22
+ v = list (map (float , values [1 :4 ]))
23
+ if swapyz :
24
+ v = v [0 ], v [2 ], v [1 ]
25
+ self .vertices .append (v )
26
+ elif values [0 ] == 'vn' :
27
+ v = list (map (float , values [1 :4 ]))
28
+ if swapyz :
29
+ v = v [0 ], v [2 ], v [1 ]
30
+ self .normals .append (v )
31
+ elif values [0 ] == 'vt' :
32
+ self .texcoords .append (list (map (float , values [1 :3 ])))
33
+ elif values [0 ] == 'f' :
34
+ face = []
35
+ texcoords = []
36
+ norms = []
37
+ for v in values [1 :]:
38
+ w = v .split ('/' )
39
+ face .append (int (w [0 ]))
40
+ if len (w ) >= 2 and len (w [1 ]) > 0 :
41
+ texcoords .append (int (w [1 ]))
42
+ else :
43
+ texcoords .append (0 )
44
+ if len (w ) >= 3 and len (w [2 ]) > 0 :
45
+ norms .append (int (w [2 ]))
46
+ else :
47
+ norms .append (0 )
48
+ self .faces .append ((face , norms , texcoords ))
49
+
50
+ def create_gl_list (self ):
51
+ if self .gl_list is not None :
52
+ return self .gl_list
53
+
54
+ self .gl_list = glGenLists (1 )
55
+ glNewList (self .gl_list , GL_COMPILE )
56
+ glFrontFace (GL_CCW )
57
+ for face in self .faces :
58
+ vertices , normals , texture_coords = face
59
+ glBegin (GL_POLYGON )
60
+ for i in range (len (vertices )):
61
+ if normals [i ] > 0 :
62
+ glNormal3fv (self .normals [normals [i ] - 1 ])
63
+ glVertex3fv (self .vertices [vertices [i ] - 1 ])
64
+ glEnd ()
65
+ glEndList ()
66
+ return self .gl_list
67
+
68
+ def render (self ):
69
+ glCallList (self .create_gl_list ())
70
+
71
+ def init_ar ():
72
+ pygame .init ()
73
+ display = (1280 , 720 )
74
+ pygame .display .set_mode (display , DOUBLEBUF | OPENGL )
75
+
40
76
glEnable (GL_DEPTH_TEST )
41
- glShadeModel (GL_SMOOTH )
77
+ glEnable (GL_LIGHTING )
78
+ glLightfv (GL_LIGHT0 , GL_POSITION , (0 , 0 , - 2 , 1 ))
79
+ glLightfv (GL_LIGHT0 , GL_AMBIENT , (0.2 , 0.2 , 0.2 , 1 ))
80
+ glLightfv (GL_LIGHT0 , GL_DIFFUSE , (0.5 , 0.5 , 0.5 , 1 ))
81
+ glEnable (GL_LIGHT0 )
82
+ glEnable (GL_COLOR_MATERIAL )
83
+ glColorMaterial (GL_FRONT_AND_BACK , GL_AMBIENT_AND_DIFFUSE )
84
+
85
+ return display
86
+
87
+ def set_projection_from_camera (intrinsic ):
42
88
glMatrixMode (GL_PROJECTION )
43
89
glLoadIdentity ()
44
- gluPerspective (45.0 , float (width )/ float (height ), 0.1 , 100.0 )
45
- glMatrixMode (GL_MODELVIEW )
46
-
47
- def draw_axis (img , imgpts , corner , length = 0.01 ):
48
- origin = tuple (corner .ravel ().astype (int ))
49
- img = cv2 .line (img , origin , tuple (imgpts [0 ].ravel ().astype (int )), (255 ,0 ,0 ), 5 )
50
- img = cv2 .line (img , origin , tuple (imgpts [1 ].ravel ().astype (int )), (0 ,255 ,0 ), 5 )
51
- img = cv2 .line (img , origin , tuple (imgpts [2 ].ravel ().astype (int )), (0 ,0 ,255 ), 5 )
52
- return img
53
-
54
- def pose_estimation (frame , aruco_dict_type , matrix_coefficients , distortion_coefficients ):
55
- gray = cv2 .cvtColor (frame , cv2 .COLOR_BGR2GRAY )
56
- cv2 .aruco_dict = cv2 .aruco .Dictionary_get (aruco_dict_type )
57
- parameters = cv2 .aruco .DetectorParameters_create ()
58
-
59
- corners , ids , rejected_img_points = cv2 .aruco .detectMarkers (gray , cv2 .aruco_dict , parameters = parameters )
60
-
61
- # Debug: Print number of markers detected
62
- print (f"Number of ArUco markers detected: { len (corners )} " )
63
-
64
- if len (corners ) > 0 :
65
- for i in range (0 , len (ids )):
66
- rvec , tvec , markerPoints = cv2 .aruco .estimatePoseSingleMarkers (corners [i ], 0.02 , matrix_coefficients ,
67
- distortion_coefficients )
68
-
69
- # Debug: Print pose information
70
- print (f"Marker { ids [i ][0 ]} - Rotation: { rvec } , Translation: { tvec } " )
71
-
72
- # Draw the axes of the marker
73
- axis_length = 0.01
74
- axis = np .float32 ([[axis_length ,0 ,0 ], [0 ,axis_length ,0 ], [0 ,0 ,- axis_length ]]).reshape (- 1 ,3 )
75
- imgpts , jac = cv2 .projectPoints (axis , rvec , tvec , matrix_coefficients , distortion_coefficients )
76
- frame = draw_axis (frame , imgpts , corners [i ][0 ][0 ])
77
-
78
- cv2 .aruco .drawDetectedMarkers (frame , corners )
79
-
80
- # Convert rotation vector to rotation matrix
81
- rotation_matrix , _ = cv2 .Rodrigues (rvec )
82
-
83
- # Combine rotation and translation into a single 4x4 transformation matrix
84
- transformation_matrix = np .eye (4 )
85
- transformation_matrix [:3 , :3 ] = rotation_matrix
86
- transformation_matrix [:3 , 3 ] = tvec .reshape (3 )
87
-
88
- # Set up OpenGL modelview matrix
89
- glMatrixMode (GL_MODELVIEW )
90
- glLoadIdentity ()
91
- glMultMatrixf (transformation_matrix .T )
92
-
93
- # Draw the 3D object
94
- glColor3f (0.0 , 1.0 , 0.0 ) # Set color to green
95
- obj .render () # Render the loaded OBJ file
96
-
97
- return frame
98
-
99
- aruco_type = "DICT_5X5_100"
100
- arucoDict = cv2 .aruco .Dictionary_get (ARUCO_DICT [aruco_type ])
101
- arucoParams = cv2 .aruco .DetectorParameters_create ()
102
-
103
- intrinsic_camera = np .array (((933.15867 , 0 , 657.59 ), (0 , 933.1586 , 400.36993 ), (0 , 0 , 1 )))
104
- distortion = np .array ((- 0.43948 , 0.18514 , 0 , 0 ))
105
-
106
- cap = cv2 .VideoCapture (0 )
107
- cap .set (cv2 .CAP_PROP_FRAME_WIDTH , 1280 )
108
- cap .set (cv2 .CAP_PROP_FRAME_HEIGHT , 720 )
109
-
110
- # Debug: Check if camera is opened successfully
111
- if not cap .isOpened ():
112
- print ("Error: Could not open camera." )
113
- sys .exit ()
114
-
115
- # Initialize Pygame and OpenGL
116
- pygame .init ()
117
- display = (1280 , 720 )
118
- pygame .display .set_mode (display , DOUBLEBUF | OPENGL )
119
- init_gl (1280 , 720 )
90
+
91
+ fx = intrinsic [0 ,0 ]
92
+ fy = intrinsic [1 ,1 ]
93
+ fovy = 2 * np .arctan (0.5 * 720 / fy ) * 180 / np .pi
94
+ aspect = (1280 * fy ) / (720 * fx )
120
95
121
- # Load the OBJ file
122
- obj = Obj ( "objects/cube.obj" , swapyz = True )
96
+ gluPerspective ( fovy , aspect , 0.1 , 100.0 )
97
+ glViewport ( 0 , 0 , 1280 , 720 )
123
98
124
- while True :
125
- ret , frame = cap .read ()
99
+ def set_modelview_from_camera (rvec , tvec ):
100
+ glMatrixMode (GL_MODELVIEW )
101
+ glLoadIdentity ()
102
+
103
+ # Adjust for the shapes returned by estimatePoseSingleMarkers
104
+ rotation = rvec [0 ][0 ] # Get the 3x1 rotation vector
105
+ translation = tvec [0 ][0 ] # Get the 3x1 translation vector
106
+
107
+ # Convert rotation vector to matrix
108
+ rmtx = cv2 .Rodrigues (rotation )[0 ]
126
109
127
- if not ret :
128
- print ("Failed to grab frame" )
129
- break
110
+ view_matrix = np .array ([[rmtx [0 ,0 ], rmtx [0 ,1 ], rmtx [0 ,2 ], translation [0 ]],
111
+ [rmtx [1 ,0 ], rmtx [1 ,1 ], rmtx [1 ,2 ], translation [1 ]],
112
+ [rmtx [2 ,0 ], rmtx [2 ,1 ], rmtx [2 ,2 ], translation [2 ]],
113
+ [0.0 , 0.0 , 0.0 , 1.0 ]])
130
114
131
- # Clear the OpenGL buffers
132
- glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT )
115
+ view_matrix = view_matrix * np .array ([1 , - 1 , - 1 , 1 ])
133
116
134
- # Set up the camera matrix
117
+ inverse_matrix = np .linalg .inv (view_matrix )
118
+ glLoadMatrixf (inverse_matrix .T )
119
+
120
+ def draw_background (frame ):
121
+ glDisable (GL_DEPTH_TEST )
135
122
glMatrixMode (GL_PROJECTION )
136
123
glLoadIdentity ()
137
- gluPerspective ( 45 , ( display [ 0 ] / display [ 1 ]) , 0.1 , 50.0 )
124
+ gluOrtho2D ( 0 , 1280 , 0 , 720 )
138
125
glMatrixMode (GL_MODELVIEW )
139
126
glLoadIdentity ()
140
- gluLookAt (0 , 0 , 5 , 0 , 0 , 0 , 0 , 1 , 0 )
141
127
142
- # Process the frame and estimate pose
143
- output = pose_estimation (frame , ARUCO_DICT [aruco_type ], intrinsic_camera , distortion )
128
+ # Convert frame to OpenGL texture format
129
+ bg_image = cv2 .flip (frame , 0 )
130
+ bg_image = cv2 .cvtColor (bg_image , cv2 .COLOR_BGR2RGB )
131
+
132
+ glEnable (GL_TEXTURE_2D )
133
+ texture_id = glGenTextures (1 )
134
+ glBindTexture (GL_TEXTURE_2D , texture_id )
135
+ glTexImage2D (GL_TEXTURE_2D , 0 , GL_RGB , 1280 , 720 , 0 , GL_RGB , GL_UNSIGNED_BYTE , bg_image )
136
+ glTexParameteri (GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR )
137
+ glTexParameteri (GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR )
144
138
145
- # Debug: Display the frame with ArUco markers and axes
146
- cv2 .imshow ('ArUco Detection' , output )
139
+ # Draw textured quad
140
+ glBegin (GL_QUADS )
141
+ glTexCoord2f (0.0 , 1.0 ); glVertex2f (0 , 0 )
142
+ glTexCoord2f (1.0 , 1.0 ); glVertex2f (1280 , 0 )
143
+ glTexCoord2f (1.0 , 0.0 ); glVertex2f (1280 , 720 )
144
+ glTexCoord2f (0.0 , 0.0 ); glVertex2f (0 , 720 )
145
+ glEnd ()
147
146
148
- # Convert the OpenCV output to a Pygame surface
149
- output = cv2 .cvtColor (output , cv2 .COLOR_BGR2RGB )
150
- output = np .rot90 (output )
151
- output = pygame .surfarray .make_surface (output )
147
+ glDeleteTextures ([texture_id ])
148
+ glDisable (GL_TEXTURE_2D )
149
+ glEnable (GL_DEPTH_TEST )
150
+
151
+ def main ():
152
+ # Initialize camera and AR
153
+ cap = cv2 .VideoCapture (0 )
154
+ cap .set (cv2 .CAP_PROP_FRAME_WIDTH , 1280 )
155
+ cap .set (cv2 .CAP_PROP_FRAME_HEIGHT , 720 )
152
156
153
- # Display the Pygame surface
154
- screen = pygame .display .get_surface ()
155
- screen .blit (output , (0 ,0 ))
157
+ display = init_ar ()
156
158
157
- pygame .display .flip ()
159
+ # Camera parameters
160
+ camera_matrix = np .array ([[933.15867 , 0 , 657.59 ],
161
+ [0 , 933.1586 , 400.36993 ],
162
+ [0 , 0 , 1 ]])
163
+ dist_coeffs = np .array ([- 0.43948 , 0.18514 , 0 , 0 ])
158
164
159
- for event in pygame .event .get ():
160
- if event .type == pygame .QUIT :
161
- pygame .quit ()
162
- cap .release ()
163
- cv2 .destroyAllWindows ()
164
- sys .exit ()
165
-
166
- # Break the loop if 'q' is pressed
167
- if cv2 .waitKey (1 ) & 0xFF == ord ('q' ):
168
- break
169
-
170
- cap .release ()
171
- cv2 .destroyAllWindows ()
172
- pygame .quit ()
165
+ # Initialize ArUco detector
166
+ aruco_dict = cv2 .aruco .Dictionary_get (cv2 .aruco .DICT_5X5_100 )
167
+ parameters = cv2 .aruco .DetectorParameters_create ()
168
+
169
+ # Load 3D model
170
+ obj = ObjLoader ("objects/cube.obj" , swapyz = True )
171
+
172
+ clock = pygame .time .Clock ()
173
+
174
+ while True :
175
+ for event in pygame .event .get ():
176
+ if event .type == pygame .QUIT :
177
+ pygame .quit ()
178
+ cap .release ()
179
+ return
180
+
181
+ ret , frame = cap .read ()
182
+ if not ret :
183
+ break
184
+
185
+ # Clear OpenGL buffers
186
+ glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT )
187
+
188
+ # Draw background (camera frame)
189
+ draw_background (frame )
190
+
191
+ # Detect ArUco markers
192
+ gray = cv2 .cvtColor (frame , cv2 .COLOR_BGR2GRAY )
193
+ corners , ids , _ = cv2 .aruco .detectMarkers (gray , aruco_dict , parameters = parameters )
194
+
195
+ if ids is not None :
196
+ # Set OpenGL camera projection
197
+ set_projection_from_camera (camera_matrix )
198
+
199
+ # Draw 3D objects for each detected marker
200
+ for i in range (len (ids )):
201
+ # Get pose of the marker
202
+ rvec , tvec , _ = cv2 .aruco .estimatePoseSingleMarkers (corners [i ], 0.02 , camera_matrix , dist_coeffs )
203
+
204
+ # Set OpenGL modelview matrix
205
+ set_modelview_from_camera (rvec , tvec )
206
+
207
+ glColor3f (0.0 , 1.0 , 0.0 ) # Set color to green
208
+ obj .render ()
209
+
210
+ pygame .display .flip ()
211
+ clock .tick (60 )
212
+
213
+ if __name__ == "__main__" :
214
+ main ()
0 commit comments