diff --git a/scripts/Depth2Disparity/depth2disparity.py b/scripts/Depth2Disparity/depth2disparity.py deleted file mode 100644 index 08c6bb232fc2aa7c70b4564cb33b96cd495e9606..0000000000000000000000000000000000000000 --- a/scripts/Depth2Disparity/depth2disparity.py +++ /dev/null @@ -1,105 +0,0 @@ -# takes 2 pixel coordinates and corresponding distance values - and a relative depth map -# caculates relative to absolute function - if linear -# applies function to relative depth map to get absolute depth map -# then convert to disparity map using depth disparity formula - - -import argparse -import numpy as np -import cv2 - -def relative2abs(rel_depth_map, coord1, dist1, coord2, dist2): - # Get the relative depth values at the two points - rel_value1 = rel_depth_map[coord1[1], coord1[0]] # (y, x) - rel_value2 = rel_depth_map[coord2[1], coord2[0]] - - # Calculate the linear transformation: depth = a * rel_depth + b - a = (dist2 - dist1) / (rel_value2 - rel_value1) - b = dist1 - a * rel_value1 - - # Apply the transformation to the entire relative depth map - abs_depth_map = a * rel_depth_map + b - - # this should not be normalised, the values in the array should equate to literal distances - return abs_depth_map - -def depth2disparity(abs_depth_map, baseline=0.176, disp_scale=2.0, disp_offset=-120): - - # Get image dimensions - height, width = abs_depth_map.shape - - # Calculate angular coordinates for each pixel - unit_w = 2.0 / width # Longitude step size - unit_h = 1.0 / height # Latitude step size - - # Initialize disparity map - disparity_map = np.zeros_like(abs_depth_map, dtype=np.float32) - - for i in range(height): - theta_t = i * unit_h * np.pi # Latitude angle for the row - - for j in range(width): - # Longitude angle (not strictly needed for disparity calculation) - phi = j * unit_w * np.pi - - # Retrieve the absolute depth (r_t) for this pixel - r_t = abs_depth_map[i, j] - - # Avoid invalid or infinite depth values - if r_t <= 0: - disparity_map[i, j] = 0 - continue - - try: - # Compute denominator with stability checks - epsilon = 1e-8 # Small value to prevent division instability - tan_theta_t = np.tan(theta_t) if np.abs(np.cos(theta_t)) > epsilon else np.sign(np.sin(theta_t)) * np.inf - - denominator = r_t * np.sin(theta_t) + r_t * np.cos(theta_t) * tan_theta_t - if denominator <= 0: - disparity_map[i, j] = 0 - continue - - # Calculate angular disparity (d) - angle_disp = np.arctan(baseline / denominator) - theta_t - - # Convert angular disparity to pixel disparity - disparity_map[i, j] = (angle_disp / (unit_h * np.pi) - disp_offset) * disp_scale - - except ZeroDivisionError: - disparity_map[i, j] = 0 - - return disparity_map - -if __name__ == "__main__": - # Set up argument parser - parser = argparse.ArgumentParser(description="Convert relative depth map to absolute depth map.") - parser.add_argument("rel_depth_map", type=str, help="Path to the relative depth map (image file).") - parser.add_argument("coord1", type=int, nargs=2, help="Pixel coordinates of the first point (x y).") - parser.add_argument("dist1", type=float, help="Absolute depth value at the first point.") - parser.add_argument("coord2", type=int, nargs=2, help="Pixel coordinates of the second point (x y).") - parser.add_argument("dist2", type=float, help="Absolute depth value at the second point.") - - # Parse arguments - args = parser.parse_args() - - # Load the relative depth map (dummy placeholder for now) - print(f"Loading relative depth map from: {args.rel_depth_map}") - rel_depth_map = cv2.imread(args.rel_depth_map, cv2.IMREAD_GRAYSCALE) # Load as grayscale - if rel_depth_map is None: - raise FileNotFoundError(f"Unable to load file: {args.rel_depth_map}") - - # Normalise depth map - rel_depth_map = rel_depth_map / 255.0 - - # Convert relative to absolute depth - abs_depth_map = relative2abs( - rel_depth_map, tuple(args.coord1), args.dist1, tuple(args.coord2), args.dist2 - ) - - # Convert depth map to disparity map - disparity_map = depth2disparity(abs_depth_map) - - # save disparity map - #cv2.imwrite(f"{args.rel_depth_map}-disparity.png", disparity_map) - \ No newline at end of file diff --git a/scripts/Depth2Disparity/depth_to_disparity.py b/scripts/Depth2Disparity/depth_to_disparity.py new file mode 100644 index 0000000000000000000000000000000000000000..e3294401beb46e68c201bb009d4e1aae3f0b5979 --- /dev/null +++ b/scripts/Depth2Disparity/depth_to_disparity.py @@ -0,0 +1,211 @@ +# takes 2 pixel coordinates and corresponding distance values - and a relative depth map +# caculates relative to absolute function - if linear +# applies function to relative depth map to get absolute depth map +# then convert to disparity map using depth disparity formula + + +import argparse +import numpy as np +import cv2 +import os +import matplotlib.pyplot as plt +import matplotlib +matplotlib.use("TkAgg") # Use TkAgg for GUI rendering +import warnings +warnings.filterwarnings( "ignore") + + + +class Depth2Disparity: + + def __init__(self, baseline=0.176, disp_scale=2.0, disp_offset=-120): + """ + Initialize the Depth2Disparity class with correct parameters. + + Parameters: + baseline (float): Baseline distance between the stereo cameras (in meters). + disp_scale (float): Scaling factor for disparity values. + disp_offset (float): Offset added to the disparity values. + """ + self.baseline = baseline + self.disp_scale = disp_scale + self.disp_offset = disp_offset + + + def execute(self, rel_depth_path, coord1, dist1, coord2, dist2): + + # Load the relative depth map + print(f"Loading relative depth map from: {rel_depth_path}") + rel_depth_map = cv2.imread(rel_depth_path, cv2.IMREAD_GRAYSCALE) # Load as grayscale + if rel_depth_map is None: + raise FileNotFoundError(f"Unable to load file: {rel_depth_path}") + + # Convert relative to absolute depth + print("Converting relative to absolute ...") + abs_depth_map = self._relative2abs(rel_depth_map, coord1, dist1, coord2, dist2) + + # Normalize depth map for saving + abs_depth_map_normalized = (abs_depth_map - abs_depth_map.min()) / (abs_depth_map.max() - abs_depth_map.min()) + abs_depth_map_normalized *= 255 + abs_depth_map_normalized = abs_depth_map_normalized.astype(np.uint8) + + # store absolute dpet map for debugging + abs_depth_path = os.path.join(os.path.dirname(rel_depth_path), "abs_depth.png") + cv2.imwrite(abs_depth_path, abs_depth_map_normalized) + print(f"Absolute depth map saved to: {abs_depth_path}\n") + + # Convert depth map to disparity map + print("Converting abs depth to disparity ...") + disparity_map, disparity_map_normalized = self._depth2disparity(abs_depth_map) + + # Determine the output path for the disparity map + disparity_dir = os.path.dirname(rel_depth_path) + disparity_base = "disp_map.png"#"depth_e.png" + disparity_path = os.path.join(disparity_dir, disparity_base) + + ## Check if an existing disparity map needs to be renamed + if os.path.exists(disparity_path): + old_depth_path = os.path.join(disparity_dir, "depth_e_old.png") + print(f"Renaming monodepth map to: {old_depth_path}") + # if pre-existing 'old depth map' exists, remove it + if os.path.exists(old_depth_path): + os.remove(old_depth_path) + os.rename(disparity_path, old_depth_path) + + # Save the new disparity map + print(f"Saving new disparity map to: {disparity_path}") + cv2.imwrite(disparity_path, disparity_map_normalized) + + + # Debug message for GUI or logs + print("Complete") + + # add typing to this method defintion + def _relative2abs(self, rel_depth_map, coord1: tuple, dist1: float, coord2: tuple, dist2: float): + + # Normalize depth map + print("Normalizing depth map...") + #rel_depth_map = rel_depth_map.astype(np.float32) / 255.0 + rel_depth_map = (rel_depth_map - rel_depth_map.min()) / (rel_depth_map.max() - rel_depth_map.min()) + + #plt.imshow(rel_depth_map, cmap='gray') + #plt.colorbar() + #plt.show() + + # Get the relative depth values at the two points + rel_value1 = float(rel_depth_map[coord1[1], coord1[0]]) # (y, x) + rel_value2 = float(rel_depth_map[coord2[1], coord2[0]]) + print(f"rel1: {rel_value1}, rel2: {rel_value2}, dist1: {dist1}, dist2: {dist2}") + + + # Calculate the linear transformation: depth = a * rel_depth + b + a = (dist2 - dist1) / (rel_value2 - rel_value1) + print("Calculated a: ", a) + b = dist1 - a * rel_value1 + print("Calculated b: ", b) + + #print("\tApplying transformation to entire depth map") + # Apply the transformation to the entire relative depth map + abs_depth_map = a * rel_depth_map + b + print("Applied transformation to entire depth map") + + #abs_depth_map = np.maximum(abs_depth_map, 0.01) + + plt.imshow(abs_depth_map, cmap='gray') + plt.colorbar() + plt.show() + + # this should not be normalised, the values in the array should equate to literal distances + return abs_depth_map + + def _depth2disparity(self, abs_depth_map): + height, width = abs_depth_map.shape + unit_h = 1.0 / height + disparity_map = np.zeros_like(abs_depth_map, dtype=np.float32) + + for i in range(height): + theta_t = i * unit_h * np.pi + for j in range(width): + r_t = max(abs_depth_map[i, j], 0.01) # Clamp small depths + if r_t <= 0: + disparity_map[i, j] = 0 + continue + try: + tan_theta_t = np.tan(theta_t) if np.abs(np.cos(theta_t)) > 1e-6 else np.sign(np.sin(theta_t)) * np.inf + denominator = max(r_t * np.sin(theta_t) + r_t * np.cos(theta_t) * tan_theta_t, 1e-6) + angle_disp = np.arctan(self.baseline / denominator) - theta_t + pixel_disp = (angle_disp / (unit_h * np.pi) - self.disp_offset) * self.disp_scale + disparity_map[i, j] = pixel_disp + except ZeroDivisionError: + disparity_map[i, j] = 0 + + # Normalize for visualization + disparity_map_normalized = (disparity_map - disparity_map.min()) / (disparity_map.max() - disparity_map.min()) + disparity_map_normalized *= 255 + disparity_map_normalized = disparity_map_normalized.astype(np.uint8) + + plt.imshow(disparity_map, cmap='gray') + plt.colorbar() + plt.show() + + return disparity_map, disparity_map_normalized + + #def __depth2disparity(self, abs_depth_map): + # """ + # Convert absolute depth map to disparity map for 360-degree images. + + # Parameters: + # abs_depth_map (numpy.ndarray): Absolute depth map with pixel values as distances in meters. + + # Returns: + # disparity_map (numpy.ndarray): Disparity map with values representing disparity. + # disparity_map_normalized (numpy.ndarray): Normalized disparity map for visualization. + # """ + # # Define parameters for the conversion + # height, width = abs_depth_map.shape + # + # # Latitude grid (θ_l) + # epsilon = 1e-6 + # latitudes = np.linspace(-np.pi / 2, np.pi / 2, height) + # latitudes_grid = np.tile(latitudes[:, np.newaxis], (1, width)) + + # # Clamp extreme latitudes to avoid instability + # latitudes_grid = np.clip(latitudes_grid, -np.pi/2 + epsilon, np.pi/2 - epsilon) + + # plt.imshow(latitudes_grid, cmap="jet") + # plt.colorbar() + # plt.title("Latitude Grid") + # plt.show() + + # self.baseline = 0.176 + # + # + # # Calculate angular disparity Δθ(x, y) + # # Δθ(x, y) = arctan(B / (r_t * sin(θ_l) + r_t * cos(θ_l) * tan(θ_l))) - θ_l + # epsilon = 1e-6 # Small value to prevent instability + # denominator = ( + # abs_depth_map * np.sin(latitudes_grid) + + # abs_depth_map * np.cos(latitudes_grid) * np.tan(latitudes_grid) + + # epsilon + # ) + # angular_disparity = np.arctan(self.baseline / denominator) - latitudes_grid + # + # self.disp_scale = 10 + # self.disp_offset = np.median(angular_disparity) + # + # # Normalize the disparity map for saving + # pixel_angle_scale = 1 # Assume scale factor is 1 if not provided (can be adjusted if needed) + # disparity_map = (angular_disparity / pixel_angle_scale - self.disp_offset) * self.disp_scale + + # # Normalize for visualization + # disparity_map_normalized = (disparity_map - disparity_map.min()) / (disparity_map.max() - disparity_map.min()) + # disparity_map_normalized *= 255 + # disparity_map_normalized = disparity_map_normalized.astype(np.uint8) + + # # Debugging visualization + # plt.imshow(disparity_map, cmap="jet") + # plt.colorbar() + # plt.title("Disparity Map (Debugging)") + # plt.show() + + # return disparity_map, disparity_map_normalized \ No newline at end of file diff --git a/scripts/debug_tool/utils/image_handlers.py b/scripts/debug_tool/utils/image_handlers.py index e1cdcdd8a8d34721cff7464c7f4c975c00869a9f..7ecd65c534ce861284f9c07e77e13265b9579f02 100644 --- a/scripts/debug_tool/utils/image_handlers.py +++ b/scripts/debug_tool/utils/image_handlers.py @@ -49,7 +49,7 @@ def load_and_resize_image(image_path, max_size=800): new_size = (int(width * ratio), int(height * ratio)) img = cv2.resize(img, new_size, interpolation=cv2.INTER_AREA) - return img + return img, ratio except Exception as e: raise Exception(f"Error loading image: {str(e)}") @@ -65,7 +65,7 @@ def update_preview(preview_label, image_path, max_size=300, error_callback=None) """ if image_path and os.path.exists(image_path): try: - img = load_and_resize_image(image_path, max_size) + img, ratio = load_and_resize_image(image_path, max_size) pixmap = convert_cv_to_pixmap(img) preview_label.setPixmap(pixmap) except Exception as e: diff --git a/scripts/simple_tab.py b/scripts/simple_tab.py index 1730f81d26edd85170154aa54341e4463fd27d00..f4b69bd9fb818e03122145e2531b060c78eeafdd 100644 --- a/scripts/simple_tab.py +++ b/scripts/simple_tab.py @@ -19,17 +19,22 @@ from debug_tool.tabs.material_tab import MaterialTab from debug_tool.tabs.edge_net_tab import EdgeNetTab from debug_tool.tabs.mbdnet_tab import MBDNetTab +from Depth2Disparity.depth_to_disparity import Depth2Disparity + class PipelineWorker(QThread): progress = pyqtSignal(str) finished = pyqtSignal(bool, str) def __init__(self, tab_instance, edgenet_flag=True,input_depth_flag=False, depth_map_path=None): super().__init__() - self.tab = tab_instance + + self.tab = tab_instance + self.distance_points = self.tab.distance_points # added distance points to class for disparity calculation + self.ratio = self.tab.ratio # image scale ratio for reversal self.edgenet_flag = edgenet_flag self.input_depth_flag = input_depth_flag self.depth_file_path = depth_map_path - + def run(self): try: self.run_pipeline() @@ -100,6 +105,26 @@ class PipelineWorker(QThread): self.progress.emit("Running depth estimation...") self.tab.depth.run_depth_estimation(False) print("Completed depth_estimation") # Debug print + + def run_depth_to_disparity(self): + print("\nStarting depth2disparity") + self.progress.emit("Converting mono-depthmap to disparity map...") + + # execute function to convert depth to disparity + real_depth_path = self.tab.depth.depth_output_path # depthmap path + coord1 = int(self.distance_points[0][0] / self.ratio), int(self.distance_points[0][1] / self.ratio) # x,y coordinates of point 1 + dist1 = float(self.distance_points[0][2]) # distance from camera to point 1 + coord2 = int(self.distance_points[1][0] / self.ratio), int(self.distance_points[1][1] / self.ratio) # x,y coordinates of point 2 + dist2 = float(self.distance_points[1][2]) # distance from camera to point 2 + print(f"{coord1}, {dist1}, {coord2}, {dist2}. Ratio: {self.ratio}") + + convert_d2d = Depth2Disparity() + print("Executing...") + convert_d2d.execute(real_depth_path, coord1, dist1, coord2, dist2) + + #print("Saved disparity map to output directory: " + real_depth_path) + self.progress.emit("Completed Disparity Map Conversion") + def run_material_recognition(self): print("Starting material_recognition") @@ -179,6 +204,7 @@ class PipelineWorker(QThread): self.copy_depth_map() else: self.run_depth_estimation() + #self.run_depth_to_disparity() self.run_material_recognition() if self.edgenet_flag: self.run_edge_net() @@ -186,7 +212,7 @@ class PipelineWorker(QThread): else: self.run_mbdnet() self.progress.emit("Pipeline completed!") - +9 class SimpleTab(QWidget): def __init__(self, config_reader): super().__init__() @@ -584,7 +610,7 @@ class SimpleTab(QWidget): update_preview(self.input_preview, file_path, error_callback=self.update_status) update_preview(self.distance_preview,file_path,max_size=1500) - pixmap = load_and_resize_image(file_path, 1500) + pixmap, self.ratio = load_and_resize_image(file_path, 1500) pixmap = convert_cv_to_pixmap(pixmap) self.distance_preview.setFixedSize(pixmap.size()) self.image_distance_group.show() @@ -662,6 +688,9 @@ class SimpleTab(QWidget): self.progress_bar.show() self.run_pipeline_btn.setEnabled(False) + # Get the distance points + self.distance_points = self.distance_preview.get_points() + self.pipeline_thread = PipelineWorker( self, edgenet_flag=self.ssc_model_combo.currentText() == "EdgeNet360", @@ -680,10 +709,6 @@ class SimpleTab(QWidget): #TODO: Add model selection for EdgeNet or MDBNet # Set the SSC model self.selected_model = self.ssc_model_combo.currentText() - - #TODO: Add distance points to the pipeline for depth estimation - # Get the distance points - self.distance_points = self.distance_preview.get_points() # Start the pipeline self.pipeline_thread.start()