"""ORY-BOX / ORY-MINI: read a sensor and get tactile information, in one loop. The whole recommended flow: sensor -> StreamPipeline -> FrameSnapshot per frame One object reads the camera, pre-processes (stitch, grey, resize), computes the deformation field and the forces, and hands back one immutable snapshot per frame. There is no stage sequence to remember and no tuple to unpack by position. What you do NOT need to touch: the compute backend, the capture queue, the stitch mode, the low-pass filter, or the order in which the stages run. Those are the SDK's concern and ``StreamPipeline`` sequences them on three concurrent threads. If you later need that control, the manual form is preserved in ``examples/archive/basic_pipeline_manual.py``. Run: python examples/basic_pipeline.py # first camera, box config python examples/basic_pipeline.py -v 0 -c mini python examples/basic_pipeline.py -v recording.mp4 -c box python examples/basic_pipeline.py --no-show # headless: print only Keys: 'q' quit, 'r' reset the tracker. """ import argparse import os import sys # Set before OpenCV is imported: stops the capture backend applying its own transform on top of # the SDK's preprocessing. This is a capture detail, not a tuning knob you need to own - it is # here so the file can be copied as a whole. os.environ.setdefault("OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS", "0") import cv2 import orisys from _display import depth_to_color, draw_contact, draw_flow_arrows def main() -> int: parser = argparse.ArgumentParser(description="Read a sensor and print its tactile information.") parser.add_argument("-v", "--video", default="0", help="camera index or video file path (default: 0)") parser.add_argument("-c", "--config", default="box", help="product config: box, mini, finger, or a config path") parser.add_argument("--cal", default=None, help="force calibration file; omit to use the sensor bundle's own") parser.add_argument("--no-show", dest="show", action="store_false", help="do not open the display windows") parser.add_argument("-verbose", action="store_true", help="SDK verbose logs") args = parser.parse_args() # ------------------------------------------------------------------ create the object # That is the whole setup. The source may be a camera index or a video path. source = int(args.video) if args.video.isdigit() else args.video sensor = orisys.Sensor(source, config_name=args.config, cal_path=args.cal, verbose=args.verbose) # contact=True runs contact analysis. include=... selects which arrays each snapshot carries; # arrays that are not included are None, so you pay only for what you use. include = ("image", "flow", "depth", "centroid", "contour") if args.show else ("image",) if args.show: print("Keys: 'q' quit, 'r' reset tracker.") try: with orisys.StreamPipeline(sensor, sensor.get_img, contact=True, include=include) as pipe: while True: # ------------------------------------------------------ read one frame # Blocks until a frame newer than the last one is ready. Everything in the # snapshot belongs to the same frame. snap = pipe.wait() if snap is None: print("\nInput ended.") break # ------------------------------------------------------ use the results # The *_raw pair is always the unfiltered integrator output. touching = "yes" if snap.centroid is not None else "no" fn_unit, fx_unit, fy_unit = sensor.force_units print( f"FPS={snap.fps:5.1f} " f"normal={snap.normal_force:8.4f} {fn_unit} (raw {snap.normal_force_raw:10.2f}) " f"shear=({snap.shear_x:7.4f} {fx_unit}, {snap.shear_y:7.4f} {fy_unit}) " f"contact={touching} age={snap.age_ms:4.1f} ms", end="\r", ) if args.show and snap.image is not None: if snap.flow is not None: cv2.imshow("deformation", draw_flow_arrows( snap.image, snap.flow, threshold=2, grid_spacing=20)) if snap.contour is not None and snap.centroid is not None: cv2.imshow("contact", draw_contact( snap.image, snap.contour, snap.centroid)) colour = depth_to_color(snap.depth) if colour is not None: cv2.imshow("depth", colour) key = cv2.waitKey(1) & 0xFF if key == ord("q"): print("\nExiting.") break if key == ord("r"): # Queued onto the flow stage, so this is safe from the consumer loop. pipe.reset() print("\nTracker reset.") except KeyboardInterrupt: print("\nInterrupted.") finally: sensor.disconnect() if args.show: cv2.destroyAllWindows() return 0 if __name__ == "__main__": sys.exit(main())