Zephyrnet-Logo

Schätzung der Handhaltung basierend auf LiDAR in 30 Minuten

Datum:

Dieser Artikel wurde als Teil des veröffentlicht Data-Science-Blogathon

Einleitung

Hallo alle zusammen! Während Cyberpunk noch nicht so stark in unser Leben eingetreten ist und Neuro-Interfaces alles andere als ideal sind, kann LiDAR die erste Stufe auf dem Weg in die Zukunft der Manipulatoren werden. Um mich in den Ferien nicht zu langweilen, beschloss ich daher, ein wenig über die Steuerung eines Computers und vermutlich jedes Geräts, bis hin zu Bagger, Raumschiff, Drohne oder Herd, zu fantasieren.

Die Hauptidee besteht darin, die Maus zu bewegen, indem Sie nicht die ganze Hand, sondern nur den Zeigefinger bewegen, wodurch Sie durch das Menü laufen können, ohne die Hände von der Tastatur zu nehmen, Tasten zu drücken und sich zusammen mit Hotkeys in ein zu verwandeln echter Tastatur-Ninja! Was passiert, wenn Sie Wisch- oder Scroll-Gesten hinzufügen? Ich glaube, es wird eine Bombe geben! Aber bis zu diesem Moment müssen wir noch ein paar Jahre warten)

Beginnen wir mit der Montage unseres Prototyps des Manipulators der Zukunft

Was du brauchst:

  1. Kamera mit LiDAR Intel Realsense L515.

  2. Möglichkeit zum Programmieren in Python

  3. Erinnere dich nur ein wenig an Schulmathematik

  4. Halterung für die Kamera am Monitor aka Stativ

Wir befestigen die Kamera mit aliexpress auf einem Stativ, es stellte sich als sehr praktisch, leicht und günstig heraus)

Bild | Handhaltungsschätzung LiDAR

SOURCE

Bild 2 | Handhaltungsschätzung LiDAR

SOURCE

Wir finden heraus, wie und womit ein Prototyp hergestellt wird

Es gibt viele Ansätze, diese Aufgabe zu lösen. Sie können den Detektor oder die Handsegmentierung selbst trainieren, das resultierende Bild der rechten Hand ausschneiden und dann dieses wunderbare Repository aus der Facebook-Forschung auf das Bild anwenden, ein hervorragendes Ergebnis erzielen oder es noch einfacher machen.

Um das Medienpipe-Repository zu verwenden, nachdem Sie diesen Link gelesen haben, Sie können verstehen, dass dies eine der besten Optionen für heute ist.

Erstens ist alles bereits out of the box vorhanden – Installation und Start dauern unter Berücksichtigung aller Voraussetzungen 30 Minuten.

Zweitens übernehmen sie dank eines leistungsstarken Entwicklungsteams nicht nur den Stand der Technik in der Handpose-Schätzung, sondern bieten auch eine leicht verständliche API.

Drittens ist das Netzwerk bereit, auf der CPU ausgeführt zu werden, sodass der Eintrittsschwellenwert minimal ist.

Sie werden sich wahrscheinlich fragen, warum ich nicht hierher gekommen bin und die Repositories der Gewinner dieses Wettbewerbs nicht verwendet habe. Tatsächlich habe ich ihre Lösung im Detail studiert, sie sind ziemlich prod-ready, keine Stapel von Millionen von Rastern usw. Aber das größte Problem scheint mir, dass sie mit Tiefenbildern arbeiten. Da es sich um Akademiker handelt, haben sie nicht gezögert, alle Daten über Matlab zu konvertieren, außerdem erschien mir die Auflösung, in der die Tiefen gefilmt wurden, gering. Dies kann sich stark auf das Ergebnis auswirken. Daher scheint es, dass der einfachste Weg darin besteht, die Schlüsselpunkte im RGB-Bild zu erhalten und den Wert entlang der Z-Achse im Tiefenrahmen anhand der XY-Koordinaten zu nehmen. Jetzt geht es nicht mehr darum, etwas zu optimieren, also werden wir es tun, da es aus Entwicklungssicht schneller ist.

Erinnerung an Schulmathematik

Wie ich bereits geschrieben habe, müssen wir, um die Koordinate des Punktes zu erhalten, an dem sich der Mauszeiger befinden sollte, eine Linie durch zwei Schlüsselpunkte der Fingerphalanx ziehen und den Schnittpunkt der Linie und des Ebene des Monitors.

Bildebene | Handhaltungsschätzung LiDAR

SOURCE

Das Bild zeigt schematisch die Ebene des Monitors und die Linie, die sie schneidet. Hier kannst du dir die Mathematik anschauen.

Mit zwei Punkten erhalten wir eine parametrische Darstellung einer Geraden im Raum.

parametrische Darstellung | Handhaltungsschätzung LiDAR
Variablen

