From a008da5138f313eca7d8fc210ae4dc84107956a5 Mon Sep 17 00:00:00 2001 From: Daniel Matyas Date: Mon, 18 Sep 2023 15:40:06 +0300 Subject: [PATCH] python/examples/gesture_rec/process.py: Code cleanup Added more commentaries and made code look somewhat nicer. Signed-off-by: Daniel Matyas --- .../python/examples/gesture_rec/notebook.py | 14 +++++----- .../python/examples/gesture_rec/process.py | 26 ++++++++++++------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/bindings/python/examples/gesture_rec/notebook.py b/bindings/python/examples/gesture_rec/notebook.py index 336b7fdd..1526a343 100644 --- a/bindings/python/examples/gesture_rec/notebook.py +++ b/bindings/python/examples/gesture_rec/notebook.py @@ -122,7 +122,7 @@ def _start_video(self): distance_scale = 255.0 / camera_range tof_frame = tof.Frame() computation_delay_start = time.time() - daniels_process_results = [] + process_results = [] executor = concurrent.futures.ProcessPoolExecutor() while True: @@ -148,22 +148,22 @@ def _start_video(self): cv.namedWindow('Depth image', cv.WINDOW_AUTOSIZE) cv.imshow('Depth image', img) - # if daniels_process_results != []: - daniels_process_results = self.update_display(daniels_process_results) + # if process_results != []: + process_results = self.update_display(process_results) # Process image every 1s if (time.time() - computation_delay_start) <= 1: continue p = executor.submit(calc_process, depth_map) - daniels_process_results.append(p) + process_results.append(p) computation_delay_start = time.time() executor.shutdown() cv.destroyWindow("Depth image") - def update_display(self, daniels_process_results): + def update_display(self, process_results): to_delete = [] - processes = concurrent.futures.as_completed(daniels_process_results) + processes = concurrent.futures.as_completed(process_results) for p in processes: try: result, self.box_start_point, self.box_end_point = p.result() @@ -175,7 +175,7 @@ def update_display(self, daniels_process_results): self.update() to_delete.append(p) - return [p for p in daniels_process_results if p not in to_delete] + return [p for p in process_results if p not in to_delete] if __name__ == '__main__': diff --git a/bindings/python/examples/gesture_rec/process.py b/bindings/python/examples/gesture_rec/process.py index 6a3a2abc..afc4bef3 100644 --- a/bindings/python/examples/gesture_rec/process.py +++ b/bindings/python/examples/gesture_rec/process.py @@ -3,6 +3,7 @@ from scipy.spatial import ConvexHull from scipy.ndimage import distance_transform_edt +# These are empirical values, which were found by trial and error pixel_no_threshold = 200 analyzed_region_distance = 38.25 hand_radius_error = 1.9 @@ -25,9 +26,10 @@ def _display_result(self): # ============================================================================= def _depth_img_hist(self): # hist[0] = number of elements - # hist[1] = distance normalized to 255 + # hist[1] = distance normalized to 255 (this was done in notebook.py) counts, bins = np.histogram(self.depth_img.ravel(), 512) hist = [counts, bins] + start = 1 while start < (len(hist[0]) - 2) and hist[0][start] < pixel_no_threshold: start += 1 @@ -35,7 +37,8 @@ def _depth_img_hist(self): stop = int((hist[1][start] + analyzed_region_distance - hist[1][0]) / bin_step) - self.stop_distance = hist[1][stop] + self.stop_distance = hist[1][stop] + # Analyze the pixel only if it is closer than a threshold self.binary_img = (self.depth_img < self.stop_distance) * 1 # ============================================================================= @@ -45,6 +48,7 @@ def _detect_hand(self): props = measure.regionprops(labels) if len(props) == 0: raise Exception("No object found.") + props.sort(key=lambda x: x.area, reverse=True) self.max_area = props[0] points = props[0].filled_image @@ -55,8 +59,8 @@ def _detect_hand(self): # Compute the distance of non-zero points (hand) to the nearest zero point (background) self.dist_map = distance_transform_edt(points) + # Indices of hand center, i.e. the point farthest from the background self.hand_center = tuple(arr[0] for arr in np.where(self.dist_map == np.max(self.dist_map))) - self.radius = hand_radius_error * np.max(self.dist_map) # ============================================================================= @@ -71,21 +75,22 @@ def _count_fingers(self): [vertices[-1] - vertices[0]], axis=0) # distance bw 2 consecutive vertices + # In cdist variables the distance units are pixels cdist = np.sqrt(dist[:, 0] ** 2 + dist[:, 1] ** 2) - # This is just some formula invented by me. It is not optimal. - # It is used to make cdist_threshold inversely proportional to distance_threshold, - # while keeping it lower than 25 + # TODO: Use a better formula + # It is used to make cdist_threshold inversely proportional to stop_distance, + # while keeping it between 0 and 25 cdist_threshold = np.sqrt(1 - self.stop_distance / 255) * 25 - cdist = (cdist <= cdist_threshold) * 1 + cdist_bin = (cdist <= cdist_threshold) * 1 # Used to check whether a cdist smaller than the threshold # is following a cdist bigger than the threshold - cdist_diff = np.append(cdist[0:len(cdist) - 1] - cdist[1:len(cdist)], - [cdist[-1] - cdist[0]], axis=0) + cdist_diff = np.append(cdist_bin[0:len(cdist_bin) - 1] - cdist_bin[1:len(cdist_bin)], + [cdist_bin[-1] - cdist_bin[0]], axis=0) # Indices of vertices which correspond to fingertips - dist_idx = np.where((cdist_diff == -1) | ((cdist_diff == 0) & (cdist == 0))) + dist_idx = np.where((cdist_diff == -1) | ((cdist_diff == 0) & (cdist_bin == 0))) dist_idx = np.array(dist_idx) + 1 # dist_idx is a double list dist_idx = dist_idx[0] @@ -123,3 +128,4 @@ def detect_gesture(self): self.resultVar = "Found " + str(len(self.fingers[0])) + " extended fingers. Rock" else: self.resultVar = "Found " + str(len(self.fingers[0])) + " extended fingers. Unknown gesture" +