Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 192 additions & 0 deletions unit-tests/live/image-quality/test-image-quality.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# License: Apache 2.0. See LICENSE file in root directory.
# Copyright(c) 2023 Intel Corporation. All Rights Reserved.

# test:device D400*

import pyrealsense2 as rs
from rspy import test, log
import os
import math

# Start depth + color streams and go through the frame to make sure it is showing a depth image
# Color stream is only used to display the way the camera is facing
# Verify that the frame does indeed have variance - therefore it is showing a depth image

# In debug mode, an image with the frames found will be displayed and saved
# Make sure you have the required packages installed
DEBUG_MODE = False

# Defines how far in cm do pixels have to be, to be considered in a different distance
# for example, 10 for 10cm, will define the range 100-109 cm as one (as 100)
DETAIL_LEVEL = 10

dev = test.find_first_device_or_exit()

cfg = rs.config()
cfg.enable_stream(rs.stream.depth, rs.format.z16, 30)
if DEBUG_MODE:
cfg.enable_stream(rs.stream.color, rs.format.bgr8, 30)


def display_image(img):
"""
Display a given image and exits when the x button or esc key are pressed
"""
import cv2

window_title = "Output Stream"
cv2.imshow(window_title, img)
while cv2.getWindowProperty(window_title, cv2.WND_PROP_VISIBLE) > 0:
k = cv2.waitKey(33)
if k == 27: # Esc key to stop
cv2.destroyAllWindows()
break
elif k == -1: # normally -1 returned
pass


def frames_to_image(depth, color, save, display):
"""
This function gets depth and color frames, transforms them to an image (numpy array)
and then save and/or display

If color frame is given, it will also concatenate it with the depth frame before doing the given action
"""
import numpy as np
import cv2

colorizer = rs.colorizer()
depth_image = np.asanyarray(colorizer.colorize(depth).get_data())
img = depth_image

if color: # if color frame was successfully captured, merge it and the depth frame
from scipy.ndimage import zoom
color_image = np.asanyarray(color.get_data())
depth_rows, _, _ = depth_image.shape
color_rows, _, _ = color_image.shape
# resize the image with the higher resolution to look like the smaller one
if depth_rows < color_rows:
color_image = zoom(color_image, (depth_rows / color_rows, depth_rows / color_rows, 1))
elif color_rows < depth_rows:
depth_image = zoom(depth_image, (color_rows / depth_rows, color_rows / depth_rows, 1))
img = np.concatenate((depth_image, color_image), axis=1)

if save:
file_name = "output_stream.png"
log.i("Saved image in", os.getcwd() + "\\" + file_name)
cv2.imwrite(file_name, img)
if display:
display_image(img)


def get_frames(config, laser_enabled):
pipeline = rs.pipeline()
pipeline_profile = pipeline.start(config)

sensor = pipeline_profile.get_device().first_depth_sensor()
sensor.set_option(rs.option.emitter_enabled, 1 if laser_enabled else 0)

# to get a proper image, we sometimes need to wait a few frames, like when the camera is facing a light source
frames = pipeline.wait_for_frames()
for i in range(30):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment about this loop

frames = pipeline.wait_for_frames()
depth = frames.get_depth_frame()
color = frames.get_color_frame()
pipeline.stop()
return depth, color


def round_to_units(num):
"""
Input: Distance of a certain point, in meters
Output: Distance according to the given detail level, in cm
"""
in_cm = round(num, 2) * 100 # convert to cm
return math.floor(in_cm / DETAIL_LEVEL) * DETAIL_LEVEL # rounds the distance according to the given unit


def sort_dict(my_dict):
my_keys = list(my_dict.keys())
my_keys.sort()
sorted_dict = {i: my_dict[i] for i in my_keys}
return sorted_dict


def get_distances(depth):
MAX_METERS = 10 # max distance that can be detected, in meters
dists = {}
total = 0
for y in range(depth.get_height()):
for x in range(depth.get_width()):
dist = depth.get_distance(x, y)
if dist >= MAX_METERS: # out of bounds, assuming it is a junk value
continue

dist = round_to_units(dist) # round according to DETAIL_LEVEL

if dists.get(dist) is not None:
dists[dist] += 1
else:
dists[dist] = 1
total += 1

dists = sort_dict(dists) # for debug convenience
log.d("Distances detected in frame are:", dists)
return dists, total


def is_depth_meaningful(config, laser_enabled=True, save_image=False, show_image=False):
"""
Checks if the camera is showing a frame with a meaningful depth.
DETAIL_LEVEL is setting how close distances need to be, to be considered the same

returns true if frame shows meaningful depth
"""

depth, color = get_frames(config, laser_enabled)

if not depth:
log.f("Error getting depth frame")
return False
if DEBUG_MODE and not color:
log.e("Error getting color frame")

dists, total = get_distances(depth)

# save or display image (only possible through manual debugging)
if save_image or show_image:
frames_to_image(depth, color, save_image, show_image)

# Goes over the distances found, and checks if any distance is the same on more than 90% of the pixels
meaningful_depth = True
for key in dists:
if dists[key] > total*0.9:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment explaining this

meaningful_depth = False
break
num_blank_pixels = dists[0]
return meaningful_depth, num_blank_pixels


################################################################################################

test.start("Testing depth frame - laser ON -", dev.get_info(rs.camera_info.name))
res, laser_black_pixels = is_depth_meaningful(cfg, laser_enabled=True, save_image=DEBUG_MODE, show_image=DEBUG_MODE)
test.check(res is True)
test.finish()

################################################################################################

test.start("Testing depth frame - laser OFF -", dev.get_info(rs.camera_info.name))
res, no_laser_black_pixels = is_depth_meaningful(cfg, laser_enabled=False, save_image=DEBUG_MODE, show_image=DEBUG_MODE)
test.check(res is True)
test.finish()

################################################################################################

test.start("Testing less black pixels with the laser on")
test.check(no_laser_black_pixels > laser_black_pixels)
test.finish()

################################################################################################

test.print_results_and_exit()