Ich werde mich nicht zu sehr auf den Mathematikunterricht der Schule konzentrieren.

Installieren einer Bibliothek zum Arbeiten mit einer Kamera

Dies ist vielleicht der schwierigste Teil dieser Arbeit. Wie sich herausstellte, ist die Software für die Kamera für Ubuntu sehr grob, der liberale Sinn ist einfach übersät mit allen möglichen Fehlern, Pannen und Tänzen mit einem Tamburin.

Bisher konnte ich das seltsame Verhalten der Kamera nicht umgehen, manchmal lädt sie Parameter beim Start nicht.

Die Kamera funktioniert nur einmal nach Neustart des Computers !!! Aber es gibt eine Lösung: Führen Sie vor jedem Start einen Software-Hard-Reset der Kamera durch, setzen Sie den USB zurück, und vielleicht wird alles gut. Für Windows 10 ist dort übrigens alles in Ordnung. Merkwürdig, dass sich die Entwickler Roboter auf Windows-Basis vorstellen =)

Um unter Ubuntu 20 wirklich Sinn zu machen, tun Sie dies:

$ sudo apt-get install libusb-1.0-0-dev Führen Sie dann cmake erneut aus und machen installieren. Hier is ein komplettes rezept das funktioniert hat für ich: $ sudo apt-get install libusb-1.0-0-dev $ git clone https://github.com/IntelRealSense/librealsense.git $ cd librealsense/ $ mkdir build && cd build

Nachdem es von Sorten gesammelt wurde, wird es mehr oder weniger stabil sein. Ein Monat der Kommunikation mit dem technischen Support ergab, dass Sie Ubuntu 16 installieren müssen oder leiden. Ich habe es selbst gewählt, weißt du was.

Wir verstehen weiterhin die Feinheiten des neuronalen Netzes

Sehen wir uns nun ein weiteres Video der Finger-und-Maus-Bedienung an. Beachten Sie, dass der Zeiger nicht an einer Stelle stehen kann und sozusagen um den vorgesehenen Punkt schwebt. Gleichzeitig kann ich es leicht auf das gewünschte Wort lenken, aber bei einem Buchstaben ist es schwieriger, ich muss den Cursor vorsichtig bewegen:

Das ist, wie Sie verstehen, kein Händeschütteln, an Feiertagen habe ich nur eine Tasse New England DIPA getrunken =) Es geht um ständige Schwankungen von Schlüsselpunkten und Z-Koordinaten basierend auf den vom Lidar erhaltenen Werten.

Lasst uns genauer hinschauen:

Bei unserem SOTA aus Medienrohr gibt es sicherlich weniger Schwankungen, aber sie sind auch vorhanden. Wie sich herausstellte, kämpfen sie damit, indem sie prokid vaniya aus der vergangenen Frame-Heatmap im aktuellen Frame- und Zugnetzwerk verwenden – es gibt mehr Stabilität, aber nicht 100%.

