Obstacle Distance Determination through Depth Mapping - Carleton-SRCL/SPOT GitHub Wiki

SDM_AC.py

Click here for raw code
      ####################################################
  # stereo depth map functions.py
  # Author: Adrian Comisso
  # Date: 2024-02-26
  #
  # Description:
  #
  # usfull links
  # -main document
  # https://docs.opencv.org/3.4/dd/d53/tutorial_py_depthmap.html
  # -issue forums
  # https://answers.opencv.org/question/59032/disparity-map-issues/
  # -how to use on video feed blog
  # https://stereopi.com/blog/opencv-and-depth-map-stereopi-tutorial
  ####################################################
  
  ####################################################
  # Imports
  ####################################################
  import cv2
  import numpy as np
  import os
  from datetime import datetime
  from matplotlib import pyplot as plt
  
  
  ####################################################
  # Functions
  ####################################################
  
  # function to split an image into two halves
  def split_image(image):
      # check if the image is 2D or 3D
      if len(image.shape) == 2:
          # convert to 3D where each layer is Red, Green, and Blue
          image = np.expand_dims(image, axis=2)
      # dimensions of the image
      dimensions = image.shape
      height = dimensions[0]
      width = dimensions[1]
      midpoint = width // 2
      # split the image into two halves
      left_half = image[:, :midpoint, :]
      right_half = image[:, midpoint:, :]
      # return the two halves
      return left_half, right_half
  
  
  # function to resize an image for display
  def resize_for_display(image, scale_percent):
      # calculate the new dimensions using percentage
      width = int(image.shape[1] * scale_percent / 100)
      height = int(image.shape[0] * scale_percent / 100)
      # new dimension tuple
      dim = (width, height)
      # resize the image to the specified dimensions for display
      resized_image = cv2.resize(image, dim, interpolation=cv2.INTER_AREA)
      # retun the resized image
      return resized_image
  
  
  # function to save an image with a unique name
  def save_image(image, filename):
      # get the current date and time
      timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
      # create a unique filename
      filename = f"{filename}_{timestamp}.png"
      # generate the output path
      output_path = os.path.join(output_file_path, filename)
      # save the image
      cv2.imwrite(output_path, image)
      # notify the user
      print(f"Saved: {output_path}")
  
  
  # function to crop the top of an image
  def crop_top(image, num_rows):
      height, width = image.shape[:2]
      cropped_image = image[num_rows:height, :]
      return cropped_image
  
  
  # function to crop the bottom of an image
  def crop_bottom(image, num_rows):
      height, width = image.shape[:2]
      cropped_image = image[0:height - num_rows, :]
      return cropped_image
  
  
  # function to rotate image by angle
  def rotate_image(image, angle):
      # get the dimensions of the image
      height, width = image.shape[:2]
      # get the center of the image
      center = (width / 2, height / 2)
      # get the rotation matrix
      rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
      # rotate the image
      rotated_image = cv2.warpAffine(image, rotation_matrix, (width, height))
      # return the rotated image
      return rotated_image
  
  
  ####################################################
  # Main Function
  ####################################################
  
  ##### Main Function #####
  def find_obstacle_depth(un_dist_img, bound_left, bound_top, bound_right, bound_bottom, show_images, show_disparity,
                          show_depth, show_points):
      ##### Inputs #####
      # color layer to use
      color_layer = 'red'
      # bounding box parameters
      step_size = 20
      # point selection padding
      padding = 20
  
      ##### Correct Tilt #####
      # split left and right images
      left_img = split_image(un_dist_img)[0]
      right_img = split_image(un_dist_img)[1]
      # amount to crop top and bottom of image to acive alignment
      pix_to_crop = 36  # ********************************************************************************
      # crop top and bottom of respective images to align rows
      right_img = crop_bottom(right_img, pix_to_crop)
      left_img = crop_top(left_img, pix_to_crop)
      # resize images for undistort
      # left_img = cv2.resize(left_img, (1280, 720))
      # right_img = cv2.resize(right_img, (1280, 720))
      if show_images:
          cv2.imshow('Left Image', resize_for_display(left_img, 50))
          cv2.imshow('Right Image', resize_for_display(right_img, 50))
  
      ##### Convert to Grey Scale #####
      # extract R, G, and B layers
      bl, gl, rl = cv2.split(left_img)
      br, gr, rr = cv2.split(right_img)
      # choose which layer to use
      if color_layer == 'red':
          left_img = rl
          right_img = rr
      elif color_layer == 'green':
          left_img = gl
          right_img = gr
      elif color_layer == 'blue':
          left_img = bl
          right_img = br
      else:  # grey scale
          left_img = cv2.cvtColor(left_img, cv2.COLOR_RGB2GRAY)
          right_img = cv2.cvtColor(right_img, cv2.COLOR_RGB2GRAY)
  
      ##### Compute Disparity Map #####
      # size of the window that gets scanned
      window_size = 3
      # create stereo disparity object
      stereo = cv2.StereoSGBM_create(minDisparity=16,  # min possible disparity (multiple of 16)
                                     numDisparities=16 * 14,  # forget what this does ****18
                                     blockSize=window_size,  # size of the window that gets scanned
                                     uniquenessRatio=5,  # how uniwue can a section be before it is discarded
                                     disp12MaxDiff=2,  # dunno
                                     speckleWindowSize=10,  # window for noise removal
                                     speckleRange=2,  # range to remove noise
                                     P1=8 * 3 * window_size ** 2,  # cost fucntion for moving vertically
                                     P2=32 * 3 * window_size ** 2,  # cost fucntion for moving horizontally
                                     mode=cv2.STEREO_SGBM_MODE_SGBM_3WAY)  # mode for the stereo disparity object
      # compute the disparity map
      disparity = stereo.compute(left_img, right_img).astype(np.float32) / 16.0
      # show disparity map
      if show_disparity:
          plt.imshow(disparity, 'gray')
          plt.show()
  
      ##### Compute Depth Map #####
      # camera parameters
      focal_length = 1080  # 694 #in pixels #1419
      baseline = 0.12  # in m
      # #compute depth map
      # for i in range(disparity.shape[0]):  # Loop through rows
      #     for j in range(disparity.shape[1]):  # Loop through columns
      #         # Apply your calculation to each pixel
      #         disparity[i, j] = focal_length*baseline/((disparity[i, j])) #curve fit conversion
      # #show depth map
      # if show_depth:
      #     plt.imshow(disparity,'gray')
      #     plt.show()
  
      ##### Compute Average Depth of Bounding Box #####
      # initialize loop variables
      sum = 0
      count = 0
      # find depth of obstacle
      for i in range(bound_top + padding, bound_bottom - padding, step_size):
          for j in range(bound_left + padding, bound_right - padding, step_size):
              # Apply your calculation to each pixel
              disparity[i, j] = focal_length * baseline / ((disparity[i, j]))
              if disparity[i, j] < 2.75:
                  sum += disparity[i, j]
                  count += 1
              disparity[i, j] = 10
      # show depth map with points
      if show_points:
          plt.imshow(disparity, 'gray')
          plt.show()
      # calculate average depth
      if count == 0:
          depth = int(1000)
      else:
          depth = sum / count
      return depth

Purpose

  • This script is designed to compute stereo depth maps from stereo image pairs captured by a camera setup. Its main purpose is to facilitate obstacle detection and localization by analyzing the depth information of objects within a previously determined bounding box. The script offers functions for image manipulation, such as splitting, resizing, cropping, and rotating images, along with stereo vision techniques to compute depth maps. It aims to provide accurate depth estimation for obstacles, enabling applications like autonomous navigation systems to perceive and navigate through their environments effectively.

Inputs

  • un_dist_img
    • This is the previously calibrated and undistorted stereo image pair (left and right) captured by the camera, containing the bounding box which specifies where the stereo depth map will be analyzed.
  • bound_left, bound_top, bound_right, bound_bottom
    • These are the coordinates of the defined bounding box of the region of interest where obstacle depth is to be analyzed.
  • show_images, show_disparity, show_depth, show_points
    • These are Boolean flags controlling the visualization of any intermediate results such as images, disparity maps, depth maps, and annotated points.

Outputs

  • Depth Map

    • The main output of the script is the depth of the obstacle within the specified bounding box region.
  • Visualizations

    • If enabled, the script may display intermediate results such as disparity maps and depth maps for visualization purposes.

Click here to go BACK

⚠️ **GitHub.com Fallback** ⚠️