From f04581b9a6226ab06ac5003dd5fc79832ab2e805 Mon Sep 17 00:00:00 2001 From: David Escalona Date: Wed, 4 Sep 2024 18:16:50 +0200 Subject: [PATCH] eiq-examples: extend recipe to download and transform models in the host Until now, the recipe only installed the binaries and some scripts in the target device. Then, user had to execute an script in the running system to download and transform the models for the demo, a process that could take up to 30 minutes. Also, a custom script was required to acomodate the environment settings before executing any demo. All of this was preventing us from creating out-of-the-box images including NPU demos for exhibitions and clients. With the new changes, the recipe now downloads and transforms all the models in the host machine, saving around 27 minutes and leaving the demos in the target ready to be executed. To ease the process, a launch script is included for all the demos to configure the environment as well as an auto-start service. https://onedigi.atlassian.net/browse/CCS-10 Co-authored-by: Isaac Hermida Signed-off-by: David Escalona --- .../python3-flatbuffers_2.0.7.bbappend | 3 + .../eiq-examples/eiq-examples_git.bbappend | 82 ++++ .../patches/0001-Customize-EiQ-demos.patch | 407 ++++++++++++++++++ ...-demo-to-use-the-landmark-full-model.patch | 22 + ...update-the-download-location-of-some.patch | 79 ++++ .../files/scripts/launch_eiq_demo.sh | 39 ++ .../files/service/eiqdemo.service | 10 + .../ethos-u-vela/ethos-u-vela_3.9.0.bbappend | 9 + 8 files changed, 651 insertions(+) create mode 100644 meta-digi-dey/dynamic-layers/meta-ml/recipes-devtools/flatbuffers/python3-flatbuffers_2.0.7.bbappend create mode 100644 meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/eiq-examples_git.bbappend create mode 100644 meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/files/patches/0001-Customize-EiQ-demos.patch create mode 100644 meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/files/patches/0002-dms-update-the-demo-to-use-the-landmark-full-model.patch create mode 100644 meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/files/patches/0003-download_models-update-the-download-location-of-some.patch create mode 100644 meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/files/scripts/launch_eiq_demo.sh create mode 100644 meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/files/service/eiqdemo.service create mode 100644 meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/ethos-u-vela/ethos-u-vela_3.9.0.bbappend diff --git a/meta-digi-dey/dynamic-layers/meta-ml/recipes-devtools/flatbuffers/python3-flatbuffers_2.0.7.bbappend b/meta-digi-dey/dynamic-layers/meta-ml/recipes-devtools/flatbuffers/python3-flatbuffers_2.0.7.bbappend new file mode 100644 index 000000000..71e737265 --- /dev/null +++ b/meta-digi-dey/dynamic-layers/meta-ml/recipes-devtools/flatbuffers/python3-flatbuffers_2.0.7.bbappend @@ -0,0 +1,3 @@ +# Copyright (C) 2024, Digi International Inc. + +BBCLASSEXTEND = "native" diff --git a/meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/eiq-examples_git.bbappend b/meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/eiq-examples_git.bbappend new file mode 100644 index 000000000..b9ca14bee --- /dev/null +++ b/meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/eiq-examples_git.bbappend @@ -0,0 +1,82 @@ +# Copyright (C) 2024, Digi International Inc. + +# Directory for models. +MODELS_DIR = "models" + +# Directory for transformation tasks. +VELA_MODELS_DIR = "vela_models" + +# The Vela native tool is required to transform the models. +DEPENDS += "ethos-u-vela-native" + +SRC_URI += " \ + file://patches/0001-Customize-EiQ-demos.patch \ + file://patches/0002-dms-update-the-demo-to-use-the-landmark-full-model.patch \ + file://patches/0003-download_models-update-the-download-location-of-some.patch \ + file://scripts/launch_eiq_demo.sh \ + file://service/eiqdemo.service \ +" + +# Custom task to download and transform the models using Vela. +do_download_transform_models() { + cd "${S}" + python3 "${S}/download_models.py" +} +do_download_transform_models[network] = "1" + +# Add the custom task to download and transform the models. +addtask do_download_transform_models after do_patch before do_install + +inherit systemd + +do_install () { + # Install scripts to /usr/bin. + install -d "${D}${bindir}/${PN}-${PV}/" + cp -r "${S}/dms" "${D}${bindir}/${PN}-${PV}/" + cp -r "${S}/face_recognition" "${D}${bindir}/${PN}-${PV}/" + cp -r "${S}/object_detection" "${D}${bindir}/${PN}-${PV}/" + cp -r "${S}/gesture_detection" "${D}${bindir}/${PN}-${PV}/" + + # Install the original models. + install -d "${D}${bindir}/${PN}-${PV}/${MODELS_DIR}" + for archive in "${S}/${MODELS_DIR}"/*.tflite; do + cp "${archive}" "${D}${bindir}/${PN}-${PV}/${MODELS_DIR}" + done + + # Install the transformed Vela models. + install -d "${D}${bindir}/${PN}-${PV}/${VELA_MODELS_DIR}" + for archive in "${S}/${VELA_MODELS_DIR}"/*.tflite; do + cp "${archive}" "${D}${bindir}/${PN}-${PV}/${VELA_MODELS_DIR}" + done + + # Install the launch script. + install -d ${D}${sysconfdir}/demos/scripts + install -m 755 ${WORKDIR}/scripts/launch_eiq_demo.sh ${D}${sysconfdir}/demos/scripts/ + # Create symlinks to execute each demo. + ln -sf launch_eiq_demo.sh ${D}${sysconfdir}/demos/scripts/launch_eiq_demo_dms.sh + ln -sf launch_eiq_demo.sh ${D}${sysconfdir}/demos/scripts/launch_eiq_demo_gesture_detection.sh + ln -sf launch_eiq_demo.sh ${D}${sysconfdir}/demos/scripts/launch_eiq_demo_face_recognition.sh + ln -sf launch_eiq_demo.sh ${D}${sysconfdir}/demos/scripts/launch_eiq_demo_object_detection.sh + + # Install the systemd service. + install -d ${D}${systemd_unitdir}/system + install -m 0644 ${WORKDIR}/service/eiqdemo.service ${D}${systemd_unitdir}/system/ +} + +SYSTEMD_SERVICE:${PN} = "eiqdemo.service" + +PACKAGES += " \ + ${PN}-service \ +" + +FILES:${PN} += " \ + ${bindir}/${PN}-${PV}/* \ + ${sysconfdir}/* \ +" + +FILES:${PN}-service = " \ + ${systemd_unitdir}/system/eiqdemo.service \ +" + +# Make this recipe available only for the CC93 platform. +COMPATIBLE_MACHINE = "(ccimx93-dvk)" diff --git a/meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/files/patches/0001-Customize-EiQ-demos.patch b/meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/files/patches/0001-Customize-EiQ-demos.patch new file mode 100644 index 000000000..e29c050d6 --- /dev/null +++ b/meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/files/patches/0001-Customize-EiQ-demos.patch @@ -0,0 +1,407 @@ +From: Isaac Hermida +Date: Fri, 15 Sep 2023 17:02:43 +0200 +Subject: [PATCH] Customize EiQ demos + +Just do the changes in the scripts to: + * Always use the NPU + * Get a better performance with USB Cameras + * Add option to use a bigger camera resolution + * Option to set the window in full screen + * Fix some exceptions of the demos + +Signed-off-by: Isaac Hermida +--- + dms/main.py | 79 +++++++++++++++++++++++++++------------ + face_recognition/main.py | 44 ++++++++++++++++++---- + gesture_detection/main.py | 32 ++++++++++++++-- + object_detection/main.py | 40 ++++++++++++++++---- + 4 files changed, 153 insertions(+), 42 deletions(-) + +diff --git a/dms/main.py b/dms/main.py +index 6e04dc3..8ba99b2 100644 +--- a/dms/main.py ++++ b/dms/main.py +@@ -14,10 +14,16 @@ from eye_landmark import EyeMesher + from face_landmark import FaceMesher + from utils import * + +-MODEL_PATH = pathlib.Path("../models/") +-DETECT_MODEL = "face_detection_front_128_full_integer_quant.tflite" +-LANDMARK_MODEL = "face_landmark_192_integer_quant.tflite" +-EYE_MODEL = "iris_landmark_quant.tflite" ++WIDTH=640 ++HEIGH=480 ++FLIP=None # None, skip, 0: Flip vertically, 1: Flip horizontally (around the y-axis), -1: Flip both vertically and horizontally ++FORMAT=0 # None, skip (YUYV, default), 0 MJPG (for usb camera) ++ ++# Always enforce the Ethos NPU, use the converted vela models ++MODEL_PATH = pathlib.Path("../vela_models/") ++DETECT_MODEL = "face_detection_front_128_full_integer_quant_vela.tflite" ++LANDMARK_MODEL = "face_landmark_192_integer_quant_vela.tflite" ++EYE_MODEL = "iris_landmark_quant_vela.tflite" + + # turn on camera + parser = argparse.ArgumentParser() +@@ -29,16 +35,31 @@ parser.add_argument( + parser.add_argument( + '-d', + '--delegate', +- default='', ++ default='/usr/lib/libethosu_delegate.so', + help='delegate path') ++parser.add_argument("-f", "--fullscreen", action="store_true", help='run on full screen mode') + args = parser.parse_args() + + if args.input.isdigit(): + cap_input = int(args.input) + else: + cap_input = args.input ++ ++# This pipeline for the OV5640 camera in case the other command fails ++# cap = cv2.VideoCapture("v4l2src device=%s ! imxvideoconvert_pxp ! video/x-raw,format=RGB16,width=%d,height=%d " \ ++# "! videoconvert ! appsink" % (args.input, WIDTH, HEIGH)) ++ + cap = cv2.VideoCapture(cap_input) ++cap.set(cv2.CAP_PROP_FRAME_WIDTH, WIDTH) ++cap.set(cv2.CAP_PROP_FRAME_HEIGHT, HEIGH) ++ ++if FORMAT == 0: ++ fourcc = cv2.VideoWriter_fourcc(*'MJPG') ++ cap.set(cv2.CAP_PROP_FOURCC, fourcc) ++ + ret, image = cap.read() ++if FLIP is not None: ++ image = cv2.flip(image, FLIP) + if not ret: + print("Can't read frame from source file ", args.input) + sys.exit(-1) +@@ -66,9 +87,9 @@ def draw_face_box(image, bboxes, landmarks, scores): + label_btmleft = bbox[:2].copy() + 10 + label_btmleft[0] += label_width + label_btmleft[1] += label_height +- cv2.rectangle(image, tuple(bbox[:2]), tuple(label_btmleft), color=(255, 0, 0), thickness=cv2.FILLED) +- cv2.putText(image, score_label, (bbox[0] + 5, label_btmleft[1] - 5), +- cv2.FONT_HERSHEY_SIMPLEX, fontScale=1.0, color=(255, 255, 255), thickness=2) ++ #cv2.rectangle(image, tuple(bbox[:2]), tuple(label_btmleft), color=(255, 0, 0), thickness=cv2.FILLED) ++ #cv2.putText(image, score_label, (bbox[0] + 5, label_btmleft[1] - 5), ++ #cv2.FONT_HERSHEY_SIMPLEX, fontScale=1.0, color=(255, 255, 255), thickness=2) + return image + + # detect single frame +@@ -111,8 +132,8 @@ def main(image): + right_eye_img = padded[right_box[0][1]:right_box[1][1], right_box[0][0]:right_box[1][0]] + left_eye_landmarks, left_iris_landmarks = eye_mesher.inference(left_eye_img) + right_eye_landmarks, right_iris_landmarks = eye_mesher.inference(right_eye_img) +- #cv2.rectangle(image_show, left_box[0], left_box[1], color=(255, 0, 0), thickness=2) +- #cv2.rectangle(image_show, right_box[0], right_box[1], color=(255, 0, 0), thickness=2) ++ cv2.rectangle(image_show, left_box[0], left_box[1], color=(255, 0, 0), thickness=2) ++ cv2.rectangle(image_show, right_box[0], right_box[1], color=(255, 0, 0), thickness=2) + left_eye_ratio = get_eye_ratio(left_eye_landmarks, image_show, left_box[0]) + right_eye_ratio = get_eye_ratio(right_eye_landmarks, image_show, right_box[0]) + +@@ -155,20 +176,32 @@ def main(image): + + + # endless loop ++window_name = "EiQ DMS demo" + while ret: +- image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) +- # detect single +- image_show = main(image) +- +- # put fps +- result = cv2.cvtColor(image_show, cv2.COLOR_RGB2BGR) +- +- # display the result +- cv2.imshow('demo', result) +- +- ret, image = cap.read() +- if cv2.waitKey(1) & 0xFF == ord('q'): +- break ++ try: ++ image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) ++ # detect single ++ image_show = main(image) ++ ++ # put fps ++ result = cv2.cvtColor(image_show, cv2.COLOR_RGB2BGR) ++ ++ cv2.namedWindow(window_name, cv2.WINDOW_NORMAL) ++ if args.fullscreen: ++ cv2.setWindowProperty(window_name, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN) ++ ++ # display the result ++ cv2.imshow(window_name, result) ++ ++ ret, image = cap.read() ++ if FLIP is not None: ++ image = cv2.flip(image, FLIP) ++ if cv2.waitKey(1) & 0xFF == ord('q'): ++ break ++ except Exception as err: ++ # Ignore exceptions ++ time.sleep(0.2) ++ print("Exception catched:%s\n... continuing with test" % repr(err)) + + time.sleep(2) + cap.release() +diff --git a/face_recognition/main.py b/face_recognition/main.py +index acc838e..33ffa71 100644 +--- a/face_recognition/main.py ++++ b/face_recognition/main.py +@@ -13,6 +13,11 @@ from face_detection import YoloFace + from face_recognition import Facenet + from face_database import FaceDatabase + ++WIDTH=640 ++HEIGH=480 ++FLIP=None # None, skip, 0: Flip vertically, 1: Flip horizontally (around the y-axis), -1: Flip both vertically and horizontally ++FORMAT=0 # None, skip (YUYV, default), 0 MJPG (for usb camera) ++ + parser = argparse.ArgumentParser() + parser.add_argument( + '-i', +@@ -22,12 +27,14 @@ parser.add_argument( + parser.add_argument( + '-d', + '--delegate', +- default='', ++ default='/usr/lib/libethosu_delegate.so', + help='delegate path') ++parser.add_argument("-f", "--fullscreen", action="store_true", help='run on full screen mode') + args = parser.parse_args() + +-detector = YoloFace("../models/yoloface_int8.tflite", args.delegate) +-recognizer = Facenet("../models/facenet_512_int_quantized.tflite", args.delegate) ++# Always enforce the Ethos NPU, use the converted vela models ++detector = YoloFace("../vela_models/yoloface_int8_vela.tflite", args.delegate) ++recognizer = Facenet("../vela_models/facenet_512_int_quantized_vela.tflite", args.delegate) + database = FaceDatabase() + + def ischar(c): +@@ -39,7 +46,7 @@ def get_inputs(img, msg): + cv2.rectangle(img, (0, 0), (img.shape[1], 40), (0, 0, 0), -1) + cv2.putText(img, msg + inputs, (30, 30), + cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) +- cv2.imshow('img', img) ++ cv2.imshow(window_name, img) + key = cv2.waitKey(20) & 0xFF + if key == 13 or key == 141: + break +@@ -68,13 +75,28 @@ if args.input.isdigit(): + cap_input = int(args.input) + else: + cap_input = args.input ++ ++# This pipeline for the OV5640 camera in case the other command fails ++# vid = cv2.VideoCapture("v4l2src device=%s ! imxvideoconvert_pxp ! video/x-raw,format=RGB16,width=%d,height=%d " \ ++# "! videoconvert ! appsink" % (args.input, WIDTH, HEIGH)) + vid = cv2.VideoCapture(cap_input) ++vid.set(cv2.CAP_PROP_FRAME_WIDTH, WIDTH) ++vid.set(cv2.CAP_PROP_FRAME_HEIGHT, HEIGH) ++ ++if FORMAT == 0: ++ fourcc = cv2.VideoWriter_fourcc(*'MJPG') ++ vid.set(cv2.CAP_PROP_FOURCC, fourcc) ++ + PADDING = 10 + tips = "Press 'a' to add person, 'd' to delete person, 'p' to print database" ++ ++window_name = "Face recognition Demo" + while True: + embeddings = None + + ret, img = vid.read() ++ if FLIP is not None: ++ img = cv2.flip(img, FLIP) + if (ret == False): + break + boxes = detector.detect(img) +@@ -97,12 +119,20 @@ while True: + + cv2.putText(img, tips, (30, 30), + cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 3) +- cv2.imshow('img', img) ++ ++ cv2.namedWindow(window_name, cv2.WINDOW_NORMAL) ++ if args.fullscreen: ++ cv2.setWindowProperty(window_name, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN) ++ ++ cv2.imshow(window_name, img) + key = cv2.waitKey(1) & 0xFF + if (key == ord('a')): + msg = "ADD. Please input name:" + name = get_inputs(img, msg) +- database.add_name(name, embeddings) ++ if embeddings: ++ database.add_name(name, embeddings) ++ else: ++ print ("Not a valid face, not adding user to database, ignoring...") + elif (key == ord('d')): + msg = "DEL. Please input name:" + name = get_inputs(img, msg) +@@ -110,7 +140,7 @@ while True: + elif (key == ord('p')): + names = ",".join(database.get_names()) + print_longtext(img, names + " Press any key to continue.") +- cv2.imshow('img', img) ++ cv2.imshow(window_name, img) + while cv2.waitKey(100) & 0xFF == 0xFF: + pass + +diff --git a/gesture_detection/main.py b/gesture_detection/main.py +index da83ce0..15b8597 100644 +--- a/gesture_detection/main.py ++++ b/gesture_detection/main.py +@@ -9,8 +9,9 @@ import time + import argparse + from hand_tracker import HandTracker + +-PALM_MODEL_PATH = "../models/palm_detection_builtin_256_integer_quant.tflite" +-LANDMARK_MODEL_PATH = "../models/hand_landmark_3d_256_integer_quant.tflite" ++# Always enforce the Ethos NPU, use the converted vela models ++PALM_MODEL_PATH = "../vela_models/palm_detection_builtin_256_integer_quant_vela.tflite" ++LANDMARK_MODEL_PATH = "../vela_models/hand_landmark_3d_256_integer_quant_vela.tflite" + ANCHORS_PATH = "anchors.csv" + + def draw_landmarks(points, frame): +@@ -52,15 +53,33 @@ parser.add_argument( + parser.add_argument( + '-d', + '--delegate', +- default='', ++ default='/usr/lib/libethosu_delegate.so', + help='delegate path') ++parser.add_argument("-f", "--fullscreen", action="store_true", help='run on full screen mode') + args = parser.parse_args() + + if args.input.isdigit(): + cap_input = int(args.input) + else: + cap_input = args.input ++ ++WIDTH=640 ++HEIGH=480 ++FLIP=None # None, skip, 0: Flip vertically, 1: Flip horizontally (around the y-axis), -1: Flip both vertically and horizontally ++FORMAT=0 # None, skip (YUYV, default), 0 MJPG (for usb camera) ++ ++# This pipeline for the OV5640 camera in case the other command fails ++# capture = cv2.VideoCapture("v4l2src device=%s ! imxvideoconvert_pxp ! video/x-raw,format=RGB16,width=%d,height=%d " \ ++# "! videoconvert ! appsink" % (args.input, WIDTH, HEIGH)) ++ + capture = cv2.VideoCapture(cap_input) ++capture.set(cv2.CAP_PROP_FRAME_WIDTH, WIDTH) ++capture.set(cv2.CAP_PROP_FRAME_HEIGHT, HEIGH) ++ ++if FORMAT == 0: ++ fourcc = cv2.VideoWriter_fourcc(*'MJPG') ++ capture.set(cv2.CAP_PROP_FOURCC, fourcc) ++ + ret, frame = capture.read() + if (frame is None): + print("Can't read frame from source file ", args.input) +@@ -68,11 +87,16 @@ if (frame is None): + + detector = HandTracker(PALM_MODEL_PATH, LANDMARK_MODEL_PATH, ANCHORS_PATH, args.delegate, box_shift=0.2, box_enlarge=1.3) + ++window_name = "Hand Gesture Demo" + while ret: + image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + points, _ = detector(image) + draw_landmarks(points, frame) +- cv2.imshow("hand", frame) ++ ++ cv2.namedWindow(window_name, cv2.WINDOW_NORMAL) ++ if args.fullscreen: ++ cv2.setWindowProperty(window_name, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN) ++ cv2.imshow(window_name, frame) + + ret, frame = capture.read() + if cv2.waitKey(1) & 0xFF == ord('q'): +diff --git a/object_detection/main.py b/object_detection/main.py +index 1356111..efa614e 100644 +--- a/object_detection/main.py ++++ b/object_detection/main.py +@@ -13,7 +13,13 @@ import argparse + + from labels import label2string + +-MODEL_PATH = "../models/ssd_mobilenet_v1_quant.tflite" ++WIDTH=640 ++HEIGH=480 ++FLIP=None # None, skip, 0: Flip vertically, 1: Flip horizontally (around the y-axis), -1: Flip both vertically and horizontally ++FORMAT=0 # None, skip (YUYV, default), 0 MJPG (for usb camera) ++ ++# Always enforce the Ethos NPU, use the converted vela models ++MODEL_PATH = "../vela_models/ssd_mobilenet_v1_quant_vela.tflite" + + parser = argparse.ArgumentParser() + parser.add_argument( +@@ -24,21 +30,31 @@ parser.add_argument( + parser.add_argument( + '-d', + '--delegate', +- default='', ++ default='/usr/lib/libethosu_delegate.so', + help='delegate path') ++parser.add_argument("-f", "--fullscreen", action="store_true", help='run on full screen mode') + args = parser.parse_args() + + if args.input.isdigit(): + cap_input = int(args.input) + else: + cap_input = args.input ++ ++# This pipeline for the OV5640 camera in case the other command fails ++# vid = cv2.VideoCapture("v4l2src device=%s ! imxvideoconvert_pxp ! video/x-raw,format=RGB16,width=%d,height=%d " \ ++# "! videoconvert ! appsink" % (args.input, WIDTH, HEIGH)) ++ + vid = cv2.VideoCapture(cap_input) ++vid.set(cv2.CAP_PROP_FRAME_WIDTH, WIDTH) ++vid.set(cv2.CAP_PROP_FRAME_HEIGHT, HEIGH) + +-if(args.delegate): +- ext_delegate = [tflite.load_delegate(args.delegate)] +- interpreter = tflite.Interpreter(model_path=MODEL_PATH, experimental_delegates=ext_delegate) +-else: +- interpreter = tflite.Interpreter(model_path=MODEL_PATH) ++if FORMAT == 0: ++ fourcc = cv2.VideoWriter_fourcc(*'MJPG') ++ vid.set(cv2.CAP_PROP_FOURCC, fourcc) ++ ++# Always enforce the Ethos NPU ++ext_delegate = [tflite.load_delegate(args.delegate)] ++interpreter = tflite.Interpreter(model_path=MODEL_PATH, experimental_delegates=ext_delegate) + interpreter.allocate_tensors() + + input_details = interpreter.get_input_details() +@@ -52,10 +68,13 @@ total_fps = 0 + total_time = 0 + + ret, frame = vid.read() ++if FLIP is not None: ++ frame = cv2.flip(frame, FLIP) + if (frame is None): + print("Can't read frame from source file ", args.input) + exit(0) + ++window_name = "Object Detection Demo" + while ret: + total_fps += 1 + loop_start = time.time() +@@ -94,9 +113,14 @@ while ret: + msg = "FPS:" + str(fps) + " Invoke time:" + str(invoke_time) + "ms" + cv2.putText(frame, msg, (0, 20), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 0), 3) + +- cv2.imshow("image", frame) ++ cv2.namedWindow(window_name, cv2.WINDOW_NORMAL) ++ if args.fullscreen: ++ cv2.setWindowProperty(window_name, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN) ++ cv2.imshow(window_name, frame) + + ret, frame = vid.read() ++ if FLIP is not None: ++ frame = cv2.flip(frame, FLIP) + if cv2.waitKey(1) & 0xFF == ord('q'): + break + diff --git a/meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/files/patches/0002-dms-update-the-demo-to-use-the-landmark-full-model.patch b/meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/files/patches/0002-dms-update-the-demo-to-use-the-landmark-full-model.patch new file mode 100644 index 000000000..4cdbaba1b --- /dev/null +++ b/meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/files/patches/0002-dms-update-the-demo-to-use-the-landmark-full-model.patch @@ -0,0 +1,22 @@ +From: David Escalona +Date: Thu, 5 Sep 2024 11:04:19 +0200 +Subject: [PATCH] dms: update the demo to use the landmark full model + +Signed-off-by: David Escalona +--- + dms/main.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dms/main.py b/dms/main.py +index 8ba99b2..e74b6be 100644 +--- a/dms/main.py ++++ b/dms/main.py +@@ -22,7 +22,7 @@ FORMAT=0 # None, skip (YUYV, default), 0 MJPG (for usb camera) + # Always enforce the Ethos NPU, use the converted vela models + MODEL_PATH = pathlib.Path("../vela_models/") + DETECT_MODEL = "face_detection_front_128_full_integer_quant_vela.tflite" +-LANDMARK_MODEL = "face_landmark_192_integer_quant_vela.tflite" ++LANDMARK_MODEL = "face_landmark_192_full_integer_quant_vela.tflite" + EYE_MODEL = "iris_landmark_quant_vela.tflite" + + # turn on camera diff --git a/meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/files/patches/0003-download_models-update-the-download-location-of-some.patch b/meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/files/patches/0003-download_models-update-the-download-location-of-some.patch new file mode 100644 index 000000000..7f75566dd --- /dev/null +++ b/meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/files/patches/0003-download_models-update-the-download-location-of-some.patch @@ -0,0 +1,79 @@ +From: David Escalona +Date: Wed, 11 Sep 2024 17:25:05 +0200 +Subject: [PATCH] download_models: update the download location of some models + +Signed-off-by: David Escalona +--- + download_models.py | 43 ++++++++++++++++++++++++++----------------- + 1 file changed, 26 insertions(+), 17 deletions(-) + +diff --git a/download_models.py b/download_models.py +index 3111f29..462f7b7 100644 +--- a/download_models.py ++++ b/download_models.py +@@ -35,21 +35,33 @@ def download_all_models(model_dir, vela_dir): + os.makedirs(model_dir, exist_ok=True) + os.makedirs(vela_dir, exist_ok=True) + ++ github_url = 'https://raw.githubusercontent.com/' ++ + #Download gesture models + #https://github.com/PINTO0309/PINTO_model_zoo +- url = 'https://drive.google.com/uc?export=download&&id=1yjWyXsac5CbGWYuHWYhhnr_9cAwg3uNI' +- path = os.path.join(model_dir, 'gesture_models.tar.gz') +- download_file('gesture recognition', url, path) +- decompress(path, model_dir) ++ #url = 'https://drive.google.com/uc?export=download&&id=1yjWyXsac5CbGWYuHWYhhnr_9cAwg3uNI' ++ #path = os.path.join(model_dir, 'gesture_models.tar.gz') ++ #download_file('gesture recognition', url, path) ++ #decompress(path, model_dir) ++ ++ #Download gesture models ++ #https://github.com/terryky/tflite_gles_app ++ url = github_url + 'terryky/tflite_gles_app/master/gl2handpose/handpose_model/' ++ file_name = 'palm_detection_builtin_256_integer_quant.tflite' ++ path = os.path.join(model_dir, file_name) ++ download_file('hand landmark', url + file_name, path) ++ file_name = 'hand_landmark_3d_256_integer_quant.tflite' ++ path = os.path.join(model_dir, file_name) ++ download_file('hand detection', url + file_name, path) + + #Download face recognition models + #https://github.com/imuncle/yoloface-50k +- url = 'https://raw.githubusercontent.com/imuncle/yoloface-50k/main/tflite/yoloface_int8.tflite' ++ url = github_url + 'imuncle/yoloface-50k/main/tflite/yoloface_int8.tflite' + path = os.path.join(model_dir, 'yoloface_int8.tflite') + download_file('face detection', url, path) + + #https://github.com/shubham0204/FaceRecognition_With_FaceNet_Android +- url = 'https://raw.githubusercontent.com/shubham0204/FaceRecognition_With_FaceNet_Android/master/app/src/main/assets/facenet_512_int_quantized.tflite' ++ url = github_url + 'shubham0204/FaceRecognition_With_FaceNet_Android/master/app/src/main/assets/facenet_512_int_quantized.tflite' + path = os.path.join(model_dir, 'facenet_512_int_quantized.tflite') + download_file('face recognition', url, path) + +@@ -67,17 +79,14 @@ def download_all_models(model_dir, vela_dir): + decompress(path, model_dir) + + #Download dms models +- #https://github.com/PINTO0309/PINTO_model_zoo +- url = "https://drive.google.com/uc?export=download&id=1YEAgUuHyJ7_fTY9XyDaALDidM6Sbzhd8" +- path = os.path.join(model_dir, 'dms_face_detection.tar.gz') +- download_file('DMS face detection', url, path) +- decompress(path, model_dir) +- +- #https://github.com/PINTO0309/PINTO_model_zoo +- url = "https://s3.ap-northeast-2.wasabisys.com/pinto-model-zoo/043_face_landmark/resources.tar.gz" +- path = os.path.join(model_dir, 'dms_face_landmark.tar.gz') +- download_file('DMS face landmark', url, path) +- decompress(path, model_dir) ++ #https://github.com/terryky/tflite_gles_app ++ url = github_url + 'terryky/tflite_gles_app/master/gl2facemesh/facemesh_model/' ++ file_name = 'face_detection_front_128_full_integer_quant.tflite' ++ path = os.path.join(model_dir, file_name) ++ download_file('DMS face detection', url + file_name, path) ++ file_name = 'face_landmark_192_full_integer_quant.tflite' ++ path = os.path.join(model_dir, file_name) ++ download_file('DMS face landmark', url + file_name, path) + + #https://github.com/PINTO0309/PINTO_model_zoo + url = "https://s3.ap-northeast-2.wasabisys.com/pinto-model-zoo/049_iris_landmark/resources.tar.gz" diff --git a/meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/files/scripts/launch_eiq_demo.sh b/meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/files/scripts/launch_eiq_demo.sh new file mode 100644 index 000000000..d719c2988 --- /dev/null +++ b/meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/files/scripts/launch_eiq_demo.sh @@ -0,0 +1,39 @@ +#!/bin/sh +#=============================================================================== +# +# Copyright (C) 2024 by Digi International Inc. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 as published by +# the Free Software Foundation. +# +# +# !Description: Launch script for NXP EIQ demos +# +#=============================================================================== + +DEFAULT_DEMO="dms" +DEMO=${DEFAULT_DEMO} + +# Try to extract the demo to launch from the script name. +DEMO_FROM_NAME="$(basename "${0}" | sed -n 's/^launch_eiq_demo_\(.*\)\.sh$/\1/p')" + +# Check if the demo to launch was passed as argument. +if [ -n "${1}" ]; then + DEMO=${1} +elif [ -n "${DEMO_FROM_NAME}" ]; then + DEMO=${DEMO_FROM_NAME} +fi + +# Build demo directory. +DEMO_DIR="/usr/bin/eiq-examples-git/${DEMO}" + +# Verify that the demo directory exists. +[ -d "${DEMO_DIR}" ] || { echo "Error: Demo ${DEMO} does not exist"; exit 1; } + +# Navigate to the demo folder +cd "${DEMO_DIR}" || exit + +# Execute the demo pre-configuring the display settings. +WAYLAND_DISPLAY=/run/wayland-0 DISPLAY=:0.0 XDG_RUNTIME_DIR=/run/user/0 python3 main.py -i /dev/video0 -f -d /usr/lib/libethosu_delegate.so & diff --git a/meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/files/service/eiqdemo.service b/meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/files/service/eiqdemo.service new file mode 100644 index 000000000..2189e5d4a --- /dev/null +++ b/meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/eiq-examples/files/service/eiqdemo.service @@ -0,0 +1,10 @@ +[Unit] +Description=Initialization of EiQ DMS demo +After=multi-user.target + +[Service] +Type=forking +ExecStart=/etc/demos/scripts/launch_eiq_demo.sh + +[Install] +WantedBy=multi-user.target diff --git a/meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/ethos-u-vela/ethos-u-vela_3.9.0.bbappend b/meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/ethos-u-vela/ethos-u-vela_3.9.0.bbappend new file mode 100644 index 000000000..a5d7eb597 --- /dev/null +++ b/meta-digi-dey/dynamic-layers/meta-ml/recipes-libraries/ethos-u-vela/ethos-u-vela_3.9.0.bbappend @@ -0,0 +1,9 @@ +# Copyright (C) 2024, Digi International Inc. + +BBCLASSEXTEND = "native" + +# Clear machine restriction. +COMPATIBLE_MACHINE:remove = "(mx93-nxp-bsp)" + +# Restrict the target build to specific machines. +COMPATIBLE_MACHINE:class-target = "(mx93-nxp-bsp)"