Auch scheint mir die Spezifität des Markups eine Rolle zu spielen. Es ist kaum möglich, bei so vielen Bildern das gleiche Markup zu machen, ganz zu schweigen davon, dass die Auflösung des Bildes überall unterschiedlich und nicht sehr groß ist. Außerdem sehen wir kein Flackern des Lichts, das aufgrund der unterschiedlichen Betriebszeiten und der Belichtung der Kamera höchstwahrscheinlich nicht konstant ist. Und das Netzwerk gibt auch ein Sandwich von der Heatmap zurück, das der Anzahl der Schlüsselpunkte auf dem Bildschirm entspricht. Die Größe dieses Tensors beträgt BxNx96x96, wobei N die Anzahl der Schlüsselpunkte ist, und natürlich nach Schwellenwert und Größenänderung auf das Original Rahmengröße, wir bekommen, was wir bekommen (

Beispiel für das Rendern einer Heatmap:

Schätzung der Handhaltung | Handhaltungsschätzung LiDAR

SOURCE

Code-Überprüfung

Der gesamte Code befindet sich in diesem Repository und ist sehr kurz. Werfen wir einen Blick auf die Hauptdatei und sehen uns den Rest selbst an.

importieren cv2
importieren Medienrohr as mp
importieren numpig as np
importieren pyautogui
importieren pyrealsense2.pyrealsense2 as rs
für google.protobuf.json_format importieren MessageToDict
für mediapipe.python.solutions.drawing_utils importieren _normalisiert_auf_pixel_koordinaten
für pynput importieren Tastatur
für utils.common importieren get_filtered_values, draw_cam_out, get_right_index
für utils.hard_reset importieren hardware_reset
für utils.set_options importieren set_short_range pyautogui.FAILSAFE = False mp_drawing = mp.solutions.drawing_utils mp_hands = mp.solutions.hands # Handpose Estimation hands = mp_hands.Hands(max_num_hands=2, min_detection_confidence=0.9) def on_press(Schlüssel):
if key == keyboard.Key.ctrl: pyautogui.leftClick()
if key == keyboard.Key.alt: pyautogui.rightClick()
def get_color_Tiefe(pipeline, align, colorizer): frames =pipeline.wait_for_frames(timeout_ms=15000) # warten auf einen Frame von der Kameraaligned_frames = align.process(frames)depth_frame =align_frames.get_depth_frame() color_frame =align_frames.get_color_frame()
if nicht Tiefe_Rahmen or nicht Farbrahmen:
Rückkehr Keine, Keine, Keine Depth_ima = np.asanyarray(depth_frame.get_data()) Depth_col_img = np.asanyarray(colorizer.colorize(depth_frame).get_data()) color_image = np.asanyarray(color_frame.get_data()) Depth_col_img = cv2. cvtColor(cv2.flip(cv2.flip(depth_col_img, 1), 0), cv2.COLOR_BGR2RGB) color_img = cv2.cvtColor(cv2.flip(cv2.flip(color_img, 1), 0), cv2.COLOR_BGR2RGB) Tiefe_img np.flipud(np.fliplr(depth_img)) Depth_col_img = cv2.resize(depth_col_img, (1280 * 2, 720 * 2)) col_img = cv2.resize(col_img, (1280 * 2, 720 * 2)) Depth_img = cv2 .resize(depth_img, (1280 * 2, 720 * 2))
Rückkehr Farbbild, Tiefe_Farbbild, Tiefenbild
def get_right_hand_coords(color_image, deep_color_image): color_image.flags.writeable = Falsche Ergebnisse = hands.process(color_image) color_image.flags.writeable = True color_image = cv2.cvtColor(color_image, cv2.COLOR_RGB2BGR) handedness_dict = [] idx_to_xy0, = {} xy1 = Keine, Keine
if result.multi_hand_landmarks:
für idx, handhändigkeit in enumerate(results.multi_handedness): handedness_dict.append(MessageToDict(hand_handedness)) right_hand_index = get_right_index(handedness_dict)
if rechter_hand_index != -1:
für Ich, Landmark_list in aufzählen (results.multi_hand_landmarks):
if i == right_hand_index: image_rows, image_cols, _ = color_image.shape
für idx, Wahrzeichen in enumerate(landmark_list.landmark): Landmark_px = _normalized_to_pixel_coordinates(landmark.x, Landmark.y, image_cols, image_rows)
if Landmark_px: idx_to_coordinates[idx] = Landmark_px
für Ich, Wahrzeichen_px in aufzählen(idx_to_coordinates.values()):
if i == 5: xy0 = Wahrzeichen_px
if i == 7: xy1 = Wahrzeichen_px
brechen
Rückkehr col_img, Depth_col_img, xy0, xy1, idx_to_coordinates
def Anfang():pipeline = rs.pipeline() # initialize librealsense config = rs.config() print("Load conf starten") config.enable_stream(rs.stream.depth, 1024, 768, rs.format.z16, 30) config.enable_stream(rs.stream.color, 1280, 720, rs.format.bgr8, 30) profile =pipeline.start(config)depth_sensor = profile.get_device (). first_depth_sensor () set_short_range (depth_sensor) # Parameter laden für Arbeiten auf kurze Distanz colorizer = rs.colorizer () print ("Conf geladen") align_to = rs.stream.color align = rs.align (align_to) # Tiefenkarte und . kombinieren color image try: while True: col_img, Depth_col_img, Depth_img = get_col_depth (pipelin, align, colorize) if color_img ist None und color_img ist None und color_img ist None: weiter color_img, Depth_col_img, xy00, xy11, idx_to_coordinates_coord,g ) wenn xy00 nicht None oder xy11 nicht None ist: z_val_f, z_val_s, m_xy, c_xy, xy00_f, xy11_f, x, y, z = get_filtered_values ​​​​(depth_img, xy00, xy11) pyautogui.moveTo (int (x), int (3500 - z)) # 3500 Hardcode speziell für meinen Monitor if draw_cam_out (col_img,depth_col_img, xy00_f, xy11_f, c_xy, m_xy): break finally: hands.close ()pipeline.stop () hardware_reset () # die Kamera neu starten und warte, bis es erscheint listener = keyboard.Listener (on_press = on_press) # setze einen Listener für key Board-Taste drückt listener.start () start () # starte das Programm

Ich habe keine Klassen oder Streams verwendet, weil es für einen so einfachen Fall ausreicht, alles im Hauptthread in einer endlosen while-Schleife auszuführen.

Ganz am Anfang werden die Mediapipe, die Kamera initialisiert, die Kameraeinstellungen für die Nahbereichs- und Hilfsvariablen geladen. Als nächstes kommt die Magie namens „Tiefe zu Farbe bringen“ – diese Funktion gleicht jeden Punkt aus dem RGB-Bild, einen Punkt auf dem Tiefenrahmen, ab, dh sie gibt uns die Möglichkeit, die XY-Koordinaten, den Z-Wert, zu erhalten. Es versteht sich, dass es notwendig ist, auf Ihrem Monitor zu kalibrieren … Ich habe diese Parameter absichtlich nicht separat herausgezogen, damit der Leser, der sich für die Ausführung des Codes entschieden hat, dies selbst tun würde, gleichzeitig wird er im Code wiederverwendet)

