pointmap.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. from multiprocessing import Process, Queue
  2. import numpy as np
  3. import cv2
  4. import pangolin
  5. import OpenGL.GL as gl
  6. # Global map // 3D map visualization using pangolin
  7. class Map(object):
  8. def __init__(self):
  9. self.frames = [] # camera frames [means camera pose]
  10. self.points = [] # 3D points of map
  11. self.state = None # variable to hold current state of the map and cam pose
  12. self.q = None # A queue for inter-process communication. | q for visualization process
  13. self.q_image = None
  14. def create_viewer(self):
  15. # Parallel Execution: The main purpose of creating this process is to run
  16. # the `viewer_thread` method in parallel with the main program.
  17. # This allows the 3D viewer to update and render frames continuously
  18. # without blocking the main execution flow.
  19. self.q = Queue() # q is initialized as a Queue
  20. self.q_image = Queue()
  21. # initializes the Parallel process with the `viewer_thread` function
  22. # the arguments that the function takes is mentioned in the args var
  23. p = Process(target=self.viewer_thread, args=(self.q,))
  24. # daemon true means, exit when main program stops
  25. p.daemon = True
  26. # starts the process
  27. p.start()
  28. def viewer_thread(self, q):
  29. # `viewer_thread` takes the q as input
  30. # initializes the viz window
  31. self.viewer_init(1280, 720)
  32. # An infinite loop that continually refreshes the viewer
  33. while True:
  34. self.viewer_refresh(q)
  35. def viewer_init(self, w, h):
  36. pangolin.CreateWindowAndBind('Main', w, h)
  37. # This ensures that only the nearest objects are rendered,
  38. # creating a realistic representation of the scene with
  39. # correct occlusions.
  40. gl.glEnable(gl.GL_DEPTH_TEST)
  41. # Sets up the camera with a projection matrix and a model-view matrix
  42. self.scam = pangolin.OpenGlRenderState(
  43. # `ProjectionMatrix` The parameters specify the width and height of the viewport (w, h), the focal lengths in the x and y directions (420, 420), the principal point coordinates (w//2, h//2), and the near and far clipping planes (0.2, 10000). The focal lengths determine the field of view,
  44. # the principal point indicates the center of the projection, and the clipping planes define the range of distances from the camera within which objects are rendered, with objects closer than 0.2 units or farther than 10000 units being clipped out of the scene.
  45. pangolin.ProjectionMatrix(w, h, 420, 420, w//2, h//2, 0.2, 10000),
  46. # pangolin.ModelViewLookAt(0, -10, -8, 0, 0, 0, 0, -1, 0) sets up the camera view matrix, which defines the position and orientation of the camera in the 3D scene. The first three parameters (0, -10, -8) specify the position of the camera in the world coordinates, indicating that the camera is located at coordinates (0, -10, -8). The next three parameters (0, 0, 0)
  47. # define the point in space the camera is looking at, which is the origin in this case. The last three parameters (0, -1, 0) represent the up direction vector, indicating which direction is considered 'up' for the camera, here pointing along the negative y-axis. This setup effectively positions the camera 10 units down and 8 units back from the origin, looking towards the origin with the 'up' direction being downwards in the y-axis, which is unconventional and might be used to achieve a specific orientation or perspective in the rendered scene.
  48. pangolin.ModelViewLookAt(0, -10, -8, 0, 0, 0, 0, -1, 0))
  49. # Creates a handler for 3D interaction.
  50. self.handler = pangolin.Handler3D(self.scam)
  51. # Creates a display context.
  52. self.dcam = pangolin.CreateDisplay()
  53. # Sets the bounds of the display
  54. self.dcam.SetBounds(0.0, 1.0, 0.0, 1.0, -w/h)
  55. # assigns handler for mouse clicking and stuff, interactive
  56. self.dcam.SetHandler(self.handler)
  57. # self.darr = None
  58. # image
  59. width, height = 480, 270
  60. self.dimg = pangolin.Display('image')
  61. self.dimg.SetBounds(0, height / 768., 0.0, width / 1024., 1024 / 768.)
  62. self.dimg.SetLock(pangolin.Lock.LockLeft, pangolin.Lock.LockTop)
  63. self.texture = pangolin.GlTexture(width, height, gl.GL_RGB, False, 0, gl.GL_RGB, gl.GL_UNSIGNED_BYTE)
  64. self.image = np.ones((height, width, 3), 'uint8')
  65. def viewer_refresh(self, q):
  66. width, height = 480, 270
  67. # Checks if the current state is None or if the queue is not empty.
  68. if self.state is None or not q.empty():
  69. # Gets the latest state from the queue.
  70. self.state = q.get()
  71. # Clears the color and depth buffers.
  72. gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
  73. # Sets the clear color to white.
  74. gl.glClearColor(1.0, 1.0, 1.0, 1.0)
  75. # Activates the display context with the current camera settings.
  76. self.dcam.Activate(self.scam)
  77. # camera trajectory line and color setup
  78. gl.glLineWidth(1)
  79. gl.glColor3f(0.0, 1.0, 0.0)
  80. pangolin.DrawCameras(self.state[0])
  81. # 3d point cloud color setup
  82. gl.glPointSize(2)
  83. gl.glColor3f(1.0, 0.0, 0.0)
  84. pangolin.DrawPoints(self.state[1])
  85. # show image
  86. if not self.q_image.empty():
  87. self.image = self.q_image.get()
  88. if self.image.ndim == 3:
  89. self.image = self.image[::-1, :, ::-1]
  90. else:
  91. self.image = np.repeat(self.image[::-1, :, np.newaxis], 3, axis=2)
  92. self.image = cv2.resize(self.image, (width, height))
  93. if True:
  94. self.texture.Upload(self.image, gl.GL_RGB, gl.GL_UNSIGNED_BYTE)
  95. self.dimg.Activate()
  96. gl.glColor3f(1.0, 1.0, 1.0)
  97. self.texture.RenderToViewport()
  98. # Finishes the current frame and swaps the buffers.
  99. pangolin.FinishFrame()
  100. def display(self):
  101. if self.q is None:
  102. return
  103. poses, pts = [], []
  104. for f in self.frames:
  105. # updating pose
  106. poses.append(f.pose)
  107. for p in self.points:
  108. # updating map points
  109. pts.append(p.pt)
  110. # updating queue
  111. self.q.put((np.array(poses), np.array(pts)))
  112. def display_image(self, ip_image):
  113. # if self.q is None:
  114. # return
  115. self.q_image.put(ip_image)
  116. class Point(object):
  117. # A Point is a 3-D point in the world
  118. # Each point is observed in multiple frames
  119. def __init__(self, mapp, loc):
  120. self.frames = []
  121. self.pt = loc
  122. self.idxs = []
  123. # assigns a unique ID to the point based on the current number of points in the map.
  124. self.id = len(mapp.points)
  125. # adds the point instance to the map’s list of points.
  126. mapp.points.append(self)
  127. def add_observation(self, frame, idx):
  128. # Frame is the frame class
  129. self.frames.append(frame)
  130. self.idxs.append(idx)