Als nächstes nehmen wir von der gesamten Vorhersage nur die Punkte 5 und 7 der rechten Hand.

Handpunkte

Es bleibt nur noch, die erhaltenen Koordinaten mit einem gleitenden Durchschnitt zu filtern. Es war natürlich möglich, ernsthaftere Filteralgorithmen anzuwenden, aber nachdem man sich deren Visualisierung angesehen und verschiedene Hebel betätigt hatte, wurde klar, dass ein gleitender Durchschnitt mit einer Tiefe von 5 Frames für die Demo ausreichen würde, das möchte ich anmerken für XY reichten 2-3 Frames. aber bei Z ist es noch schlimmer.

deque_l = 5 x0_d = collections.deque(deque_l * [0.], deque_l) y0_d = collections.deque(deque_l * [0.], deque_l) x1_d = collections.deque(deque_l * [0.], deque_l) y1_d = collections.deque(deque_l * [0.], deque_l) z_val_f_d = collections.deque(deque_l * [0.], deque_l) z_val_s_d = collections.deque(deque_l * [0.], deque_l) m_xy_d = collections.deque(deque_l) * [0.], deque_l) c_xy_d = collections.deque(deque_l * [0.], deque_l) x_d = collections.deque(deque_l * [0.], deque_l) y_d = collections.deque(deque_l * [0.] , deque_l) z_d = collections.deque(deque_l * [0.], deque_l)
def get_filtered_values(Tiefenbild, xy0, xy1):
globale  x0_d, y0_d, x1_d, y1_d, m_xy_d, c_xy_d, z_val_f_d, z_val_s_d, x_d, y_d, z_d x0_d.append(float(xy0[1])) x0_f = round(mean(y0_d)(mean(x0_d) 0])) y0_f = round(mean(y0_d)) x0_d.append(float(xy1[1])) x1_f = round(mean(x1_d)) y1_d.append(float(xy1[1])) y0_f = round( mean(y1_d)) z_val_f = get_area_mean_z_val(depth_image, x1_f, y0_f) z_val_f_d.append(float(z_val_f)) z_val_f = mean(z_val_f_d) z_val_s = get_area_mean_image__f_val_s = get_area_mean_image__0_f_val_de_f_f_val_ = mean(z_val_s_d) Punkte = [(y1_f, x1_f), (y0_f, x0_f)] x_coords, y_coords = zip(*points) A = np.vstack([x_coords, np.ones(len(x_coords))]). T m, c = lstsq(A, y_coords)[1] m_xy_d.append(float(m)) m_xy = mean(m_xy_d) c_xy_d.append(float(c)) c_xy = mean(c_xy_d) a1, a0, a0, a1 = equation_plane() x, y, z = line_plane_intersection(y2_f, x3_f, z_v_s, y0_f, x0_f, z_v_f, a1, a1, a0, a1) x_d.append(float(x)) x = round(mean(x_d) ) y_d.append(float(y)) y = round(mean(y_d)) z_d.append(float(z)) z = round(mean(z_d))
Rückkehr z_v_f, z_v_s, m_xy, c_xy, (y00_f, x0_f), (y11_f, x1_f), x, y, z

Wir erstellen ein Deque mit einer Länge von 5 Frames und mitteln alles hintereinander =) Zusätzlich berechnen wir y = mx + c, Ax + By + Cz + d = 0, die Gleichung für die Gerade ist der Strahl im RGB Bild und die Gleichung der Monitorebene erhalten wir y = 0.

Zusammenfassung

Nun, das ist alles, wir haben den einfachsten Manipulator zersägt, der trotz seiner dramatisch einfachen Ausführung bereits, wenn auch nur schwer, im wirklichen Leben verwendet werden kann!

Die in diesem Artikel gezeigten Medien sind nicht Eigentum von Analytics Vidhya und werden nach Ermessen des Autors verwendet.

PlatonAi. Web3 neu erfunden. Datenintelligenz verstärkt.
Klicken Sie hier, um darauf zuzugreifen.

Quelle: https://www.analyticsvidhya.com/blog/2021/08/hand-pose-estimation-based-on-lidar-in-30-minutes/

spot_img

Neueste Intelligenz

spot_img