diff --git a/.github/workflows/hil_testing_nightly.yml b/.github/workflows/hil_testing_nightly.yml new file mode 100644 index 0000000000..0fea5b0288 --- /dev/null +++ b/.github/workflows/hil_testing_nightly.yml @@ -0,0 +1,28 @@ +name: DepthAI Core HIL Testing Nightly + +on: + workflow_dispatch: + +permissions: + contents: read + +# Only allow latest nightly run on same branch to be tested +concurrency: + group: ci-nightly-tests-${{ github.ref }}-1 + cancel-in-progress: true + +jobs: + run_ptp_fsync_tests: + uses: ./.github/workflows/test_child.yml + with: + flavor: "vanilla" + job_prefix: "vanilla-nightly" + run_standard_tests: false + luxonis_os_versions_to_test: "[]" + luxonis_os_versions_to_test_fsync: "['1.33.0']" + luxonis_os_versions_to_test_ptp: "['1.33.0']" + secrets: + CONTAINER_REGISTRY: ${{ secrets.CONTAINER_REGISTRY }} + HIL_PAT_TOKEN: ${{ secrets.HIL_PAT_TOKEN }} + CI_TELEMETRY_URL: ${{ secrets.CI_TELEMETRY_URL }} + CI_TELEMETRY_API_KEY: ${{ secrets.CI_TELEMETRY_API_KEY }} diff --git a/.github/workflows/hil_testing_pull_request.yml b/.github/workflows/hil_testing_pull_request.yml new file mode 100644 index 0000000000..567da0fbe9 --- /dev/null +++ b/.github/workflows/hil_testing_pull_request.yml @@ -0,0 +1,62 @@ +name: DepthAI Core HIL Testing PR + +# Trigger on submitted reviews or when manually marked as testable +on: + pull_request_review: + types: [submitted] + pull_request: + types: [labeled] + +permissions: + contents: read + +# Cancel older runs for the same PR and trigger. +concurrency: + group: depthai-core-pr-testing-${{ github.event.pull_request.number }}-${{ github.event.review.state || github.event.label.name }} + cancel-in-progress: true + +jobs: + run_pr_tests: + name: Run Linux PR HIL tests + if: > + ( + github.event.review.state == 'approved' || + (github.event_name == 'pull_request' && github.event.label.name == 'testable') + ) && + ( + github.event.pull_request.base.ref == 'main' || + github.event.pull_request.base.ref == 'develop' + ) + uses: ./.github/workflows/test_child.yml + with: + flavor: "vanilla" + job_prefix: "pr-approved" + luxonis_os_versions_to_test: "['1.33.0']" + secrets: + CONTAINER_REGISTRY: ${{ secrets.CONTAINER_REGISTRY }} + HIL_PAT_TOKEN: ${{ secrets.HIL_PAT_TOKEN }} + CI_TELEMETRY_URL: ${{ secrets.CI_TELEMETRY_URL }} + CI_TELEMETRY_API_KEY: ${{ secrets.CI_TELEMETRY_API_KEY }} + + required_hil_test_report: + name: Required HIL test report + needs: [run_pr_tests] + if: > + always() && + ( + github.event.review.state == 'approved' || + (github.event_name == 'pull_request' && github.event.label.name == 'testable') + ) && + ( + github.event.pull_request.base.ref == 'main' || + github.event.pull_request.base.ref == 'develop' + ) + runs-on: ubuntu-latest + steps: + - name: Check required test results + run: | + if [[ "${{ needs.run_pr_tests.result }}" != "success" ]]; then + echo "Required HIL tests failed or did not complete." + echo "run_pr_tests: ${{ needs.run_pr_tests.result }}" + exit 1 + fi diff --git a/.github/workflows/hil_testing_schedule_develop.yml b/.github/workflows/hil_testing_schedule_develop.yml new file mode 100644 index 0000000000..a96191c5f3 --- /dev/null +++ b/.github/workflows/hil_testing_schedule_develop.yml @@ -0,0 +1,45 @@ +name: DepthAI Core HIL Testing Develop Schedule + +on: + schedule: + - cron: '0 1 * * *' # Every day at 01:00 UTC + - cron: '0 3 * * 6' # Saturdays at 03:00 UTC + +permissions: + actions: write + +jobs: + dispatch: + runs-on: ubuntu-latest + steps: + - name: Trigger develop HIL testing + if: github.event.schedule == '0 3 * * 6' + env: + GH_TOKEN: ${{ github.token }} + run: gh workflow run hil_testing_weekly.yml --repo "$GITHUB_REPOSITORY" --ref develop + + - name: Trigger nightly develop HIL testing + if: github.event.schedule == '0 1 * * *' + env: + GH_TOKEN: ${{ github.token }} + run: gh workflow run hil_testing_nightly.yml --repo "$GITHUB_REPOSITORY" --ref develop + + - name: Notify Slack + if: github.event.schedule == '0 3 * * 6' + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} + SLACK_BOT_CHANNEL_ID: ${{ secrets.SLACK_BOT_CHANNEL_ID_TEST }} + SCHEDULER_RUN_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} + run: | + MESSAGE="*Scheduled DepthAI Core HIL tests triggered* + Branch: develop + Workflow: DepthAI Core HIL Testing + Scheduler Run: $SCHEDULER_RUN_URL" + + jq -n --arg user_id "$SLACK_BOT_CHANNEL_ID" --arg message "$MESSAGE" \ + '{"channel": $user_id, "text": $message}' > payload.json + + curl -X POST https://slack.com/api/chat.postMessage \ + -H "Authorization: Bearer $SLACK_BOT_TOKEN" \ + -H "Content-type: application/json" \ + --data @payload.json diff --git a/.github/workflows/hil_testing_weekly.yml b/.github/workflows/hil_testing_weekly.yml new file mode 100644 index 0000000000..b813758425 --- /dev/null +++ b/.github/workflows/hil_testing_weekly.yml @@ -0,0 +1,227 @@ +name: DepthAI Core HIL Testing Weekly + +on: + workflow_dispatch: + inputs: + test_os: + description: Run Linux, Windows, and macOS tests + type: boolean + default: true + test_replay: + description: Run replay tests + type: boolean + default: true + test_sanitizers: + description: Run sanitizer tests + type: boolean + default: true + +permissions: + actions: read + contents: read + +# Only allow latest run on same branch to be tested +concurrency: + group: ci-tests-${{ github.ref }}-1 + cancel-in-progress: true + +jobs: + select_tests: + runs-on: ubuntu-latest + outputs: + test_all: ${{ steps.selection.outputs.test_all }} + steps: + - id: selection + run: echo "test_all=${{ !inputs.test_os && !inputs.test_replay && !inputs.test_sanitizers }}" >> "$GITHUB_OUTPUT" + + run_vanilla_tests: + needs: select_tests + if: inputs.test_os || needs.select_tests.outputs.test_all == 'true' + uses: ./.github/workflows/test_child.yml + with: + flavor: "vanilla" + luxonis_os_versions_to_test: "['1.20.5','1.27.1','1.33.0']" + luxonis_os_versions_to_test_rgb: "['1.33.0']" + luxonis_os_versions_to_test_usb: "['1.33.0']" + secrets: + CONTAINER_REGISTRY: ${{ secrets.CONTAINER_REGISTRY }} + HIL_PAT_TOKEN: ${{ secrets.HIL_PAT_TOKEN }} + CI_TELEMETRY_URL: ${{ secrets.CI_TELEMETRY_URL }} + CI_TELEMETRY_API_KEY: ${{ secrets.CI_TELEMETRY_API_KEY }} + + run_tsan_tests: + needs: select_tests + if: inputs.test_sanitizers || needs.select_tests.outputs.test_all == 'true' + uses: ./.github/workflows/test_child.yml + with: + flavor: "tsan" + luxonis_os_versions_to_test: "['1.33.0']" + secrets: + CONTAINER_REGISTRY: ${{ secrets.CONTAINER_REGISTRY }} + HIL_PAT_TOKEN: ${{ secrets.HIL_PAT_TOKEN }} + CI_TELEMETRY_URL: ${{ secrets.CI_TELEMETRY_URL }} + CI_TELEMETRY_API_KEY: ${{ secrets.CI_TELEMETRY_API_KEY }} + + run_asan-ubsan_tests: + needs: select_tests + if: inputs.test_sanitizers || needs.select_tests.outputs.test_all == 'true' + uses: ./.github/workflows/test_child.yml + with: + flavor: "asan-ubsan" + luxonis_os_versions_to_test: "['1.33.0']" + secrets: + CONTAINER_REGISTRY: ${{ secrets.CONTAINER_REGISTRY }} + HIL_PAT_TOKEN: ${{ secrets.HIL_PAT_TOKEN }} + CI_TELEMETRY_URL: ${{ secrets.CI_TELEMETRY_URL }} + CI_TELEMETRY_API_KEY: ${{ secrets.CI_TELEMETRY_API_KEY }} + + run_windows_tests: + needs: select_tests + if: inputs.test_os || needs.select_tests.outputs.test_all == 'true' + uses: ./.github/workflows/test_child_windows.yml + with: + luxonis_os_versions_to_test: "['1.33.0']" + luxonis_os_versions_to_test_usb: '["1.33.0"]' + secrets: + CONTAINER_REGISTRY: ${{ secrets.CONTAINER_REGISTRY }} + HIL_PAT_TOKEN: ${{ secrets.HIL_PAT_TOKEN }} + CI_TELEMETRY_URL: ${{ secrets.CI_TELEMETRY_URL }} + CI_TELEMETRY_API_KEY: ${{ secrets.CI_TELEMETRY_API_KEY }} + + run_replay_tests: + needs: select_tests + if: inputs.test_replay || needs.select_tests.outputs.test_all == 'true' + uses: ./.github/workflows/test_child.yml + with: + flavor: "vanilla" + job_prefix: "vanilla-replay" + enable_replay_tests: true + rvc2_timeout_minutes: 1440 + luxonis_os_versions_to_test: "['1.33.0']" + secrets: + CONTAINER_REGISTRY: ${{ secrets.CONTAINER_REGISTRY }} + HIL_PAT_TOKEN: ${{ secrets.HIL_PAT_TOKEN }} + CI_TELEMETRY_URL: ${{ secrets.CI_TELEMETRY_URL }} + CI_TELEMETRY_API_KEY: ${{ secrets.CI_TELEMETRY_API_KEY }} + + run_vanilla_mac_tests: + needs: select_tests + if: inputs.test_os || needs.select_tests.outputs.test_all == 'true' + uses: ./.github/workflows/test_child_mac.yml + with: + flavor: "vanilla" + luxonis_os_versions_to_test: "['1.33.0']" + luxonis_os_versions_to_test_usb: '["1.33.0"]' + secrets: + HIL_PAT_TOKEN: ${{ secrets.HIL_PAT_TOKEN }} + CI_TELEMETRY_URL: ${{ secrets.CI_TELEMETRY_URL }} + CI_TELEMETRY_API_KEY: ${{ secrets.CI_TELEMETRY_API_KEY }} + + notify_slack: + needs: + - select_tests + - run_vanilla_tests + - run_tsan_tests + - run_asan-ubsan_tests + - run_windows_tests + - run_replay_tests + - run_vanilla_mac_tests + if: always() + runs-on: ubuntu-latest + steps: + - name: Download JUnit reports + uses: actions/download-artifact@v4 + continue-on-error: true + with: + pattern: junit-reports-* + path: junit-reports + + - name: Send Slack test summary + continue-on-error: true + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} + SLACK_BOT_CHANNEL_ID: ${{ secrets.SLACK_BOT_CHANNEL_ID_TEST }} + NEEDS_JSON: ${{ toJson(needs) }} + RUN_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} + run: | + SUMMARY=$(jq -r ' + def job_label: + { + run_vanilla_tests: "Linux", + run_tsan_tests: "TSan", + "run_asan-ubsan_tests": "ASan/UBSan", + run_windows_tests: "Windows", + run_replay_tests: "Replay", + run_vanilla_mac_tests: "macOS" + }[.] // .; + + to_entries + | map(select(.key != "select_tests")) + | map("- " + (.key | job_label) + ": `" + .value.result + "`") + | join("\n") + ' <<< "$NEEDS_JSON") + + FAILED_COUNT=$(jq '[to_entries[].value.result | select(. == "failure" or . == "cancelled")] | length' <<< "$NEEDS_JSON") + CTEST_SUMMARY=$(python3 - <<'PY' + from pathlib import Path + from xml.etree import ElementTree as ET + import re + + passed = failed = total = 0 + failed_tests = [] + summary_re = re.compile(r"Passed=(\d+), Failed=(\d+), Total=(\d+)") + + for report in Path("junit-reports").rglob("*.xml"): + root = ET.parse(report).getroot() + for suite in root.findall(".//testsuite"): + for prop in suite.findall("./properties/property"): + if prop.get("name") != "ctest.summary": + continue + match = summary_re.fullmatch(prop.get("value", "")) + if match: + suite_passed, suite_failed, suite_total = map(int, match.groups()) + passed += suite_passed + failed += suite_failed + total += suite_total + for case in suite.findall("./testcase"): + if case.find("./failure") is not None: + failed_tests.append(f"{suite.get('name', report.stem)} - {case.get('name', 'unknown')}") + + if total == 0: + print("CTest reports: no summaries found") + else: + print(f"CTest totals: {passed} passed, {failed} failed, {total} total") + if failed_tests: + print("Failed tests:") + for test in failed_tests[:10]: + print(f"- {test}") + if len(failed_tests) > 10: + print(f"- ...and {len(failed_tests) - 10} more") + PY + ) + + if [ "$FAILED_COUNT" -gt 0 ]; then + RESULT="failed" + else + RESULT="passed" + fi + + MESSAGE="*DepthAI Core HIL tests finished* + Branch: $GITHUB_REF_NAME + Result: $RESULT + Run: $RUN_URL + + $SUMMARY" + if [ -n "$CTEST_SUMMARY" ]; then + MESSAGE="$MESSAGE + + $CTEST_SUMMARY" + fi + + jq -n --arg channel "$SLACK_BOT_CHANNEL_ID" --arg message "$MESSAGE" \ + '{"channel": $channel, "text": $message}' > payload.json + + curl -X POST https://slack.com/api/chat.postMessage \ + -H "Authorization: Bearer $SLACK_BOT_TOKEN" \ + -H "Content-type: application/json" \ + --data @payload.json diff --git a/.github/workflows/main.workflow.yml b/.github/workflows/main.workflow.yml index ec7f86bce7..669a1bae08 100644 --- a/.github/workflows/main.workflow.yml +++ b/.github/workflows/main.workflow.yml @@ -17,7 +17,7 @@ on: branches: - main - develop - types: [opened, reopened, labeled] + types: [labeled] jobs: @@ -155,7 +155,7 @@ jobs: - name: Install dependencies if: matrix.os == 'macos-latest' run: | - brew install opencv + brew install opencv@4 - name: Install dependencies if: matrix.os == 'ubuntu-latest' @@ -166,7 +166,7 @@ jobs: - name: Install dependencies if: matrix.os == 'windows-2022' run: | - choco install opencv + choco install opencv --version=4.13.0 --yes echo "OpenCV_DIR=C:\tools\opencv\build" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - name: Build @@ -203,7 +203,7 @@ jobs: - name: Install dependencies if: matrix.os == 'macos-latest' run: | - brew install opencv + brew install opencv@4 - name: Install dependencies if: matrix.os == 'ubuntu-latest' @@ -214,7 +214,7 @@ jobs: - name: Install dependencies if: matrix.os == 'windows-2022' run: | - choco install opencv + choco install opencv --version=4.13.0 --yes echo "OpenCV_DIR=C:\tools\opencv\build" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append echo "CMAKE_GENERATOR=Visual Studio 17 2022" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append echo "CMAKE_GENERATOR_PLATFORM=${{ matrix.platform }}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append diff --git a/.github/workflows/python-main.yml b/.github/workflows/python-main.yml index 9c660a6701..3109c2d2dd 100644 --- a/.github/workflows/python-main.yml +++ b/.github/workflows/python-main.yml @@ -18,7 +18,7 @@ on: branches: - main - develop - types: [opened, reopened, labeled] + types: [labeled] ################################### ################################### @@ -34,11 +34,18 @@ jobs: runs-on: ubuntu-latest outputs: should_run: ${{ steps.check.outputs.should_run }} + wheel_python_versions: ${{ steps.check.outputs.wheel_python_versions }} + wheel_python_sets: ${{ steps.check.outputs.wheel_python_sets }} + wheel_macos_runners: ${{ steps.check.outputs.wheel_macos_runners }} steps: - name: Evaluate trigger condition id: check + env: + CHECK_EVENT_NAME: ${{ github.event_name }} + CHECK_GITHUB_REF: ${{ github.ref }} run: | - EVENT_NAME="${{ github.event_name }}" + EVENT_NAME="$CHECK_EVENT_NAME" + GITHUB_REF="$CHECK_GITHUB_REF" RAW_LABELS='${{ toJson(github.event.pull_request.labels) }}' if [[ "$RAW_LABELS" == "null" || -z "$RAW_LABELS" ]]; then @@ -48,13 +55,26 @@ jobs: fi SHOULD_RUN="true" + WHEEL_PYTHON_VERSIONS='["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]' + WHEEL_PYTHON_SETS='["cp39-cp39", "cp310-cp310", "cp311-cp311", "cp312-cp312", "cp313-cp313", "cp314-cp314"]' + WHEEL_MACOS_RUNNERS='["macos-14"]' + + if [[ "$GITHUB_REF" == "refs/heads/main" || "$GITHUB_REF" == "refs/heads/develop" || "$GITHUB_REF" == refs/heads/release* || "$GITHUB_REF" == refs/tags/v* ]]; then + WHEEL_MACOS_RUNNERS='["macos-15-intel", "macos-14"]' + fi + if [[ "$EVENT_NAME" == "pull_request" ]]; then + WHEEL_PYTHON_VERSIONS='["3.14"]' + WHEEL_PYTHON_SETS='["cp314-cp314"]' if ! echo "$LABELS" | jq -r '.[].name' | grep -q "testable"; then SHOULD_RUN="false" fi fi echo "should_run=$SHOULD_RUN" >> "$GITHUB_OUTPUT" + echo "wheel_python_versions=$WHEEL_PYTHON_VERSIONS" >> "$GITHUB_OUTPUT" + echo "wheel_python_sets=$WHEEL_PYTHON_SETS" >> "$GITHUB_OUTPUT" + echo "wheel_macos_runners=$WHEEL_MACOS_RUNNERS" >> "$GITHUB_OUTPUT" # Job which builds docstrings for the rest of the wheel builds build-docstrings: @@ -216,11 +236,11 @@ jobs: # This job builds wheels for Windows x86_64 arch build-windows-x86_64: - needs: build-docstrings + needs: [precheck, build-docstrings] runs-on: windows-2022 strategy: matrix: - python-version: [3.9, '3.10', '3.11', '3.12', '3.13', '3.14'] + python-version: ${{ fromJson(needs.precheck.outputs.wheel_python_versions) }} python-architecture: [x64] # TODO(Morato) - re-enable x86 fail-fast: false env: @@ -306,11 +326,11 @@ jobs: # This job builds wheels for macOS arch build-macos: - needs: build-docstrings + needs: [precheck, build-docstrings] strategy: matrix: - python-version: [3.9, '3.10', '3.11', '3.12', '3.13', '3.14'] - os: [macos-15-intel, macos-14] + python-version: ${{ fromJson(needs.precheck.outputs.wheel_python_versions) }} + os: ${{ fromJson(needs.precheck.outputs.wheel_macos_runners) }} fail-fast: false runs-on: ${{ matrix.os }} env: @@ -384,14 +404,17 @@ jobs: path: bindings/python/wheelhouse/audited/* combine-macos-wheels: - needs: build-macos + needs: [precheck, build-macos] strategy: matrix: - os: [macos-15-intel, macos-14] + os: ${{ fromJson(needs.precheck.outputs.wheel_macos_runners) }} fail-fast: false runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: "3.14" - name: Download audited wheels uses: actions/download-artifact@v4 with: @@ -431,7 +454,7 @@ jobs: # This job builds wheels for x86_64 arch build-linux-x86_64: - needs: build-docstrings + needs: [precheck, build-docstrings] runs-on: ubuntu-latest container: image: quay.io/pypa/manylinux_2_28_x86_64:2025.11.10-2 @@ -439,7 +462,7 @@ jobs: PLAT: manylinux_2_28_x86_64 strategy: matrix: - python-set: ["cp39-cp39", "cp310-cp310", "cp311-cp311", "cp312-cp312", "cp313-cp313", "cp314-cp314"] + python-set: ${{ fromJson(needs.precheck.outputs.wheel_python_sets) }} env: DEPTHAI_BUILD_BASALT: ON DEPTHAI_BUILD_PCL: ON @@ -538,7 +561,7 @@ jobs: run: | set -euo pipefail - PYBIN="/opt/python/cp310-cp310/bin/python" + PYBIN="/opt/python/cp314-cp314/bin/python" # Resolve the exact dev version (includes commit hash) ver=$("$PYBIN" -c "import os,sys,pathlib; sys.path.insert(0, str(pathlib.Path('bindings/python').resolve())); import find_version as v; print(v.get_package_dev_version(os.environ['BUILD_COMMIT_HASH']))") @@ -556,7 +579,7 @@ jobs: # This job builds wheels for ARM64 arch build-linux-arm64: - needs: build-docstrings + needs: [precheck, build-docstrings] runs-on: ubuntu-24.04-arm timeout-minutes: 1440 # Set timeout to 24 hours container: @@ -565,7 +588,7 @@ jobs: PLAT: manylinux_2_28_aarch64 strategy: matrix: - python-set: ["cp39-cp39", "cp310-cp310", "cp311-cp311", "cp312-cp312", "cp313-cp313", "cp314-cp314"] + python-set: ${{ fromJson(needs.precheck.outputs.wheel_python_sets) }} env: # workaround required for cache@v3, https://github.com/actions/cache/issues/1428 VCPKG_FORCE_SYSTEM_BINARIES: "1" # Needed so vpckg can bootstrap itself @@ -667,7 +690,7 @@ jobs: run: | set -euo pipefail - PYBIN="/opt/python/cp310-cp310/bin/python" + PYBIN="/opt/python/cp314-cp314/bin/python" # Resolve the exact dev version (includes commit hash) ver=$("$PYBIN" -c "import os,sys,pathlib; sys.path.insert(0, str(pathlib.Path('bindings/python').resolve())); import find_version as v; print(v.get_package_dev_version(os.environ['BUILD_COMMIT_HASH']))") @@ -697,7 +720,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: "3.12" + python-version: "3.14" - name: Combine wheels run: | python -m pip install "delvewheel==1.12.1" # Install delvewheel for patching wheels diff --git a/.github/workflows/test.workflow.yml b/.github/workflows/test.workflow.yml deleted file mode 100644 index 14190f618c..0000000000 --- a/.github/workflows/test.workflow.yml +++ /dev/null @@ -1,176 +0,0 @@ -name: DepthAI Core HIL Testing - -on: - workflow_dispatch: - push: - branches: - - main - - develop - - 'release*' - tags: - - 'v*' - pull_request: - branches: - - main - - develop - types: [opened, reopened, labeled] - -# Only allow latest run on same branch to be tested -concurrency: - group: ci-tests-${{ github.ref }}-1 - cancel-in-progress: true - -jobs: - - precheck: - runs-on: ubuntu-latest - outputs: - linux: ${{ steps.check.outputs.linux }} - windows: ${{ steps.check.outputs.windows }} - mac: ${{ steps.check.outputs.mac }} - replay: ${{ steps.check.outputs.replay }} - sanitizers: ${{ steps.check.outputs.sanitizers }} - dcl: ${{ steps.check.outputs.dcl }} - env: - RAW_LABELS: ${{ toJson(github.event.pull_request.labels) }} - steps: - - name: Evaluate trigger condition - id: check - run: | - EVENT_NAME="${{ github.event_name }}" - - if [[ -z "$RAW_LABELS" || "$RAW_LABELS" == "null" ]]; then - LABELS="[]" - else - LABELS="$RAW_LABELS" - fi - - has_label() { - echo "$LABELS" | jq -r '.[].name' | grep -qx "$1" - } - - if [[ "$EVENT_NAME" != "pull_request" ]]; then - echo "linux=true" >> "$GITHUB_OUTPUT" - echo "windows=true" >> "$GITHUB_OUTPUT" - echo "mac=true" >> "$GITHUB_OUTPUT" - echo "replay=true" >> "$GITHUB_OUTPUT" - echo "sanitizers=true" >> "$GITHUB_OUTPUT" - echo "dcl=false" >> "$GITHUB_OUTPUT" - exit 0 - fi - - ALL=false - OS=false - - has_label "testable_all" && ALL=true - has_label "testable_os" && OS=true - - { has_label "testable" || [[ "$OS" == "true" || "$ALL" == "true" ]]; } \ - && LINUX=true || LINUX=false - - { [[ "$OS" == "true" || "$ALL" == "true" ]]; } \ - && WINDOWS=true || WINDOWS=false - - { [[ "$OS" == "true" || "$ALL" == "true" ]]; } \ - && MAC=true || MAC=false - - { has_label "testable_replay" || [[ "$ALL" == "true" ]]; } \ - && REPLAY=true || REPLAY=false - - { has_label "testable_sanitizers" || [[ "$ALL" == "true" ]]; } \ - && SANITIZERS=true || SANITIZERS=false - - has_label "testable_with_dcl" && DCL=true || DCL=false - - echo "linux=$LINUX" >> "$GITHUB_OUTPUT" - echo "windows=$WINDOWS" >> "$GITHUB_OUTPUT" - echo "mac=$MAC" >> "$GITHUB_OUTPUT" - echo "replay=$REPLAY" >> "$GITHUB_OUTPUT" - echo "sanitizers=$SANITIZERS" >> "$GITHUB_OUTPUT" - echo "dcl=$DCL" >> "$GITHUB_OUTPUT" - - - run_vanilla_tests: - needs: [precheck] - if: needs.precheck.outputs.linux == 'true' - uses: ./.github/workflows/test_child.yml - with: - flavor: "vanilla" - luxonis_os_versions_to_test: "['1.20.5','1.27.1','1.33.0']" - luxonis_os_versions_to_test_rgb: "['1.33.0']" - luxonis_os_versions_to_test_usb: "['1.33.0']" - luxonis_os_versions_to_test_fsync: "['1.33.0']" - luxonis_os_versions_to_test_ptp: "['1.33.0']" - secrets: - CONTAINER_REGISTRY: ${{ secrets.CONTAINER_REGISTRY }} - HIL_PAT_TOKEN: ${{ secrets.HIL_PAT_TOKEN }} - CI_TELEMETRY_URL: ${{ secrets.CI_TELEMETRY_URL }} - CI_TELEMETRY_API_KEY: ${{ secrets.CI_TELEMETRY_API_KEY }} - - run_tsan_tests: - needs: [precheck] - if: needs.precheck.outputs.sanitizers == 'true' - uses: ./.github/workflows/test_child.yml - with: - flavor: "tsan" - luxonis_os_versions_to_test: "['1.33.0']" - secrets: - CONTAINER_REGISTRY: ${{ secrets.CONTAINER_REGISTRY }} - HIL_PAT_TOKEN: ${{ secrets.HIL_PAT_TOKEN }} - CI_TELEMETRY_URL: ${{ secrets.CI_TELEMETRY_URL }} - CI_TELEMETRY_API_KEY: ${{ secrets.CI_TELEMETRY_API_KEY }} - - run_asan-ubsan_tests: - needs: [precheck] - if: needs.precheck.outputs.sanitizers == 'true' - uses: ./.github/workflows/test_child.yml - with: - flavor: "asan-ubsan" - luxonis_os_versions_to_test: "['1.33.0']" - secrets: - CONTAINER_REGISTRY: ${{ secrets.CONTAINER_REGISTRY }} - HIL_PAT_TOKEN: ${{ secrets.HIL_PAT_TOKEN }} - CI_TELEMETRY_URL: ${{ secrets.CI_TELEMETRY_URL }} - CI_TELEMETRY_API_KEY: ${{ secrets.CI_TELEMETRY_API_KEY }} - - run_windows_tests: - needs: [precheck] - if: needs.precheck.outputs.windows == 'true' - uses: ./.github/workflows/test_child_windows.yml - with: - luxonis_os_versions_to_test: "['1.33.0']" - luxonis_os_versions_to_test_usb: ${{ github.event_name != 'pull_request' && '["1.33.0"]' || '[]' }} - secrets: - CONTAINER_REGISTRY: ${{ secrets.CONTAINER_REGISTRY }} - HIL_PAT_TOKEN: ${{ secrets.HIL_PAT_TOKEN }} - CI_TELEMETRY_URL: ${{ secrets.CI_TELEMETRY_URL }} - CI_TELEMETRY_API_KEY: ${{ secrets.CI_TELEMETRY_API_KEY }} - - run_replay_tests: - needs: [precheck] - if: needs.precheck.outputs.replay == 'true' - uses: ./.github/workflows/test_child.yml - with: - flavor: "vanilla" - job_prefix: "vanilla-replay" - enable_replay_tests: true - rvc2_timeout_minutes: 1440 - luxonis_os_versions_to_test: "['1.33.0']" - secrets: - CONTAINER_REGISTRY: ${{ secrets.CONTAINER_REGISTRY }} - HIL_PAT_TOKEN: ${{ secrets.HIL_PAT_TOKEN }} - CI_TELEMETRY_URL: ${{ secrets.CI_TELEMETRY_URL }} - CI_TELEMETRY_API_KEY: ${{ secrets.CI_TELEMETRY_API_KEY }} - - run_vanilla_mac_tests: - needs: [precheck] - if: needs.precheck.outputs.mac == 'true' - uses: ./.github/workflows/test_child_mac.yml - with: - flavor: "vanilla" - luxonis_os_versions_to_test: "['1.33.0']" - luxonis_os_versions_to_test_usb: ${{ github.event_name != 'pull_request' && '["1.33.0"]' || '[]' }} - secrets: - HIL_PAT_TOKEN: ${{ secrets.HIL_PAT_TOKEN }} - CI_TELEMETRY_URL: ${{ secrets.CI_TELEMETRY_URL }} - CI_TELEMETRY_API_KEY: ${{ secrets.CI_TELEMETRY_API_KEY }} diff --git a/.github/workflows/test_child.yml b/.github/workflows/test_child.yml index 7b897c9f41..4f408db49c 100644 --- a/.github/workflows/test_child.yml +++ b/.github/workflows/test_child.yml @@ -17,6 +17,10 @@ on: required: false type: number default: 720 + run_standard_tests: + required: false + type: boolean + default: true luxonis_os_versions_to_test: required: true type: string @@ -64,7 +68,7 @@ jobs: fi BRANCH_NAME="${{ github.ref_name }}" PULL_REQUEST="false" - if [[ -n "${{ github.head_ref }}" ]]; then + if [[ -n "${{ github.event.pull_request.number }}" ]]; then BRANCH_NAME="${{ github.ref }}" PULL_REQUEST="true" fi @@ -79,6 +83,7 @@ jobs: linux_rvc2_test: needs: [build_docker_container] + if: inputs.run_standard_tests runs-on: ['self-hosted', 'testbed-runner'] timeout-minutes: ${{ inputs.rvc2_timeout_minutes }} steps: @@ -106,6 +111,7 @@ jobs: # Testing linux_rvc4_test: needs: [build_docker_container] + if: inputs.run_standard_tests && inputs.luxonis_os_versions_to_test != '[]' strategy: matrix: rvc4os: ${{ fromJson(inputs.luxonis_os_versions_to_test) }} @@ -135,6 +141,7 @@ jobs: linux_rvc4_usb_test: needs: [build_docker_container] + if: inputs.run_standard_tests && inputs.luxonis_os_versions_to_test_usb != '[]' strategy: matrix: rvc4os: ${{ fromJson(inputs.luxonis_os_versions_to_test_usb) }} @@ -160,6 +167,7 @@ jobs: linux_rvc4_rgb_test: needs: [build_docker_container] + if: inputs.run_standard_tests && inputs.luxonis_os_versions_to_test_rgb != '[]' strategy: matrix: rvc4os: ${{ fromJson(inputs.luxonis_os_versions_to_test_rgb) }} @@ -186,7 +194,7 @@ jobs: report_linux_test_results: name: Report Linux test results (${{ inputs.job_prefix || inputs.flavor }}) needs: [linux_rvc2_test, linux_rvc4_test,linux_rvc4_usb_test, linux_rvc4_rgb_test] - if: always() + if: ${{ always() && inputs.run_standard_tests }} runs-on: ubuntu-latest permissions: contents: read @@ -242,6 +250,7 @@ jobs: linux_rvc4_fsync_test: needs: [build_docker_container] + if: inputs.luxonis_os_versions_to_test_fsync != '[]' strategy: matrix: rvc4os: ${{ fromJson(inputs.luxonis_os_versions_to_test_fsync) }} @@ -265,6 +274,7 @@ jobs: linux_rvc4_ptp_test: needs: [build_docker_container] + if: inputs.luxonis_os_versions_to_test_ptp != '[]' strategy: matrix: rvc4os: ${{ fromJson(inputs.luxonis_os_versions_to_test_ptp) }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 706bbd7de8..20f58f7c8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,7 +75,7 @@ else() endif() # Create depthai project -project(depthai VERSION "3.8.0" LANGUAGES CXX C) +project(depthai VERSION "3.9.0" LANGUAGES CXX C) set(DEPTHAI_PRE_RELEASE_TYPE "") # Valid options are "alpha", "beta", "rc", "" set(DEPTHAI_PRE_RELEASE_VERSION "0") # Valid options are "0", "1", "2", ... @@ -434,6 +434,66 @@ set(TARGET_CORE_SOURCES src/modelzoo/Zoo.cpp ) +if(DEPTHAI_BUILD_BETA) + list(APPEND TARGET_CORE_SOURCES + src/beta/BetaNode.cpp + src/beta/datatype/Classifications.cpp + src/beta/datatype/Clusters.cpp + src/beta/datatype/ImgDetectionsFilterConfig.cpp + src/beta/datatype/Keypoints.cpp + src/beta/datatype/Lines.cpp + src/beta/datatype/Map2D.cpp + src/beta/datatype/Predictions.cpp + src/beta/datatype/ClassificationSequenceParserConfig.cpp + src/beta/datatype/FastSAMParserConfig.cpp + src/beta/datatype/HRNetParserConfig.cpp + src/beta/datatype/MLSDParserConfig.cpp + src/beta/datatype/MPPalmDetectionParserConfig.cpp + src/beta/datatype/MapOutputParserConfig.cpp + src/beta/datatype/PPTextDetectionParserConfig.cpp + src/beta/datatype/RFDETRParserConfig.cpp + src/beta/datatype/SCRFDParserConfig.cpp + src/beta/datatype/SuperAnimalParserConfig.cpp + src/beta/datatype/XFeatMonoParserConfig.cpp + src/beta/datatype/XFeatStereoParserConfig.cpp + src/beta/datatype/YuNetParserConfig.cpp + src/beta/node/ClassificationParser.cpp + src/beta/node/ClassificationSequenceParser.cpp + src/beta/node/EmbeddingsParser.cpp + src/beta/node/FastSAMParser.cpp + src/beta/node/HRNetParser.cpp + src/beta/node/ImageOutputParser.cpp + src/beta/node/ImgDetectionsFilter.cpp + src/beta/node/KeypointParser.cpp + src/beta/node/LaneDetectionParser.cpp + src/beta/node/MapOutputParser.cpp + src/beta/node/MLSDParser.cpp + src/beta/node/MPPalmDetectionParser.cpp + src/beta/node/PPTextDetectionParser.cpp + src/beta/node/RegressionParser.cpp + src/beta/node/RFDETRParser.cpp + src/beta/node/SCRFDParser.cpp + src/beta/node/SuperAnimalParser.cpp + src/beta/node/XFeatMonoParser.cpp + src/beta/node/XFeatStereoParser.cpp + src/beta/node/YuNetParser.cpp + src/beta/properties/ImgDetectionsFilterProperties.cpp + src/beta/utilities/Classification/ClassificationUtils.cpp + src/beta/utilities/Detection/DetectionUtils.cpp + src/beta/utilities/Detection/MaskUtils.cpp + src/beta/utilities/FastSAM/FastSAMUtils.cpp + src/beta/utilities/Keypoints/KeypointsUtils.cpp + src/beta/utilities/LaneDetection/LaneDetectionUtils.cpp + src/beta/utilities/MediaPipe/MediaPipeUtils.cpp + src/beta/utilities/MLSD/MLSDUtils.cpp + src/beta/utilities/PPText/PPTextUtils.cpp + src/beta/utilities/RFDETR/RFDETRUtils.cpp + src/beta/utilities/SCRFD/SCRFDUtils.cpp + src/beta/utilities/XFeat/XFeatUtils.cpp + src/beta/utilities/YuNet/YuNetUtils.cpp + ) +endif() + if(DEPTHAI_ENABLE_EVENTS_MANAGER) list(APPEND TARGET_CORE_SOURCES src/utility/EventsManager.cpp @@ -465,6 +525,7 @@ if(DEPTHAI_DYNAMIC_CALIBRATION_SUPPORT) endif() set(TARGET_OPENCV_SOURCES + src/opencv/ColorizeDepthFrame.cpp src/opencv/ImgFrame.cpp src/pipeline/node/host/Display.cpp src/pipeline/node/host/HostCamera.cpp @@ -524,6 +585,9 @@ add_library(${TARGET_CORE_NAME} ${TARGET_CORE_SOURCES}) add_library("${PROJECT_NAME}::${TARGET_CORE_ALIAS}" ALIAS ${TARGET_CORE_NAME}) # Specify that we are building core target_compile_definitions(${TARGET_CORE_NAME} PUBLIC DEPTHAI_TARGET_CORE) +if(DEPTHAI_BUILD_BETA) + target_compile_definitions(${TARGET_CORE_NAME} PUBLIC DEPTHAI_HAVE_BETA) +endif() # Specifies name of generated IMPORTED target (set to alias) set_target_properties(${TARGET_CORE_NAME} PROPERTIES EXPORT_NAME ${TARGET_CORE_ALIAS}) # Add to list of targets to export and install diff --git a/README.md b/README.md index f12bf42d84..d9cdfce3ee 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,28 @@ cmake -S. -Bbuild -D'DEPTHAI_BUILD_EXAMPLES=ON' cmake --build build ``` +## Beta features +The `beta` namespace is a staging area for experimental DepthAI features. It +allows new features to be developed and iterated on quickly before they are +promoted to the main `depthai` namespace. + +Beta features are well-developed, but minor API and behavioral changes may occur between DepthAI releases without notice. + +In C++, beta nodes are available under `dai::beta::node`: + +```cpp +auto node = pipeline.create(); +``` + +In Python, they are available under `dai.beta.node`: + +```python +node = pipeline.create(dai.beta.node.ImgDetectionsFilter) +``` + +On-device execution of Beta nodes is supported only on RVC4. If running Beta nodes on RVC2, DepthAI +automatically configures beta nodes to run on the host. + ## Dependencies - CMake >= 3.20 - C/C++17 compiler @@ -195,49 +217,49 @@ For a full list of options, see `cmake/depthaiOptions.cmake` file. The following environment variables can be set to alter default behavior of the library without having to recompile -| Environment variable | Description | -|--------------|-----------| -| DEPTHAI_LEVEL | Sets logging verbosity, 'trace', 'debug', 'info', 'warn', 'error' and 'off' | -| XLINK_LEVEL | Sets logging verbosity of XLink library, 'debug'. 'info', 'warn', 'error', 'fatal' and 'off' | -| DEPTHAI_INSTALL_SIGNAL_HANDLER | Set to 0 to disable installing Backward signal handler for stack trace printing | -| DEPTHAI_DEBUGGER | Enables debugger-friendly behavior. `ON` is equivalent to `DEPTHAI_WATCHDOG=0` and `DEPTHAI_RPC_READ_TIMEOUT=0`. Explicit values of those variables still take precedence. | -| DEPTHAI_WATCHDOG | Sets device watchdog timeout. Useful for debugging (`DEPTHAI_WATCHDOG=0`), to prevent device reset while the process is paused. | -| DEPTHAI_WATCHDOG_INITIAL_DELAY | Specifies delay after which the device watchdog starts. | -| DEPTHAI_SEARCH_TIMEOUT | Specifies timeout in milliseconds for device searching in blocking functions. | -| DEPTHAI_CONNECT_TIMEOUT | Specifies timeout in milliseconds for establishing a connection to a given device. | -| DEPTHAI_BOOTUP_TIMEOUT | Specifies timeout in milliseconds for waiting the device to boot after sending the binary. | -| DEPTHAI_RECONNECT_TIMEOUT | Specifies timeout in milliseconds for reconnecting to a device after a connection loss. If set to 0, reconnect is disabled. | -| DEPTHAI_PROTOCOL | Restricts default search to the specified protocol. Options: `any`, `usb`, `tcpip`, `tcpshd`. | -| DEPTHAI_PLATFORM | Restricts default search to the specified platform. Options: `any`, `rvc2`, `rvc3`, `rvc4`. | -| DEPTHAI_RPC_READ_TIMEOUT | Specifies timeout in milliseconds for reading RPC responses. If 0, wait indefinitely. | -| DEPTHAI_RPC_WRITE_TIMEOUT | Specifies timeout in milliseconds for writing RPC requests. If 0, wait indefinitely. | -| DEPTHAI_DEVICE_MXID_LIST | Restricts default search to the specified MXIDs. Accepts comma separated list of MXIDs. Lists filter results in an "AND" manner and not "OR" | -| DEPTHAI_DEVICE_ID_LIST | Alias to MXID list. Lists filter results in an "AND" manner and not "OR" | -| DEPTHAI_DEVICE_NAME_LIST | Restricts default search to the specified NAMEs. Accepts comma separated list of NAMEs. Lists filter results in an "AND" manner and not "OR". It also looks for NAMEs outside of the host's subnet in case of tcpip. | -| DEPTHAI_DEVICE_BINARY | Overrides device Firmware binary. Mostly for internal debugging purposes. | -| DEPTHAI_DEVICE_RVC4_FWP | Overrides device RVC4 Firmware binary. Mostly for internal debugging purposes. | -| DEPTHAI_BOOTLOADER_BINARY_USB | Overrides device USB Bootloader binary. Mostly for internal debugging purposes. | -| DEPTHAI_BOOTLOADER_BINARY_ETH | Overrides device Network Bootloader binary. Mostly for internal debugging purposes. | -| DEPTHAI_ALLOW_FACTORY_FLASHING | Internal use only | -| DEPTHAI_LIBUSB_ANDROID_JAVAVM | JavaVM pointer that is passed to libusb for rootless Android interaction with devices. Interpreted as decimal value of uintptr_t | -| DEPTHAI_CACHE_DIR | Overrides the default DepthAI cache root directory. Default: macOS `~/Library/Caches/depthai`, Windows `%LOCALAPPDATA%\\depthai\\cache`, Linux `${XDG_CACHE_HOME:-~/.cache}/depthai` | -| DEPTHAI_CRASHDUMP | Directory in which to save the crash dump. Automatic crash dump collection is disabled if set to 0. | -| DEPTHAI_CRASHDUMP_TIMEOUT | Specifies the duration in milliseconds to wait for device reboot when obtaining a crash dump. Automatic crash dump collection is disabled if set to 0. | -| DEPTHAI_TELEMETRY | Telemetry is enabled by default. Set to `0` or `false` to disable event capture. | -| DEPTHAI_TELEMETRY_URL | Overrides the telemetry capture URL. | -| DEPTHAI_TELEMETRY_API_KEY | Overrides the telemetry API key. | -| DEPTHAI_DISABLE_CRASHDUMP_COLLECTION | Disables automatic crash dump collection used to improve the library | -| DEPTHAI_HUB_EVENTS_BASE_URL | URL for events of the Luxonis Hub | -| DEPTHAI_HUB_API_KEY | API key for the Luxonis Hub | -| DEPTHAI_ZOO_INTERNET_CHECK | (Default) 1 - perform internet check, if available, download the newest model version 0 - skip internet check and use cached model | -| DEPTHAI_ZOO_INTERNET_CHECK_TIMEOUT | (Default) 1000 - timeout in milliseconds for the internet check | -| DEPTHAI_ZOO_CACHE_PATH | (Default) `${DEPTHAI_CACHE_DIR}/models` - Folder where cached zoo models are stored | -| DEPTHAI_ZOO_MODELS_PATH | (Default) depthai_models - Folder where zoo model description files are stored | -| DEPTHAI_RECORD | Enables holistic record to the specified directory. | -| DEPTHAI_REPLAY | Replays holistic replay from the specified file or directory. | -| DEPTHAI_PROFILING | Enables runtime profiling of data transfer between the host and connected devices. Set to 1 to enable. Requires DEPTHAI_LEVEL=debug or lower to print. | -| DEPTHAI_PIPELINE_DEBUGGING | Enables pipeline debugging with state dumps. DEPTHAI_LEVEL=trace is required to print the state dumps. | -| DEPTHAI_AUTOCALIBRATION | Runs recalibration of the stereo pair and, by default, flashes successful calibration to non-volatile memory (EEPROM). `CONTINUOUS`: runs check repetitively; `ON_START`: runs calibration only at the start of the pipeline; `OFF`: no recalibration. The same mode can be configured from code with `pipeline.setAutoCalibrationMode(...)`. If this environment variable is set, it overrides the pipeline-set value. AutoCalibration currently initializes only for stereo inputs at 1280x800. | +| Environment variable | Description | +| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| DEPTHAI_LEVEL | Sets logging verbosity, 'trace', 'debug', 'info', 'warn', 'error' and 'off' | +| XLINK_LEVEL | Sets logging verbosity of XLink library, 'debug'. 'info', 'warn', 'error', 'fatal' and 'off' | +| DEPTHAI_INSTALL_SIGNAL_HANDLER | Set to 0 to disable installing Backward signal handler for stack trace printing | +| DEPTHAI_DEBUGGER | Enables debugger-friendly behavior. `ON` is equivalent to `DEPTHAI_WATCHDOG=0` and `DEPTHAI_RPC_READ_TIMEOUT=0`. Explicit values of those variables still take precedence. | +| DEPTHAI_WATCHDOG | Sets device watchdog timeout. Useful for debugging (`DEPTHAI_WATCHDOG=0`), to prevent device reset while the process is paused. | +| DEPTHAI_WATCHDOG_INITIAL_DELAY | Specifies delay after which the device watchdog starts. | +| DEPTHAI_SEARCH_TIMEOUT | Specifies timeout in milliseconds for device searching in blocking functions. | +| DEPTHAI_CONNECT_TIMEOUT | Specifies timeout in milliseconds for establishing a connection to a given device. | +| DEPTHAI_BOOTUP_TIMEOUT | Specifies timeout in milliseconds for waiting the device to boot after sending the binary. | +| DEPTHAI_RECONNECT_TIMEOUT | Specifies timeout in milliseconds for reconnecting to a device after a connection loss. If set to 0, reconnect is disabled. | +| DEPTHAI_PROTOCOL | Restricts default search to the specified protocol. Options: `any`, `usb`, `tcpip`, `tcpshd`. | +| DEPTHAI_PLATFORM | Restricts default search to the specified platform. Options: `any`, `rvc2`, `rvc3`, `rvc4`. | +| DEPTHAI_RPC_READ_TIMEOUT | Specifies timeout in milliseconds for reading RPC responses. If 0, wait indefinitely. | +| DEPTHAI_RPC_WRITE_TIMEOUT | Specifies timeout in milliseconds for writing RPC requests. If 0, wait indefinitely. | +| DEPTHAI_DEVICE_MXID_LIST | Restricts default search to the specified MXIDs. Accepts comma separated list of MXIDs. Lists filter results in an "AND" manner and not "OR" | +| DEPTHAI_DEVICE_ID_LIST | Alias to MXID list. Lists filter results in an "AND" manner and not "OR" | +| DEPTHAI_DEVICE_NAME_LIST | Restricts default search to the specified NAMEs. Accepts comma separated list of NAMEs. Lists filter results in an "AND" manner and not "OR". It also looks for NAMEs outside of the host's subnet in case of tcpip. | +| DEPTHAI_DEVICE_BINARY | Overrides device Firmware binary. Mostly for internal debugging purposes. | +| DEPTHAI_DEVICE_RVC4_FWP | Overrides device RVC4 Firmware binary. Mostly for internal debugging purposes. | +| DEPTHAI_BOOTLOADER_BINARY_USB | Overrides device USB Bootloader binary. Mostly for internal debugging purposes. | +| DEPTHAI_BOOTLOADER_BINARY_ETH | Overrides device Network Bootloader binary. Mostly for internal debugging purposes. | +| DEPTHAI_ALLOW_FACTORY_FLASHING | Internal use only | +| DEPTHAI_LIBUSB_ANDROID_JAVAVM | JavaVM pointer that is passed to libusb for rootless Android interaction with devices. Interpreted as decimal value of uintptr_t | +| DEPTHAI_CACHE_DIR | Overrides the default DepthAI cache root directory. Default: macOS `~/Library/Caches/depthai`, Windows `%LOCALAPPDATA%\\depthai\\cache`, Linux `${XDG_CACHE_HOME:-~/.cache}/depthai` | +| DEPTHAI_CRASHDUMP | Directory in which to save the crash dump. Automatic crash dump collection is disabled if set to 0. | +| DEPTHAI_CRASHDUMP_TIMEOUT | Specifies the duration in milliseconds to wait for device reboot when obtaining a crash dump. Automatic crash dump collection is disabled if set to 0. | +| DEPTHAI_TELEMETRY | Telemetry is enabled by default. Set to `0` or `false` to disable event capture. | +| DEPTHAI_TELEMETRY_URL | Overrides the telemetry capture URL. | +| DEPTHAI_TELEMETRY_API_KEY | Overrides the telemetry API key. | +| DEPTHAI_DISABLE_CRASHDUMP_COLLECTION | Disables automatic crash dump collection used to improve the library | +| DEPTHAI_HUB_EVENTS_BASE_URL | URL for events of the Luxonis Hub | +| DEPTHAI_HUB_API_KEY | API key for the Luxonis Hub | +| DEPTHAI_ZOO_INTERNET_CHECK | (Default) 1 - perform internet check, if available, download the newest model version 0 - skip internet check and use cached model | +| DEPTHAI_ZOO_INTERNET_CHECK_TIMEOUT | (Default) 1000 - timeout in milliseconds for the internet check | +| DEPTHAI_ZOO_CACHE_PATH | (Default) `${DEPTHAI_CACHE_DIR}/models` - Folder where cached zoo models are stored | +| DEPTHAI_ZOO_MODELS_PATH | (Default) depthai_models - Folder where zoo model description files are stored | +| DEPTHAI_RECORD | Enables holistic record to the specified directory. | +| DEPTHAI_REPLAY | Replays holistic replay from the specified file or directory. | +| DEPTHAI_PROFILING | Enables runtime profiling of data transfer between the host and connected devices. Set to 1 to enable. Requires DEPTHAI_LEVEL=debug or lower to print. | +| DEPTHAI_PIPELINE_DEBUGGING | Enables pipeline debugging with state dumps. DEPTHAI_LEVEL=trace is required to print the state dumps. | +| DEPTHAI_AUTOCALIBRATION | Runs recalibration of the stereo pair and, by default, flashes successful calibration to non-volatile memory (EEPROM). `CONTINUOUS`: runs check repetitively; `ON_START`: runs calibration only at the start of the pipeline; `OFF`: no recalibration. The same mode can be configured from code with `pipeline.setAutoCalibrationMode(...)`. If this environment variable is set, it overrides the pipeline-set value. AutoCalibration currently initializes only for stereo inputs at 1280x800. | ## Running tests diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index e447bf896b..4bbd39c2bb 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -168,6 +168,51 @@ set(SOURCE_LIST src/remote_connection/RemoteConnectionBindings.cpp src/utility/EventsManagerBindings.cpp ) + +if(DEPTHAI_BUILD_BETA) + list(APPEND SOURCE_LIST + src/beta/datatype/ClassificationsBindings.cpp + src/beta/datatype/ClustersBindings.cpp + src/beta/datatype/KeypointsBindings.cpp + src/beta/datatype/LinesBindings.cpp + src/beta/datatype/Map2DBindings.cpp + src/beta/datatype/PredictionsBindings.cpp + src/beta/datatype/ClassificationSequenceParserConfigBindings.cpp + src/beta/datatype/FastSAMParserConfigBindings.cpp + src/beta/datatype/HRNetParserConfigBindings.cpp + src/beta/datatype/MLSDParserConfigBindings.cpp + src/beta/datatype/MPPalmDetectionParserConfigBindings.cpp + src/beta/datatype/MapOutputParserConfigBindings.cpp + src/beta/datatype/PPTextDetectionParserConfigBindings.cpp + src/beta/datatype/RFDETRParserConfigBindings.cpp + src/beta/datatype/SCRFDParserConfigBindings.cpp + src/beta/datatype/SuperAnimalParserConfigBindings.cpp + src/beta/datatype/XFeatMonoParserConfigBindings.cpp + src/beta/datatype/XFeatStereoParserConfigBindings.cpp + src/beta/datatype/YuNetParserConfigBindings.cpp + src/beta/node/ClassificationParserBindings.cpp + src/beta/node/ClassificationSequenceParserBindings.cpp + src/beta/node/EmbeddingsParserBindings.cpp + src/beta/node/FastSAMParserBindings.cpp + src/beta/node/HRNetParserBindings.cpp + src/beta/node/ImageOutputParserBindings.cpp + src/beta/node/ImgDetectionsFilterBindings.cpp + src/beta/node/KeypointParserBindings.cpp + src/beta/node/LaneDetectionParserBindings.cpp + src/beta/node/MapOutputParserBindings.cpp + src/beta/node/MLSDParserBindings.cpp + src/beta/node/MPPalmDetectionParserBindings.cpp + src/beta/node/PPTextDetectionParserBindings.cpp + src/beta/node/RegressionParserBindings.cpp + src/beta/node/RFDETRParserBindings.cpp + src/beta/node/SCRFDParserBindings.cpp + src/beta/node/SuperAnimalParserBindings.cpp + src/beta/node/XFeatMonoParserBindings.cpp + src/beta/node/XFeatStereoParserBindings.cpp + src/beta/node/YuNetParserBindings.cpp + ) +endif() + if(DEPTHAI_MERGED_TARGET) list(APPEND SOURCE_LIST external/pybind11_opencv_numpy/ndarray_converter.cpp diff --git a/bindings/python/depthai_cli/depthai_cli.py b/bindings/python/depthai_cli/depthai_cli.py index e847a217ec..90ea5318a6 100644 --- a/bindings/python/depthai_cli/depthai_cli.py +++ b/bindings/python/depthai_cli/depthai_cli.py @@ -12,22 +12,50 @@ ) # Execution from source CAM_TEST_PATH = str(CAM_TEST_PATH) +if os.path.exists(os.path.join(here, "flash_network_bootloader.py")): # Installed package + FLASH_NETWORK_BOOTLOADER_PATH = Path(here) / "flash_network_bootloader.py" +else: + FLASH_NETWORK_BOOTLOADER_PATH = ( + Path(here) + / ".." + / ".." + / ".." + / "utilities" + / "flash_network_bootloader.py" + ) # Execution from source + -def cli() -> int: +def cli(argv=None) -> int: import argparse import sys import depthai as dai parser = argparse.ArgumentParser(description="DepthAI CLI", add_help=True) parser.add_argument("-v", "--version", action="store_true", help="Print version and exit.") parser.add_argument("-l", "--list-devices", action="store_true", help="List connected devices.") + parser.add_argument("-f", "--flash", action="store_true", + help="Safely update an RVC2 NETWORK bootloader; remaining arguments are passed to the flashing command.", + ) subparsers = parser.add_subparsers(dest="command", help="Sub-commands") # Define the parser for the "cam_test" command cam_test_parser = subparsers.add_parser("cam_test", help="Commands and options for cam_test", add_help=False) cam_test_parser.add_argument("args", nargs=argparse.REMAINDER, help="Arguments to pass to cam_test") + cli_args = sys.argv[1:] if argv is None else list(argv) + + # Dispatch before parsing so `depthai --flash --help` displays the flashing + # command's help and all following options pass through unchanged. + if cli_args and cli_args[0] in ("-f", "--flash"): + return subprocess.run( + [sys.executable, str(FLASH_NETWORK_BOOTLOADER_PATH)] + cli_args[1:] + ).returncode + # subparser REMINDER args would get parsed too if we used parse_args, so we have to handle unknown args manually - args, unknown_args = parser.parse_known_args() - if args.command == "cam_test": + args, unknown_args = parser.parse_known_args(cli_args) + if args.flash: + return subprocess.run( + [sys.executable, str(FLASH_NETWORK_BOOTLOADER_PATH)] + unknown_args + ).returncode + elif args.command == "cam_test": cam_test_path = CAM_TEST_PATH return subprocess.run([sys.executable, cam_test_path] + cam_test_parser.parse_args().args[1:]).returncode # Parse other subcommands here diff --git a/bindings/python/external/pybind11_opencv_numpy b/bindings/python/external/pybind11_opencv_numpy index dce3bfc926..7926f9a81c 160000 --- a/bindings/python/external/pybind11_opencv_numpy +++ b/bindings/python/external/pybind11_opencv_numpy @@ -1 +1 @@ -Subproject commit dce3bfc926ef8b047bf7798af1aca93c3e192944 +Subproject commit 7926f9a81cced260d16166437272e1491905230a diff --git a/bindings/python/setup.py b/bindings/python/setup.py index c26fe4cb34..cba1052137 100644 --- a/bindings/python/setup.py +++ b/bindings/python/setup.py @@ -184,14 +184,21 @@ def run(self): def build_extension(self, ext): if ext.name == DEPTHAI_CLI_MODULE_NAME: - # Copy cam_test.py and it's dependencies to depthai_cli/ + # Copy CLI scripts and their dependencies to depthai_cli/ cam_test_path = os.path.join(str(repo_root), "utilities", "cam_test.py") cam_test_dest = os.path.join(self.build_lib, DEPTHAI_CLI_MODULE_NAME, "cam_test.py") cam_test_gui_path = os.path.join(str(repo_root), "utilities", "cam_test_gui.py") cam_test_gui_dest = os.path.join(self.build_lib, DEPTHAI_CLI_MODULE_NAME, "cam_test_gui.py") stress_test_path = os.path.join(str(repo_root), "utilities", "stress_test.py") stress_test_dest = os.path.join(self.build_lib, DEPTHAI_CLI_MODULE_NAME, "stress_test.py") - files_to_copy = [(cam_test_path, cam_test_dest), (cam_test_gui_path, cam_test_gui_dest), (stress_test_path, stress_test_dest)] + flash_network_bootloader_path = os.path.join(str(repo_root), "utilities", "flash_network_bootloader.py") + flash_network_bootloader_dest = os.path.join(self.build_lib, DEPTHAI_CLI_MODULE_NAME, "flash_network_bootloader.py") + files_to_copy = [ + (cam_test_path, cam_test_dest), + (cam_test_gui_path, cam_test_gui_dest), + (stress_test_path, stress_test_dest), + (flash_network_bootloader_path, flash_network_bootloader_dest), + ] for src, dst in files_to_copy: with open(src, "r") as f: with open(dst, "w") as f2: @@ -210,6 +217,8 @@ def build_extension(self, ext): cmake_args += ['-DDEPTHAI_BUILD_PYTHON=ON'] cmake_args += ['-DDEPTHAI_ENABLE_EVENTS_MANAGER=ON'] + if env.get('DEPTHAI_BUILD_BETA') == 'ON': + cmake_args += ['-DDEPTHAI_BUILD_BETA=ON'] # build shared libs only in CI - for downstream wheel bundling if env.get("CI") is not None: diff --git a/bindings/python/src/DatatypeBindings.cpp b/bindings/python/src/DatatypeBindings.cpp index 1c8f6f111c..2ecee46182 100644 --- a/bindings/python/src/DatatypeBindings.cpp +++ b/bindings/python/src/DatatypeBindings.cpp @@ -50,6 +50,27 @@ void bind_auto_calibration_result(pybind11::module& m, void* pCallstack); #endif // DEPTHAI_HAVE_DYNAMIC_CALIBRATION_SUPPORT void bind_vppconfig(pybind11::module& m, void* pCallstack); void bind_gate_control(pybind11::module& m, void* pCallstack); +#ifdef DEPTHAI_HAVE_BETA +void bind_beta_classifications(pybind11::module& m, void* pCallstack); +void bind_beta_classificationsequenceparserconfig(pybind11::module& m, void* pCallstack); +void bind_beta_fastsamparserconfig(pybind11::module& m, void* pCallstack); +void bind_beta_hrnetparserconfig(pybind11::module& m, void* pCallstack); +void bind_beta_mlsdparserconfig(pybind11::module& m, void* pCallstack); +void bind_beta_mppalmdetectionparserconfig(pybind11::module& m, void* pCallstack); +void bind_beta_mapoutputparserconfig(pybind11::module& m, void* pCallstack); +void bind_beta_pptextdetectionparserconfig(pybind11::module& m, void* pCallstack); +void bind_beta_rfdetrparserconfig(pybind11::module& m, void* pCallstack); +void bind_beta_scrfdparserconfig(pybind11::module& m, void* pCallstack); +void bind_beta_superanimalparserconfig(pybind11::module& m, void* pCallstack); +void bind_beta_xfeatmonoparserconfig(pybind11::module& m, void* pCallstack); +void bind_beta_xfeatstereoparserconfig(pybind11::module& m, void* pCallstack); +void bind_beta_yunetparserconfig(pybind11::module& m, void* pCallstack); +void bind_beta_clusters(pybind11::module& m, void* pCallstack); +void bind_beta_keypoints(pybind11::module& m, void* pCallstack); +void bind_beta_lines(pybind11::module& m, void* pCallstack); +void bind_beta_map2d(pybind11::module& m, void* pCallstack); +void bind_beta_predictions(pybind11::module& m, void* pCallstack); +#endif // DEPTHAI_HAVE_BETA void DatatypeBindings::addToCallstack(std::deque& callstack) { // Bind common datatypebindings @@ -103,6 +124,27 @@ void DatatypeBindings::addToCallstack(std::deque& callstack) { callstack.push_front(bind_auto_calibration_config); callstack.push_front(bind_auto_calibration_result); #endif // DEPTHAI_HAVE_DYNAMIC_CALIBRATION_SUPPORT +#ifdef DEPTHAI_HAVE_BETA + callstack.push_front(bind_beta_classifications); + callstack.push_front(bind_beta_clusters); + callstack.push_front(bind_beta_keypoints); + callstack.push_front(bind_beta_lines); + callstack.push_front(bind_beta_map2d); + callstack.push_front(bind_beta_predictions); + callstack.push_front(bind_beta_classificationsequenceparserconfig); + callstack.push_front(bind_beta_fastsamparserconfig); + callstack.push_front(bind_beta_hrnetparserconfig); + callstack.push_front(bind_beta_mlsdparserconfig); + callstack.push_front(bind_beta_mppalmdetectionparserconfig); + callstack.push_front(bind_beta_mapoutputparserconfig); + callstack.push_front(bind_beta_pptextdetectionparserconfig); + callstack.push_front(bind_beta_rfdetrparserconfig); + callstack.push_front(bind_beta_scrfdparserconfig); + callstack.push_front(bind_beta_superanimalparserconfig); + callstack.push_front(bind_beta_xfeatmonoparserconfig); + callstack.push_front(bind_beta_xfeatstereoparserconfig); + callstack.push_front(bind_beta_yunetparserconfig); +#endif // DEPTHAI_HAVE_BETA } void DatatypeBindings::bind(pybind11::module& m, void* pCallstack) { @@ -170,6 +212,28 @@ void DatatypeBindings::bind(pybind11::module& m, void* pCallstack) { .value("DynamicCalibrationResult", DatatypeEnum::DynamicCalibrationResult) .value("AutoCalibrationConfig", DatatypeEnum::AutoCalibrationConfig) .value("AutoCalibrationResult", DatatypeEnum::AutoCalibrationResult) - .value("CalibrationQuality", DatatypeEnum::CalibrationQuality) - .value("CoverageData", DatatypeEnum::CoverageData); + .value("CalibrationQuality", DatatypeEnum::CalibrationQuality); +#ifdef DEPTHAI_HAVE_BETA + datatypeEnum.value("ImgDetectionsFilterConfig", DatatypeEnum::ImgDetectionsFilterConfig); + datatypeEnum.value("Classifications", DatatypeEnum::Classifications); + datatypeEnum.value("Keypoints", DatatypeEnum::Keypoints); + datatypeEnum.value("Clusters", DatatypeEnum::Clusters); + datatypeEnum.value("Map2D", DatatypeEnum::Map2D); + datatypeEnum.value("Lines", DatatypeEnum::Lines); + datatypeEnum.value("Predictions", DatatypeEnum::Predictions); + datatypeEnum.value("FastSAMParserConfig", DatatypeEnum::FastSAMParserConfig); + datatypeEnum.value("HRNetParserConfig", DatatypeEnum::HRNetParserConfig); + datatypeEnum.value("MLSDParserConfig", DatatypeEnum::MLSDParserConfig); + datatypeEnum.value("MPPalmDetectionParserConfig", DatatypeEnum::MPPalmDetectionParserConfig); + datatypeEnum.value("PPTextDetectionParserConfig", DatatypeEnum::PPTextDetectionParserConfig); + datatypeEnum.value("RFDETRParserConfig", DatatypeEnum::RFDETRParserConfig); + datatypeEnum.value("SCRFDParserConfig", DatatypeEnum::SCRFDParserConfig); + datatypeEnum.value("SuperAnimalParserConfig", DatatypeEnum::SuperAnimalParserConfig); + datatypeEnum.value("YuNetParserConfig", DatatypeEnum::YuNetParserConfig); + datatypeEnum.value("ClassificationSequenceParserConfig", DatatypeEnum::ClassificationSequenceParserConfig); + datatypeEnum.value("MapOutputParserConfig", DatatypeEnum::MapOutputParserConfig); + datatypeEnum.value("XFeatMonoParserConfig", DatatypeEnum::XFeatMonoParserConfig); + datatypeEnum.value("XFeatStereoParserConfig", DatatypeEnum::XFeatStereoParserConfig); +#endif // DEPTHAI_HAVE_BETA + datatypeEnum.value("CoverageData", DatatypeEnum::CoverageData); } diff --git a/bindings/python/src/DeviceBindings.cpp b/bindings/python/src/DeviceBindings.cpp index 50333efa21..6460adfaac 100644 --- a/bindings/python/src/DeviceBindings.cpp +++ b/bindings/python/src/DeviceBindings.cpp @@ -510,6 +510,14 @@ void DeviceBindings::bind(pybind11::module& m, void* pCallstack) { return d.getConnectedCameras(); }, DOC(dai, DeviceBase, getConnectedCameras)) + .def( + "getConnectedCameras", + [](DeviceBase& d, CameraSensorType type) { + py::gil_scoped_release release; + return d.getConnectedCameras(type); + }, + py::arg("type"), + DOC(dai, DeviceBase, getConnectedCameras, 2)) .def( "getConnectionInterfaces", [](DeviceBase& d) { diff --git a/bindings/python/src/beta/datatype/ClassificationSequenceParserConfigBindings.cpp b/bindings/python/src/beta/datatype/ClassificationSequenceParserConfigBindings.cpp new file mode 100644 index 0000000000..4d2a3b5431 --- /dev/null +++ b/bindings/python/src/beta/datatype/ClassificationSequenceParserConfigBindings.cpp @@ -0,0 +1,46 @@ +#include "DatatypeBindings.hpp" +#include "depthai/beta/datatype/ClassificationSequenceParserConfig.hpp" +#include "pipeline/CommonBindings.hpp" + +void bind_beta_classificationsequenceparserconfig(pybind11::module& m, void* pCallstack) { + using namespace dai; + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_, + Buffer, + std::shared_ptr> + config(betaModule, "ClassificationSequenceParserConfig", DOC(dai, beta, ClassificationSequenceParserConfig)); + + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + + config.def(py::init<>()) + .def("__repr__", &beta::ClassificationSequenceParserConfig::str) + .def_readwrite("ignoredIndexes", &beta::ClassificationSequenceParserConfig::ignoredIndexes) + .def_readwrite("removeDuplicates", &beta::ClassificationSequenceParserConfig::removeDuplicates) + .def_readwrite("concatenateClasses", &beta::ClassificationSequenceParserConfig::concatenateClasses) + .def("setIgnoredIndexes", + &beta::ClassificationSequenceParserConfig::setIgnoredIndexes, + py::arg("indexes"), + DOC(dai, beta, ClassificationSequenceParserConfig, setIgnoredIndexes)) + .def("getIgnoredIndexes", + &beta::ClassificationSequenceParserConfig::getIgnoredIndexes, + DOC(dai, beta, ClassificationSequenceParserConfig, getIgnoredIndexes)) + .def("setRemoveDuplicates", + &beta::ClassificationSequenceParserConfig::setRemoveDuplicates, + py::arg("enabled"), + DOC(dai, beta, ClassificationSequenceParserConfig, setRemoveDuplicates)) + .def("getRemoveDuplicates", + &beta::ClassificationSequenceParserConfig::getRemoveDuplicates, + DOC(dai, beta, ClassificationSequenceParserConfig, getRemoveDuplicates)) + .def("setConcatenateClasses", + &beta::ClassificationSequenceParserConfig::setConcatenateClasses, + py::arg("enabled"), + DOC(dai, beta, ClassificationSequenceParserConfig, setConcatenateClasses)) + .def("getConcatenateClasses", + &beta::ClassificationSequenceParserConfig::getConcatenateClasses, + DOC(dai, beta, ClassificationSequenceParserConfig, getConcatenateClasses)) + .def("validate", &beta::ClassificationSequenceParserConfig::validate, DOC(dai, beta, ClassificationSequenceParserConfig, validate)); +} diff --git a/bindings/python/src/beta/datatype/ClassificationsBindings.cpp b/bindings/python/src/beta/datatype/ClassificationsBindings.cpp new file mode 100644 index 0000000000..f8813fb08e --- /dev/null +++ b/bindings/python/src/beta/datatype/ClassificationsBindings.cpp @@ -0,0 +1,50 @@ +#include +#include +#include + +#include + +#include "DatatypeBindings.hpp" +#include "depthai/beta/datatype/Classifications.hpp" +#include "pipeline/CommonBindings.hpp" + +namespace { + +py::array_t toNumpyScores(const std::vector& scores) { + py::array_t arr(static_cast(scores.size())); + if(!scores.empty()) { + std::memcpy(arr.mutable_data(), scores.data(), scores.size() * sizeof(float)); + } + return arr; +} + +} // namespace + +void bind_beta_classifications(pybind11::module& m, void* pCallstack) { + using namespace dai; + + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_, Buffer, Transformable, std::shared_ptr> classifications( + betaModule, "Classifications", DOC(dai, beta, Classifications)); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + classifications.def(py::init<>()) + .def("__repr__", &beta::Classifications::str) + .def_readwrite("classes", &beta::Classifications::classes, DOC(dai, beta, Classifications, classes)) + .def_property( + "scores", + [](const beta::Classifications& message) { return toNumpyScores(message.scores); }, + [](beta::Classifications& message, std::vector scores) { message.scores = std::move(scores); }, + DOC(dai, beta, Classifications, scores)) + .def("getTopClass", &beta::Classifications::getTopClass, DOC(dai, beta, Classifications, getTopClass)) + .def("getTopScore", &beta::Classifications::getTopScore, DOC(dai, beta, Classifications, getTopScore)) + .def("transformTo", &beta::Classifications::transformTo, py::arg("target"), DOC(dai, beta, Classifications, transformTo)) + .def("getVisualizationMessage", &beta::Classifications::getVisualizationMessage, DOC(dai, beta, Classifications, getVisualizationMessage)); +} diff --git a/bindings/python/src/beta/datatype/ClustersBindings.cpp b/bindings/python/src/beta/datatype/ClustersBindings.cpp new file mode 100644 index 0000000000..f493acbd9a --- /dev/null +++ b/bindings/python/src/beta/datatype/ClustersBindings.cpp @@ -0,0 +1,33 @@ +#include +#include + +#include "DatatypeBindings.hpp" +#include "depthai/beta/datatype/Clusters.hpp" +#include "pipeline/CommonBindings.hpp" + +void bind_beta_clusters(pybind11::module& m, void* pCallstack) { + using namespace dai; + + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_ cluster(betaModule, "Cluster", DOC(dai, beta, Cluster)); + py::class_, Buffer, Transformable, std::shared_ptr> clusters( + betaModule, "Clusters", DOC(dai, beta, Clusters)); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + cluster.def(py::init<>()) + .def_readwrite("label", &beta::Cluster::label, DOC(dai, beta, Cluster, label)) + .def_readwrite("points", &beta::Cluster::points, DOC(dai, beta, Cluster, points)); + + clusters.def(py::init<>()) + .def("__repr__", &beta::Clusters::str) + .def_readwrite("clusters", &beta::Clusters::clusters, DOC(dai, beta, Clusters, clusters)) + .def("transformTo", &beta::Clusters::transformTo, py::arg("target"), DOC(dai, beta, Clusters, transformTo)) + .def("getVisualizationMessage", &beta::Clusters::getVisualizationMessage, DOC(dai, beta, Clusters, getVisualizationMessage)); +} diff --git a/bindings/python/src/beta/datatype/FastSAMParserConfigBindings.cpp b/bindings/python/src/beta/datatype/FastSAMParserConfigBindings.cpp new file mode 100644 index 0000000000..0f20f20300 --- /dev/null +++ b/bindings/python/src/beta/datatype/FastSAMParserConfigBindings.cpp @@ -0,0 +1,49 @@ +#include "DatatypeBindings.hpp" +#include "depthai/beta/datatype/FastSAMParserConfig.hpp" +#include "pipeline/CommonBindings.hpp" + +void bind_beta_fastsamparserconfig(pybind11::module& m, void* pCallstack) { + using namespace dai; + + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_, Buffer, std::shared_ptr> config( + betaModule, "FastSAMParserConfig", DOC(dai, beta, FastSAMParserConfig)); + py::enum_ prompt(config, "Prompt", DOC(dai, beta, FastSAMParserConfig, Prompt)); + + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + + prompt.value("EVERYTHING", beta::FastSAMParserConfig::Prompt::EVERYTHING, DOC(dai, beta, FastSAMParserConfig, Prompt, EVERYTHING)) + .value("POINT", beta::FastSAMParserConfig::Prompt::POINT, DOC(dai, beta, FastSAMParserConfig, Prompt, POINT)) + .value("BOUNDING_BOX", beta::FastSAMParserConfig::Prompt::BOUNDING_BOX, DOC(dai, beta, FastSAMParserConfig, Prompt, BOUNDING_BOX)); + + config.def(py::init<>()) + .def("__repr__", &beta::FastSAMParserConfig::str) + .def_readwrite("confidenceThreshold", &beta::FastSAMParserConfig::confidenceThreshold) + .def_readwrite("iouThreshold", &beta::FastSAMParserConfig::iouThreshold) + .def_readwrite("maskConfidence", &beta::FastSAMParserConfig::maskConfidence) + .def_readwrite("prompt", &beta::FastSAMParserConfig::prompt) + .def_readwrite("points", &beta::FastSAMParserConfig::points) + .def_readwrite("pointLabel", &beta::FastSAMParserConfig::pointLabel) + .def_readwrite("boundingBox", &beta::FastSAMParserConfig::boundingBox) + .def("setConfidenceThreshold", + &beta::FastSAMParserConfig::setConfidenceThreshold, + py::arg("threshold"), + DOC(dai, beta, FastSAMParserConfig, setConfidenceThreshold)) + .def("getConfidenceThreshold", &beta::FastSAMParserConfig::getConfidenceThreshold, DOC(dai, beta, FastSAMParserConfig, getConfidenceThreshold)) + .def("setIouThreshold", &beta::FastSAMParserConfig::setIouThreshold, py::arg("threshold"), DOC(dai, beta, FastSAMParserConfig, setIouThreshold)) + .def("getIouThreshold", &beta::FastSAMParserConfig::getIouThreshold, DOC(dai, beta, FastSAMParserConfig, getIouThreshold)) + .def("setMaskConfidence", &beta::FastSAMParserConfig::setMaskConfidence, py::arg("threshold"), DOC(dai, beta, FastSAMParserConfig, setMaskConfidence)) + .def("getMaskConfidence", &beta::FastSAMParserConfig::getMaskConfidence, DOC(dai, beta, FastSAMParserConfig, getMaskConfidence)) + .def("setPrompt", &beta::FastSAMParserConfig::setPrompt, py::arg("prompt"), DOC(dai, beta, FastSAMParserConfig, setPrompt)) + .def("getPrompt", &beta::FastSAMParserConfig::getPrompt, DOC(dai, beta, FastSAMParserConfig, getPrompt)) + .def("setPoints", &beta::FastSAMParserConfig::setPoints, py::arg("x"), py::arg("y"), DOC(dai, beta, FastSAMParserConfig, setPoints)) + .def("getPoints", &beta::FastSAMParserConfig::getPoints, DOC(dai, beta, FastSAMParserConfig, getPoints)) + .def("setPointLabel", &beta::FastSAMParserConfig::setPointLabel, py::arg("label"), DOC(dai, beta, FastSAMParserConfig, setPointLabel)) + .def("getPointLabel", &beta::FastSAMParserConfig::getPointLabel, DOC(dai, beta, FastSAMParserConfig, getPointLabel)) + .def("setBoundingBox", &beta::FastSAMParserConfig::setBoundingBox, py::arg("boundingBox"), DOC(dai, beta, FastSAMParserConfig, setBoundingBox)) + .def("getBoundingBox", &beta::FastSAMParserConfig::getBoundingBox, DOC(dai, beta, FastSAMParserConfig, getBoundingBox)) + .def("validate", &beta::FastSAMParserConfig::validate, DOC(dai, beta, FastSAMParserConfig, validate)); +} diff --git a/bindings/python/src/beta/datatype/HRNetParserConfigBindings.cpp b/bindings/python/src/beta/datatype/HRNetParserConfigBindings.cpp new file mode 100644 index 0000000000..70ad6a8dd5 --- /dev/null +++ b/bindings/python/src/beta/datatype/HRNetParserConfigBindings.cpp @@ -0,0 +1,22 @@ +#include "DatatypeBindings.hpp" +#include "depthai/beta/datatype/HRNetParserConfig.hpp" +#include "pipeline/CommonBindings.hpp" + +void bind_beta_hrnetparserconfig(pybind11::module& m, void* pCallstack) { + using namespace dai; + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_, Buffer, std::shared_ptr> config( + betaModule, "HRNetParserConfig", DOC(dai, beta, HRNetParserConfig)); + + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + + config.def(py::init<>()) + .def("__repr__", &beta::HRNetParserConfig::str) + .def_readwrite("scoreThreshold", &beta::HRNetParserConfig::scoreThreshold) + .def("setScoreThreshold", &beta::HRNetParserConfig::setScoreThreshold, py::arg("threshold"), DOC(dai, beta, HRNetParserConfig, setScoreThreshold)) + .def("getScoreThreshold", &beta::HRNetParserConfig::getScoreThreshold, DOC(dai, beta, HRNetParserConfig, getScoreThreshold)) + .def("validate", &beta::HRNetParserConfig::validate, DOC(dai, beta, HRNetParserConfig, validate)); +} diff --git a/bindings/python/src/beta/datatype/KeypointsBindings.cpp b/bindings/python/src/beta/datatype/KeypointsBindings.cpp new file mode 100644 index 0000000000..9786885d68 --- /dev/null +++ b/bindings/python/src/beta/datatype/KeypointsBindings.cpp @@ -0,0 +1,42 @@ +#include +#include + +#include "DatatypeBindings.hpp" +#include "depthai/beta/datatype/Keypoints.hpp" +#include "pipeline/CommonBindings.hpp" + +void bind_beta_keypoints(pybind11::module& m, void* pCallstack) { + using namespace dai; + + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_, Buffer, Transformable, std::shared_ptr> keypoints( + betaModule, "Keypoints", DOC(dai, beta, Keypoints)); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + keypoints.def(py::init<>()) + .def("__repr__", &beta::Keypoints::str) + .def_readwrite("keypointsList", &beta::Keypoints::keypointsList, DOC(dai, beta, Keypoints, keypointsList)) + .def("getKeypoints", &beta::Keypoints::getKeypoints, DOC(dai, beta, Keypoints, getKeypoints)) + .def("setKeypoints", + py::overload_cast&>(&beta::Keypoints::setKeypoints), + py::arg("keypoints"), + DOC(dai, beta, Keypoints, setKeypoints)) + .def("setKeypoints", + py::overload_cast&, const std::vector&>(&beta::Keypoints::setKeypoints), + py::arg("keypoints"), + py::arg("edges"), + DOC(dai, beta, Keypoints, setKeypoints, 2)) + .def("getEdges", &beta::Keypoints::getEdges, DOC(dai, beta, Keypoints, getEdges)) + .def("setEdges", &beta::Keypoints::setEdges, py::arg("edges"), DOC(dai, beta, Keypoints, setEdges)) + .def("getPoints2f", &beta::Keypoints::getPoints2f, DOC(dai, beta, Keypoints, getPoints2f)) + .def("getPoints3f", &beta::Keypoints::getPoints3f, DOC(dai, beta, Keypoints, getPoints3f)) + .def("transformTo", &beta::Keypoints::transformTo, py::arg("target"), DOC(dai, beta, Keypoints, transformTo)) + .def("getVisualizationMessage", &beta::Keypoints::getVisualizationMessage, DOC(dai, beta, Keypoints, getVisualizationMessage)); +} diff --git a/bindings/python/src/beta/datatype/LinesBindings.cpp b/bindings/python/src/beta/datatype/LinesBindings.cpp new file mode 100644 index 0000000000..95e490d430 --- /dev/null +++ b/bindings/python/src/beta/datatype/LinesBindings.cpp @@ -0,0 +1,33 @@ +#include +#include + +#include "DatatypeBindings.hpp" +#include "depthai/beta/datatype/Lines.hpp" +#include "pipeline/CommonBindings.hpp" + +void bind_beta_lines(pybind11::module& m, void* pCallstack) { + using namespace dai; + + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_ line(betaModule, "Line", DOC(dai, beta, Line)); + py::class_, Buffer, Transformable, std::shared_ptr> lines(betaModule, "Lines", DOC(dai, beta, Lines)); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + line.def(py::init<>()) + .def_readwrite("startPoint", &beta::Line::startPoint, DOC(dai, beta, Line, startPoint)) + .def_readwrite("endPoint", &beta::Line::endPoint, DOC(dai, beta, Line, endPoint)) + .def_readwrite("confidence", &beta::Line::confidence, DOC(dai, beta, Line, confidence)); + + lines.def(py::init<>()) + .def("__repr__", &beta::Lines::str) + .def_readwrite("lines", &beta::Lines::lines, DOC(dai, beta, Lines, lines)) + .def("transformTo", &beta::Lines::transformTo, py::arg("target"), DOC(dai, beta, Lines, transformTo)) + .def("getVisualizationMessage", &beta::Lines::getVisualizationMessage, DOC(dai, beta, Lines, getVisualizationMessage)); +} diff --git a/bindings/python/src/beta/datatype/MLSDParserConfigBindings.cpp b/bindings/python/src/beta/datatype/MLSDParserConfigBindings.cpp new file mode 100644 index 0000000000..d0329909d7 --- /dev/null +++ b/bindings/python/src/beta/datatype/MLSDParserConfigBindings.cpp @@ -0,0 +1,29 @@ +#include "DatatypeBindings.hpp" +#include "depthai/beta/datatype/MLSDParserConfig.hpp" +#include "pipeline/CommonBindings.hpp" + +void bind_beta_mlsdparserconfig(pybind11::module& m, void* pCallstack) { + using namespace dai; + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_, Buffer, std::shared_ptr> config( + betaModule, "MLSDParserConfig", DOC(dai, beta, MLSDParserConfig)); + + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + + config.def(py::init<>()) + .def("__repr__", &beta::MLSDParserConfig::str) + .def_readwrite("topK", &beta::MLSDParserConfig::topK) + .def_readwrite("scoreThreshold", &beta::MLSDParserConfig::scoreThreshold) + .def_readwrite("distanceThreshold", &beta::MLSDParserConfig::distanceThreshold) + .def("setTopK", &beta::MLSDParserConfig::setTopK, py::arg("topK"), DOC(dai, beta, MLSDParserConfig, setTopK)) + .def("getTopK", &beta::MLSDParserConfig::getTopK, DOC(dai, beta, MLSDParserConfig, getTopK)) + .def("setScoreThreshold", &beta::MLSDParserConfig::setScoreThreshold, py::arg("threshold"), DOC(dai, beta, MLSDParserConfig, setScoreThreshold)) + .def("getScoreThreshold", &beta::MLSDParserConfig::getScoreThreshold, DOC(dai, beta, MLSDParserConfig, getScoreThreshold)) + .def( + "setDistanceThreshold", &beta::MLSDParserConfig::setDistanceThreshold, py::arg("threshold"), DOC(dai, beta, MLSDParserConfig, setDistanceThreshold)) + .def("getDistanceThreshold", &beta::MLSDParserConfig::getDistanceThreshold, DOC(dai, beta, MLSDParserConfig, getDistanceThreshold)) + .def("validate", &beta::MLSDParserConfig::validate, DOC(dai, beta, MLSDParserConfig, validate)); +} diff --git a/bindings/python/src/beta/datatype/MPPalmDetectionParserConfigBindings.cpp b/bindings/python/src/beta/datatype/MPPalmDetectionParserConfigBindings.cpp new file mode 100644 index 0000000000..cc8d5885e1 --- /dev/null +++ b/bindings/python/src/beta/datatype/MPPalmDetectionParserConfigBindings.cpp @@ -0,0 +1,39 @@ +#include "DatatypeBindings.hpp" +#include "depthai/beta/datatype/MPPalmDetectionParserConfig.hpp" +#include "pipeline/CommonBindings.hpp" + +void bind_beta_mppalmdetectionparserconfig(pybind11::module& m, void* pCallstack) { + using namespace dai; + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_, Buffer, std::shared_ptr> config( + betaModule, "MPPalmDetectionParserConfig", DOC(dai, beta, MPPalmDetectionParserConfig)); + + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + + config.def(py::init<>()) + .def("__repr__", &beta::MPPalmDetectionParserConfig::str) + .def_readwrite("confidenceThreshold", &beta::MPPalmDetectionParserConfig::confidenceThreshold) + .def_readwrite("iouThreshold", &beta::MPPalmDetectionParserConfig::iouThreshold) + .def_readwrite("maxDetections", &beta::MPPalmDetectionParserConfig::maxDetections) + .def("setConfidenceThreshold", + &beta::MPPalmDetectionParserConfig::setConfidenceThreshold, + py::arg("threshold"), + DOC(dai, beta, MPPalmDetectionParserConfig, setConfidenceThreshold)) + .def("getConfidenceThreshold", + &beta::MPPalmDetectionParserConfig::getConfidenceThreshold, + DOC(dai, beta, MPPalmDetectionParserConfig, getConfidenceThreshold)) + .def("setIouThreshold", + &beta::MPPalmDetectionParserConfig::setIouThreshold, + py::arg("threshold"), + DOC(dai, beta, MPPalmDetectionParserConfig, setIouThreshold)) + .def("getIouThreshold", &beta::MPPalmDetectionParserConfig::getIouThreshold, DOC(dai, beta, MPPalmDetectionParserConfig, getIouThreshold)) + .def("setMaxDetections", + &beta::MPPalmDetectionParserConfig::setMaxDetections, + py::arg("maxDetections"), + DOC(dai, beta, MPPalmDetectionParserConfig, setMaxDetections)) + .def("getMaxDetections", &beta::MPPalmDetectionParserConfig::getMaxDetections, DOC(dai, beta, MPPalmDetectionParserConfig, getMaxDetections)) + .def("validate", &beta::MPPalmDetectionParserConfig::validate, DOC(dai, beta, MPPalmDetectionParserConfig, validate)); +} diff --git a/bindings/python/src/beta/datatype/Map2DBindings.cpp b/bindings/python/src/beta/datatype/Map2DBindings.cpp new file mode 100644 index 0000000000..0c7ea76d8c --- /dev/null +++ b/bindings/python/src/beta/datatype/Map2DBindings.cpp @@ -0,0 +1,61 @@ +#include +#include +#include + +#include + +#include "DatatypeBindings.hpp" +#include "depthai/beta/datatype/Map2D.hpp" +#include "pipeline/CommonBindings.hpp" + +void bind_beta_map2d(pybind11::module& m, void* pCallstack) { + using namespace dai; + + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_, Buffer, Transformable, std::shared_ptr> map2D(betaModule, "Map2D", DOC(dai, beta, Map2D)); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + map2D.def(py::init<>()) + .def("__repr__", &beta::Map2D::str) + .def( + "getMap", + [](const beta::Map2D& self) { + const auto data = self.getMap(); + if(data.empty()) { + return py::array_t(); + } + const auto width = static_cast(self.getWidth()); + const auto height = static_cast(self.getHeight()); + py::array_t arr({height, width}); + std::memcpy(arr.mutable_data(), data.data(), data.size() * sizeof(float)); + return arr; + }, + DOC(dai, beta, Map2D, getMap)) + .def( + "setMap", + [](beta::Map2D& self, const py::array& map) { + if(map.ndim() != 2) { + throw py::value_error("2D map must be a 2D array"); + } + if(!map.dtype().is(py::dtype::of())) { + throw py::value_error("2D map must be an array of floats"); + } + py::array_t contiguous(map); + const auto height = static_cast(contiguous.shape(0)); + const auto width = static_cast(contiguous.shape(1)); + self.setMap(dai::span(contiguous.data(), height * width), width, height); + }, + py::arg("map"), + DOC(dai, beta, Map2D, setMap)) + .def("getWidth", &beta::Map2D::getWidth, DOC(dai, beta, Map2D, getWidth)) + .def("getHeight", &beta::Map2D::getHeight, DOC(dai, beta, Map2D, getHeight)) + .def("transformTo", &beta::Map2D::transformTo, py::arg("target"), DOC(dai, beta, Map2D, transformTo)) + .def("getVisualizationMessage", &beta::Map2D::getVisualizationMessage, DOC(dai, beta, Map2D, getVisualizationMessage)); +} diff --git a/bindings/python/src/beta/datatype/MapOutputParserConfigBindings.cpp b/bindings/python/src/beta/datatype/MapOutputParserConfigBindings.cpp new file mode 100644 index 0000000000..03a06339f9 --- /dev/null +++ b/bindings/python/src/beta/datatype/MapOutputParserConfigBindings.cpp @@ -0,0 +1,22 @@ +#include "DatatypeBindings.hpp" +#include "depthai/beta/datatype/MapOutputParserConfig.hpp" +#include "pipeline/CommonBindings.hpp" + +void bind_beta_mapoutputparserconfig(pybind11::module& m, void* pCallstack) { + using namespace dai; + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_, Buffer, std::shared_ptr> config( + betaModule, "MapOutputParserConfig", DOC(dai, beta, MapOutputParserConfig)); + + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + + config.def(py::init<>()) + .def("__repr__", &beta::MapOutputParserConfig::str) + .def_readwrite("minMaxScaling", &beta::MapOutputParserConfig::minMaxScaling) + .def("setMinMaxScaling", &beta::MapOutputParserConfig::setMinMaxScaling, py::arg("enabled"), DOC(dai, beta, MapOutputParserConfig, setMinMaxScaling)) + .def("getMinMaxScaling", &beta::MapOutputParserConfig::getMinMaxScaling, DOC(dai, beta, MapOutputParserConfig, getMinMaxScaling)) + .def("validate", &beta::MapOutputParserConfig::validate, DOC(dai, beta, MapOutputParserConfig, validate)); +} diff --git a/bindings/python/src/beta/datatype/PPTextDetectionParserConfigBindings.cpp b/bindings/python/src/beta/datatype/PPTextDetectionParserConfigBindings.cpp new file mode 100644 index 0000000000..98d2ec4a6f --- /dev/null +++ b/bindings/python/src/beta/datatype/PPTextDetectionParserConfigBindings.cpp @@ -0,0 +1,39 @@ +#include "DatatypeBindings.hpp" +#include "depthai/beta/datatype/PPTextDetectionParserConfig.hpp" +#include "pipeline/CommonBindings.hpp" + +void bind_beta_pptextdetectionparserconfig(pybind11::module& m, void* pCallstack) { + using namespace dai; + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_, Buffer, std::shared_ptr> config( + betaModule, "PPTextDetectionParserConfig", DOC(dai, beta, PPTextDetectionParserConfig)); + + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + + config.def(py::init<>()) + .def("__repr__", &beta::PPTextDetectionParserConfig::str) + .def_readwrite("confidenceThreshold", &beta::PPTextDetectionParserConfig::confidenceThreshold) + .def_readwrite("maskThreshold", &beta::PPTextDetectionParserConfig::maskThreshold) + .def_readwrite("maxDetections", &beta::PPTextDetectionParserConfig::maxDetections) + .def("setConfidenceThreshold", + &beta::PPTextDetectionParserConfig::setConfidenceThreshold, + py::arg("threshold"), + DOC(dai, beta, PPTextDetectionParserConfig, setConfidenceThreshold)) + .def("getConfidenceThreshold", + &beta::PPTextDetectionParserConfig::getConfidenceThreshold, + DOC(dai, beta, PPTextDetectionParserConfig, getConfidenceThreshold)) + .def("setMaskThreshold", + &beta::PPTextDetectionParserConfig::setMaskThreshold, + py::arg("threshold"), + DOC(dai, beta, PPTextDetectionParserConfig, setMaskThreshold)) + .def("getMaskThreshold", &beta::PPTextDetectionParserConfig::getMaskThreshold, DOC(dai, beta, PPTextDetectionParserConfig, getMaskThreshold)) + .def("setMaxDetections", + &beta::PPTextDetectionParserConfig::setMaxDetections, + py::arg("maxDetections"), + DOC(dai, beta, PPTextDetectionParserConfig, setMaxDetections)) + .def("getMaxDetections", &beta::PPTextDetectionParserConfig::getMaxDetections, DOC(dai, beta, PPTextDetectionParserConfig, getMaxDetections)) + .def("validate", &beta::PPTextDetectionParserConfig::validate, DOC(dai, beta, PPTextDetectionParserConfig, validate)); +} diff --git a/bindings/python/src/beta/datatype/PredictionsBindings.cpp b/bindings/python/src/beta/datatype/PredictionsBindings.cpp new file mode 100644 index 0000000000..cd1c6c433d --- /dev/null +++ b/bindings/python/src/beta/datatype/PredictionsBindings.cpp @@ -0,0 +1,32 @@ +#include +#include + +#include "DatatypeBindings.hpp" +#include "depthai/beta/datatype/Predictions.hpp" +#include "pipeline/CommonBindings.hpp" + +void bind_beta_predictions(pybind11::module& m, void* pCallstack) { + using namespace dai; + + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_ prediction(betaModule, "Prediction", DOC(dai, beta, Prediction)); + py::class_, Buffer, Transformable, std::shared_ptr> predictions( + betaModule, "Predictions", DOC(dai, beta, Predictions)); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + prediction.def(py::init<>()).def_readwrite("prediction", &beta::Prediction::prediction, DOC(dai, beta, Prediction, prediction)); + + predictions.def(py::init<>()) + .def("__repr__", &beta::Predictions::str) + .def_readwrite("predictions", &beta::Predictions::predictions, DOC(dai, beta, Predictions, predictions)) + .def("getFirstPrediction", &beta::Predictions::getFirstPrediction, DOC(dai, beta, Predictions, getFirstPrediction)) + .def("transformTo", &beta::Predictions::transformTo, py::arg("target"), DOC(dai, beta, Predictions, transformTo)) + .def("getVisualizationMessage", &beta::Predictions::getVisualizationMessage, DOC(dai, beta, Predictions, getVisualizationMessage)); +} diff --git a/bindings/python/src/beta/datatype/RFDETRParserConfigBindings.cpp b/bindings/python/src/beta/datatype/RFDETRParserConfigBindings.cpp new file mode 100644 index 0000000000..eb804bfb38 --- /dev/null +++ b/bindings/python/src/beta/datatype/RFDETRParserConfigBindings.cpp @@ -0,0 +1,31 @@ +#include "DatatypeBindings.hpp" +#include "depthai/beta/datatype/RFDETRParserConfig.hpp" +#include "pipeline/CommonBindings.hpp" + +void bind_beta_rfdetrparserconfig(pybind11::module& m, void* pCallstack) { + using namespace dai; + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_, Buffer, std::shared_ptr> config( + betaModule, "RFDETRParserConfig", DOC(dai, beta, RFDETRParserConfig)); + + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + + config.def(py::init<>()) + .def("__repr__", &beta::RFDETRParserConfig::str) + .def_readwrite("confidenceThreshold", &beta::RFDETRParserConfig::confidenceThreshold) + .def_readwrite("maxDetections", &beta::RFDETRParserConfig::maxDetections) + .def_readwrite("maskConfidence", &beta::RFDETRParserConfig::maskConfidence) + .def("setConfidenceThreshold", + &beta::RFDETRParserConfig::setConfidenceThreshold, + py::arg("threshold"), + DOC(dai, beta, RFDETRParserConfig, setConfidenceThreshold)) + .def("getConfidenceThreshold", &beta::RFDETRParserConfig::getConfidenceThreshold, DOC(dai, beta, RFDETRParserConfig, getConfidenceThreshold)) + .def("setMaxDetections", &beta::RFDETRParserConfig::setMaxDetections, py::arg("maxDetections"), DOC(dai, beta, RFDETRParserConfig, setMaxDetections)) + .def("getMaxDetections", &beta::RFDETRParserConfig::getMaxDetections, DOC(dai, beta, RFDETRParserConfig, getMaxDetections)) + .def("setMaskConfidence", &beta::RFDETRParserConfig::setMaskConfidence, py::arg("threshold"), DOC(dai, beta, RFDETRParserConfig, setMaskConfidence)) + .def("getMaskConfidence", &beta::RFDETRParserConfig::getMaskConfidence, DOC(dai, beta, RFDETRParserConfig, getMaskConfidence)) + .def("validate", &beta::RFDETRParserConfig::validate, DOC(dai, beta, RFDETRParserConfig, validate)); +} diff --git a/bindings/python/src/beta/datatype/SCRFDParserConfigBindings.cpp b/bindings/python/src/beta/datatype/SCRFDParserConfigBindings.cpp new file mode 100644 index 0000000000..6efead9887 --- /dev/null +++ b/bindings/python/src/beta/datatype/SCRFDParserConfigBindings.cpp @@ -0,0 +1,31 @@ +#include "DatatypeBindings.hpp" +#include "depthai/beta/datatype/SCRFDParserConfig.hpp" +#include "pipeline/CommonBindings.hpp" + +void bind_beta_scrfdparserconfig(pybind11::module& m, void* pCallstack) { + using namespace dai; + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_, Buffer, std::shared_ptr> config( + betaModule, "SCRFDParserConfig", DOC(dai, beta, SCRFDParserConfig)); + + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + + config.def(py::init<>()) + .def("__repr__", &beta::SCRFDParserConfig::str) + .def_readwrite("confidenceThreshold", &beta::SCRFDParserConfig::confidenceThreshold) + .def_readwrite("iouThreshold", &beta::SCRFDParserConfig::iouThreshold) + .def_readwrite("maxDetections", &beta::SCRFDParserConfig::maxDetections) + .def("setConfidenceThreshold", + &beta::SCRFDParserConfig::setConfidenceThreshold, + py::arg("threshold"), + DOC(dai, beta, SCRFDParserConfig, setConfidenceThreshold)) + .def("getConfidenceThreshold", &beta::SCRFDParserConfig::getConfidenceThreshold, DOC(dai, beta, SCRFDParserConfig, getConfidenceThreshold)) + .def("setIouThreshold", &beta::SCRFDParserConfig::setIouThreshold, py::arg("threshold"), DOC(dai, beta, SCRFDParserConfig, setIouThreshold)) + .def("getIouThreshold", &beta::SCRFDParserConfig::getIouThreshold, DOC(dai, beta, SCRFDParserConfig, getIouThreshold)) + .def("setMaxDetections", &beta::SCRFDParserConfig::setMaxDetections, py::arg("maxDetections"), DOC(dai, beta, SCRFDParserConfig, setMaxDetections)) + .def("getMaxDetections", &beta::SCRFDParserConfig::getMaxDetections, DOC(dai, beta, SCRFDParserConfig, getMaxDetections)) + .def("validate", &beta::SCRFDParserConfig::validate, DOC(dai, beta, SCRFDParserConfig, validate)); +} diff --git a/bindings/python/src/beta/datatype/SuperAnimalParserConfigBindings.cpp b/bindings/python/src/beta/datatype/SuperAnimalParserConfigBindings.cpp new file mode 100644 index 0000000000..12840bdaad --- /dev/null +++ b/bindings/python/src/beta/datatype/SuperAnimalParserConfigBindings.cpp @@ -0,0 +1,25 @@ +#include "DatatypeBindings.hpp" +#include "depthai/beta/datatype/SuperAnimalParserConfig.hpp" +#include "pipeline/CommonBindings.hpp" + +void bind_beta_superanimalparserconfig(pybind11::module& m, void* pCallstack) { + using namespace dai; + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_, Buffer, std::shared_ptr> config( + betaModule, "SuperAnimalParserConfig", DOC(dai, beta, SuperAnimalParserConfig)); + + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + + config.def(py::init<>()) + .def("__repr__", &beta::SuperAnimalParserConfig::str) + .def_readwrite("scoreThreshold", &beta::SuperAnimalParserConfig::scoreThreshold) + .def("setScoreThreshold", + &beta::SuperAnimalParserConfig::setScoreThreshold, + py::arg("threshold"), + DOC(dai, beta, SuperAnimalParserConfig, setScoreThreshold)) + .def("getScoreThreshold", &beta::SuperAnimalParserConfig::getScoreThreshold, DOC(dai, beta, SuperAnimalParserConfig, getScoreThreshold)) + .def("validate", &beta::SuperAnimalParserConfig::validate, DOC(dai, beta, SuperAnimalParserConfig, validate)); +} diff --git a/bindings/python/src/beta/datatype/XFeatMonoParserConfigBindings.cpp b/bindings/python/src/beta/datatype/XFeatMonoParserConfigBindings.cpp new file mode 100644 index 0000000000..68da5049d9 --- /dev/null +++ b/bindings/python/src/beta/datatype/XFeatMonoParserConfigBindings.cpp @@ -0,0 +1,22 @@ +#include "DatatypeBindings.hpp" +#include "depthai/beta/datatype/XFeatMonoParserConfig.hpp" +#include "pipeline/CommonBindings.hpp" + +void bind_beta_xfeatmonoparserconfig(pybind11::module& m, void* pCallstack) { + using namespace dai; + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_, Buffer, std::shared_ptr> config( + betaModule, "XFeatMonoParserConfig", DOC(dai, beta, XFeatMonoParserConfig)); + + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + + config.def(py::init<>()) + .def("__repr__", &beta::XFeatMonoParserConfig::str) + .def_readwrite("maxKeypoints", &beta::XFeatMonoParserConfig::maxKeypoints) + .def("setMaxKeypoints", &beta::XFeatMonoParserConfig::setMaxKeypoints, py::arg("maxKeypoints"), DOC(dai, beta, XFeatMonoParserConfig, setMaxKeypoints)) + .def("getMaxKeypoints", &beta::XFeatMonoParserConfig::getMaxKeypoints, DOC(dai, beta, XFeatMonoParserConfig, getMaxKeypoints)) + .def("validate", &beta::XFeatMonoParserConfig::validate, DOC(dai, beta, XFeatMonoParserConfig, validate)); +} diff --git a/bindings/python/src/beta/datatype/XFeatStereoParserConfigBindings.cpp b/bindings/python/src/beta/datatype/XFeatStereoParserConfigBindings.cpp new file mode 100644 index 0000000000..b7088a6a1b --- /dev/null +++ b/bindings/python/src/beta/datatype/XFeatStereoParserConfigBindings.cpp @@ -0,0 +1,25 @@ +#include "DatatypeBindings.hpp" +#include "depthai/beta/datatype/XFeatStereoParserConfig.hpp" +#include "pipeline/CommonBindings.hpp" + +void bind_beta_xfeatstereoparserconfig(pybind11::module& m, void* pCallstack) { + using namespace dai; + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_, Buffer, std::shared_ptr> config( + betaModule, "XFeatStereoParserConfig", DOC(dai, beta, XFeatStereoParserConfig)); + + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + + config.def(py::init<>()) + .def("__repr__", &beta::XFeatStereoParserConfig::str) + .def_readwrite("maxKeypoints", &beta::XFeatStereoParserConfig::maxKeypoints) + .def("setMaxKeypoints", + &beta::XFeatStereoParserConfig::setMaxKeypoints, + py::arg("maxKeypoints"), + DOC(dai, beta, XFeatStereoParserConfig, setMaxKeypoints)) + .def("getMaxKeypoints", &beta::XFeatStereoParserConfig::getMaxKeypoints, DOC(dai, beta, XFeatStereoParserConfig, getMaxKeypoints)) + .def("validate", &beta::XFeatStereoParserConfig::validate, DOC(dai, beta, XFeatStereoParserConfig, validate)); +} diff --git a/bindings/python/src/beta/datatype/YuNetParserConfigBindings.cpp b/bindings/python/src/beta/datatype/YuNetParserConfigBindings.cpp new file mode 100644 index 0000000000..d0d6d64988 --- /dev/null +++ b/bindings/python/src/beta/datatype/YuNetParserConfigBindings.cpp @@ -0,0 +1,31 @@ +#include "DatatypeBindings.hpp" +#include "depthai/beta/datatype/YuNetParserConfig.hpp" +#include "pipeline/CommonBindings.hpp" + +void bind_beta_yunetparserconfig(pybind11::module& m, void* pCallstack) { + using namespace dai; + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_, Buffer, std::shared_ptr> config( + betaModule, "YuNetParserConfig", DOC(dai, beta, YuNetParserConfig)); + + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + + config.def(py::init<>()) + .def("__repr__", &beta::YuNetParserConfig::str) + .def_readwrite("confidenceThreshold", &beta::YuNetParserConfig::confidenceThreshold) + .def_readwrite("iouThreshold", &beta::YuNetParserConfig::iouThreshold) + .def_readwrite("maxDetections", &beta::YuNetParserConfig::maxDetections) + .def("setConfidenceThreshold", + &beta::YuNetParserConfig::setConfidenceThreshold, + py::arg("threshold"), + DOC(dai, beta, YuNetParserConfig, setConfidenceThreshold)) + .def("getConfidenceThreshold", &beta::YuNetParserConfig::getConfidenceThreshold, DOC(dai, beta, YuNetParserConfig, getConfidenceThreshold)) + .def("setIouThreshold", &beta::YuNetParserConfig::setIouThreshold, py::arg("threshold"), DOC(dai, beta, YuNetParserConfig, setIouThreshold)) + .def("getIouThreshold", &beta::YuNetParserConfig::getIouThreshold, DOC(dai, beta, YuNetParserConfig, getIouThreshold)) + .def("setMaxDetections", &beta::YuNetParserConfig::setMaxDetections, py::arg("maxDetections"), DOC(dai, beta, YuNetParserConfig, setMaxDetections)) + .def("getMaxDetections", &beta::YuNetParserConfig::getMaxDetections, DOC(dai, beta, YuNetParserConfig, getMaxDetections)) + .def("validate", &beta::YuNetParserConfig::validate, DOC(dai, beta, YuNetParserConfig, validate)); +} diff --git a/bindings/python/src/beta/node/ClassificationParserBindings.cpp b/bindings/python/src/beta/node/ClassificationParserBindings.cpp new file mode 100644 index 0000000000..8c368fe9e5 --- /dev/null +++ b/bindings/python/src/beta/node/ClassificationParserBindings.cpp @@ -0,0 +1,46 @@ +#include + +#include "depthai/beta/node/ClassificationParser.hpp" +#include "pipeline/node/Common.hpp" + +void bind_beta_classificationparser(pybind11::module& m, void* pCallstack) { + using namespace dai; + using namespace dai::beta::node; + + auto classificationParser = ADD_BETA_NODE_DERIVED(ClassificationParser, dai::DeviceNode); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + classificationParser.def_readonly("input", &ClassificationParser::input, DOC(dai, beta, node, ClassificationParser, input)) + .def_readonly("out", &ClassificationParser::out, DOC(dai, beta, node, ClassificationParser, out)) + .def( + "build", + [](ClassificationParser& self, Node::Output& nnInput, const ClassificationParser::Model& model) { return self.build(nnInput, model); }, + py::arg("input"), + py::arg("model"), + DOC(dai, beta, node, ClassificationParser, build)) + .def("build", + py::overload_cast(&ClassificationParser::build), + py::arg("input"), + py::arg("head"), + DOC(dai, beta, node, ClassificationParser, build, 2)) + .def("setNNArchive", &ClassificationParser::setNNArchive, py::arg("nnArchive"), DOC(dai, beta, node, ClassificationParser, setNNArchive)) + .def("setNNArchiveHead", &ClassificationParser::setNNArchiveHead, py::arg("head"), DOC(dai, beta, node, ClassificationParser, setNNArchiveHead)) + .def("setOutputLayerName", + &ClassificationParser::setOutputLayerName, + py::arg("outputLayerName"), + DOC(dai, beta, node, ClassificationParser, setOutputLayerName)) + .def("getOutputLayerName", &ClassificationParser::getOutputLayerName, DOC(dai, beta, node, ClassificationParser, getOutputLayerName)) + .def("setClasses", &ClassificationParser::setClasses, py::arg("classes"), DOC(dai, beta, node, ClassificationParser, setClasses)) + .def("getClasses", &ClassificationParser::getClasses, DOC(dai, beta, node, ClassificationParser, getClasses)) + .def("setSoftmax", &ClassificationParser::setSoftmax, py::arg("isSoftmax"), DOC(dai, beta, node, ClassificationParser, setSoftmax)) + .def("getSoftmax", &ClassificationParser::getSoftmax, DOC(dai, beta, node, ClassificationParser, getSoftmax)) + .def("setRunOnHost", &ClassificationParser::setRunOnHost, py::arg("runOnHost"), DOC(dai, beta, node, ClassificationParser, setRunOnHost)) + .def("runOnHost", &ClassificationParser::runOnHost, DOC(dai, beta, node, ClassificationParser, runOnHost)); +} diff --git a/bindings/python/src/beta/node/ClassificationSequenceParserBindings.cpp b/bindings/python/src/beta/node/ClassificationSequenceParserBindings.cpp new file mode 100644 index 0000000000..b9a76626af --- /dev/null +++ b/bindings/python/src/beta/node/ClassificationSequenceParserBindings.cpp @@ -0,0 +1,82 @@ +#include + +#include "depthai/beta/node/ClassificationSequenceParser.hpp" +#include "pipeline/node/Common.hpp" + +void bind_beta_classificationsequenceparser(pybind11::module& m, void* pCallstack) { + using namespace dai; + using namespace dai::beta::node; + + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_ properties(betaModule, "ClassificationSequenceParserProperties"); + auto classificationSequenceParser = ADD_BETA_NODE_DERIVED(ClassificationSequenceParser, dai::DeviceNode); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + properties.def_readwrite("initialConfig", &beta::ClassificationSequenceParserProperties::initialConfig) + .def_readwrite("outputLayerName", &beta::ClassificationSequenceParserProperties::outputLayerName) + .def_readwrite("classes", &beta::ClassificationSequenceParserProperties::classes) + .def_readwrite("nClasses", &beta::ClassificationSequenceParserProperties::nClasses) + .def_readwrite("isSoftmax", &beta::ClassificationSequenceParserProperties::isSoftmax); + + classificationSequenceParser.def_readonly("inputConfig", &ClassificationSequenceParser::inputConfig, DOC(dai, beta, node, ClassificationSequenceParser, inputConfig)) + .def_readonly("initialConfig", &ClassificationSequenceParser::initialConfig, DOC(dai, beta, node, ClassificationSequenceParser, initialConfig)) + .def_readonly("input", &ClassificationSequenceParser::input, DOC(dai, beta, node, ClassificationSequenceParser, input)) + .def_readonly("out", &ClassificationSequenceParser::out, DOC(dai, beta, node, ClassificationSequenceParser, out)) + .def( + "build", + [](ClassificationSequenceParser& self, Node::Output& nnInput, const ClassificationSequenceParser::Model& model) { + return self.build(nnInput, model); + }, + py::arg("input"), + py::arg("model"), + DOC(dai, beta, node, ClassificationSequenceParser, build)) + .def("build", + py::overload_cast(&ClassificationSequenceParser::build), + py::arg("input"), + py::arg("head"), + DOC(dai, beta, node, ClassificationSequenceParser, build, 2)) + .def( + "setNNArchive", &ClassificationSequenceParser::setNNArchive, py::arg("nnArchive"), DOC(dai, beta, node, ClassificationSequenceParser, setNNArchive)) + .def("setNNArchiveHead", + &ClassificationSequenceParser::setNNArchiveHead, + py::arg("head"), + DOC(dai, beta, node, ClassificationSequenceParser, setNNArchiveHead)) + .def("setOutputLayerName", + &ClassificationSequenceParser::setOutputLayerName, + py::arg("outputLayerName"), + DOC(dai, beta, node, ClassificationSequenceParser, setOutputLayerName)) + .def("getOutputLayerName", &ClassificationSequenceParser::getOutputLayerName, DOC(dai, beta, node, ClassificationSequenceParser, getOutputLayerName)) + .def("setClasses", &ClassificationSequenceParser::setClasses, py::arg("classes"), DOC(dai, beta, node, ClassificationSequenceParser, setClasses)) + .def("getClasses", &ClassificationSequenceParser::getClasses, DOC(dai, beta, node, ClassificationSequenceParser, getClasses)) + .def("setSoftmax", &ClassificationSequenceParser::setSoftmax, py::arg("isSoftmax"), DOC(dai, beta, node, ClassificationSequenceParser, setSoftmax)) + .def("getSoftmax", &ClassificationSequenceParser::getSoftmax, DOC(dai, beta, node, ClassificationSequenceParser, getSoftmax)) + .def("setIgnoredIndexes", + &ClassificationSequenceParser::setIgnoredIndexes, + py::arg("ignoredIndexes"), + DOC(dai, beta, node, ClassificationSequenceParser, setIgnoredIndexes)) + .def("getIgnoredIndexes", &ClassificationSequenceParser::getIgnoredIndexes, DOC(dai, beta, node, ClassificationSequenceParser, getIgnoredIndexes)) + .def("setRemoveDuplicates", + &ClassificationSequenceParser::setRemoveDuplicates, + py::arg("removeDuplicates"), + DOC(dai, beta, node, ClassificationSequenceParser, setRemoveDuplicates)) + .def("getRemoveDuplicates", &ClassificationSequenceParser::getRemoveDuplicates, DOC(dai, beta, node, ClassificationSequenceParser, getRemoveDuplicates)) + .def("setConcatenateClasses", + &ClassificationSequenceParser::setConcatenateClasses, + py::arg("concatenateClasses"), + DOC(dai, beta, node, ClassificationSequenceParser, setConcatenateClasses)) + .def("getConcatenateClasses", + &ClassificationSequenceParser::getConcatenateClasses, + DOC(dai, beta, node, ClassificationSequenceParser, getConcatenateClasses)) + .def( + "setRunOnHost", &ClassificationSequenceParser::setRunOnHost, py::arg("runOnHost"), DOC(dai, beta, node, ClassificationSequenceParser, setRunOnHost)) + .def("runOnHost", &ClassificationSequenceParser::runOnHost, DOC(dai, beta, node, ClassificationSequenceParser, runOnHost)); + + classificationSequenceParser.attr("Properties") = properties; +} diff --git a/bindings/python/src/beta/node/EmbeddingsParserBindings.cpp b/bindings/python/src/beta/node/EmbeddingsParserBindings.cpp new file mode 100644 index 0000000000..da9bade8e2 --- /dev/null +++ b/bindings/python/src/beta/node/EmbeddingsParserBindings.cpp @@ -0,0 +1,40 @@ +#include + +#include "depthai/beta/node/EmbeddingsParser.hpp" +#include "pipeline/node/Common.hpp" + +void bind_beta_embeddingsparser(pybind11::module& m, void* pCallstack) { + using namespace dai; + using namespace dai::beta::node; + + auto embeddingsParser = ADD_BETA_NODE_DERIVED(EmbeddingsParser, dai::DeviceNode); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + embeddingsParser.def_readonly("input", &EmbeddingsParser::input, DOC(dai, beta, node, EmbeddingsParser, input)) + .def_readonly("out", &EmbeddingsParser::out, DOC(dai, beta, node, EmbeddingsParser, out)) + .def( + "build", + [](EmbeddingsParser& self, Node::Output& nnInput, const EmbeddingsParser::Model& model) { return self.build(nnInput, model); }, + py::arg("input"), + py::arg("model"), + DOC(dai, beta, node, EmbeddingsParser, build)) + .def("build", + py::overload_cast(&EmbeddingsParser::build), + py::arg("input"), + py::arg("head"), + DOC(dai, beta, node, EmbeddingsParser, build, 2)) + .def("setNNArchive", &EmbeddingsParser::setNNArchive, py::arg("nnArchive"), DOC(dai, beta, node, EmbeddingsParser, setNNArchive)) + .def("setNNArchiveHead", &EmbeddingsParser::setNNArchiveHead, py::arg("head"), DOC(dai, beta, node, EmbeddingsParser, setNNArchiveHead)) + .def( + "setOutputLayerName", &EmbeddingsParser::setOutputLayerName, py::arg("outputLayerName"), DOC(dai, beta, node, EmbeddingsParser, setOutputLayerName)) + .def("getOutputLayerName", &EmbeddingsParser::getOutputLayerName, DOC(dai, beta, node, EmbeddingsParser, getOutputLayerName)) + .def("setRunOnHost", &EmbeddingsParser::setRunOnHost, py::arg("runOnHost"), DOC(dai, beta, node, EmbeddingsParser, setRunOnHost)) + .def("runOnHost", &EmbeddingsParser::runOnHost, DOC(dai, beta, node, EmbeddingsParser, runOnHost)); +} diff --git a/bindings/python/src/beta/node/FastSAMParserBindings.cpp b/bindings/python/src/beta/node/FastSAMParserBindings.cpp new file mode 100644 index 0000000000..efd7516657 --- /dev/null +++ b/bindings/python/src/beta/node/FastSAMParserBindings.cpp @@ -0,0 +1,72 @@ +#include + +#include "depthai/beta/node/FastSAMParser.hpp" +#include "pipeline/node/Common.hpp" + +void bind_beta_fastsamparser(pybind11::module& m, void* pCallstack) { + using namespace dai; + using namespace dai::beta::node; + + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_ properties(betaModule, "FastSAMParserProperties"); + auto fastsamParser = ADD_BETA_NODE_DERIVED(FastSAMParser, dai::DeviceNode); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + properties.def_readwrite("initialConfig", &beta::FastSAMParserProperties::initialConfig) + .def_readwrite("numClasses", &beta::FastSAMParserProperties::numClasses) + .def_readwrite("yoloOutputs", &beta::FastSAMParserProperties::yoloOutputs) + .def_readwrite("maskOutputs", &beta::FastSAMParserProperties::maskOutputs) + .def_readwrite("protosOutput", &beta::FastSAMParserProperties::protosOutput); + + fastsamParser.def_readonly("inputConfig", &FastSAMParser::inputConfig, DOC(dai, beta, node, FastSAMParser, inputConfig)) + .def_readonly("initialConfig", &FastSAMParser::initialConfig, DOC(dai, beta, node, FastSAMParser, initialConfig)) + .def_readonly("input", &FastSAMParser::input, DOC(dai, beta, node, FastSAMParser, input)) + .def_readonly("out", &FastSAMParser::out, DOC(dai, beta, node, FastSAMParser, out)) + .def( + "build", + [](FastSAMParser& self, Node::Output& nnInput, const FastSAMParser::Model& model) { return self.build(nnInput, model); }, + py::arg("input"), + py::arg("model"), + DOC(dai, beta, node, FastSAMParser, build)) + .def("build", + py::overload_cast(&FastSAMParser::build), + py::arg("input"), + py::arg("head"), + DOC(dai, beta, node, FastSAMParser, build, 2)) + .def("setNNArchive", &FastSAMParser::setNNArchive, py::arg("nnArchive"), DOC(dai, beta, node, FastSAMParser, setNNArchive)) + .def("setNNArchiveHead", &FastSAMParser::setNNArchiveHead, py::arg("head"), DOC(dai, beta, node, FastSAMParser, setNNArchiveHead)) + .def( + "setConfidenceThreshold", &FastSAMParser::setConfidenceThreshold, py::arg("threshold"), DOC(dai, beta, node, FastSAMParser, setConfidenceThreshold)) + .def("getConfidenceThreshold", &FastSAMParser::getConfidenceThreshold, DOC(dai, beta, node, FastSAMParser, getConfidenceThreshold)) + .def("setNumClasses", &FastSAMParser::setNumClasses, py::arg("numClasses"), DOC(dai, beta, node, FastSAMParser, setNumClasses)) + .def("getNumClasses", &FastSAMParser::getNumClasses, DOC(dai, beta, node, FastSAMParser, getNumClasses)) + .def("setIouThreshold", &FastSAMParser::setIouThreshold, py::arg("iouThreshold"), DOC(dai, beta, node, FastSAMParser, setIouThreshold)) + .def("getIouThreshold", &FastSAMParser::getIouThreshold, DOC(dai, beta, node, FastSAMParser, getIouThreshold)) + .def("setMaskConfidence", &FastSAMParser::setMaskConfidence, py::arg("maskConfidence"), DOC(dai, beta, node, FastSAMParser, setMaskConfidence)) + .def("getMaskConfidence", &FastSAMParser::getMaskConfidence, DOC(dai, beta, node, FastSAMParser, getMaskConfidence)) + .def("setPrompt", &FastSAMParser::setPrompt, py::arg("prompt"), DOC(dai, beta, node, FastSAMParser, setPrompt)) + .def("getPrompt", &FastSAMParser::getPrompt, DOC(dai, beta, node, FastSAMParser, getPrompt)) + .def("setPoints", &FastSAMParser::setPoints, py::arg("x"), py::arg("y"), DOC(dai, beta, node, FastSAMParser, setPoints)) + .def("getPoints", &FastSAMParser::getPoints, DOC(dai, beta, node, FastSAMParser, getPoints)) + .def("setPointLabel", &FastSAMParser::setPointLabel, py::arg("pointLabel"), DOC(dai, beta, node, FastSAMParser, setPointLabel)) + .def("getPointLabel", &FastSAMParser::getPointLabel, DOC(dai, beta, node, FastSAMParser, getPointLabel)) + .def("setBoundingBox", &FastSAMParser::setBoundingBox, py::arg("bbox"), DOC(dai, beta, node, FastSAMParser, setBoundingBox)) + .def("getBoundingBox", &FastSAMParser::getBoundingBox, DOC(dai, beta, node, FastSAMParser, getBoundingBox)) + .def("setYoloOutputs", &FastSAMParser::setYoloOutputs, py::arg("yoloOutputs"), DOC(dai, beta, node, FastSAMParser, setYoloOutputs)) + .def("getYoloOutputs", &FastSAMParser::getYoloOutputs, DOC(dai, beta, node, FastSAMParser, getYoloOutputs)) + .def("setMaskOutputs", &FastSAMParser::setMaskOutputs, py::arg("maskOutputs"), DOC(dai, beta, node, FastSAMParser, setMaskOutputs)) + .def("getMaskOutputs", &FastSAMParser::getMaskOutputs, DOC(dai, beta, node, FastSAMParser, getMaskOutputs)) + .def("setProtosOutput", &FastSAMParser::setProtosOutput, py::arg("protosOutput"), DOC(dai, beta, node, FastSAMParser, setProtosOutput)) + .def("getProtosOutput", &FastSAMParser::getProtosOutput, DOC(dai, beta, node, FastSAMParser, getProtosOutput)) + .def("setRunOnHost", &FastSAMParser::setRunOnHost, py::arg("runOnHost"), DOC(dai, beta, node, FastSAMParser, setRunOnHost)) + .def("runOnHost", &FastSAMParser::runOnHost, DOC(dai, beta, node, FastSAMParser, runOnHost)); + + fastsamParser.attr("Properties") = properties; +} diff --git a/bindings/python/src/beta/node/HRNetParserBindings.cpp b/bindings/python/src/beta/node/HRNetParserBindings.cpp new file mode 100644 index 0000000000..f2881d12cc --- /dev/null +++ b/bindings/python/src/beta/node/HRNetParserBindings.cpp @@ -0,0 +1,56 @@ +#include + +#include "depthai/beta/node/HRNetParser.hpp" +#include "pipeline/node/Common.hpp" + +void bind_beta_hrnetparser(pybind11::module& m, void* pCallstack) { + using namespace dai; + using namespace dai::beta::node; + + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_ properties(betaModule, "HRNetParserProperties"); + auto hrnetParser = ADD_BETA_NODE_DERIVED(HRNetParser, dai::DeviceNode); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + properties.def_readwrite("initialConfig", &beta::HRNetParserProperties::initialConfig) + .def_readwrite("outputLayerName", &beta::HRNetParserProperties::outputLayerName) + .def_readwrite("labelNames", &beta::HRNetParserProperties::labelNames) + .def_readwrite("edges", &beta::HRNetParserProperties::edges); + + hrnetParser.def_readonly("inputConfig", &HRNetParser::inputConfig, DOC(dai, beta, node, HRNetParser, inputConfig)) + .def_readonly("initialConfig", &HRNetParser::initialConfig, DOC(dai, beta, node, HRNetParser, initialConfig)) + .def_readonly("input", &HRNetParser::input, DOC(dai, beta, node, HRNetParser, input)) + .def_readonly("out", &HRNetParser::out, DOC(dai, beta, node, HRNetParser, out)) + .def( + "build", + [](HRNetParser& self, Node::Output& nnInput, const HRNetParser::Model& model) { return self.build(nnInput, model); }, + py::arg("input"), + py::arg("model"), + DOC(dai, beta, node, HRNetParser, build)) + .def("build", + py::overload_cast(&HRNetParser::build), + py::arg("input"), + py::arg("head"), + DOC(dai, beta, node, HRNetParser, build, 2)) + .def("setNNArchive", &HRNetParser::setNNArchive, py::arg("nnArchive"), DOC(dai, beta, node, HRNetParser, setNNArchive)) + .def("setNNArchiveHead", &HRNetParser::setNNArchiveHead, py::arg("head"), DOC(dai, beta, node, HRNetParser, setNNArchiveHead)) + .def("setOutputLayerName", &HRNetParser::setOutputLayerName, py::arg("outputLayerName"), DOC(dai, beta, node, HRNetParser, setOutputLayerName)) + .def("getOutputLayerName", &HRNetParser::getOutputLayerName, DOC(dai, beta, node, HRNetParser, getOutputLayerName)) + .def("setScoreThreshold", &HRNetParser::setScoreThreshold, py::arg("threshold"), DOC(dai, beta, node, HRNetParser, setScoreThreshold)) + .def("getScoreThreshold", &HRNetParser::getScoreThreshold, DOC(dai, beta, node, HRNetParser, getScoreThreshold)) + .def("setLabelNames", &HRNetParser::setLabelNames, py::arg("labelNames"), DOC(dai, beta, node, HRNetParser, setLabelNames)) + .def("getLabelNames", &HRNetParser::getLabelNames, DOC(dai, beta, node, HRNetParser, getLabelNames)) + .def("setEdges", &HRNetParser::setEdges, py::arg("edges"), DOC(dai, beta, node, HRNetParser, setEdges)) + .def("getEdges", &HRNetParser::getEdges, DOC(dai, beta, node, HRNetParser, getEdges)) + .def("setRunOnHost", &HRNetParser::setRunOnHost, py::arg("runOnHost"), DOC(dai, beta, node, HRNetParser, setRunOnHost)) + .def("runOnHost", &HRNetParser::runOnHost, DOC(dai, beta, node, HRNetParser, runOnHost)); + + hrnetParser.attr("Properties") = properties; +} diff --git a/bindings/python/src/beta/node/ImageOutputParserBindings.cpp b/bindings/python/src/beta/node/ImageOutputParserBindings.cpp new file mode 100644 index 0000000000..707977fe6e --- /dev/null +++ b/bindings/python/src/beta/node/ImageOutputParserBindings.cpp @@ -0,0 +1,44 @@ +#include + +#include "depthai/beta/node/ImageOutputParser.hpp" +#include "pipeline/node/Common.hpp" + +void bind_beta_imageoutputparser(pybind11::module& m, void* pCallstack) { + using namespace dai; + using namespace dai::beta::node; + + auto imageOutputParser = ADD_BETA_NODE_DERIVED(ImageOutputParser, dai::DeviceNode); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + imageOutputParser.def_readonly("input", &ImageOutputParser::input, DOC(dai, beta, node, ImageOutputParser, input)) + .def_readonly("out", &ImageOutputParser::out, DOC(dai, beta, node, ImageOutputParser, out)) + .def( + "build", + [](ImageOutputParser& self, Node::Output& nnInput, const ImageOutputParser::Model& model) { return self.build(nnInput, model); }, + py::arg("input"), + py::arg("model"), + DOC(dai, beta, node, ImageOutputParser, build)) + .def("build", + py::overload_cast(&ImageOutputParser::build), + py::arg("input"), + py::arg("head"), + DOC(dai, beta, node, ImageOutputParser, build, 2)) + .def("setNNArchive", &ImageOutputParser::setNNArchive, py::arg("nnArchive"), DOC(dai, beta, node, ImageOutputParser, setNNArchive)) + .def("setNNArchiveHead", &ImageOutputParser::setNNArchiveHead, py::arg("head"), DOC(dai, beta, node, ImageOutputParser, setNNArchiveHead)) + .def("setOutputLayerName", + &ImageOutputParser::setOutputLayerName, + py::arg("outputLayerName"), + DOC(dai, beta, node, ImageOutputParser, setOutputLayerName)) + .def("getOutputLayerName", &ImageOutputParser::getOutputLayerName, DOC(dai, beta, node, ImageOutputParser, getOutputLayerName)) + .def("setBGROutput", &ImageOutputParser::setBGROutput, py::arg("outputIsBGR") = true, DOC(dai, beta, node, ImageOutputParser, setBGROutput)) + .def("getBGROutput", &ImageOutputParser::getBGROutput, DOC(dai, beta, node, ImageOutputParser, getBGROutput)) + .def("setRunOnHost", &ImageOutputParser::setRunOnHost, py::arg("runOnHost"), DOC(dai, beta, node, ImageOutputParser, setRunOnHost)) + .def("runOnHost", &ImageOutputParser::runOnHost, DOC(dai, beta, node, ImageOutputParser, runOnHost)); +} diff --git a/bindings/python/src/beta/node/ImgDetectionsFilterBindings.cpp b/bindings/python/src/beta/node/ImgDetectionsFilterBindings.cpp new file mode 100644 index 0000000000..4bfbdd9407 --- /dev/null +++ b/bindings/python/src/beta/node/ImgDetectionsFilterBindings.cpp @@ -0,0 +1,49 @@ + +#include "DatatypeBindings.hpp" +#include "depthai/beta/node/ImgDetectionsFilter.hpp" +#include "pipeline/node/Common.hpp" + +void bind_beta_imgdetectionsfilter(pybind11::module& m, void* pCallstack) { + using namespace dai; + using namespace dai::beta::node; + + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_, Buffer, std::shared_ptr> + imgDetectionsFilterConfig(betaModule, "ImgDetectionsFilterConfig"); + py::class_ imgDetectionsFilterProperties(betaModule, "ImgDetectionsFilterProperties"); + auto imgDetectionsFilter = ADD_BETA_NODE_DERIVED(ImgDetectionsFilter, dai::DeviceNode); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + imgDetectionsFilterConfig.def(py::init<>()) + .def("__repr__", &beta::ImgDetectionsFilterConfig::str) + .def_readwrite("labelsToKeep", &beta::ImgDetectionsFilterConfig::labelsToKeep) + .def_readwrite("labelsToReject", &beta::ImgDetectionsFilterConfig::labelsToReject) + .def_readwrite("confidenceThreshold", &beta::ImgDetectionsFilterConfig::confidenceThreshold) + .def_readwrite("minArea", &beta::ImgDetectionsFilterConfig::minArea) + .def_readwrite("nmsDisabled", &beta::ImgDetectionsFilterConfig::nmsDisabled) + .def_readwrite("nmsConfidenceThreshold", &beta::ImgDetectionsFilterConfig::nmsConfidenceThreshold) + .def_readwrite("nmsIouThreshold", &beta::ImgDetectionsFilterConfig::nmsIouThreshold) + .def_readwrite("sortingDisabled", &beta::ImgDetectionsFilterConfig::sortingDisabled) + .def_readwrite("sortDescending", &beta::ImgDetectionsFilterConfig::sortDescending) + .def_readwrite("firstK", &beta::ImgDetectionsFilterConfig::firstK) + .def("isNoOp", &beta::ImgDetectionsFilterConfig::isNoOp); + + imgDetectionsFilterProperties.def_readwrite("initialConfig", &beta::ImgDetectionsFilterProperties::initialConfig); + + imgDetectionsFilter.def_readonly("input", &ImgDetectionsFilter::input, DOC(dai, beta, node, ImgDetectionsFilter, input)) + .def_readonly("inputConfig", &ImgDetectionsFilter::inputConfig, DOC(dai, beta, node, ImgDetectionsFilter, inputConfig)) + .def_readonly("output", &ImgDetectionsFilter::output, DOC(dai, beta, node, ImgDetectionsFilter, output)) + .def_readonly("initialConfig", &ImgDetectionsFilter::initialConfig, DOC(dai, beta, node, ImgDetectionsFilter, initialConfig)) + .def("setRunOnHost", &ImgDetectionsFilter::setRunOnHost, py::arg("runOnHost"), DOC(dai, beta, node, ImgDetectionsFilter, setRunOnHost)) + .def("runOnHost", &ImgDetectionsFilter::runOnHost, DOC(dai, beta, node, ImgDetectionsFilter, runOnHost)); + + imgDetectionsFilter.attr("Properties") = imgDetectionsFilterProperties; + imgDetectionsFilter.attr("Config") = imgDetectionsFilterConfig; +} diff --git a/bindings/python/src/beta/node/KeypointParserBindings.cpp b/bindings/python/src/beta/node/KeypointParserBindings.cpp new file mode 100644 index 0000000000..8b78059ecb --- /dev/null +++ b/bindings/python/src/beta/node/KeypointParserBindings.cpp @@ -0,0 +1,49 @@ +#include + +#include "depthai/beta/node/KeypointParser.hpp" +#include "pipeline/node/Common.hpp" + +void bind_beta_keypointparser(pybind11::module& m, void* pCallstack) { + using namespace dai; + using namespace dai::beta::node; + + auto keypointParser = ADD_BETA_NODE_DERIVED(KeypointParser, dai::DeviceNode); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + keypointParser.def_readonly("input", &KeypointParser::input, DOC(dai, beta, node, KeypointParser, input)) + .def_readonly("out", &KeypointParser::out, DOC(dai, beta, node, KeypointParser, out)) + .def( + "build", + [](KeypointParser& self, Node::Output& nnInput, const KeypointParser::Model& model) { return self.build(nnInput, model); }, + py::arg("input"), + py::arg("model"), + DOC(dai, beta, node, KeypointParser, build)) + .def("build", + py::overload_cast(&KeypointParser::build), + py::arg("input"), + py::arg("head"), + DOC(dai, beta, node, KeypointParser, build, 2)) + .def("setNNArchive", &KeypointParser::setNNArchive, py::arg("nnArchive"), DOC(dai, beta, node, KeypointParser, setNNArchive)) + .def("setNNArchiveHead", &KeypointParser::setNNArchiveHead, py::arg("head"), DOC(dai, beta, node, KeypointParser, setNNArchiveHead)) + .def("setOutputLayerName", &KeypointParser::setOutputLayerName, py::arg("outputLayerName"), DOC(dai, beta, node, KeypointParser, setOutputLayerName)) + .def("getOutputLayerName", &KeypointParser::getOutputLayerName, DOC(dai, beta, node, KeypointParser, getOutputLayerName)) + .def("setScaleFactor", &KeypointParser::setScaleFactor, py::arg("scaleFactor"), DOC(dai, beta, node, KeypointParser, setScaleFactor)) + .def("getScaleFactor", &KeypointParser::getScaleFactor, DOC(dai, beta, node, KeypointParser, getScaleFactor)) + .def("setNumKeypoints", &KeypointParser::setNumKeypoints, py::arg("nKeypoints"), DOC(dai, beta, node, KeypointParser, setNumKeypoints)) + .def("getNumKeypoints", &KeypointParser::getNumKeypoints, DOC(dai, beta, node, KeypointParser, getNumKeypoints)) + .def("setScoreThreshold", &KeypointParser::setScoreThreshold, py::arg("threshold"), DOC(dai, beta, node, KeypointParser, setScoreThreshold)) + .def("getScoreThreshold", &KeypointParser::getScoreThreshold, DOC(dai, beta, node, KeypointParser, getScoreThreshold)) + .def("setLabelNames", &KeypointParser::setLabelNames, py::arg("labelNames"), DOC(dai, beta, node, KeypointParser, setLabelNames)) + .def("getLabelNames", &KeypointParser::getLabelNames, DOC(dai, beta, node, KeypointParser, getLabelNames)) + .def("setEdges", &KeypointParser::setEdges, py::arg("edges"), DOC(dai, beta, node, KeypointParser, setEdges)) + .def("getEdges", &KeypointParser::getEdges, DOC(dai, beta, node, KeypointParser, getEdges)) + .def("setRunOnHost", &KeypointParser::setRunOnHost, py::arg("runOnHost"), DOC(dai, beta, node, KeypointParser, setRunOnHost)) + .def("runOnHost", &KeypointParser::runOnHost, DOC(dai, beta, node, KeypointParser, runOnHost)); +} diff --git a/bindings/python/src/beta/node/LaneDetectionParserBindings.cpp b/bindings/python/src/beta/node/LaneDetectionParserBindings.cpp new file mode 100644 index 0000000000..555f923854 --- /dev/null +++ b/bindings/python/src/beta/node/LaneDetectionParserBindings.cpp @@ -0,0 +1,50 @@ +#include + +#include "depthai/beta/node/LaneDetectionParser.hpp" +#include "pipeline/node/Common.hpp" + +void bind_beta_lanedetectionparser(pybind11::module& m, void* pCallstack) { + using namespace dai; + using namespace dai::beta::node; + + auto laneDetectionParser = ADD_BETA_NODE_DERIVED(LaneDetectionParser, dai::DeviceNode); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + laneDetectionParser.def_readonly("input", &LaneDetectionParser::input, DOC(dai, beta, node, LaneDetectionParser, input)) + .def_readonly("out", &LaneDetectionParser::out, DOC(dai, beta, node, LaneDetectionParser, out)) + .def( + "build", + [](LaneDetectionParser& self, Node::Output& nnInput, const LaneDetectionParser::Model& model) { return self.build(nnInput, model); }, + py::arg("input"), + py::arg("model"), + DOC(dai, beta, node, LaneDetectionParser, build)) + .def("build", + py::overload_cast(&LaneDetectionParser::build), + py::arg("input"), + py::arg("head"), + DOC(dai, beta, node, LaneDetectionParser, build, 2)) + .def("setNNArchive", &LaneDetectionParser::setNNArchive, py::arg("nnArchive"), DOC(dai, beta, node, LaneDetectionParser, setNNArchive)) + .def("setNNArchiveHead", &LaneDetectionParser::setNNArchiveHead, py::arg("head"), DOC(dai, beta, node, LaneDetectionParser, setNNArchiveHead)) + .def("setOutputLayerName", + &LaneDetectionParser::setOutputLayerName, + py::arg("outputLayerName"), + DOC(dai, beta, node, LaneDetectionParser, setOutputLayerName)) + .def("getOutputLayerName", &LaneDetectionParser::getOutputLayerName, DOC(dai, beta, node, LaneDetectionParser, getOutputLayerName)) + .def("setRowAnchors", &LaneDetectionParser::setRowAnchors, py::arg("rowAnchors"), DOC(dai, beta, node, LaneDetectionParser, setRowAnchors)) + .def("getRowAnchors", &LaneDetectionParser::getRowAnchors, DOC(dai, beta, node, LaneDetectionParser, getRowAnchors)) + .def("setGridingNum", &LaneDetectionParser::setGridingNum, py::arg("gridingNum"), DOC(dai, beta, node, LaneDetectionParser, setGridingNum)) + .def("getGridingNum", &LaneDetectionParser::getGridingNum, DOC(dai, beta, node, LaneDetectionParser, getGridingNum)) + .def("setClsNumPerLane", &LaneDetectionParser::setClsNumPerLane, py::arg("clsNumPerLane"), DOC(dai, beta, node, LaneDetectionParser, setClsNumPerLane)) + .def("getClsNumPerLane", &LaneDetectionParser::getClsNumPerLane, DOC(dai, beta, node, LaneDetectionParser, getClsNumPerLane)) + .def("setInputSize", &LaneDetectionParser::setInputSize, py::arg("width"), py::arg("height"), DOC(dai, beta, node, LaneDetectionParser, setInputSize)) + .def("getInputSize", &LaneDetectionParser::getInputSize, DOC(dai, beta, node, LaneDetectionParser, getInputSize)) + .def("setRunOnHost", &LaneDetectionParser::setRunOnHost, py::arg("runOnHost"), DOC(dai, beta, node, LaneDetectionParser, setRunOnHost)) + .def("runOnHost", &LaneDetectionParser::runOnHost, DOC(dai, beta, node, LaneDetectionParser, runOnHost)); +} diff --git a/bindings/python/src/beta/node/MLSDParserBindings.cpp b/bindings/python/src/beta/node/MLSDParserBindings.cpp new file mode 100644 index 0000000000..154948cc2e --- /dev/null +++ b/bindings/python/src/beta/node/MLSDParserBindings.cpp @@ -0,0 +1,60 @@ +#include + +#include "depthai/beta/node/MLSDParser.hpp" +#include "pipeline/node/Common.hpp" + +void bind_beta_mlsdparser(pybind11::module& m, void* pCallstack) { + using namespace dai; + using namespace dai::beta::node; + + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_ properties(betaModule, "MLSDParserProperties"); + auto mlsdParser = ADD_BETA_NODE_DERIVED(MLSDParser, dai::DeviceNode); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + properties.def_readwrite("initialConfig", &beta::MLSDParserProperties::initialConfig) + .def_readwrite("outputLayerTPMap", &beta::MLSDParserProperties::outputLayerTPMap) + .def_readwrite("outputLayerHeat", &beta::MLSDParserProperties::outputLayerHeat) + .def_readwrite("inputSize", &beta::MLSDParserProperties::inputSize); + + mlsdParser.def_readonly("inputConfig", &MLSDParser::inputConfig, DOC(dai, beta, node, MLSDParser, inputConfig)) + .def_readonly("initialConfig", &MLSDParser::initialConfig, DOC(dai, beta, node, MLSDParser, initialConfig)) + .def_readonly("input", &MLSDParser::input, DOC(dai, beta, node, MLSDParser, input)) + .def_readonly("out", &MLSDParser::out, DOC(dai, beta, node, MLSDParser, out)) + .def( + "build", + [](MLSDParser& self, Node::Output& nnInput, const MLSDParser::Model& model) { return self.build(nnInput, model); }, + py::arg("input"), + py::arg("model"), + DOC(dai, beta, node, MLSDParser, build)) + .def("build", + py::overload_cast(&MLSDParser::build), + py::arg("input"), + py::arg("head"), + DOC(dai, beta, node, MLSDParser, build, 2)) + .def("setNNArchive", &MLSDParser::setNNArchive, py::arg("nnArchive"), DOC(dai, beta, node, MLSDParser, setNNArchive)) + .def("setNNArchiveHead", &MLSDParser::setNNArchiveHead, py::arg("head"), DOC(dai, beta, node, MLSDParser, setNNArchiveHead)) + .def("setOutputLayerTPMap", &MLSDParser::setOutputLayerTPMap, py::arg("outputLayerTPMap"), DOC(dai, beta, node, MLSDParser, setOutputLayerTPMap)) + .def("getOutputLayerTPMap", &MLSDParser::getOutputLayerTPMap, DOC(dai, beta, node, MLSDParser, getOutputLayerTPMap)) + .def("setOutputLayerHeat", &MLSDParser::setOutputLayerHeat, py::arg("outputLayerHeat"), DOC(dai, beta, node, MLSDParser, setOutputLayerHeat)) + .def("getOutputLayerHeat", &MLSDParser::getOutputLayerHeat, DOC(dai, beta, node, MLSDParser, getOutputLayerHeat)) + .def("setTopK", &MLSDParser::setTopK, py::arg("topK"), DOC(dai, beta, node, MLSDParser, setTopK)) + .def("getTopK", &MLSDParser::getTopK, DOC(dai, beta, node, MLSDParser, getTopK)) + .def("setScoreThreshold", &MLSDParser::setScoreThreshold, py::arg("scoreThreshold"), DOC(dai, beta, node, MLSDParser, setScoreThreshold)) + .def("getScoreThreshold", &MLSDParser::getScoreThreshold, DOC(dai, beta, node, MLSDParser, getScoreThreshold)) + .def("setDistanceThreshold", &MLSDParser::setDistanceThreshold, py::arg("distanceThreshold"), DOC(dai, beta, node, MLSDParser, setDistanceThreshold)) + .def("getDistanceThreshold", &MLSDParser::getDistanceThreshold, DOC(dai, beta, node, MLSDParser, getDistanceThreshold)) + .def("setInputSize", &MLSDParser::setInputSize, py::arg("width"), py::arg("height"), DOC(dai, beta, node, MLSDParser, setInputSize)) + .def("getInputSize", &MLSDParser::getInputSize, DOC(dai, beta, node, MLSDParser, getInputSize)) + .def("setRunOnHost", &MLSDParser::setRunOnHost, py::arg("runOnHost"), DOC(dai, beta, node, MLSDParser, setRunOnHost)) + .def("runOnHost", &MLSDParser::runOnHost, DOC(dai, beta, node, MLSDParser, runOnHost)); + + mlsdParser.attr("Properties") = properties; +} diff --git a/bindings/python/src/beta/node/MPPalmDetectionParserBindings.cpp b/bindings/python/src/beta/node/MPPalmDetectionParserBindings.cpp new file mode 100644 index 0000000000..3c41a6025c --- /dev/null +++ b/bindings/python/src/beta/node/MPPalmDetectionParserBindings.cpp @@ -0,0 +1,69 @@ +#include + +#include "depthai/beta/node/MPPalmDetectionParser.hpp" +#include "pipeline/node/Common.hpp" + +void bind_beta_mppalmdetectionparser(pybind11::module& m, void* pCallstack) { + using namespace dai; + using namespace dai::beta::node; + + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_ mpPalmDetectionParserProperties(betaModule, "MPPalmDetectionParserProperties"); + auto mpPalmDetectionParser = ADD_BETA_NODE_DERIVED(MPPalmDetectionParser, dai::DeviceNode); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + mpPalmDetectionParserProperties.def_readwrite("initialConfig", &beta::MPPalmDetectionParserProperties::initialConfig) + .def_readwrite("outputLayerNames", &beta::MPPalmDetectionParserProperties::outputLayerNames) + .def_readwrite("scale", &beta::MPPalmDetectionParserProperties::scale) + .def_readwrite("labelNames", &beta::MPPalmDetectionParserProperties::labelNames); + + mpPalmDetectionParser.def_readonly("input", &MPPalmDetectionParser::input, DOC(dai, beta, node, MPPalmDetectionParser, input)) + .def_readonly("inputConfig", &MPPalmDetectionParser::inputConfig, DOC(dai, beta, node, MPPalmDetectionParser, inputConfig)) + .def_readonly("out", &MPPalmDetectionParser::out, DOC(dai, beta, node, MPPalmDetectionParser, out)) + .def_readonly("initialConfig", &MPPalmDetectionParser::initialConfig, DOC(dai, beta, node, MPPalmDetectionParser, initialConfig)) + .def( + "build", + [](MPPalmDetectionParser& self, Node::Output& nnInput, const MPPalmDetectionParser::Model& model) { return self.build(nnInput, model); }, + py::arg("input"), + py::arg("model"), + DOC(dai, beta, node, MPPalmDetectionParser, build)) + .def("build", + py::overload_cast(&MPPalmDetectionParser::build), + py::arg("input"), + py::arg("head"), + DOC(dai, beta, node, MPPalmDetectionParser, build, 2)) + .def("setNNArchive", &MPPalmDetectionParser::setNNArchive, py::arg("nnArchive"), DOC(dai, beta, node, MPPalmDetectionParser, setNNArchive)) + .def("setNNArchiveHead", &MPPalmDetectionParser::setNNArchiveHead, py::arg("head"), DOC(dai, beta, node, MPPalmDetectionParser, setNNArchiveHead)) + .def("setOutputLayerNames", + &MPPalmDetectionParser::setOutputLayerNames, + py::arg("outputLayerNames"), + DOC(dai, beta, node, MPPalmDetectionParser, setOutputLayerNames)) + .def("getOutputLayerNames", &MPPalmDetectionParser::getOutputLayerNames, DOC(dai, beta, node, MPPalmDetectionParser, getOutputLayerNames)) + .def("setConfidenceThreshold", + &MPPalmDetectionParser::setConfidenceThreshold, + py::arg("threshold"), + DOC(dai, beta, node, MPPalmDetectionParser, setConfidenceThreshold)) + .def("getConfidenceThreshold", &MPPalmDetectionParser::getConfidenceThreshold, DOC(dai, beta, node, MPPalmDetectionParser, getConfidenceThreshold)) + .def("setIouThreshold", &MPPalmDetectionParser::setIouThreshold, py::arg("threshold"), DOC(dai, beta, node, MPPalmDetectionParser, setIouThreshold)) + .def("getIouThreshold", &MPPalmDetectionParser::getIouThreshold, DOC(dai, beta, node, MPPalmDetectionParser, getIouThreshold)) + .def("setMaxDetections", + &MPPalmDetectionParser::setMaxDetections, + py::arg("maxDetections"), + DOC(dai, beta, node, MPPalmDetectionParser, setMaxDetections)) + .def("getMaxDetections", &MPPalmDetectionParser::getMaxDetections, DOC(dai, beta, node, MPPalmDetectionParser, getMaxDetections)) + .def("setScale", &MPPalmDetectionParser::setScale, py::arg("scale"), DOC(dai, beta, node, MPPalmDetectionParser, setScale)) + .def("getScale", &MPPalmDetectionParser::getScale, DOC(dai, beta, node, MPPalmDetectionParser, getScale)) + .def("setLabelNames", &MPPalmDetectionParser::setLabelNames, py::arg("labelNames"), DOC(dai, beta, node, MPPalmDetectionParser, setLabelNames)) + .def("getLabelNames", &MPPalmDetectionParser::getLabelNames, DOC(dai, beta, node, MPPalmDetectionParser, getLabelNames)) + .def("setRunOnHost", &MPPalmDetectionParser::setRunOnHost, py::arg("runOnHost"), DOC(dai, beta, node, MPPalmDetectionParser, setRunOnHost)) + .def("runOnHost", &MPPalmDetectionParser::runOnHost, DOC(dai, beta, node, MPPalmDetectionParser, runOnHost)); + + mpPalmDetectionParser.attr("Properties") = mpPalmDetectionParserProperties; +} diff --git a/bindings/python/src/beta/node/MapOutputParserBindings.cpp b/bindings/python/src/beta/node/MapOutputParserBindings.cpp new file mode 100644 index 0000000000..0dd60f0c8f --- /dev/null +++ b/bindings/python/src/beta/node/MapOutputParserBindings.cpp @@ -0,0 +1,50 @@ +#include + +#include "depthai/beta/node/MapOutputParser.hpp" +#include "pipeline/node/Common.hpp" + +void bind_beta_mapoutputparser(pybind11::module& m, void* pCallstack) { + using namespace dai; + using namespace dai::beta::node; + + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_ properties(betaModule, "MapOutputParserProperties"); + auto mapOutputParser = ADD_BETA_NODE_DERIVED(MapOutputParser, dai::DeviceNode); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + properties.def_readwrite("initialConfig", &beta::MapOutputParserProperties::initialConfig) + .def_readwrite("outputLayerName", &beta::MapOutputParserProperties::outputLayerName); + + mapOutputParser.def_readonly("inputConfig", &MapOutputParser::inputConfig, DOC(dai, beta, node, MapOutputParser, inputConfig)) + .def_readonly("initialConfig", &MapOutputParser::initialConfig, DOC(dai, beta, node, MapOutputParser, initialConfig)) + .def_readonly("input", &MapOutputParser::input, DOC(dai, beta, node, MapOutputParser, input)) + .def_readonly("out", &MapOutputParser::out, DOC(dai, beta, node, MapOutputParser, out)) + .def( + "build", + [](MapOutputParser& self, Node::Output& nnInput, const MapOutputParser::Model& model) { return self.build(nnInput, model); }, + py::arg("input"), + py::arg("model"), + DOC(dai, beta, node, MapOutputParser, build)) + .def("build", + py::overload_cast(&MapOutputParser::build), + py::arg("input"), + py::arg("head"), + DOC(dai, beta, node, MapOutputParser, build, 2)) + .def("setNNArchive", &MapOutputParser::setNNArchive, py::arg("nnArchive"), DOC(dai, beta, node, MapOutputParser, setNNArchive)) + .def("setNNArchiveHead", &MapOutputParser::setNNArchiveHead, py::arg("head"), DOC(dai, beta, node, MapOutputParser, setNNArchiveHead)) + .def("setOutputLayerName", &MapOutputParser::setOutputLayerName, py::arg("outputLayerName"), DOC(dai, beta, node, MapOutputParser, setOutputLayerName)) + .def("getOutputLayerName", &MapOutputParser::getOutputLayerName, DOC(dai, beta, node, MapOutputParser, getOutputLayerName)) + .def("setMinMaxScaling", &MapOutputParser::setMinMaxScaling, py::arg("minMaxScaling") = true, DOC(dai, beta, node, MapOutputParser, setMinMaxScaling)) + .def("getMinMaxScaling", &MapOutputParser::getMinMaxScaling, DOC(dai, beta, node, MapOutputParser, getMinMaxScaling)) + .def("setRunOnHost", &MapOutputParser::setRunOnHost, py::arg("runOnHost"), DOC(dai, beta, node, MapOutputParser, setRunOnHost)) + .def("runOnHost", &MapOutputParser::runOnHost, DOC(dai, beta, node, MapOutputParser, runOnHost)); + + mapOutputParser.attr("Properties") = properties; +} diff --git a/bindings/python/src/beta/node/PPTextDetectionParserBindings.cpp b/bindings/python/src/beta/node/PPTextDetectionParserBindings.cpp new file mode 100644 index 0000000000..343eb6e702 --- /dev/null +++ b/bindings/python/src/beta/node/PPTextDetectionParserBindings.cpp @@ -0,0 +1,66 @@ +#include + +#include "depthai/beta/node/PPTextDetectionParser.hpp" +#include "pipeline/node/Common.hpp" + +void bind_beta_pptextdetectionparser(pybind11::module& m, void* pCallstack) { + using namespace dai; + using namespace dai::beta::node; + + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_ ppTextDetectionParserProperties(betaModule, "PPTextDetectionParserProperties"); + auto ppTextDetectionParser = ADD_BETA_NODE_DERIVED(PPTextDetectionParser, dai::DeviceNode); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + ppTextDetectionParserProperties.def_readwrite("initialConfig", &beta::PPTextDetectionParserProperties::initialConfig) + .def_readwrite("outputLayerName", &beta::PPTextDetectionParserProperties::outputLayerName); + + ppTextDetectionParser.def_readonly("input", &PPTextDetectionParser::input, DOC(dai, beta, node, PPTextDetectionParser, input)) + .def_readonly("inputConfig", &PPTextDetectionParser::inputConfig, DOC(dai, beta, node, PPTextDetectionParser, inputConfig)) + .def_readonly("out", &PPTextDetectionParser::out, DOC(dai, beta, node, PPTextDetectionParser, out)) + .def_readonly("initialConfig", &PPTextDetectionParser::initialConfig, DOC(dai, beta, node, PPTextDetectionParser, initialConfig)) + .def( + "build", + [](PPTextDetectionParser& self, Node::Output& nnInput, const PPTextDetectionParser::Model& model) { return self.build(nnInput, model); }, + py::arg("input"), + py::arg("model"), + DOC(dai, beta, node, PPTextDetectionParser, build)) + .def("build", + py::overload_cast(&PPTextDetectionParser::build), + py::arg("input"), + py::arg("head"), + DOC(dai, beta, node, PPTextDetectionParser, build, 2)) + .def("setNNArchive", &PPTextDetectionParser::setNNArchive, py::arg("nnArchive"), DOC(dai, beta, node, PPTextDetectionParser, setNNArchive)) + .def("setNNArchiveHead", &PPTextDetectionParser::setNNArchiveHead, py::arg("head"), DOC(dai, beta, node, PPTextDetectionParser, setNNArchiveHead)) + .def("setOutputLayerName", + &PPTextDetectionParser::setOutputLayerName, + py::arg("outputLayerName"), + DOC(dai, beta, node, PPTextDetectionParser, setOutputLayerName)) + .def("getOutputLayerName", &PPTextDetectionParser::getOutputLayerName, DOC(dai, beta, node, PPTextDetectionParser, getOutputLayerName)) + .def("setConfidenceThreshold", + &PPTextDetectionParser::setConfidenceThreshold, + py::arg("threshold"), + DOC(dai, beta, node, PPTextDetectionParser, setConfidenceThreshold)) + .def("getConfidenceThreshold", &PPTextDetectionParser::getConfidenceThreshold, DOC(dai, beta, node, PPTextDetectionParser, getConfidenceThreshold)) + .def("setMaskThreshold", + &PPTextDetectionParser::setMaskThreshold, + py::arg("maskThreshold"), + DOC(dai, beta, node, PPTextDetectionParser, setMaskThreshold)) + .def("getMaskThreshold", &PPTextDetectionParser::getMaskThreshold, DOC(dai, beta, node, PPTextDetectionParser, getMaskThreshold)) + .def("setMaxDetections", + &PPTextDetectionParser::setMaxDetections, + py::arg("maxDetections"), + DOC(dai, beta, node, PPTextDetectionParser, setMaxDetections)) + .def("getMaxDetections", &PPTextDetectionParser::getMaxDetections, DOC(dai, beta, node, PPTextDetectionParser, getMaxDetections)) + .def("setRunOnHost", &PPTextDetectionParser::setRunOnHost, py::arg("runOnHost"), DOC(dai, beta, node, PPTextDetectionParser, setRunOnHost)) + .def("runOnHost", &PPTextDetectionParser::runOnHost, DOC(dai, beta, node, PPTextDetectionParser, runOnHost)); + + ppTextDetectionParser.attr("Properties") = ppTextDetectionParserProperties; +} diff --git a/bindings/python/src/beta/node/RFDETRParserBindings.cpp b/bindings/python/src/beta/node/RFDETRParserBindings.cpp new file mode 100644 index 0000000000..682ef2f301 --- /dev/null +++ b/bindings/python/src/beta/node/RFDETRParserBindings.cpp @@ -0,0 +1,60 @@ +#include + +#include "depthai/beta/node/RFDETRParser.hpp" +#include "pipeline/node/Common.hpp" + +void bind_beta_rfdetrparser(pybind11::module& m, void* pCallstack) { + using namespace dai; + using namespace dai::beta::node; + + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_ rfdetrParserProperties(betaModule, "RFDETRParserProperties"); + auto rfdetrParser = ADD_BETA_NODE_DERIVED(RFDETRParser, dai::DeviceNode); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + rfdetrParserProperties.def_readwrite("initialConfig", &beta::RFDETRParserProperties::initialConfig) + .def_readwrite("labelNames", &beta::RFDETRParserProperties::labelNames) + .def_readwrite("outputLayerNames", &beta::RFDETRParserProperties::outputLayerNames) + .def_readwrite("inputSize", &beta::RFDETRParserProperties::inputSize); + + rfdetrParser.def_readonly("input", &RFDETRParser::input, DOC(dai, beta, node, RFDETRParser, input)) + .def_readonly("inputConfig", &RFDETRParser::inputConfig, DOC(dai, beta, node, RFDETRParser, inputConfig)) + .def_readonly("out", &RFDETRParser::out, DOC(dai, beta, node, RFDETRParser, out)) + .def_readonly("initialConfig", &RFDETRParser::initialConfig, DOC(dai, beta, node, RFDETRParser, initialConfig)) + .def( + "build", + [](RFDETRParser& self, Node::Output& nnInput, const RFDETRParser::Model& model) { return self.build(nnInput, model); }, + py::arg("input"), + py::arg("model"), + DOC(dai, beta, node, RFDETRParser, build)) + .def("build", + py::overload_cast(&RFDETRParser::build), + py::arg("input"), + py::arg("head"), + DOC(dai, beta, node, RFDETRParser, build, 2)) + .def("setNNArchive", &RFDETRParser::setNNArchive, py::arg("nnArchive"), DOC(dai, beta, node, RFDETRParser, setNNArchive)) + .def("setNNArchiveHead", &RFDETRParser::setNNArchiveHead, py::arg("head"), DOC(dai, beta, node, RFDETRParser, setNNArchiveHead)) + .def("setConfidenceThreshold", &RFDETRParser::setConfidenceThreshold, py::arg("threshold"), DOC(dai, beta, node, RFDETRParser, setConfidenceThreshold)) + .def("getConfidenceThreshold", &RFDETRParser::getConfidenceThreshold, DOC(dai, beta, node, RFDETRParser, getConfidenceThreshold)) + .def("setMaxDetections", &RFDETRParser::setMaxDetections, py::arg("maxDetections"), DOC(dai, beta, node, RFDETRParser, setMaxDetections)) + .def("getMaxDetections", &RFDETRParser::getMaxDetections, DOC(dai, beta, node, RFDETRParser, getMaxDetections)) + .def("setLabelNames", &RFDETRParser::setLabelNames, py::arg("labelNames"), DOC(dai, beta, node, RFDETRParser, setLabelNames)) + .def("getLabelNames", &RFDETRParser::getLabelNames, DOC(dai, beta, node, RFDETRParser, getLabelNames)) + .def("setMaskConfidence", &RFDETRParser::setMaskConfidence, py::arg("maskConfidence"), DOC(dai, beta, node, RFDETRParser, setMaskConfidence)) + .def("getMaskConfidence", &RFDETRParser::getMaskConfidence, DOC(dai, beta, node, RFDETRParser, getMaskConfidence)) + .def("setOutputLayerNames", &RFDETRParser::setOutputLayerNames, py::arg("outputLayerNames"), DOC(dai, beta, node, RFDETRParser, setOutputLayerNames)) + .def("getOutputLayerNames", &RFDETRParser::getOutputLayerNames, DOC(dai, beta, node, RFDETRParser, getOutputLayerNames)) + .def("setInputSize", &RFDETRParser::setInputSize, py::arg("width"), py::arg("height"), DOC(dai, beta, node, RFDETRParser, setInputSize)) + .def("getInputSize", &RFDETRParser::getInputSize, DOC(dai, beta, node, RFDETRParser, getInputSize)) + .def("setRunOnHost", &RFDETRParser::setRunOnHost, py::arg("runOnHost"), DOC(dai, beta, node, RFDETRParser, setRunOnHost)) + .def("runOnHost", &RFDETRParser::runOnHost, DOC(dai, beta, node, RFDETRParser, runOnHost)); + + rfdetrParser.attr("Properties") = rfdetrParserProperties; +} diff --git a/bindings/python/src/beta/node/RegressionParserBindings.cpp b/bindings/python/src/beta/node/RegressionParserBindings.cpp new file mode 100644 index 0000000000..983474f440 --- /dev/null +++ b/bindings/python/src/beta/node/RegressionParserBindings.cpp @@ -0,0 +1,40 @@ +#include + +#include "depthai/beta/node/RegressionParser.hpp" +#include "pipeline/node/Common.hpp" + +void bind_beta_regressionparser(pybind11::module& m, void* pCallstack) { + using namespace dai; + using namespace dai::beta::node; + + auto regressionParser = ADD_BETA_NODE_DERIVED(RegressionParser, dai::DeviceNode); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + regressionParser.def_readonly("input", &RegressionParser::input, DOC(dai, beta, node, RegressionParser, input)) + .def_readonly("out", &RegressionParser::out, DOC(dai, beta, node, RegressionParser, out)) + .def( + "build", + [](RegressionParser& self, Node::Output& nnInput, const RegressionParser::Model& model) { return self.build(nnInput, model); }, + py::arg("input"), + py::arg("model"), + DOC(dai, beta, node, RegressionParser, build)) + .def("build", + py::overload_cast(&RegressionParser::build), + py::arg("input"), + py::arg("head"), + DOC(dai, beta, node, RegressionParser, build, 2)) + .def("setNNArchive", &RegressionParser::setNNArchive, py::arg("nnArchive"), DOC(dai, beta, node, RegressionParser, setNNArchive)) + .def("setNNArchiveHead", &RegressionParser::setNNArchiveHead, py::arg("head"), DOC(dai, beta, node, RegressionParser, setNNArchiveHead)) + .def( + "setOutputLayerName", &RegressionParser::setOutputLayerName, py::arg("outputLayerName"), DOC(dai, beta, node, RegressionParser, setOutputLayerName)) + .def("getOutputLayerName", &RegressionParser::getOutputLayerName, DOC(dai, beta, node, RegressionParser, getOutputLayerName)) + .def("setRunOnHost", &RegressionParser::setRunOnHost, py::arg("runOnHost"), DOC(dai, beta, node, RegressionParser, setRunOnHost)) + .def("runOnHost", &RegressionParser::runOnHost, DOC(dai, beta, node, RegressionParser, runOnHost)); +} diff --git a/bindings/python/src/beta/node/SCRFDParserBindings.cpp b/bindings/python/src/beta/node/SCRFDParserBindings.cpp new file mode 100644 index 0000000000..66f97f4d05 --- /dev/null +++ b/bindings/python/src/beta/node/SCRFDParserBindings.cpp @@ -0,0 +1,66 @@ +#include + +#include "depthai/beta/node/SCRFDParser.hpp" +#include "pipeline/node/Common.hpp" + +void bind_beta_scrfdparser(pybind11::module& m, void* pCallstack) { + using namespace dai; + using namespace dai::beta::node; + + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_ scrfdParserProperties(betaModule, "SCRFDParserProperties"); + auto scrfdParser = ADD_BETA_NODE_DERIVED(SCRFDParser, dai::DeviceNode); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + scrfdParserProperties.def_readwrite("initialConfig", &beta::SCRFDParserProperties::initialConfig) + .def_readwrite("outputLayerNames", &beta::SCRFDParserProperties::outputLayerNames) + .def_readwrite("inputSize", &beta::SCRFDParserProperties::inputSize) + .def_readwrite("featStrideFpn", &beta::SCRFDParserProperties::featStrideFpn) + .def_readwrite("numAnchors", &beta::SCRFDParserProperties::numAnchors) + .def_readwrite("labelNames", &beta::SCRFDParserProperties::labelNames); + + scrfdParser.def_readonly("input", &SCRFDParser::input, DOC(dai, beta, node, SCRFDParser, input)) + .def_readonly("inputConfig", &SCRFDParser::inputConfig, DOC(dai, beta, node, SCRFDParser, inputConfig)) + .def_readonly("out", &SCRFDParser::out, DOC(dai, beta, node, SCRFDParser, out)) + .def_readonly("initialConfig", &SCRFDParser::initialConfig, DOC(dai, beta, node, SCRFDParser, initialConfig)) + .def( + "build", + [](SCRFDParser& self, Node::Output& nnInput, const SCRFDParser::Model& model) { return self.build(nnInput, model); }, + py::arg("input"), + py::arg("model"), + DOC(dai, beta, node, SCRFDParser, build)) + .def("build", + py::overload_cast(&SCRFDParser::build), + py::arg("input"), + py::arg("head"), + DOC(dai, beta, node, SCRFDParser, build, 2)) + .def("setNNArchive", &SCRFDParser::setNNArchive, py::arg("nnArchive"), DOC(dai, beta, node, SCRFDParser, setNNArchive)) + .def("setNNArchiveHead", &SCRFDParser::setNNArchiveHead, py::arg("head"), DOC(dai, beta, node, SCRFDParser, setNNArchiveHead)) + .def("setOutputLayerNames", &SCRFDParser::setOutputLayerNames, py::arg("outputLayerNames"), DOC(dai, beta, node, SCRFDParser, setOutputLayerNames)) + .def("getOutputLayerNames", &SCRFDParser::getOutputLayerNames, DOC(dai, beta, node, SCRFDParser, getOutputLayerNames)) + .def("setConfidenceThreshold", &SCRFDParser::setConfidenceThreshold, py::arg("threshold"), DOC(dai, beta, node, SCRFDParser, setConfidenceThreshold)) + .def("getConfidenceThreshold", &SCRFDParser::getConfidenceThreshold, DOC(dai, beta, node, SCRFDParser, getConfidenceThreshold)) + .def("setIouThreshold", &SCRFDParser::setIouThreshold, py::arg("threshold"), DOC(dai, beta, node, SCRFDParser, setIouThreshold)) + .def("getIouThreshold", &SCRFDParser::getIouThreshold, DOC(dai, beta, node, SCRFDParser, getIouThreshold)) + .def("setMaxDetections", &SCRFDParser::setMaxDetections, py::arg("maxDetections"), DOC(dai, beta, node, SCRFDParser, setMaxDetections)) + .def("getMaxDetections", &SCRFDParser::getMaxDetections, DOC(dai, beta, node, SCRFDParser, getMaxDetections)) + .def("setInputSize", &SCRFDParser::setInputSize, py::arg("width"), py::arg("height"), DOC(dai, beta, node, SCRFDParser, setInputSize)) + .def("getInputSize", &SCRFDParser::getInputSize, DOC(dai, beta, node, SCRFDParser, getInputSize)) + .def("setFeatStrideFPN", &SCRFDParser::setFeatStrideFPN, py::arg("featStrideFpn"), DOC(dai, beta, node, SCRFDParser, setFeatStrideFPN)) + .def("getFeatStrideFPN", &SCRFDParser::getFeatStrideFPN, DOC(dai, beta, node, SCRFDParser, getFeatStrideFPN)) + .def("setNumAnchors", &SCRFDParser::setNumAnchors, py::arg("numAnchors"), DOC(dai, beta, node, SCRFDParser, setNumAnchors)) + .def("getNumAnchors", &SCRFDParser::getNumAnchors, DOC(dai, beta, node, SCRFDParser, getNumAnchors)) + .def("setLabelNames", &SCRFDParser::setLabelNames, py::arg("labelNames"), DOC(dai, beta, node, SCRFDParser, setLabelNames)) + .def("getLabelNames", &SCRFDParser::getLabelNames, DOC(dai, beta, node, SCRFDParser, getLabelNames)) + .def("setRunOnHost", &SCRFDParser::setRunOnHost, py::arg("runOnHost"), DOC(dai, beta, node, SCRFDParser, setRunOnHost)) + .def("runOnHost", &SCRFDParser::runOnHost, DOC(dai, beta, node, SCRFDParser, runOnHost)); + + scrfdParser.attr("Properties") = scrfdParserProperties; +} diff --git a/bindings/python/src/beta/node/SuperAnimalParserBindings.cpp b/bindings/python/src/beta/node/SuperAnimalParserBindings.cpp new file mode 100644 index 0000000000..5f938632da --- /dev/null +++ b/bindings/python/src/beta/node/SuperAnimalParserBindings.cpp @@ -0,0 +1,65 @@ +#include + +#include "depthai/beta/node/SuperAnimalParser.hpp" +#include "pipeline/node/Common.hpp" + +void bind_beta_superanimalparser(pybind11::module& m, void* pCallstack) { + using namespace dai; + using namespace dai::beta::node; + + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_ properties(betaModule, "SuperAnimalParserProperties"); + auto superAnimalParser = ADD_BETA_NODE_DERIVED(SuperAnimalParser, dai::DeviceNode); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + properties.def_readwrite("initialConfig", &beta::SuperAnimalParserProperties::initialConfig) + .def_readwrite("outputLayerName", &beta::SuperAnimalParserProperties::outputLayerName) + .def_readwrite("scaleFactor", &beta::SuperAnimalParserProperties::scaleFactor) + .def_readwrite("nKeypoints", &beta::SuperAnimalParserProperties::nKeypoints) + .def_readwrite("labelNames", &beta::SuperAnimalParserProperties::labelNames) + .def_readwrite("edges", &beta::SuperAnimalParserProperties::edges); + + superAnimalParser.def_readonly("inputConfig", &SuperAnimalParser::inputConfig, DOC(dai, beta, node, SuperAnimalParser, inputConfig)) + .def_readonly("initialConfig", &SuperAnimalParser::initialConfig, DOC(dai, beta, node, SuperAnimalParser, initialConfig)) + .def_readonly("input", &SuperAnimalParser::input, DOC(dai, beta, node, SuperAnimalParser, input)) + .def_readonly("out", &SuperAnimalParser::out, DOC(dai, beta, node, SuperAnimalParser, out)) + .def( + "build", + [](SuperAnimalParser& self, Node::Output& nnInput, const SuperAnimalParser::Model& model) { return self.build(nnInput, model); }, + py::arg("input"), + py::arg("model"), + DOC(dai, beta, node, SuperAnimalParser, build)) + .def("build", + py::overload_cast(&SuperAnimalParser::build), + py::arg("input"), + py::arg("head"), + DOC(dai, beta, node, SuperAnimalParser, build, 2)) + .def("setNNArchive", &SuperAnimalParser::setNNArchive, py::arg("nnArchive"), DOC(dai, beta, node, SuperAnimalParser, setNNArchive)) + .def("setNNArchiveHead", &SuperAnimalParser::setNNArchiveHead, py::arg("head"), DOC(dai, beta, node, SuperAnimalParser, setNNArchiveHead)) + .def("setOutputLayerName", + &SuperAnimalParser::setOutputLayerName, + py::arg("outputLayerName"), + DOC(dai, beta, node, SuperAnimalParser, setOutputLayerName)) + .def("getOutputLayerName", &SuperAnimalParser::getOutputLayerName, DOC(dai, beta, node, SuperAnimalParser, getOutputLayerName)) + .def("setScaleFactor", &SuperAnimalParser::setScaleFactor, py::arg("scaleFactor"), DOC(dai, beta, node, SuperAnimalParser, setScaleFactor)) + .def("getScaleFactor", &SuperAnimalParser::getScaleFactor, DOC(dai, beta, node, SuperAnimalParser, getScaleFactor)) + .def("setNumKeypoints", &SuperAnimalParser::setNumKeypoints, py::arg("nKeypoints"), DOC(dai, beta, node, SuperAnimalParser, setNumKeypoints)) + .def("getNumKeypoints", &SuperAnimalParser::getNumKeypoints, DOC(dai, beta, node, SuperAnimalParser, getNumKeypoints)) + .def("setScoreThreshold", &SuperAnimalParser::setScoreThreshold, py::arg("threshold"), DOC(dai, beta, node, SuperAnimalParser, setScoreThreshold)) + .def("getScoreThreshold", &SuperAnimalParser::getScoreThreshold, DOC(dai, beta, node, SuperAnimalParser, getScoreThreshold)) + .def("setLabelNames", &SuperAnimalParser::setLabelNames, py::arg("labelNames"), DOC(dai, beta, node, SuperAnimalParser, setLabelNames)) + .def("getLabelNames", &SuperAnimalParser::getLabelNames, DOC(dai, beta, node, SuperAnimalParser, getLabelNames)) + .def("setEdges", &SuperAnimalParser::setEdges, py::arg("edges"), DOC(dai, beta, node, SuperAnimalParser, setEdges)) + .def("getEdges", &SuperAnimalParser::getEdges, DOC(dai, beta, node, SuperAnimalParser, getEdges)) + .def("setRunOnHost", &SuperAnimalParser::setRunOnHost, py::arg("runOnHost"), DOC(dai, beta, node, SuperAnimalParser, setRunOnHost)) + .def("runOnHost", &SuperAnimalParser::runOnHost, DOC(dai, beta, node, SuperAnimalParser, runOnHost)); + + superAnimalParser.attr("Properties") = properties; +} diff --git a/bindings/python/src/beta/node/XFeatMonoParserBindings.cpp b/bindings/python/src/beta/node/XFeatMonoParserBindings.cpp new file mode 100644 index 0000000000..1b56b208d0 --- /dev/null +++ b/bindings/python/src/beta/node/XFeatMonoParserBindings.cpp @@ -0,0 +1,72 @@ +#include + +#include "depthai/beta/node/XFeatMonoParser.hpp" +#include "pipeline/node/Common.hpp" + +void bind_beta_xfeatmonoparser(pybind11::module& m, void* pCallstack) { + using namespace dai; + using namespace dai::beta::node; + + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_ properties(betaModule, "XFeatMonoParserProperties"); + auto xfeatMonoParser = ADD_BETA_NODE_DERIVED(XFeatMonoParser, dai::DeviceNode); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + properties.def_readwrite("initialConfig", &beta::XFeatMonoParserProperties::initialConfig) + .def_readwrite("outputLayerFeats", &beta::XFeatMonoParserProperties::outputLayerFeats) + .def_readwrite("outputLayerKeypoints", &beta::XFeatMonoParserProperties::outputLayerKeypoints) + .def_readwrite("outputLayerHeatmaps", &beta::XFeatMonoParserProperties::outputLayerHeatmaps) + .def_readwrite("originalSize", &beta::XFeatMonoParserProperties::originalSize) + .def_readwrite("inputSize", &beta::XFeatMonoParserProperties::inputSize); + + xfeatMonoParser.def_readonly("inputConfig", &XFeatMonoParser::inputConfig, DOC(dai, beta, node, XFeatMonoParser, inputConfig)) + .def_readonly("initialConfig", &XFeatMonoParser::initialConfig, DOC(dai, beta, node, XFeatMonoParser, initialConfig)) + .def_readonly("input", &XFeatMonoParser::input, DOC(dai, beta, node, XFeatMonoParser, input)) + .def_readonly("out", &XFeatMonoParser::out, DOC(dai, beta, node, XFeatMonoParser, out)) + .def( + "build", + [](XFeatMonoParser& self, Node::Output& nnInput, const XFeatMonoParser::Model& model) { return self.build(nnInput, model); }, + py::arg("input"), + py::arg("model"), + DOC(dai, beta, node, XFeatMonoParser, build)) + .def("build", + py::overload_cast(&XFeatMonoParser::build), + py::arg("input"), + py::arg("head"), + DOC(dai, beta, node, XFeatMonoParser, build, 2)) + .def("setNNArchive", &XFeatMonoParser::setNNArchive, py::arg("nnArchive"), DOC(dai, beta, node, XFeatMonoParser, setNNArchive)) + .def("setNNArchiveHead", &XFeatMonoParser::setNNArchiveHead, py::arg("head"), DOC(dai, beta, node, XFeatMonoParser, setNNArchiveHead)) + .def("setOutputLayerFeats", + &XFeatMonoParser::setOutputLayerFeats, + py::arg("outputLayerFeats"), + DOC(dai, beta, node, XFeatMonoParser, setOutputLayerFeats)) + .def("getOutputLayerFeats", &XFeatMonoParser::getOutputLayerFeats, DOC(dai, beta, node, XFeatMonoParser, getOutputLayerFeats)) + .def("setOutputLayerKeypoints", + &XFeatMonoParser::setOutputLayerKeypoints, + py::arg("outputLayerKeypoints"), + DOC(dai, beta, node, XFeatMonoParser, setOutputLayerKeypoints)) + .def("getOutputLayerKeypoints", &XFeatMonoParser::getOutputLayerKeypoints, DOC(dai, beta, node, XFeatMonoParser, getOutputLayerKeypoints)) + .def("setOutputLayerHeatmaps", + &XFeatMonoParser::setOutputLayerHeatmaps, + py::arg("outputLayerHeatmaps"), + DOC(dai, beta, node, XFeatMonoParser, setOutputLayerHeatmaps)) + .def("getOutputLayerHeatmaps", &XFeatMonoParser::getOutputLayerHeatmaps, DOC(dai, beta, node, XFeatMonoParser, getOutputLayerHeatmaps)) + .def("setOriginalSize", &XFeatMonoParser::setOriginalSize, py::arg("width"), py::arg("height"), DOC(dai, beta, node, XFeatMonoParser, setOriginalSize)) + .def("getOriginalSize", &XFeatMonoParser::getOriginalSize, DOC(dai, beta, node, XFeatMonoParser, getOriginalSize)) + .def("setInputSize", &XFeatMonoParser::setInputSize, py::arg("width"), py::arg("height"), DOC(dai, beta, node, XFeatMonoParser, setInputSize)) + .def("getInputSize", &XFeatMonoParser::getInputSize, DOC(dai, beta, node, XFeatMonoParser, getInputSize)) + .def("setMaxKeypoints", &XFeatMonoParser::setMaxKeypoints, py::arg("maxKeypoints"), DOC(dai, beta, node, XFeatMonoParser, setMaxKeypoints)) + .def("getMaxKeypoints", &XFeatMonoParser::getMaxKeypoints, DOC(dai, beta, node, XFeatMonoParser, getMaxKeypoints)) + .def("setTrigger", &XFeatMonoParser::setTrigger, DOC(dai, beta, node, XFeatMonoParser, setTrigger)) + .def("setRunOnHost", &XFeatMonoParser::setRunOnHost, py::arg("runOnHost"), DOC(dai, beta, node, XFeatMonoParser, setRunOnHost)) + .def("runOnHost", &XFeatMonoParser::runOnHost, DOC(dai, beta, node, XFeatMonoParser, runOnHost)); + + xfeatMonoParser.attr("Properties") = properties; +} diff --git a/bindings/python/src/beta/node/XFeatStereoParserBindings.cpp b/bindings/python/src/beta/node/XFeatStereoParserBindings.cpp new file mode 100644 index 0000000000..7afcfb4fa0 --- /dev/null +++ b/bindings/python/src/beta/node/XFeatStereoParserBindings.cpp @@ -0,0 +1,80 @@ +#include + +#include "depthai/beta/node/XFeatStereoParser.hpp" +#include "pipeline/node/Common.hpp" + +void bind_beta_xfeatstereoparser(pybind11::module& m, void* pCallstack) { + using namespace dai; + using namespace dai::beta::node; + + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_ properties(betaModule, "XFeatStereoParserProperties"); + auto xfeatStereoParser = ADD_BETA_NODE_DERIVED(XFeatStereoParser, dai::DeviceNode); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + properties.def_readwrite("initialConfig", &beta::XFeatStereoParserProperties::initialConfig) + .def_readwrite("outputLayerFeats", &beta::XFeatStereoParserProperties::outputLayerFeats) + .def_readwrite("outputLayerKeypoints", &beta::XFeatStereoParserProperties::outputLayerKeypoints) + .def_readwrite("outputLayerHeatmaps", &beta::XFeatStereoParserProperties::outputLayerHeatmaps) + .def_readwrite("originalSize", &beta::XFeatStereoParserProperties::originalSize) + .def_readwrite("inputSize", &beta::XFeatStereoParserProperties::inputSize); + + xfeatStereoParser.def_readonly("inputConfig", &XFeatStereoParser::inputConfig, DOC(dai, beta, node, XFeatStereoParser, inputConfig)) + .def_readonly("initialConfig", &XFeatStereoParser::initialConfig, DOC(dai, beta, node, XFeatStereoParser, initialConfig)) + .def_readonly("referenceInput", &XFeatStereoParser::referenceInput, DOC(dai, beta, node, XFeatStereoParser, referenceInput)) + .def_readonly("targetInput", &XFeatStereoParser::targetInput, DOC(dai, beta, node, XFeatStereoParser, targetInput)) + .def_readonly("out", &XFeatStereoParser::out, DOC(dai, beta, node, XFeatStereoParser, out)) + .def( + "build", + [](XFeatStereoParser& self, Node::Output& reference, Node::Output& target, const XFeatStereoParser::Model& model) { + return self.build(reference, target, model); + }, + py::arg("reference"), + py::arg("target"), + py::arg("model"), + DOC(dai, beta, node, XFeatStereoParser, build)) + .def("build", + py::overload_cast(&XFeatStereoParser::build), + py::arg("reference"), + py::arg("target"), + py::arg("head"), + DOC(dai, beta, node, XFeatStereoParser, build, 2)) + .def("setNNArchive", &XFeatStereoParser::setNNArchive, py::arg("nnArchive"), DOC(dai, beta, node, XFeatStereoParser, setNNArchive)) + .def("setNNArchiveHead", &XFeatStereoParser::setNNArchiveHead, py::arg("head"), DOC(dai, beta, node, XFeatStereoParser, setNNArchiveHead)) + .def("setOutputLayerFeats", + &XFeatStereoParser::setOutputLayerFeats, + py::arg("outputLayerFeats"), + DOC(dai, beta, node, XFeatStereoParser, setOutputLayerFeats)) + .def("getOutputLayerFeats", &XFeatStereoParser::getOutputLayerFeats, DOC(dai, beta, node, XFeatStereoParser, getOutputLayerFeats)) + .def("setOutputLayerKeypoints", + &XFeatStereoParser::setOutputLayerKeypoints, + py::arg("outputLayerKeypoints"), + DOC(dai, beta, node, XFeatStereoParser, setOutputLayerKeypoints)) + .def("getOutputLayerKeypoints", &XFeatStereoParser::getOutputLayerKeypoints, DOC(dai, beta, node, XFeatStereoParser, getOutputLayerKeypoints)) + .def("setOutputLayerHeatmaps", + &XFeatStereoParser::setOutputLayerHeatmaps, + py::arg("outputLayerHeatmaps"), + DOC(dai, beta, node, XFeatStereoParser, setOutputLayerHeatmaps)) + .def("getOutputLayerHeatmaps", &XFeatStereoParser::getOutputLayerHeatmaps, DOC(dai, beta, node, XFeatStereoParser, getOutputLayerHeatmaps)) + .def("setOriginalSize", + &XFeatStereoParser::setOriginalSize, + py::arg("width"), + py::arg("height"), + DOC(dai, beta, node, XFeatStereoParser, setOriginalSize)) + .def("getOriginalSize", &XFeatStereoParser::getOriginalSize, DOC(dai, beta, node, XFeatStereoParser, getOriginalSize)) + .def("setInputSize", &XFeatStereoParser::setInputSize, py::arg("width"), py::arg("height"), DOC(dai, beta, node, XFeatStereoParser, setInputSize)) + .def("getInputSize", &XFeatStereoParser::getInputSize, DOC(dai, beta, node, XFeatStereoParser, getInputSize)) + .def("setMaxKeypoints", &XFeatStereoParser::setMaxKeypoints, py::arg("maxKeypoints"), DOC(dai, beta, node, XFeatStereoParser, setMaxKeypoints)) + .def("getMaxKeypoints", &XFeatStereoParser::getMaxKeypoints, DOC(dai, beta, node, XFeatStereoParser, getMaxKeypoints)) + .def("setRunOnHost", &XFeatStereoParser::setRunOnHost, py::arg("runOnHost"), DOC(dai, beta, node, XFeatStereoParser, setRunOnHost)) + .def("runOnHost", &XFeatStereoParser::runOnHost, DOC(dai, beta, node, XFeatStereoParser, runOnHost)); + + xfeatStereoParser.attr("Properties") = properties; +} diff --git a/bindings/python/src/beta/node/YuNetParserBindings.cpp b/bindings/python/src/beta/node/YuNetParserBindings.cpp new file mode 100644 index 0000000000..6d1668fd07 --- /dev/null +++ b/bindings/python/src/beta/node/YuNetParserBindings.cpp @@ -0,0 +1,66 @@ +#include + +#include "depthai/beta/node/YuNetParser.hpp" +#include "pipeline/node/Common.hpp" + +void bind_beta_yunetparser(pybind11::module& m, void* pCallstack) { + using namespace dai; + using namespace dai::beta::node; + + auto betaModule = m.def_submodule("beta", "Experimental APIs"); + py::class_ yunetParserProperties(betaModule, "YuNetParserProperties"); + auto yunetParser = ADD_BETA_NODE_DERIVED(YuNetParser, dai::DeviceNode); + + /////////////////////////////////////////////////////////////////////// + // Callstack handling + Callstack* callstack = (Callstack*)pCallstack; + auto cb = callstack->top(); + callstack->pop(); + cb(m, pCallstack); + /////////////////////////////////////////////////////////////////////// + + yunetParserProperties.def_readwrite("initialConfig", &beta::YuNetParserProperties::initialConfig) + .def_readwrite("locOutputLayerName", &beta::YuNetParserProperties::locOutputLayerName) + .def_readwrite("confOutputLayerName", &beta::YuNetParserProperties::confOutputLayerName) + .def_readwrite("iouOutputLayerName", &beta::YuNetParserProperties::iouOutputLayerName) + .def_readwrite("inputSize", &beta::YuNetParserProperties::inputSize) + .def_readwrite("labelNames", &beta::YuNetParserProperties::labelNames); + + yunetParser.def_readonly("input", &YuNetParser::input, DOC(dai, beta, node, YuNetParser, input)) + .def_readonly("inputConfig", &YuNetParser::inputConfig, DOC(dai, beta, node, YuNetParser, inputConfig)) + .def_readonly("out", &YuNetParser::out, DOC(dai, beta, node, YuNetParser, out)) + .def_readonly("initialConfig", &YuNetParser::initialConfig, DOC(dai, beta, node, YuNetParser, initialConfig)) + .def( + "build", + [](YuNetParser& self, Node::Output& nnInput, const YuNetParser::Model& model) { return self.build(nnInput, model); }, + py::arg("input"), + py::arg("model"), + DOC(dai, beta, node, YuNetParser, build)) + .def("build", + py::overload_cast(&YuNetParser::build), + py::arg("input"), + py::arg("head"), + DOC(dai, beta, node, YuNetParser, build, 2)) + .def("setNNArchive", &YuNetParser::setNNArchive, py::arg("nnArchive"), DOC(dai, beta, node, YuNetParser, setNNArchive)) + .def("setNNArchiveHead", &YuNetParser::setNNArchiveHead, py::arg("head"), DOC(dai, beta, node, YuNetParser, setNNArchiveHead)) + .def("setOutputLayerLoc", &YuNetParser::setOutputLayerLoc, py::arg("locOutputLayerName"), DOC(dai, beta, node, YuNetParser, setOutputLayerLoc)) + .def("getOutputLayerLoc", &YuNetParser::getOutputLayerLoc, DOC(dai, beta, node, YuNetParser, getOutputLayerLoc)) + .def("setOutputLayerConf", &YuNetParser::setOutputLayerConf, py::arg("confOutputLayerName"), DOC(dai, beta, node, YuNetParser, setOutputLayerConf)) + .def("getOutputLayerConf", &YuNetParser::getOutputLayerConf, DOC(dai, beta, node, YuNetParser, getOutputLayerConf)) + .def("setOutputLayerIou", &YuNetParser::setOutputLayerIou, py::arg("iouOutputLayerName"), DOC(dai, beta, node, YuNetParser, setOutputLayerIou)) + .def("getOutputLayerIou", &YuNetParser::getOutputLayerIou, DOC(dai, beta, node, YuNetParser, getOutputLayerIou)) + .def("setConfidenceThreshold", &YuNetParser::setConfidenceThreshold, py::arg("threshold"), DOC(dai, beta, node, YuNetParser, setConfidenceThreshold)) + .def("getConfidenceThreshold", &YuNetParser::getConfidenceThreshold, DOC(dai, beta, node, YuNetParser, getConfidenceThreshold)) + .def("setIouThreshold", &YuNetParser::setIouThreshold, py::arg("threshold"), DOC(dai, beta, node, YuNetParser, setIouThreshold)) + .def("getIouThreshold", &YuNetParser::getIouThreshold, DOC(dai, beta, node, YuNetParser, getIouThreshold)) + .def("setMaxDetections", &YuNetParser::setMaxDetections, py::arg("maxDetections"), DOC(dai, beta, node, YuNetParser, setMaxDetections)) + .def("getMaxDetections", &YuNetParser::getMaxDetections, DOC(dai, beta, node, YuNetParser, getMaxDetections)) + .def("setInputSize", &YuNetParser::setInputSize, py::arg("width"), py::arg("height"), DOC(dai, beta, node, YuNetParser, setInputSize)) + .def("getInputSize", &YuNetParser::getInputSize, DOC(dai, beta, node, YuNetParser, getInputSize)) + .def("setLabelNames", &YuNetParser::setLabelNames, py::arg("labelNames"), DOC(dai, beta, node, YuNetParser, setLabelNames)) + .def("getLabelNames", &YuNetParser::getLabelNames, DOC(dai, beta, node, YuNetParser, getLabelNames)) + .def("setRunOnHost", &YuNetParser::setRunOnHost, py::arg("runOnHost"), DOC(dai, beta, node, YuNetParser, setRunOnHost)) + .def("runOnHost", &YuNetParser::runOnHost, DOC(dai, beta, node, YuNetParser, runOnHost)); + + yunetParser.attr("Properties") = yunetParserProperties; +} diff --git a/bindings/python/src/capabilities/ImgFrameCapabilityBindings.cpp b/bindings/python/src/capabilities/ImgFrameCapabilityBindings.cpp index d9f3a53bd3..585c78822f 100644 --- a/bindings/python/src/capabilities/ImgFrameCapabilityBindings.cpp +++ b/bindings/python/src/capabilities/ImgFrameCapabilityBindings.cpp @@ -28,6 +28,7 @@ void ImgFrameCapabilityBindings::bind(pybind11::module& m, void* pCallstack) { .def_readwrite("type", &ImgFrameCapability::type) .def_readwrite("resizeMode", &ImgFrameCapability::resizeMode) .def_readwrite("enableUndistortion", &ImgFrameCapability::enableUndistortion) + .def_readwrite("alphaScaling", &ImgFrameCapability::alphaScaling) ; } diff --git a/bindings/python/src/nn_archive/NNArchiveBindings.cpp b/bindings/python/src/nn_archive/NNArchiveBindings.cpp index 9f9f7a41ff..a3620a782b 100644 --- a/bindings/python/src/nn_archive/NNArchiveBindings.cpp +++ b/bindings/python/src/nn_archive/NNArchiveBindings.cpp @@ -186,6 +186,7 @@ void NNArchiveBindings::bind(pybind11::module& m, void* pCallstack) { v1metadata.def_readwrite("maxDet", &v1::Metadata::maxDet, DOC(dai, nn_archive, v1, Metadata, maxDet)); v1metadata.def_readwrite("nClasses", &v1::Metadata::nClasses, DOC(dai, nn_archive, v1, Metadata, nClasses)); v1metadata.def_readwrite("isSoftmax", &v1::Metadata::isSoftmax, DOC(dai, nn_archive, v1, Metadata, isSoftmax)); + v1metadata.def_readwrite("backgroundClass", &v1::Metadata::backgroundClass, DOC(dai, nn_archive, v1, Metadata, backgroundClass)); v1metadata.def_readwrite("boxesOutputs", &v1::Metadata::boxesOutputs, DOC(dai, nn_archive, v1, Metadata, boxesOutputs)); v1metadata.def_readwrite("scoresOutputs", &v1::Metadata::scoresOutputs, DOC(dai, nn_archive, v1, Metadata, scoresOutputs)); v1metadata.def_readwrite("anglesOutputs", &v1::Metadata::anglesOutputs, DOC(dai, nn_archive, v1, Metadata, anglesOutputs)); @@ -196,6 +197,7 @@ void NNArchiveBindings::bind(pybind11::module& m, void* pCallstack) { v1metadata.def_readwrite("protosOutputs", &v1::Metadata::protosOutputs, DOC(dai, nn_archive, v1, Metadata, protosOutputs)); v1metadata.def_readwrite("subtype", &v1::Metadata::subtype, DOC(dai, nn_archive, v1, Metadata, subtype)); v1metadata.def_readwrite("yoloOutputs", &v1::Metadata::yoloOutputs, DOC(dai, nn_archive, v1, Metadata, yoloOutputs)); + v1metadata.def_readwrite("strides", &v1::Metadata::strides, DOC(dai, nn_archive, v1, Metadata, strides)); v1metadata.def_readwrite("extraParams", &v1::Metadata::extraParams, DOC(dai, nn_archive, v1, Metadata, extraParams)); v1metadataClass.def(py::init<>()); diff --git a/bindings/python/src/pipeline/PipelineBindings.cpp b/bindings/python/src/pipeline/PipelineBindings.cpp index ce8464133d..fa990058fb 100644 --- a/bindings/python/src/pipeline/PipelineBindings.cpp +++ b/bindings/python/src/pipeline/PipelineBindings.cpp @@ -271,9 +271,10 @@ void PipelineBindings::bind(pybind11::module& m, void* pCallstack) { py::object nodeClass = py::module::import("depthai").attr("node").attr("ThreadedHostNode"); auto isSubclass = issubclass(class_, nodeClass).cast(); - // Check if the class is directly from bindings (__module__ == "depthai.node"). If so, the node comes from bindings, + // Check if the class is directly from stable or beta bindings. If so, the node comes from bindings, // so we create in the same manner as device nodes. - auto isFromBindings = class_.attr("__module__").cast() == "depthai.node"; + const auto nodeModule = class_.attr("__module__").cast(); + auto isFromBindings = nodeModule == "depthai.node" || nodeModule == "depthai.beta.node"; // Create a copy from kwargs and add autoAddToPipeline to false // Check if the node is a ColorCamera or a MonoCamera node and issue a deprecation warning diff --git a/bindings/python/src/pipeline/datatype/AprilTagsBindings.cpp b/bindings/python/src/pipeline/datatype/AprilTagsBindings.cpp index 563d20fecd..ccc973d28e 100644 --- a/bindings/python/src/pipeline/datatype/AprilTagsBindings.cpp +++ b/bindings/python/src/pipeline/datatype/AprilTagsBindings.cpp @@ -46,13 +46,5 @@ void bind_apriltags(pybind11::module& m, void* pCallstack) { aprilTags.def(py::init<>()) .def("__repr__", &AprilTags::str) .def_readwrite("aprilTags", &AprilTags::aprilTags) - .def("getTimestamp", &AprilTags::Buffer::getTimestamp, DOC(dai, Buffer, getTimestamp)) - .def("getTimestampDevice", &AprilTags::Buffer::getTimestampDevice, DOC(dai, Buffer, getTimestampDevice)) - .def("getTimestampSystem", &AprilTags::Buffer::getTimestampSystem, DOC(dai, Buffer, getTimestampSystem)) - .def("getSequenceNum", &AprilTags::Buffer::getSequenceNum, DOC(dai, Buffer, getSequenceNum)) - .def("transformTo", &AprilTags::transformTo, py::arg("target"), DOC(dai, AprilTags, transformTo)) - // .def("setTimestamp", &AprilTags::setTimestamp, DOC(dai, Buffer, setTimestamp)) - // .def("setTimestampDevice", &AprilTags::setTimestampDevice, DOC(dai, Buffer, setTimestampDevice)) - // .def("setSequenceNum", &AprilTags::setSequenceNum, DOC(dai, Buffer, setSequenceNum)) - ; + .def("transformTo", &AprilTags::transformTo, py::arg("target"), DOC(dai, AprilTags, transformTo)); } diff --git a/bindings/python/src/pipeline/datatype/EncodedFrameBindings.cpp b/bindings/python/src/pipeline/datatype/EncodedFrameBindings.cpp index d1d1099627..817bbd452c 100644 --- a/bindings/python/src/pipeline/datatype/EncodedFrameBindings.cpp +++ b/bindings/python/src/pipeline/datatype/EncodedFrameBindings.cpp @@ -74,13 +74,9 @@ void bind_encodedframe(pybind11::module& m, void* pCallstack) { encodedFrame.def(py::init<>()) .def("__repr__", &EncodedFrame::str) // getters - .def("getTimestamp", py::overload_cast<>(&EncodedFrame::Buffer::getTimestamp, py::const_), DOC(dai, Buffer, getTimestamp)) - .def("getTimestampDevice", py::overload_cast<>(&EncodedFrame::Buffer::getTimestampDevice, py::const_), DOC(dai, Buffer, getTimestampDevice)) - .def("getTimestampSystem", py::overload_cast<>(&EncodedFrame::Buffer::getTimestampSystem, py::const_), DOC(dai, Buffer, getTimestampSystem)) .def("getInstanceNum", &EncodedFrame::getInstanceNum, DOC(dai, EncodedFrame, getInstanceNum)) .def("getWidth", &EncodedFrame::getWidth, DOC(dai, EncodedFrame, getWidth)) .def("getHeight", &EncodedFrame::getHeight, DOC(dai, EncodedFrame, getHeight)) - .def("getSequenceNum", &EncodedFrame::Buffer::getSequenceNum, DOC(dai, Buffer, getSequenceNum)) .def("getExposureTime", &EncodedFrame::getExposureTime, DOC(dai, EncodedFrame, getExposureTime)) .def("getSensitivity", &EncodedFrame::getSensitivity, DOC(dai, EncodedFrame, getSensitivity)) .def("getColorTemperature", &EncodedFrame::getColorTemperature, DOC(dai, EncodedFrame, getColorTemperature)) @@ -96,13 +92,6 @@ void bind_encodedframe(pybind11::module& m, void* pCallstack) { .def("getLossless", &EncodedFrame::getLossless, DOC(dai, EncodedFrame, getLossless)) .def("getProfile", &EncodedFrame::getProfile, DOC(dai, EncodedFrame, getProfile)) .def("getTransformation", [](EncodedFrame& msg) { return msg.transformation; }) - // // setters - // .def("setTimestamp", &EncodedFrame::setTimestamp, - // DOC(dai, EncodedFrame, setTimestamp)) - // .def("setTimestampDevice", &EncodedFrame::setTimestampDevice, - // DOC(dai, EncodedFrame, setTimestampDevice)) - // .def("setSequenceNum", &EncodedFrame::setSequenceNum, - // DOC(dai, EncodedFrame, setSequenceNum)) .def("setWidth", &EncodedFrame::setWidth, py::arg("width"), DOC(dai, EncodedFrame, setWidth)) .def("setHeight", &EncodedFrame::setHeight, py::arg("height"), DOC(dai, EncodedFrame, setHeight)) .def("setSize", diff --git a/bindings/python/src/pipeline/datatype/ImgAnnotationsBindings.cpp b/bindings/python/src/pipeline/datatype/ImgAnnotationsBindings.cpp index 1939a26367..29a0dab55a 100644 --- a/bindings/python/src/pipeline/datatype/ImgAnnotationsBindings.cpp +++ b/bindings/python/src/pipeline/datatype/ImgAnnotationsBindings.cpp @@ -95,10 +95,6 @@ void bind_imageannotations(pybind11::module& m, void* pCallstack) { imageAnnotations.def(py::init<>(), DOC(dai, ImgAnnotations, ImgAnnotations)) .def(py::init&>(), DOC(dai, ImgAnnotations, ImgAnnotations, 2)) .def_readwrite("annotations", &ImgAnnotations::annotations) - .def("getTimestamp", &ImgAnnotations::Buffer::getTimestamp, DOC(dai, Buffer, getTimestamp)) - .def("getTimestampDevice", &ImgAnnotations::Buffer::getTimestampDevice, DOC(dai, Buffer, getTimestampDevice)) - .def("getTimestampSystem", &ImgAnnotations::Buffer::getTimestampSystem, DOC(dai, Buffer, getTimestampSystem)) - .def("getSequenceNum", &ImgAnnotations::Buffer::getSequenceNum, DOC(dai, Buffer, getSequenceNum)) .def("getTransformation", [](ImgAnnotations& msg) { return msg.transformation; }) .def("setTransformation", [](ImgAnnotations& msg, const std::optional& transformation) { msg.transformation = transformation; }); } diff --git a/bindings/python/src/pipeline/datatype/ImgDetectionsBindings.cpp b/bindings/python/src/pipeline/datatype/ImgDetectionsBindings.cpp index de2ec7f442..8958317c86 100644 --- a/bindings/python/src/pipeline/datatype/ImgDetectionsBindings.cpp +++ b/bindings/python/src/pipeline/datatype/ImgDetectionsBindings.cpp @@ -135,10 +135,6 @@ void bind_imgdetections(pybind11::module& m, void* pCallstack) { [](ImgDetections& det, size_t val) { det.segmentationMaskHeight = val; }, DOC(dai, ImgDetectionsT, segmentationMaskHeight), py::return_value_policy::reference_internal) - .def("getTimestamp", &dai::ImgDetectionsT::Buffer::getTimestamp, DOC(dai, Buffer, getTimestamp)) - .def("getTimestampDevice", &dai::ImgDetectionsT::Buffer::getTimestampDevice, DOC(dai, Buffer, getTimestampDevice)) - .def("getTimestampSystem", &dai::ImgDetectionsT::Buffer::getTimestampSystem, DOC(dai, Buffer, getTimestampSystem)) - .def("getSequenceNum", &dai::ImgDetectionsT::Buffer::getSequenceNum, DOC(dai, Buffer, getSequenceNum)) .def( "getTransformation", [](ImgDetections& msg) { return msg.transformation; }, DOC(dai, ImgFrame, getTransformation)) .def( diff --git a/bindings/python/src/pipeline/datatype/ImgFrameBindings.cpp b/bindings/python/src/pipeline/datatype/ImgFrameBindings.cpp index 373fe247be..80549db62f 100644 --- a/bindings/python/src/pipeline/datatype/ImgFrameBindings.cpp +++ b/bindings/python/src/pipeline/datatype/ImgFrameBindings.cpp @@ -8,11 +8,11 @@ // depthai #include "depthai/common/ImgTransformations.hpp" #include "depthai/pipeline/datatype/ImgFrame.hpp" +#include "depthai/utility/ColorizeDepthFrame.hpp" #include "ndarray_converter.h" // pybind #include #include -#include void bind_imgframe(pybind11::module& m, void* pCallstack) { using namespace dai; @@ -239,7 +239,6 @@ void bind_imgframe(pybind11::module& m, void* pCallstack) { py::overload_cast(&ImgFrame::getTimestampSystem, py::const_), py::arg("offset"), DOC(dai, ImgFrame, getTimestampSystem)) - .def("getSequenceNum", &ImgFrame::Buffer::getSequenceNum, DOC(dai, Buffer, getSequenceNum)) .def("getInstanceNum", &ImgFrame::getInstanceNum, DOC(dai, ImgFrame, getInstanceNum)) .def("getCategory", &ImgFrame::getCategory, DOC(dai, ImgFrame, getCategory)) .def("getWidth", &ImgFrame::getWidth, DOC(dai, ImgFrame, getWidth)) @@ -304,12 +303,8 @@ void bind_imgframe(pybind11::module& m, void* pCallstack) { DOC(dai, ImgFrame, setCvFrame)) #endif // setters - .def("setTimestamp", &ImgFrame::setTimestamp, py::arg("timestamp"), DOC(dai, Buffer, setTimestamp)) - .def("setTimestampDevice", &ImgFrame::setTimestampDevice, py::arg("timestampDevice"), DOC(dai, Buffer, setTimestampDevice)) - .def("setTimestampSystem", &ImgFrame::setTimestampSystem, py::arg("timestampSystem"), DOC(dai, Buffer, setTimestampSystem)) .def("setInstanceNum", &ImgFrame::setInstanceNum, py::arg("instance"), DOC(dai, ImgFrame, setInstanceNum)) .def("setCategory", &ImgFrame::setCategory, py::arg("category"), DOC(dai, ImgFrame, setCategory)) - // .def("setSequenceNum", &ImgFrame::setSequenceNum, py::arg("seq"), DOC(dai, ImgFrame, setSequenceNum)) .def("setWidth", &ImgFrame::setWidth, py::arg("width"), DOC(dai, ImgFrame, setWidth)) .def("setStride", &ImgFrame::setStride, py::arg("stride"), DOC(dai, ImgFrame, setStride)) .def("setHeight", &ImgFrame::setHeight, py::arg("height"), DOC(dai, ImgFrame, setHeight)) @@ -329,4 +324,30 @@ void bind_imgframe(pybind11::module& m, void* pCallstack) { // add aliases dai.ImgFrame.Type and dai.ImgFrame.Specs // m.attr("ImgFrame").attr("Type") = m.attr("RawImgFrame").attr("Type"); // m.attr("ImgFrame").attr("Specs") = m.attr("RawImgFrame").attr("Specs"); + +#ifdef DEPTHAI_HAVE_OPENCV_SUPPORT + auto utility = m.def_submodule("utility", "Utility functions"); + utility.def( + "colorizeDepthFrame", + [](const ImgFrame& frame, float minDepth, float maxDepth, int colormap, bool useLog) { + return dai::utility::colorizeDepthFrame(frame, minDepth, maxDepth, static_cast(colormap), useLog); + }, + py::arg("frame"), + py::arg("minDepth") = 300.0f, + py::arg("maxDepth") = 12000.0f, + py::arg("colormap") = static_cast(cv::COLORMAP_JET), + py::arg("useLog") = true, + DOC(dai, utility, colorizeDepthFrame)); + utility.def( + "colorizeDepthFrame", + [](const cv::Mat& frame, float minDepth, float maxDepth, int colormap, bool useLog) { + return dai::utility::colorizeDepthFrame(frame, minDepth, maxDepth, static_cast(colormap), useLog); + }, + py::arg("frame"), + py::arg("minDepth") = 300.0f, + py::arg("maxDepth") = 12000.0f, + py::arg("colormap") = static_cast(cv::COLORMAP_JET), + py::arg("useLog") = true, + DOC(dai, utility, colorizeDepthFrame, 2)); +#endif } diff --git a/bindings/python/src/pipeline/datatype/MessageGroupBindings.cpp b/bindings/python/src/pipeline/datatype/MessageGroupBindings.cpp index 22e0f1bc73..fdc4eb0b46 100644 --- a/bindings/python/src/pipeline/datatype/MessageGroupBindings.cpp +++ b/bindings/python/src/pipeline/datatype/MessageGroupBindings.cpp @@ -51,13 +51,5 @@ void bind_message_group(pybind11::module& m, void* pCallstack) { .def("isSynced", &MessageGroup::isSynced, py::arg("thresholdNs"), DOC(dai, MessageGroup, isSynced)) .def("getIntervalNs", &MessageGroup::getIntervalNs, DOC(dai, MessageGroup, getIntervalNs)) .def("getNumMessages", &MessageGroup::getNumMessages, DOC(dai, MessageGroup, getNumMessages)) - .def("getMessageNames", &MessageGroup::getMessageNames, DOC(dai, MessageGroup, getMessageNames)) - .def("getTimestamp", &MessageGroup::Buffer::getTimestamp, DOC(dai, Buffer, getTimestamp)) - .def("getTimestampDevice", &MessageGroup::Buffer::getTimestampDevice, DOC(dai, Buffer, getTimestampDevice)) - .def("getTimestampSystem", &MessageGroup::Buffer::getTimestampSystem, DOC(dai, Buffer, getTimestampSystem)) - .def("getSequenceNum", &MessageGroup::Buffer::getSequenceNum, DOC(dai, Buffer, getSequenceNum)) - .def("setTimestamp", &MessageGroup::setTimestamp, py::arg("timestamp"), DOC(dai, Buffer, setTimestamp)) - .def("setTimestampDevice", &MessageGroup::setTimestampDevice, py::arg("timestampDevice"), DOC(dai, Buffer, setTimestampDevice)) - .def("setTimestampSystem", &MessageGroup::setTimestampSystem, py::arg("timestampSystem"), DOC(dai, Buffer, setTimestampSystem)) - .def("setSequenceNum", &MessageGroup::setSequenceNum, py::arg("sequenceNum"), DOC(dai, Buffer, setSequenceNum)); + .def("getMessageNames", &MessageGroup::getMessageNames, DOC(dai, MessageGroup, getMessageNames)); } diff --git a/bindings/python/src/pipeline/datatype/NNDataBindings.cpp b/bindings/python/src/pipeline/datatype/NNDataBindings.cpp index c5fe8449a7..b60cf7fe32 100644 --- a/bindings/python/src/pipeline/datatype/NNDataBindings.cpp +++ b/bindings/python/src/pipeline/datatype/NNDataBindings.cpp @@ -177,16 +177,6 @@ void bind_nndata(pybind11::module& m, void* pCallstack) { // PyErr_WarnEx(PyExc_DeprecationWarning, "Use 'getTensor()' // instead", 1); return obj.getFirstLayerInt32(); // }, DOC(dai, NNData, getFirstLayerInt32)) - // TODO(Morato) - is this needed - doesn't get inherited from Buffer? - .def("getTimestamp", &NNData::Buffer::getTimestamp, DOC(dai, Buffer, getTimestamp)) - .def("getTimestampDevice", &NNData::Buffer::getTimestampDevice, DOC(dai, Buffer, getTimestampDevice)) - .def("getTimestampSystem", &NNData::Buffer::getTimestampSystem, DOC(dai, Buffer, getTimestampSystem)) - .def("getSequenceNum", &NNData::Buffer::getSequenceNum, DOC(dai, Buffer, getSequenceNum)) - .def("setTimestamp", &NNData::setTimestamp, py::arg("timestamp"), DOC(dai, Buffer, setTimestamp)) - .def("setTimestampDevice", &NNData::setTimestampDevice, py::arg("timestampDevice"), DOC(dai, Buffer, setTimestampDevice)) - .def("setTimestampSystem", &NNData::setTimestampSystem, py::arg("timestampSystem"), DOC(dai, Buffer, setTimestampSystem)) - .def("setSequenceNum", &NNData::setSequenceNum, py::arg("sequenceNum"), DOC(dai, Buffer, setSequenceNum)) - .def("addTensor", static_cast&, TensorInfo::StorageOrder)>(&NNData::addTensor), py::arg("name"), diff --git a/bindings/python/src/pipeline/datatype/PipelineEventBindings.cpp b/bindings/python/src/pipeline/datatype/PipelineEventBindings.cpp index 828585fcb5..901e5750e2 100644 --- a/bindings/python/src/pipeline/datatype/PipelineEventBindings.cpp +++ b/bindings/python/src/pipeline/datatype/PipelineEventBindings.cpp @@ -56,13 +56,5 @@ void bind_pipelineevent(pybind11::module& m, void* pCallstack) { .def_readwrite("queueSize", &PipelineEvent::queueSize, DOC(dai, PipelineEvent, queueSize)) .def_readwrite("interval", &PipelineEvent::interval, DOC(dai, PipelineEvent, interval)) .def_readwrite("type", &PipelineEvent::type, DOC(dai, PipelineEvent, type)) - .def_readwrite("source", &PipelineEvent::source, DOC(dai, PipelineEvent, source)) - .def("getTimestamp", &PipelineEvent::Buffer::getTimestamp, DOC(dai, Buffer, getTimestamp)) - .def("getTimestampDevice", &PipelineEvent::Buffer::getTimestampDevice, DOC(dai, Buffer, getTimestampDevice)) - .def("getTimestampSystem", &PipelineEvent::Buffer::getTimestampSystem, DOC(dai, Buffer, getTimestampSystem)) - .def("getSequenceNum", &PipelineEvent::Buffer::getSequenceNum, DOC(dai, Buffer, getSequenceNum)) - .def("setTimestamp", &PipelineEvent::setTimestamp, py::arg("timestamp"), DOC(dai, Buffer, setTimestamp)) - .def("setTimestampDevice", &PipelineEvent::setTimestampDevice, py::arg("timestampDevice"), DOC(dai, Buffer, setTimestampDevice)) - .def("setTimestampSystem", &PipelineEvent::setTimestampSystem, py::arg("timestampSystem"), DOC(dai, Buffer, setTimestampSystem)) - .def("setSequenceNum", &PipelineEvent::setSequenceNum, py::arg("sequenceNum"), DOC(dai, Buffer, setSequenceNum)); + .def_readwrite("source", &PipelineEvent::source, DOC(dai, PipelineEvent, source)); } diff --git a/bindings/python/src/pipeline/datatype/PipelineStateBindings.cpp b/bindings/python/src/pipeline/datatype/PipelineStateBindings.cpp index 61f896dbb5..db98bb07ae 100644 --- a/bindings/python/src/pipeline/datatype/PipelineStateBindings.cpp +++ b/bindings/python/src/pipeline/datatype/PipelineStateBindings.cpp @@ -106,13 +106,5 @@ void bind_pipelinestate(pybind11::module& m, void* pCallstack) { // Message pipelineState.def(py::init<>()) .def("__repr__", &PipelineState::str) - .def_readwrite("nodeStates", &PipelineState::nodeStates, DOC(dai, PipelineState, nodeStates)) - .def("getTimestamp", &PipelineState::Buffer::getTimestamp, DOC(dai, Buffer, getTimestamp)) - .def("getTimestampDevice", &PipelineState::Buffer::getTimestampDevice, DOC(dai, Buffer, getTimestampDevice)) - .def("getTimestampSystem", &PipelineState::Buffer::getTimestampSystem, DOC(dai, Buffer, getTimestampSystem)) - .def("getSequenceNum", &PipelineState::Buffer::getSequenceNum, DOC(dai, Buffer, getSequenceNum)) - .def("setTimestamp", &PipelineState::setTimestamp, py::arg("timestamp"), DOC(dai, Buffer, setTimestamp)) - .def("setTimestampDevice", &PipelineState::setTimestampDevice, py::arg("timestampDevice"), DOC(dai, Buffer, setTimestampDevice)) - .def("setTimestampSystem", &PipelineState::setTimestampSystem, py::arg("timestampSystem"), DOC(dai, Buffer, setTimestampSystem)) - .def("setSequenceNum", &PipelineState::setSequenceNum, py::arg("sequenceNum"), DOC(dai, Buffer, setSequenceNum)); + .def_readwrite("nodeStates", &PipelineState::nodeStates, DOC(dai, PipelineState, nodeStates)); } diff --git a/bindings/python/src/pipeline/datatype/PointCloudDataBindings.cpp b/bindings/python/src/pipeline/datatype/PointCloudDataBindings.cpp index e063bec7dc..5484263b09 100644 --- a/bindings/python/src/pipeline/datatype/PointCloudDataBindings.cpp +++ b/bindings/python/src/pipeline/datatype/PointCloudDataBindings.cpp @@ -123,10 +123,6 @@ void bind_pointclouddata(pybind11::module& m, void* pCallstack) { .def("isOrganized", &PointCloudData::isOrganized, DOC(dai, PointCloudData, isOrganized)) .def("isColor", &PointCloudData::isColor, DOC(dai, PointCloudData, isColor)) .def("setInstanceNum", &PointCloudData::setInstanceNum, py::arg("instanceNum"), DOC(dai, PointCloudData, setInstanceNum)) - .def("getTimestamp", &PointCloudData::Buffer::getTimestamp, DOC(dai, Buffer, getTimestamp)) - .def("getTimestampDevice", &PointCloudData::Buffer::getTimestampDevice, DOC(dai, Buffer, getTimestampDevice)) - .def("getTimestampSystem", &PointCloudData::Buffer::getTimestampSystem, DOC(dai, Buffer, getTimestampSystem)) - .def("getSequenceNum", &PointCloudData::Buffer::getSequenceNum, DOC(dai, Buffer, getSequenceNum)) .def("setPoints", [](py::object& obj, py::array_t& arr) { if(arr.ndim() != 2 || arr.shape(1) != 3) { diff --git a/bindings/python/src/pipeline/datatype/SegmentationMaskBindings.cpp b/bindings/python/src/pipeline/datatype/SegmentationMaskBindings.cpp index 80a2ad2d8b..962080474c 100644 --- a/bindings/python/src/pipeline/datatype/SegmentationMaskBindings.cpp +++ b/bindings/python/src/pipeline/datatype/SegmentationMaskBindings.cpp @@ -129,16 +129,8 @@ void bind_segmentationmask(pybind11::module& m, void* pCallstack) { py::arg("calculateRotation") = false, DOC(dai, SegmentationMask, getBoundingBoxes)) #endif - .def("getTimestamp", &SegmentationMask::getTimestamp, DOC(dai, Buffer, getTimestamp)) - .def("getTimestampDevice", &SegmentationMask::getTimestampDevice, DOC(dai, Buffer, getTimestampDevice)) - .def("getTimestampSystem", &SegmentationMask::getTimestampSystem, DOC(dai, Buffer, getTimestampSystem)) - .def("getSequenceNum", &SegmentationMask::getSequenceNum, DOC(dai, Buffer, getSequenceNum)) .def( "getTransformation", [](SegmentationMask& msg) { return msg.transformation; }, DOC(dai, ImgFrame, getTransformation)) - .def("setTimestamp", &SegmentationMask::setTimestamp, py::arg("timestamp"), DOC(dai, Buffer, setTimestamp)) - .def("setTimestampDevice", &SegmentationMask::setTimestampDevice, py::arg("timestampDevice"), DOC(dai, Buffer, setTimestampDevice)) - .def("setTimestampSystem", &SegmentationMask::setTimestampSystem, py::arg("timestampSystem"), DOC(dai, Buffer, setTimestampSystem)) - .def("setSequenceNum", &SegmentationMask::setSequenceNum, py::arg("sequenceNum"), DOC(dai, Buffer, setSequenceNum)) .def( "setTransformation", [](SegmentationMask& msg, const ImgTransformation& transformation) { msg.transformation = transformation; }, diff --git a/bindings/python/src/pipeline/datatype/SpatialImgDetectionsBindings.cpp b/bindings/python/src/pipeline/datatype/SpatialImgDetectionsBindings.cpp index 8025c62862..91d6f68f68 100644 --- a/bindings/python/src/pipeline/datatype/SpatialImgDetectionsBindings.cpp +++ b/bindings/python/src/pipeline/datatype/SpatialImgDetectionsBindings.cpp @@ -128,10 +128,6 @@ void bind_spatialimgdetections(pybind11::module& m, void* pCallstack) { [](SpatialImgDetections& det, std::vector& val) { det.detections = val; }, DOC(dai, ImgDetectionsT, detections), py::return_value_policy::reference_internal) - .def("getTimestamp", &SpatialImgDetections::Buffer::getTimestamp, DOC(dai, Buffer, getTimestamp)) - .def("getTimestampDevice", &SpatialImgDetections::Buffer::getTimestampDevice, DOC(dai, Buffer, getTimestampDevice)) - .def("getTimestampSystem", &SpatialImgDetections::Buffer::getTimestampSystem, DOC(dai, Buffer, getTimestampSystem)) - .def("getSequenceNum", &SpatialImgDetections::Buffer::getSequenceNum, DOC(dai, Buffer, getSequenceNum)) .def("getTransformation", [](SpatialImgDetections& msg) { return msg.transformation; }) .def("setTransformation", [](SpatialImgDetections& msg, const std::optional& transformation) { msg.transformation = transformation; }) @@ -162,8 +158,5 @@ void bind_spatialimgdetections(pybind11::module& m, void* pCallstack) { py::arg("semantic_class"), DOC(dai, ImgDetectionsT, getCvSegmentationMaskByClass)) #endif - // .def("setTimestamp", &SpatialImgDetections::setTimestamp, DOC(dai, SpatialImgDetections, setTimestamp)) - // .def("setTimestampDevice", &SpatialImgDetections::setTimestampDevice, DOC(dai, SpatialImgDetections, setTimestampDevice)) - // .def("setSequenceNum", &SpatialImgDetections::setSequenceNum, DOC(dai, SpatialImgDetections, setSequenceNum)) ; } diff --git a/bindings/python/src/pipeline/datatype/SpatialLocationCalculatorDataBindings.cpp b/bindings/python/src/pipeline/datatype/SpatialLocationCalculatorDataBindings.cpp index 458412d6e5..7017014f59 100644 --- a/bindings/python/src/pipeline/datatype/SpatialLocationCalculatorDataBindings.cpp +++ b/bindings/python/src/pipeline/datatype/SpatialLocationCalculatorDataBindings.cpp @@ -52,13 +52,5 @@ void bind_spatiallocationcalculatordata(pybind11::module& m, void* pCallstack) { "spatialLocations", [](SpatialLocationCalculatorData& loc) -> std::vector& { return loc.spatialLocations; }, [](SpatialLocationCalculatorData& loc, std::vector& val) { loc.spatialLocations = val; }, - DOC(dai, SpatialLocationCalculatorData, spatialLocations)) - .def("getTimestamp", &SpatialLocationCalculatorData::Buffer::getTimestamp, DOC(dai, Buffer, getTimestamp)) - .def("getTimestampDevice", &SpatialLocationCalculatorData::Buffer::getTimestampDevice, DOC(dai, Buffer, getTimestampDevice)) - .def("getTimestampSystem", &SpatialLocationCalculatorData::Buffer::getTimestampSystem, DOC(dai, Buffer, getTimestampSystem)) - .def("getSequenceNum", &SpatialLocationCalculatorData::Buffer::getSequenceNum, DOC(dai, Buffer, getSequenceNum)) - // .def("setTimestamp", &SpatialLocationCalculatorData::setTimestamp, DOC(dai, SpatialLocationCalculatorData, setTimestamp)) - // .def("setTimestampDevice", &SpatialLocationCalculatorData::setTimestampDevice, DOC(dai, SpatialLocationCalculatorData, setTimestampDevice)) - // .def("setSequenceNum", &SpatialLocationCalculatorData::setSequenceNum, DOC(dai, SpatialLocationCalculatorData, setSequenceNum)) - ; + DOC(dai, SpatialLocationCalculatorData, spatialLocations)); } diff --git a/bindings/python/src/pipeline/datatype/TrackedFeaturesBindings.cpp b/bindings/python/src/pipeline/datatype/TrackedFeaturesBindings.cpp index 53669e3461..952ac8e6cf 100644 --- a/bindings/python/src/pipeline/datatype/TrackedFeaturesBindings.cpp +++ b/bindings/python/src/pipeline/datatype/TrackedFeaturesBindings.cpp @@ -54,13 +54,5 @@ void bind_trackedfeatures(pybind11::module& m, void* pCallstack) { "trackedFeatures", [](TrackedFeatures& feat) -> std::vector& { return feat.trackedFeatures; }, [](TrackedFeatures& feat, std::vector val) { feat.trackedFeatures = std::move(val); }, - DOC(dai, TrackedFeatures, trackedFeatures)) - .def("getTimestamp", &TrackedFeatures::Buffer::getTimestamp, DOC(dai, Buffer, getTimestamp)) - .def("getTimestampDevice", &TrackedFeatures::Buffer::getTimestampDevice, DOC(dai, Buffer, getTimestampDevice)) - .def("getTimestampSystem", &TrackedFeatures::Buffer::getTimestampSystem, DOC(dai, Buffer, getTimestampSystem)) - .def("getSequenceNum", &TrackedFeatures::Buffer::getSequenceNum, DOC(dai, Buffer, getSequenceNum)) - // .def("setTimestamp", &TrackedFeatures::setTimestamp, DOC(dai, TrackedFeatures, setTimestamp)) - // .def("setTimestampDevice", &TrackedFeatures::setTimestampDevice, DOC(dai, TrackedFeatures, setTimestampDevice)) - // .def("setSequenceNum", &TrackedFeatures::setSequenceNum, DOC(dai, TrackedFeatures, setSequenceNum)) - ; + DOC(dai, TrackedFeatures, trackedFeatures)); } diff --git a/bindings/python/src/pipeline/datatype/TrackletsBindings.cpp b/bindings/python/src/pipeline/datatype/TrackletsBindings.cpp index ab3c6c3280..3cedaf6076 100644 --- a/bindings/python/src/pipeline/datatype/TrackletsBindings.cpp +++ b/bindings/python/src/pipeline/datatype/TrackletsBindings.cpp @@ -68,10 +68,6 @@ void bind_tracklets(pybind11::module& m, void* pCallstack) { [](Tracklets& track) -> std::vector& { return track.tracklets; }, [](Tracklets& track, std::vector val) { track.tracklets = std::move(val); }, DOC(dai, Tracklets, tracklets)) - .def("getTimestamp", &Tracklets::Buffer::getTimestamp, DOC(dai, Buffer, getTimestamp)) - .def("getTimestampDevice", &Tracklets::Buffer::getTimestampDevice, DOC(dai, Buffer, getTimestampDevice)) - .def("getTimestampSystem", &Tracklets::Buffer::getTimestampSystem, DOC(dai, Buffer, getTimestampSystem)) - .def("getSequenceNum", &Tracklets::Buffer::getSequenceNum, DOC(dai, Buffer, getSequenceNum)) .def("getTransformation", [](Tracklets& msg) { if(!msg.transformation.has_value()) { @@ -80,9 +76,5 @@ void bind_tracklets(pybind11::module& m, void* pCallstack) { return *msg.transformation; }) .def("setTransformation", [](Tracklets& msg, const ImgTransformation& transformation) { msg.transformation = transformation; }) - .def("transformTo", &Tracklets::transformTo, py::arg("target"), DOC(dai, Tracklets, transformTo)) - // .def("setTimestamp", &Tracklets::setTimestamp, DOC(dai, Tracklets, setTimestamp)) - // .def("setTimestampDevice", &Tracklets::setTimestampDevice, DOC(dai, Tracklets, setTimestampDevice)) - // .def("setSequenceNum", &Tracklets::setSequenceNum, DOC(dai, Tracklets, setSequenceNum)) - ; + .def("transformTo", &Tracklets::transformTo, py::arg("target"), DOC(dai, Tracklets, transformTo)); } diff --git a/bindings/python/src/pipeline/node/CameraBindings.cpp b/bindings/python/src/pipeline/node/CameraBindings.cpp index 5d90b4b035..5e3fc040af 100644 --- a/bindings/python/src/pipeline/node/CameraBindings.cpp +++ b/bindings/python/src/pipeline/node/CameraBindings.cpp @@ -62,13 +62,18 @@ void bind_camera(pybind11::module& m, void* pCallstack) { // .def("setCamera", &Camera::setCamera, "name"_a, DOC(dai, node, Camera, setCamera)) // .def("getCamera", &Camera::getCamera, DOC(dai, node, Camera, getCamera)) .def("requestOutput", - py::overload_cast&, std::optional, ImgResizeMode, std::optional, std::optional>( - &Camera::requestOutput), + py::overload_cast&, + std::optional, + ImgResizeMode, + std::optional, + std::optional, + std::optional>(&Camera::requestOutput), "size"_a, "type"_a = std::nullopt, "resizeMode"_a = dai::ImgResizeMode::CROP, "fps"_a = std::nullopt, "enableUndistortion"_a = std::nullopt, + "alphaScaling"_a = std::nullopt, py::return_value_policy::reference_internal, DOC(dai, node, Camera, requestOutput)) .def("requestIspOutput", diff --git a/bindings/python/src/pipeline/node/Common.hpp b/bindings/python/src/pipeline/node/Common.hpp index d31ca08624..4d77bf1a88 100644 --- a/bindings/python/src/pipeline/node/Common.hpp +++ b/bindings/python/src/pipeline/node/Common.hpp @@ -16,6 +16,7 @@ extern std::vector(dai::Pipeline&, py::object class_)>>> pyNodeCreateMap; extern py::handle daiNodeModule; extern py::handle daiNodeInternalModule; +extern py::handle daiBetaNodeModule; template py::class_> addNode(const char* name, const char* docstring = nullptr) { @@ -31,6 +32,13 @@ py::class_> addNodeInternal(const char* name, con return node; } +template +py::class_> addBetaNode(const char* name, const char* docstring = nullptr) { + auto node = py::class_>(daiBetaNodeModule, name, docstring); + pyNodeCreateMap.push_back(std::make_pair(node, [](dai::Pipeline& p, py::object class_) { return p.create(); })); + return node; +} + template py::class_> addNodeAbstract(const char* name, const char* docstring = nullptr) { auto node = py::class_>(daiNodeModule, name, docstring); @@ -48,4 +56,5 @@ py::class_> addNodeAbstract(const char* name, con #define ADD_NODE_ABSTRACT(NodeName) addNodeAbstract(#NodeName, DOC(dai, node, NodeName)) #define ADD_NODE_DERIVED_ABSTRACT(NodeName, Derived) addNodeAbstract(#NodeName, DOC(dai, node, NodeName)) #define ADD_NODE_DOC(NodeName, docstring) addNode(#NodeName, docstring) -#define ADD_NODE_DERIVED_DOC(NodeName, Derived, docstring) addNode(#NodeName, docstring) \ No newline at end of file +#define ADD_NODE_DERIVED_DOC(NodeName, Derived, docstring) addNode(#NodeName, docstring) +#define ADD_BETA_NODE_DERIVED(NodeName, Derived) addBetaNode(#NodeName, DOC(dai, beta, node, NodeName)) diff --git a/bindings/python/src/pipeline/node/DetectionNetworkBindings.cpp b/bindings/python/src/pipeline/node/DetectionNetworkBindings.cpp index fbfd6a3466..e4d4a13043 100644 --- a/bindings/python/src/pipeline/node/DetectionNetworkBindings.cpp +++ b/bindings/python/src/pipeline/node/DetectionNetworkBindings.cpp @@ -52,6 +52,28 @@ void bind_detectionnetwork(pybind11::module& m, void* pCallstack) { }, DETECTION_NETWORK_BUILD_PYARGS, DETECTION_NETWORK_PYARGS) + .def( + "build", + [](DetectionNetwork& self, + const std::shared_ptr& input, + const DetectionNetwork::Model& model, + std::optional fps, + std::optional resizeMode) { return self.build(input, model, fps, resizeMode); }, + py::arg("input"), + py::arg("model"), + py::arg("fps") = std::nullopt, + py::arg_v("resizeMode", dai::ImgResizeMode::CROP, "dai.ImgResizeMode.CROP"), + DOC(dai, node, DetectionNetwork, build)) + .def( + "build", + [](DetectionNetwork& self, const std::shared_ptr& input, const DetectionNetwork::Model& model, const ImgFrameCapability& capability) { + return self.build(input, model, capability); + }, + py::arg("input"), + py::arg("model"), + py::arg("capability"), + DOC(dai, node, DetectionNetwork, build, 3)) + // Backwards-compatible Camera build methods forwarding to the consolidated Model path .def( "build", [](DetectionNetwork& self, @@ -88,16 +110,17 @@ void bind_detectionnetwork(pybind11::module& m, void* pCallstack) { py::arg("fps") = std::nullopt, py::arg_v("resizeMode", dai::ImgResizeMode::CROP, "dai.ImgResizeMode.CROP"), DOC(dai, node, DetectionNetwork, build)) +#ifdef DEPTHAI_HAVE_OPENCV_SUPPORT .def( "build", - [](DetectionNetwork& self, const std::shared_ptr& input, const DetectionNetwork::Model& model, const ImgFrameCapability& capability) { - return self.build(input, model, capability); + [](DetectionNetwork& self, const std::shared_ptr& input, const DetectionNetwork::Model& model, std::optional fps) { + return self.build(input, model, fps); }, py::arg("input"), py::arg("model"), - py::arg("capability"), - DOC(dai, node, DetectionNetwork, build, 3)) -#ifdef DEPTHAI_HAVE_OPENCV_SUPPORT + py::arg("fps") = std::nullopt, + DOC(dai, node, DetectionNetwork, build, 4)) + // Backwards-compatible ReplayVideo build methods forwarding to the consolidated Model path .def( "build", [](DetectionNetwork& self, const std::shared_ptr& input, NNModelDescription modelDesc, std::optional fps) { @@ -110,7 +133,7 @@ void bind_detectionnetwork(pybind11::module& m, void* pCallstack) { .def( "build", [](DetectionNetwork& self, const std::shared_ptr& input, const std::string& model, std::optional fps) { - return self.build(input, DetectionNetwork::Model{NNModelDescription{model}}, fps); + return self.build(input, DetectionNetwork::Model{model}, fps); }, py::arg("input"), py::arg("model"), diff --git a/bindings/python/src/pipeline/node/ImageManipBindings.cpp b/bindings/python/src/pipeline/node/ImageManipBindings.cpp index 6b75d6fedc..03b4acac52 100644 --- a/bindings/python/src/pipeline/node/ImageManipBindings.cpp +++ b/bindings/python/src/pipeline/node/ImageManipBindings.cpp @@ -39,6 +39,7 @@ void bind_imagemanip(pybind11::module& m, void* pCallstack) { imageManipProperties.def_readwrite("initialConfig", &ImageManipProperties::initialConfig) .def_readwrite("outputFrameSize", &ImageManipProperties::outputFrameSize) .def_readwrite("numFramesPool", &ImageManipProperties::numFramesPool) + .def_readwrite("maxPoolSize", &ImageManipProperties::maxPoolSize) .def_readwrite("backend", &ImageManipProperties::backend) .def_readwrite("performanceMode", &ImageManipProperties::performanceMode); @@ -54,7 +55,8 @@ void bind_imagemanip(pybind11::module& m, void* pCallstack) { .def("setBackend", &ImageManip::setBackend, DOC(dai, node, ImageManip, setBackend)) .def("setPerformanceMode", &ImageManip::setPerformanceMode, DOC(dai, node, ImageManip, setPerformanceMode)) .def("setNumFramesPool", &ImageManip::setNumFramesPool, DOC(dai, node, ImageManip, setNumFramesPool)) - .def("setMaxOutputFrameSize", &ImageManip::setMaxOutputFrameSize, DOC(dai, node, ImageManip, setMaxOutputFrameSize)); + .def("setMaxOutputFrameSize", &ImageManip::setMaxOutputFrameSize, DOC(dai, node, ImageManip, setMaxOutputFrameSize)) + .def("setMaxPoolSize", &ImageManip::setMaxPoolSize, DOC(dai, node, ImageManip, setMaxPoolSize)); // Properties alias daiNodeModule.attr("ImageManip").attr("Properties") = imageManipProperties; diff --git a/bindings/python/src/pipeline/node/NeuralNetworkBindings.cpp b/bindings/python/src/pipeline/node/NeuralNetworkBindings.cpp index 6c35cd1466..352bec6443 100644 --- a/bindings/python/src/pipeline/node/NeuralNetworkBindings.cpp +++ b/bindings/python/src/pipeline/node/NeuralNetworkBindings.cpp @@ -71,6 +71,28 @@ void bind_neuralnetwork(pybind11::module& m, void* pCallstack) { py::arg("input"), py::arg("nnArchive"), DOC(dai, node, NeuralNetwork, build)) + .def( + "build", + [](NeuralNetwork& self, + const std::shared_ptr& input, + const NeuralNetwork::Model& model, + std::optional fps, + std::optional resizeMode) { return self.build(input, model, fps, resizeMode); }, + py::arg("input"), + py::arg("model"), + py::arg("fps") = std::nullopt, + py::arg_v("resizeMode", dai::ImgResizeMode::CROP, "dai.ImgResizeMode.CROP"), + DOC(dai, node, NeuralNetwork, build, 2)) + .def( + "build", + [](NeuralNetwork& self, const std::shared_ptr& input, const NeuralNetwork::Model& model, const ImgFrameCapability& capability) { + return self.build(input, model, capability); + }, + py::arg("input"), + py::arg("model"), + py::arg("capability"), + DOC(dai, node, NeuralNetwork, build, 3)) + // Backwards-compatible Camera build methods forwarding to the consolidated Model path .def( "build", [](NeuralNetwork& self, @@ -107,20 +129,21 @@ void bind_neuralnetwork(pybind11::module& m, void* pCallstack) { py::arg("fps") = std::nullopt, py::arg_v("resizeMode", dai::ImgResizeMode::CROP, "dai.ImgResizeMode.CROP"), DOC(dai, node, NeuralNetwork, build, 2)) +#ifdef DEPTHAI_HAVE_OPENCV_SUPPORT .def( "build", - [](NeuralNetwork& self, const std::shared_ptr& input, const NeuralNetwork::Model& model, const ImgFrameCapability& capability) { - return self.build(input, model, capability); + [](NeuralNetwork& self, const std::shared_ptr& input, const NeuralNetwork::Model& model, std::optional fps) { + return self.build(input, model, fps); }, py::arg("input"), py::arg("model"), - py::arg("capability"), - DOC(dai, node, NeuralNetwork, build, 3)) -#ifdef DEPTHAI_HAVE_OPENCV_SUPPORT + py::arg("fps") = std::nullopt, + DOC(dai, node, NeuralNetwork, build, 4)) + // Backwards-compatible ReplayVideo build methods forwarding to the consolidated Model path .def( "build", - [](NeuralNetwork& self, const std::shared_ptr& input, const NNModelDescription modelDesc, std::optional fps) { - return self.build(input, NeuralNetwork::Model{modelDesc}, fps); + [](NeuralNetwork& self, const std::shared_ptr& input, NNModelDescription modelDesc, std::optional fps) { + return self.build(input, NeuralNetwork::Model{std::move(modelDesc)}, fps); }, py::arg("input"), py::arg("modelDesc"), @@ -129,7 +152,7 @@ void bind_neuralnetwork(pybind11::module& m, void* pCallstack) { .def( "build", [](NeuralNetwork& self, const std::shared_ptr& input, const std::string& model, std::optional fps) { - return self.build(input, NeuralNetwork::Model{NNModelDescription{model}}, fps); + return self.build(input, NeuralNetwork::Model{model}, fps); }, py::arg("input"), py::arg("model"), diff --git a/bindings/python/src/pipeline/node/NodeBindings.cpp b/bindings/python/src/pipeline/node/NodeBindings.cpp index 0bcb86b220..9d0a2713ef 100644 --- a/bindings/python/src/pipeline/node/NodeBindings.cpp +++ b/bindings/python/src/pipeline/node/NodeBindings.cpp @@ -65,6 +65,7 @@ bool isCreatingNodeFromPipelineCreate() { std::vector(dai::Pipeline&, py::object class_)>>> pyNodeCreateMap; py::handle daiNodeModule; py::handle daiNodeInternalModule; +py::handle daiBetaNodeModule; std::vector(dai::Pipeline&, py::object class_)>>> NodeBindings::getNodeCreateMap() { return pyNodeCreateMap; @@ -191,6 +192,28 @@ void bind_depth(pybind11::module& m, void* pCallstack); void bind_neuralassistedstereo(pybind11::module& m, void* pCallstack); void bind_vpp(pybind11::module& m, void* pCallstack); void bind_gate(pybind11::module& m, void* pCallstack); +#ifdef DEPTHAI_HAVE_BETA +void bind_beta_classificationparser(pybind11::module& m, void* pCallstack); +void bind_beta_classificationsequenceparser(pybind11::module& m, void* pCallstack); +void bind_beta_embeddingsparser(pybind11::module& m, void* pCallstack); +void bind_beta_fastsamparser(pybind11::module& m, void* pCallstack); +void bind_beta_hrnetparser(pybind11::module& m, void* pCallstack); +void bind_beta_imageoutputparser(pybind11::module& m, void* pCallstack); +void bind_beta_imgdetectionsfilter(pybind11::module& m, void* pCallstack); +void bind_beta_keypointparser(pybind11::module& m, void* pCallstack); +void bind_beta_lanedetectionparser(pybind11::module& m, void* pCallstack); +void bind_beta_mapoutputparser(pybind11::module& m, void* pCallstack); +void bind_beta_mlsdparser(pybind11::module& m, void* pCallstack); +void bind_beta_mppalmdetectionparser(pybind11::module& m, void* pCallstack); +void bind_beta_pptextdetectionparser(pybind11::module& m, void* pCallstack); +void bind_beta_regressionparser(pybind11::module& m, void* pCallstack); +void bind_beta_rfdetrparser(pybind11::module& m, void* pCallstack); +void bind_beta_scrfdparser(pybind11::module& m, void* pCallstack); +void bind_beta_superanimalparser(pybind11::module& m, void* pCallstack); +void bind_beta_xfeatmonoparser(pybind11::module& m, void* pCallstack); +void bind_beta_xfeatstereoparser(pybind11::module& m, void* pCallstack); +void bind_beta_yunetparser(pybind11::module& m, void* pCallstack); +#endif #ifdef DEPTHAI_HAVE_BASALT_SUPPORT void bind_basaltnode(pybind11::module& m, void* pCallstack); #endif @@ -249,6 +272,28 @@ void NodeBindings::addToCallstack(std::deque& callstack) { callstack.push_front(bind_neuralassistedstereo); callstack.push_front(bind_vpp); callstack.push_front(bind_gate); +#ifdef DEPTHAI_HAVE_BETA + callstack.push_front(bind_beta_classificationparser); + callstack.push_front(bind_beta_classificationsequenceparser); + callstack.push_front(bind_beta_embeddingsparser); + callstack.push_front(bind_beta_fastsamparser); + callstack.push_front(bind_beta_hrnetparser); + callstack.push_front(bind_beta_imageoutputparser); + callstack.push_front(bind_beta_imgdetectionsfilter); + callstack.push_front(bind_beta_keypointparser); + callstack.push_front(bind_beta_lanedetectionparser); + callstack.push_front(bind_beta_mapoutputparser); + callstack.push_front(bind_beta_mlsdparser); + callstack.push_front(bind_beta_mppalmdetectionparser); + callstack.push_front(bind_beta_pptextdetectionparser); + callstack.push_front(bind_beta_regressionparser); + callstack.push_front(bind_beta_rfdetrparser); + callstack.push_front(bind_beta_scrfdparser); + callstack.push_front(bind_beta_superanimalparser); + callstack.push_front(bind_beta_xfeatmonoparser); + callstack.push_front(bind_beta_xfeatstereoparser); + callstack.push_front(bind_beta_yunetparser); +#endif #ifdef DEPTHAI_HAVE_BASALT_SUPPORT callstack.push_front(bind_basaltnode); #endif @@ -270,6 +315,9 @@ void NodeBindings::bind(pybind11::module& m, void* pCallstack) { // Move properties into nodes and nodes under 'node' submodule daiNodeModule = m.def_submodule("node"); daiNodeInternalModule = m.def_submodule("node").def_submodule("internal"); +#ifdef DEPTHAI_HAVE_BETA + daiBetaNodeModule = m.def_submodule("beta", "Experimental APIs").def_submodule("node", "Experimental nodes"); +#endif // XLink bridge structures py::class_> pyXLinkInBridge( diff --git a/bindings/python/src/pipeline/node/SegmentationParserBindings.cpp b/bindings/python/src/pipeline/node/SegmentationParserBindings.cpp index a4cc887da5..f3f4224711 100644 --- a/bindings/python/src/pipeline/node/SegmentationParserBindings.cpp +++ b/bindings/python/src/pipeline/node/SegmentationParserBindings.cpp @@ -42,16 +42,18 @@ void bind_segmentationparser(pybind11::module& m, void* pCallstack) { py::arg("input"), py::arg("model"), DOC(dai, node, SegmentationParser, build)) - .def("build", - py::overload_cast(&SegmentationParser::build), - py::arg("input"), - py::arg("nnArchive"), - DOC(dai, node, SegmentationParser, build, 2)) + // Backwards-compatible NNArchive build method forwarding to the consolidated Model path + .def( + "build", + [](SegmentationParser& self, Node::Output& input, const NNArchive& nnArchive) { return self.build(input, SegmentationParser::Model{nnArchive}); }, + py::arg("input"), + py::arg("nnArchive"), + DOC(dai, node, SegmentationParser, build)) .def("build", py::overload_cast(&SegmentationParser::build), py::arg("input"), py::arg("head"), - DOC(dai, node, SegmentationParser, build, 3)) + DOC(dai, node, SegmentationParser, build, 2)) .def("setNNArchive", py::overload_cast(&SegmentationParser::setNNArchive), py::arg("nnArchive"), diff --git a/bindings/python/src/pipeline/node/SpatialDetectionNetworkBindings.cpp b/bindings/python/src/pipeline/node/SpatialDetectionNetworkBindings.cpp index 138c04592b..e65f8ab69c 100644 --- a/bindings/python/src/pipeline/node/SpatialDetectionNetworkBindings.cpp +++ b/bindings/python/src/pipeline/node/SpatialDetectionNetworkBindings.cpp @@ -35,6 +35,33 @@ void bind_spatialdetectionnetwork(pybind11::module& m, void* pCallstack) { // Node spatialDetectionNetwork // Build methods with DepthSource variant + .def( + "build", + [](SpatialDetectionNetwork& self, + const std::shared_ptr& input, + const node::DepthSource& depthSource, + const SpatialDetectionNetwork::Model& model, + std::optional fps, + std::optional resizeMode) { return self.build(input, depthSource, model, fps, resizeMode); }, + py::arg("input"), + py::arg("depthSource"), + py::arg("model"), + py::arg("fps") = std::nullopt, + py::arg("resizeMode") = std::nullopt, + DOC(dai, node, SpatialDetectionNetwork, build)) + .def( + "build", + [](SpatialDetectionNetwork& self, + const std::shared_ptr& input, + const node::DepthSource& depthSource, + const SpatialDetectionNetwork::Model& model, + const ImgFrameCapability& capability) { return self.build(input, depthSource, model, capability); }, + py::arg("input"), + py::arg("depthSource"), + py::arg("model"), + py::arg("capability"), + DOC(dai, node, SpatialDetectionNetwork, build, 2)) + // Backwards-compatible build methods forwarding to the consolidated Model path .def( "build", [](SpatialDetectionNetwork& self, diff --git a/bindings/python/tests/CMakeLists.txt b/bindings/python/tests/CMakeLists.txt index 80eb40ad22..1333456cf2 100644 --- a/bindings/python/tests/CMakeLists.txt +++ b/bindings/python/tests/CMakeLists.txt @@ -24,6 +24,13 @@ set(PYBIND11_TEST_FILES "host_node_create_deprecation_test.py" ) +if(DEPTHAI_BUILD_BETA) + list(APPEND PYBIND11_TEST_FILES + "beta_img_detections_filter_test.py" + "beta_parser_config_test.py" + ) +endif() + string(REPLACE ".cpp" ".py" PYBIND11_PYTEST_FILES "${PYBIND11_TEST_FILES}") # Create the binding library at the end diff --git a/bindings/python/tests/beta_img_detections_filter_test.py b/bindings/python/tests/beta_img_detections_filter_test.py new file mode 100644 index 0000000000..fb25249d64 --- /dev/null +++ b/bindings/python/tests/beta_img_detections_filter_test.py @@ -0,0 +1,32 @@ +import depthai as dai + + +def test_img_detections_filter_is_available_in_beta_node_namespace(): + with dai.Pipeline(False) as pipeline: + node = pipeline.create(dai.beta.node.ImgDetectionsFilter) + + assert isinstance(node, dai.beta.node.ImgDetectionsFilter) + + +def test_img_detections_filter_passes_detections_through_unchanged(): + with dai.Pipeline(False) as pipeline: + node = pipeline.create(dai.beta.node.ImgDetectionsFilter) + input_queue = node.input.createInputQueue() + output_queue = node.output.createOutputQueue() + + detections = dai.ImgDetections() + detections.setSequenceNum(42) + detection = dai.ImgDetection() + detection.label = 7 + detection.confidence = 0.75 + detections.detections = [detection] + + pipeline.start() + input_queue.send(detections) + output = output_queue.get(timeout=1.0) + + assert output is detections + assert output.getSequenceNum() == 42 + assert len(output.detections) == 1 + assert output.detections[0].label == 7 + assert output.detections[0].confidence == 0.75 diff --git a/bindings/python/tests/beta_parser_config_test.py b/bindings/python/tests/beta_parser_config_test.py new file mode 100644 index 0000000000..5fc1a5901d --- /dev/null +++ b/bindings/python/tests/beta_parser_config_test.py @@ -0,0 +1,77 @@ +import numpy as np +import depthai as dai + + +PARSER_CONFIGS = [ + ("FastSAMParser", "FastSAMParserConfig", "FastSAMParserProperties"), + ("HRNetParser", "HRNetParserConfig", "HRNetParserProperties"), + ("MLSDParser", "MLSDParserConfig", "MLSDParserProperties"), + ("MPPalmDetectionParser", "MPPalmDetectionParserConfig", "MPPalmDetectionParserProperties"), + ("PPTextDetectionParser", "PPTextDetectionParserConfig", "PPTextDetectionParserProperties"), + ("RFDETRParser", "RFDETRParserConfig", "RFDETRParserProperties"), + ("SCRFDParser", "SCRFDParserConfig", "SCRFDParserProperties"), + ("SuperAnimalParser", "SuperAnimalParserConfig", "SuperAnimalParserProperties"), + ("YuNetParser", "YuNetParserConfig", "YuNetParserProperties"), + ("ClassificationSequenceParser", "ClassificationSequenceParserConfig", "ClassificationSequenceParserProperties"), + ("MapOutputParser", "MapOutputParserConfig", "MapOutputParserProperties"), + ("XFeatMonoParser", "XFeatMonoParserConfig", "XFeatMonoParserProperties"), + ("XFeatStereoParser", "XFeatStereoParserConfig", "XFeatStereoParserProperties"), +] + + +PROPERTY_FIELDS = { + "FastSAMParserProperties": ("numClasses", "yoloOutputs", "maskOutputs", "protosOutput"), + "HRNetParserProperties": ("outputLayerName", "labelNames", "edges"), + "MLSDParserProperties": ("outputLayerTPMap", "outputLayerHeat", "inputSize"), + "MPPalmDetectionParserProperties": ("outputLayerNames", "scale", "labelNames"), + "PPTextDetectionParserProperties": ("outputLayerName",), + "RFDETRParserProperties": ("labelNames", "outputLayerNames", "inputSize"), + "SCRFDParserProperties": ("outputLayerNames", "inputSize", "featStrideFpn", "numAnchors", "labelNames"), + "SuperAnimalParserProperties": ("outputLayerName", "scaleFactor", "nKeypoints", "labelNames", "edges"), + "YuNetParserProperties": ("locOutputLayerName", "confOutputLayerName", "iouOutputLayerName", "inputSize", "labelNames"), + "ClassificationSequenceParserProperties": ("outputLayerName", "classes", "nClasses", "isSoftmax"), + "MapOutputParserProperties": ("outputLayerName",), + "XFeatMonoParserProperties": ("outputLayerFeats", "outputLayerKeypoints", "outputLayerHeatmaps", "originalSize", "inputSize"), + "XFeatStereoParserProperties": ("outputLayerFeats", "outputLayerKeypoints", "outputLayerHeatmaps", "originalSize", "inputSize"), +} + +def test_every_parser_config_and_node_config_surface_is_bound(): + with dai.Pipeline(False) as pipeline: + for node_name, config_name, properties_name in PARSER_CONFIGS: + config_type = getattr(dai.beta, config_name) + properties_type = getattr(dai.beta, properties_name) + node_type = getattr(dai.beta.node, node_name) + + config = config_type() + node = pipeline.create(node_type) + + assert config.validate() + assert isinstance(node.initialConfig, config_type) + assert node.inputConfig is not None + assert node_type.Properties is properties_type + for field in ("initialConfig", *PROPERTY_FIELDS[properties_name]): + assert hasattr(properties_type, field) + + +def test_map_output_parser_accepts_and_applies_a_runtime_config(): + with dai.Pipeline(False) as pipeline: + parser = pipeline.create(dai.beta.node.MapOutputParser) + parser.setOutputLayerName("map") + parser.inputConfig.setWaitForMessage(True) + + input_queue = parser.input.createInputQueue() + config_queue = parser.inputConfig.createInputQueue() + output_queue = parser.out.createOutputQueue() + + config = dai.beta.MapOutputParserConfig() + config.minMaxScaling = True + + nn_data = dai.NNData() + nn_data.addTensor("map", np.array([[2.0, 4.0]], dtype=np.float32)) + + config_queue.send(config) + pipeline.start() + input_queue.send(nn_data) + output = output_queue.get(timeout=1.0) + + np.testing.assert_allclose(output.getMap(), np.array([[0.0, 1.0]], dtype=np.float32)) diff --git a/bindings/python/tests/imgframe_test.py b/bindings/python/tests/imgframe_test.py index 902fa92517..3aaeb20023 100644 --- a/bindings/python/tests/imgframe_test.py +++ b/bindings/python/tests/imgframe_test.py @@ -44,6 +44,10 @@ def assert_images_close(expected, recovered, tolerance, msg): assert max_diff <= tolerance, f"{msg} max abs diff too high: {max_diff} > {tolerance}" +def test_colorize_depth_frame_is_in_utility_namespace(): + assert callable(dai.utility.colorizeDepthFrame) + assert not hasattr(dai, "colorizeDepthFrame") + COLOR_TYPES = [ pytest.param(dai.ImgFrame.Type.BGR888p, 0.5, id="BGR888p"), diff --git a/bindings/python/tests/inherited_messages_test.py b/bindings/python/tests/inherited_messages_test.py index 84dd1c0dde..c64163eaf8 100644 --- a/bindings/python/tests/inherited_messages_test.py +++ b/bindings/python/tests/inherited_messages_test.py @@ -78,6 +78,45 @@ def __init__(self, number=0): assert isinstance(message, CustomMessage) assert message.test_field == i +def test_buffer_methods_are_inherited_by_derived_messages(): + timestamp = dai.Buffer().getTimestamp() + device_timestamp = dai.Buffer().getTimestampDevice() + system_timestamp = dai.Buffer().getTimestampSystem() + + for message_type in [ + dai.AprilTags, + dai.EncodedFrame, + dai.ImgAnnotations, + dai.ImgDetections, + dai.MessageGroup, + dai.NNData, + dai.PipelineEvent, + dai.PipelineState, + dai.PointCloudData, + dai.SegmentationMask, + dai.SpatialImgDetections, + dai.SpatialLocationCalculatorData, + dai.TrackedFeatures, + dai.Tracklets, + ]: + message = message_type() + message.setTimestamp(timestamp) + message.setTimestampDevice(device_timestamp) + message.setTimestampSystem(system_timestamp) + message.setSequenceNum(42) + + assert message.getTimestamp() == timestamp + assert message.getTimestampDevice() == device_timestamp + assert message.getTimestampSystem() == system_timestamp + assert message.getSequenceNum() == 42 + +def test_imgframe_keeps_timestamp_overloads(): + frame = dai.ImgFrame() + + assert frame.getTimestamp(dai.CameraExposureOffset.END) == frame.getTimestamp() + assert frame.getTimestampDevice(dai.CameraExposureOffset.END) == frame.getTimestampDevice() + assert frame.getTimestampSystem(dai.CameraExposureOffset.END) == frame.getTimestampSystem() + def test_transformable_buffer_dispatches_to_python_override(): class CustomTransformableBuffer(dai.TransformableBuffer): def __init__(self): diff --git a/cmake/Depthai/DepthaiBootloaderConfig.cmake b/cmake/Depthai/DepthaiBootloaderConfig.cmake index 360cb718f8..d4bac1fad2 100644 --- a/cmake/Depthai/DepthaiBootloaderConfig.cmake +++ b/cmake/Depthai/DepthaiBootloaderConfig.cmake @@ -3,5 +3,5 @@ set(DEPTHAI_BOOTLOADER_MATURITY "release") # set(DEPTHAI_BOOTLOADER_MATURITY "snapshot") # "version if applicable" -set(DEPTHAI_BOOTLOADER_VERSION "0.0.28") -# set(DEPTHAI_BOOTLOADER_VERSION "0.0.24+57c26493754e2f00e57f6594b0b1a317f762d5f2") +set(DEPTHAI_BOOTLOADER_VERSION "0.0.29") +# set(DEPTHAI_BOOTLOADER_VERSION "0.0.28+f07b375cd0d8590ba573c3aea923006d8a4469b0") diff --git a/cmake/Depthai/DepthaiDeviceRVC4Config.cmake b/cmake/Depthai/DepthaiDeviceRVC4Config.cmake index 27106edbfb..a7429b33de 100644 --- a/cmake/Depthai/DepthaiDeviceRVC4Config.cmake +++ b/cmake/Depthai/DepthaiDeviceRVC4Config.cmake @@ -3,4 +3,4 @@ set(DEPTHAI_DEVICE_RVC4_MATURITY "snapshot") # "version if applicable" -set(DEPTHAI_DEVICE_RVC4_VERSION "0.0.1+c0f6b6100c93f21e29c361cffad1f83bba0418ca") +set(DEPTHAI_DEVICE_RVC4_VERSION "0.0.1+58d182b6fce698d1c84c07cac440535774defbde") diff --git a/cmake/Depthai/DepthaiDeviceSideConfig.cmake b/cmake/Depthai/DepthaiDeviceSideConfig.cmake index 275ce3abc6..c9a961eab3 100644 --- a/cmake/Depthai/DepthaiDeviceSideConfig.cmake +++ b/cmake/Depthai/DepthaiDeviceSideConfig.cmake @@ -2,7 +2,7 @@ set(DEPTHAI_DEVICE_SIDE_MATURITY "snapshot") # "full commit hash of device side binary" -set(DEPTHAI_DEVICE_SIDE_COMMIT "c3f4bc9cef3937fa987135129757d950d1fbbd19") +set(DEPTHAI_DEVICE_SIDE_COMMIT "ac60599e9650fbfec38ff4b5acb4448a402889b0") # "version if applicable" set(DEPTHAI_DEVICE_SIDE_VERSION "") diff --git a/cmake/Depthai/DepthaiVisualizerConfig.cmake b/cmake/Depthai/DepthaiVisualizerConfig.cmake index ac62eb6edc..2e2da608c5 100644 --- a/cmake/Depthai/DepthaiVisualizerConfig.cmake +++ b/cmake/Depthai/DepthaiVisualizerConfig.cmake @@ -1,2 +1,2 @@ # "full commit hash of depthai visualizer static files" -set(DEPTHAI_VISUALIZER_COMMIT "3.5.2") +set(DEPTHAI_VISUALIZER_COMMIT "3.7.6") diff --git a/cmake/depthaiConfig.cmake.in b/cmake/depthaiConfig.cmake.in index 96ecd1fd01..311ae45959 100644 --- a/cmake/depthaiConfig.cmake.in +++ b/cmake/depthaiConfig.cmake.in @@ -8,6 +8,7 @@ set(DEPTHAI_OPENCV_SUPPORT @DEPTHAI_OPENCV_SUPPORT@) set(DEPTHAI_PCL_SUPPORT @DEPTHAI_PCL_SUPPORT@) set(DEPTHAI_XTENSOR_SUPPORT @DEPTHAI_XTENSOR_SUPPORT@) set(DEPTHAI_DYNAMIC_CALIBRATION_SUPPORT @DEPTHAI_DYNAMIC_CALIBRATION_SUPPORT@) +set(DEPTHAI_BUILD_BETA @DEPTHAI_BUILD_BETA@) if(DEPTHAI_OPENCV_SUPPORT) find_dependency(OpenCV 4 CONFIG REQUIRED) diff --git a/cmake/depthaiOptions.cmake b/cmake/depthaiOptions.cmake index 4d2991ee0f..b18168bc34 100644 --- a/cmake/depthaiOptions.cmake +++ b/cmake/depthaiOptions.cmake @@ -32,6 +32,7 @@ option(DEPTHAI_DYNAMIC_CALIBRATION_SUPPORT "Enable Dynamic Calibration support" # Build Behaviour option(DEPTHAI_MERGED_TARGET "Enable merged target build" ON) +option(DEPTHAI_BUILD_BETA "Build beta namespace APIs" ON) option(DEPTHAI_BUILD_PYTHON "Build python bindings" OFF) option(DEPTHAI_BUILD_TESTS "Build tests" OFF) option(DEPTHAI_BUILD_EXAMPLES "Build examples - Requires OpenCV library to be installed" OFF) diff --git a/examples/cpp/AutoCalibration/auto_calibration_example.cpp b/examples/cpp/AutoCalibration/auto_calibration_example.cpp index 49d5dab7a1..400498e821 100644 --- a/examples/cpp/AutoCalibration/auto_calibration_example.cpp +++ b/examples/cpp/AutoCalibration/auto_calibration_example.cpp @@ -9,25 +9,6 @@ #include "depthai/depthai.hpp" -// Visualization helper -void showDepth(const cv::Mat& depthFrame, const std::string& windowName = "Depth", int minDistance = 500, int maxDistance = 5000) { - if(maxDistance <= minDistance) return; - - cv::Mat clipped = depthFrame.clone(); - clipped.setTo(minDistance, depthFrame < minDistance); - clipped.setTo(maxDistance, depthFrame > maxDistance); - - cv::Mat displayFrame; - double scale = 255.0 / (maxDistance - minDistance); - double offset = -minDistance * scale; - clipped.convertTo(displayFrame, CV_8UC1, scale, offset); - - cv::Mat colorMap; - cv::applyColorMap(displayFrame, colorMap, cv::COLORMAP_TURBO); - - cv::imshow(windowName, colorMap); -} - std::tuple rotationMatrixToEulerAngles(const cv::Matx33d& rotationMatrix) { constexpr double kPi = 3.14159265358979323846; const double sy = std::sqrt(rotationMatrix(0, 0) * rotationMatrix(0, 0) + rotationMatrix(1, 0) * rotationMatrix(1, 0)); @@ -111,7 +92,7 @@ int main() { // Nodes auto camLeft = pipeline.create()->build(dai::CameraBoardSocket::CAM_B); auto camRight = pipeline.create()->build(dai::CameraBoardSocket::CAM_C); - auto stereo = pipeline.create(); + auto depth = pipeline.create(); // AutoCalibration node auto dcWorker = pipeline.create(); @@ -125,13 +106,9 @@ int main() { config->validationSetSize = 5; config->dataConfidenceThreshold = 0.3; - // Links - camLeft->requestOutput({1280, 800})->link(stereo->left); - camRight->requestOutput({1280, 800})->link(stereo->right); - // Queues auto workerOutputQueue = dcWorker->output.createOutputQueue(); - auto stereoOut = stereo->depth.createOutputQueue(); + auto stereoOut = depth->depth().createOutputQueue(); pipeline.start(); @@ -148,7 +125,7 @@ int main() { } auto depth = stereoOut->get(); - showDepth(depth->getCvFrame(), "Depth", 500, 5000); + cv::imshow("Depth", dai::utility::colorizeDepthFrame(*depth, 500.0f, 12000.0f, cv::COLORMAP_TURBO, true).getCvFrame()); if(cv::waitKey(1) == 'q') break; } diff --git a/examples/cpp/Beta/CMakeLists.txt b/examples/cpp/Beta/CMakeLists.txt new file mode 100644 index 0000000000..d688258eb2 --- /dev/null +++ b/examples/cpp/Beta/CMakeLists.txt @@ -0,0 +1,21 @@ +project(beta_parser_examples) +cmake_minimum_required(VERSION 3.10) + +## function: dai_add_example(example_name example_src enable_test use_pcl) +## function: dai_set_example_test_labels(example_name ...) + +dai_add_example(beta_classification_parser classification_parser.cpp OFF OFF) +dai_add_example(beta_classification_sequence_parser classification_sequence_parser.cpp OFF OFF) +dai_add_example(beta_embeddings_parser embeddings_parser.cpp OFF OFF) +dai_add_example(beta_fastsam_parser fastsam_parser.cpp OFF OFF) +dai_add_example(beta_hrnet_parser hrnet_parser.cpp OFF OFF) +dai_add_example(beta_image_output_parser image_output_parser.cpp OFF OFF) +dai_add_example(beta_keypoint_parser keypoint_parser.cpp OFF OFF) +dai_add_example(beta_lane_detection_parser lane_detection_parser.cpp OFF OFF) +dai_add_example(beta_map_output_parser map_output_parser.cpp OFF OFF) +dai_add_example(beta_mlsd_parser mlsd_parser.cpp OFF OFF) +dai_add_example(beta_mp_palm_detection_parser mp_palm_detection_parser.cpp OFF OFF) +dai_add_example(beta_pp_text_detection_parser pp_text_detection_parser.cpp OFF OFF) +dai_add_example(beta_scrfd_parser scrfd_parser.cpp OFF OFF) +dai_add_example(beta_superanimal_parser superanimal_parser.cpp OFF OFF) +dai_add_example(beta_yunet_parser yunet_parser.cpp OFF OFF) diff --git a/examples/cpp/Beta/classification_parser.cpp b/examples/cpp/Beta/classification_parser.cpp new file mode 100644 index 0000000000..d2a0a7e126 --- /dev/null +++ b/examples/cpp/Beta/classification_parser.cpp @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include + +#include "depthai/beta/datatypes.hpp" +#include "depthai/beta/nodes.hpp" +#include "depthai/depthai.hpp" + +int main() { + dai::Pipeline pipeline; + + const std::string modelSlug = "luxonis/emotion-recognition:260x260"; + dai::NNModelDescription modelDescription{modelSlug}; + modelDescription.platform = pipeline.getDefaultDevice()->getPlatformAsString(); + dai::NNArchive modelArchive(dai::getModelFromZoo(modelDescription)); + + auto cameraNode = pipeline.create()->build(); + auto neuralNetwork = pipeline.create()->build(cameraNode, modelArchive); + auto parserNode = pipeline.create()->build(neuralNetwork->out, modelArchive); + + auto frameQueue = neuralNetwork->passthrough.createOutputQueue(); + auto outputQueue = parserNode->out.createOutputQueue(); + + pipeline.start(); + + while(pipeline.isRunning()) { + auto frameMessage = frameQueue->get(); + auto parserOutput = outputQueue->get(); + cv::Mat frame = frameMessage->getCvFrame(); + const auto count = std::min({5, parserOutput->classes.size(), parserOutput->scores.size()}); + for(std::size_t index = 0; index < count; ++index) { + std::ostringstream text; + text << parserOutput->classes[index] << ": " << std::fixed << std::setprecision(2) << parserOutput->scores[index]; + cv::putText(frame, text.str(), cv::Point(20, 35 + static_cast(index) * 25), cv::FONT_HERSHEY_SIMPLEX, 0.65, cv::Scalar(0, 255, 0), 2); + } + + cv::imshow("ClassificationParser", frame); + if(cv::waitKey(1) == 'q') break; + } + + return 0; +} diff --git a/examples/cpp/Beta/classification_sequence_parser.cpp b/examples/cpp/Beta/classification_sequence_parser.cpp new file mode 100644 index 0000000000..28d2747eaf --- /dev/null +++ b/examples/cpp/Beta/classification_sequence_parser.cpp @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "depthai/beta/datatypes.hpp" +#include "depthai/beta/nodes.hpp" +#include "depthai/depthai.hpp" + +int main() { + dai::Pipeline pipeline; + + const std::string modelSlug = "luxonis/paddle-text-recognition:320x48"; + dai::NNModelDescription modelDescription{modelSlug}; + modelDescription.platform = pipeline.getDefaultDevice()->getPlatformAsString(); + dai::NNArchive modelArchive(dai::getModelFromZoo(modelDescription)); + + auto cameraNode = pipeline.create()->build(); + auto neuralNetwork = pipeline.create()->build(cameraNode, modelArchive); + auto parserNode = pipeline.create()->build(neuralNetwork->out, modelArchive); + + auto frameQueue = neuralNetwork->passthrough.createOutputQueue(); + auto outputQueue = parserNode->out.createOutputQueue(); + auto configQueue = parserNode->inputConfig.createInputQueue(); + auto config = parserNode->initialConfig; + + pipeline.start(); + std::cout << "Controls: 't' toggle remove duplicates, 'q' quit." << std::endl; + + while(pipeline.isRunning()) { + auto frameMessage = frameQueue->get(); + auto parserOutput = outputQueue->get(); + cv::Mat frame = frameMessage->getCvFrame(); + const bool characterSequence = + std::all_of(parserOutput->classes.begin(), parserOutput->classes.end(), [](const auto& label) { return label.size() <= 1; }); + std::ostringstream decodedText; + for(std::size_t index = 0; index < parserOutput->classes.size(); ++index) { + if(index > 0 && !characterSequence) decodedText << ' '; + decodedText << parserOutput->classes[index]; + } + cv::putText(frame, decodedText.str(), cv::Point(20, 35), cv::FONT_HERSHEY_SIMPLEX, 0.65, cv::Scalar(0, 255, 0), 2); + + cv::imshow("ClassificationSequenceParser", frame); + const int key = cv::waitKey(1); + if(key == 'q') break; + if(key == 't') { + config->removeDuplicates = !config->removeDuplicates; + configQueue->send(config); + std::cout << "Remove duplicates: " << config->removeDuplicates << std::endl; + } + } + + return 0; +} diff --git a/examples/cpp/Beta/embeddings_parser.cpp b/examples/cpp/Beta/embeddings_parser.cpp new file mode 100644 index 0000000000..32e1e2041b --- /dev/null +++ b/examples/cpp/Beta/embeddings_parser.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include + +#include "depthai/beta/datatypes.hpp" +#include "depthai/beta/nodes.hpp" +#include "depthai/depthai.hpp" + +int main() { + dai::Pipeline pipeline; + + const std::string modelSlug = "luxonis/arcface:lfw-112x112"; + dai::NNModelDescription modelDescription{modelSlug}; + modelDescription.platform = pipeline.getDefaultDevice()->getPlatformAsString(); + dai::NNArchive modelArchive(dai::getModelFromZoo(modelDescription)); + + auto cameraNode = pipeline.create()->build(); + auto neuralNetwork = pipeline.create()->build(cameraNode, modelArchive); + auto parserNode = pipeline.create()->build(neuralNetwork->out, modelArchive); + + auto frameQueue = neuralNetwork->passthrough.createOutputQueue(); + auto outputQueue = parserNode->out.createOutputQueue(); + + pipeline.start(); + + while(pipeline.isRunning()) { + auto frameMessage = frameQueue->get(); + auto parserOutput = outputQueue->get(); + cv::Mat frame = frameMessage->getCvFrame(); + const auto embedding = parserOutput->getFirstTensor(); + double squaredNorm = 0.0; + for(const auto value : embedding) squaredNorm += static_cast(value) * value; + std::ostringstream text; + text << "Embedding size: " << embedding.size() << ", norm: " << std::fixed << std::setprecision(2) << std::sqrt(squaredNorm); + cv::putText(frame, text.str(), cv::Point(20, 35), cv::FONT_HERSHEY_SIMPLEX, 0.65, cv::Scalar(0, 255, 0), 2); + + cv::imshow("EmbeddingsParser", frame); + if(cv::waitKey(1) == 'q') break; + } + + return 0; +} diff --git a/examples/cpp/Beta/fastsam_parser.cpp b/examples/cpp/Beta/fastsam_parser.cpp new file mode 100644 index 0000000000..de03abc43a --- /dev/null +++ b/examples/cpp/Beta/fastsam_parser.cpp @@ -0,0 +1,60 @@ +#include +#include +#include +#include +#include +#include + +#include "depthai/beta/datatypes.hpp" +#include "depthai/beta/nodes.hpp" +#include "depthai/depthai.hpp" + +int main() { + dai::Pipeline pipeline; + + const std::string modelSlug = "luxonis/fastsam-s:512x288"; + dai::NNModelDescription modelDescription{modelSlug}; + modelDescription.platform = pipeline.getDefaultDevice()->getPlatformAsString(); + dai::NNArchive modelArchive(dai::getModelFromZoo(modelDescription)); + + auto cameraNode = pipeline.create()->build(); + auto neuralNetwork = pipeline.create()->build(cameraNode, modelArchive); + auto parserNode = pipeline.create()->build(neuralNetwork->out, modelArchive); + + auto frameQueue = neuralNetwork->passthrough.createOutputQueue(); + auto outputQueue = parserNode->out.createOutputQueue(); + auto configQueue = parserNode->inputConfig.createInputQueue(); + auto config = parserNode->initialConfig; + + pipeline.start(); + std::cout << "Controls: '+' increase confidence threshold, '-' decrease it, 'q' quit." << std::endl; + + while(pipeline.isRunning()) { + auto frameMessage = frameQueue->get(); + auto parserOutput = outputQueue->get(); + cv::Mat frame = frameMessage->getCvFrame(); + cv::Mat mask = parserOutput->getCvMask(); + cv::resize(mask, mask, frame.size(), 0.0, 0.0, cv::INTER_NEAREST); + cv::Mat scaledMask; + mask.convertTo(scaledMask, CV_8U, 37.0); + cv::Mat coloredMask; + cv::applyColorMap(scaledMask, coloredMask, cv::COLORMAP_TURBO); + coloredMask.setTo(cv::Scalar(0, 0, 0), mask == 255); + cv::addWeighted(frame, 0.6, coloredMask, 0.4, 0.0, frame); + + cv::imshow("FastSAMParser", frame); + const int key = cv::waitKey(1); + if(key == 'q') break; + if(key == '+') { + config->confidenceThreshold = std::min(1.0f, config->confidenceThreshold + 0.1f); + configQueue->send(config); + std::cout << "Confidence threshold: " << config->confidenceThreshold << std::endl; + } else if(key == '-') { + config->confidenceThreshold = std::max(0.0f, config->confidenceThreshold - 0.1f); + configQueue->send(config); + std::cout << "Confidence threshold: " << config->confidenceThreshold << std::endl; + } + } + + return 0; +} diff --git a/examples/cpp/Beta/hrnet_parser.cpp b/examples/cpp/Beta/hrnet_parser.cpp new file mode 100644 index 0000000000..a692aedf4b --- /dev/null +++ b/examples/cpp/Beta/hrnet_parser.cpp @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "depthai/beta/datatypes.hpp" +#include "depthai/beta/nodes.hpp" +#include "depthai/depthai.hpp" + +int main() { + dai::Pipeline pipeline; + + const std::string modelSlug = "luxonis/lite-hrnet:18-coco-288x384"; + dai::NNModelDescription modelDescription{modelSlug}; + modelDescription.platform = pipeline.getDefaultDevice()->getPlatformAsString(); + dai::NNArchive modelArchive(dai::getModelFromZoo(modelDescription)); + + auto cameraNode = pipeline.create()->build(); + auto neuralNetwork = pipeline.create()->build(cameraNode, modelArchive); + auto parserNode = pipeline.create()->build(neuralNetwork->out, modelArchive); + + auto frameQueue = neuralNetwork->passthrough.createOutputQueue(); + auto outputQueue = parserNode->out.createOutputQueue(); + auto configQueue = parserNode->inputConfig.createInputQueue(); + auto config = parserNode->initialConfig; + + pipeline.start(); + std::cout << "Controls: '+' increase score threshold, '-' decrease it, 'q' quit." << std::endl; + + while(pipeline.isRunning()) { + auto frameMessage = frameQueue->get(); + auto parserOutput = outputQueue->get(); + cv::Mat frame = frameMessage->getCvFrame(); + std::vector points; + for(const auto& point : parserOutput->getPoints2f()) { + points.emplace_back(static_cast(point.x * frame.cols), static_cast(point.y * frame.rows)); + } + for(const auto& edge : parserOutput->getEdges()) { + cv::line(frame, points.at(edge[0]), points.at(edge[1]), cv::Scalar(0, 255, 0), 2); + } + for(const auto& point : points) cv::circle(frame, point, 3, cv::Scalar(0, 0, 255), -1); + + cv::imshow("HRNetParser", frame); + const int key = cv::waitKey(1); + if(key == 'q') break; + if(key == '+') { + config->scoreThreshold = std::min(1.0f, config->scoreThreshold + 0.1f); + configQueue->send(config); + std::cout << "Score threshold: " << config->scoreThreshold << std::endl; + } else if(key == '-') { + config->scoreThreshold = std::max(0.0f, config->scoreThreshold - 0.1f); + configQueue->send(config); + std::cout << "Score threshold: " << config->scoreThreshold << std::endl; + } + } + + return 0; +} diff --git a/examples/cpp/Beta/image_output_parser.cpp b/examples/cpp/Beta/image_output_parser.cpp new file mode 100644 index 0000000000..7fbe508318 --- /dev/null +++ b/examples/cpp/Beta/image_output_parser.cpp @@ -0,0 +1,36 @@ +#include +#include + +#include "depthai/beta/datatypes.hpp" +#include "depthai/beta/nodes.hpp" +#include "depthai/depthai.hpp" + +int main() { + dai::Pipeline pipeline; + + const std::string modelSlug = "luxonis/dncnn3:320x240"; + dai::NNModelDescription modelDescription{modelSlug}; + modelDescription.platform = pipeline.getDefaultDevice()->getPlatformAsString(); + dai::NNArchive modelArchive(dai::getModelFromZoo(modelDescription)); + + auto cameraNode = pipeline.create()->build(); + auto neuralNetwork = pipeline.create()->build(cameraNode, modelArchive); + auto parserNode = pipeline.create()->build(neuralNetwork->out, modelArchive); + + auto frameQueue = neuralNetwork->passthrough.createOutputQueue(); + auto outputQueue = parserNode->out.createOutputQueue(); + + pipeline.start(); + + while(pipeline.isRunning()) { + auto frameMessage = frameQueue->get(); + auto parserOutput = outputQueue->get(); + cv::Mat frame = frameMessage->getCvFrame(); + frame = parserOutput->getCvFrame(); + + cv::imshow("ImageOutputParser", frame); + if(cv::waitKey(1) == 'q') break; + } + + return 0; +} diff --git a/examples/cpp/Beta/keypoint_parser.cpp b/examples/cpp/Beta/keypoint_parser.cpp new file mode 100644 index 0000000000..3dab1abf2e --- /dev/null +++ b/examples/cpp/Beta/keypoint_parser.cpp @@ -0,0 +1,44 @@ +#include +#include +#include + +#include "depthai/beta/datatypes.hpp" +#include "depthai/beta/nodes.hpp" +#include "depthai/depthai.hpp" + +int main() { + dai::Pipeline pipeline; + + const std::string modelSlug = "luxonis/mediapipe-face-landmarker:192x192"; + dai::NNModelDescription modelDescription{modelSlug}; + modelDescription.platform = pipeline.getDefaultDevice()->getPlatformAsString(); + dai::NNArchive modelArchive(dai::getModelFromZoo(modelDescription)); + + auto cameraNode = pipeline.create()->build(); + auto neuralNetwork = pipeline.create()->build(cameraNode, modelArchive); + auto parserNode = pipeline.create()->build(neuralNetwork->out, modelArchive); + + auto frameQueue = neuralNetwork->passthrough.createOutputQueue(); + auto outputQueue = parserNode->out.createOutputQueue(); + + pipeline.start(); + + while(pipeline.isRunning()) { + auto frameMessage = frameQueue->get(); + auto parserOutput = outputQueue->get(); + cv::Mat frame = frameMessage->getCvFrame(); + std::vector points; + for(const auto& point : parserOutput->getPoints2f()) { + points.emplace_back(static_cast(point.x * frame.cols), static_cast(point.y * frame.rows)); + } + for(const auto& edge : parserOutput->getEdges()) { + cv::line(frame, points.at(edge[0]), points.at(edge[1]), cv::Scalar(0, 255, 0), 2); + } + for(const auto& point : points) cv::circle(frame, point, 3, cv::Scalar(0, 0, 255), -1); + + cv::imshow("KeypointParser", frame); + if(cv::waitKey(1) == 'q') break; + } + + return 0; +} diff --git a/examples/cpp/Beta/lane_detection_parser.cpp b/examples/cpp/Beta/lane_detection_parser.cpp new file mode 100644 index 0000000000..6fc9d89a2a --- /dev/null +++ b/examples/cpp/Beta/lane_detection_parser.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "depthai/beta/datatypes.hpp" +#include "depthai/beta/nodes.hpp" +#include "depthai/depthai.hpp" + +int main() { + dai::Pipeline pipeline; + + const std::string modelSlug = "luxonis/ultra-fast-lane-detection:culane-800x288"; + dai::NNModelDescription modelDescription{modelSlug}; + modelDescription.platform = pipeline.getDefaultDevice()->getPlatformAsString(); + dai::NNArchive modelArchive(dai::getModelFromZoo(modelDescription)); + + auto cameraNode = pipeline.create()->build(); + auto neuralNetwork = pipeline.create()->build(cameraNode, modelArchive); + auto parserNode = pipeline.create()->build(neuralNetwork->out, modelArchive); + + auto frameQueue = neuralNetwork->passthrough.createOutputQueue(); + auto outputQueue = parserNode->out.createOutputQueue(); + + pipeline.start(); + + while(pipeline.isRunning()) { + auto frameMessage = frameQueue->get(); + auto parserOutput = outputQueue->get(); + cv::Mat frame = frameMessage->getCvFrame(); + for(const auto& cluster : parserOutput->clusters) { + std::vector points; + for(const auto& point : cluster.points) { + points.emplace_back(static_cast(point.x * frame.cols), static_cast(point.y * frame.rows)); + } + if(points.size() > 1) cv::polylines(frame, points, false, cv::Scalar(0, 255, 0), 3); + } + + cv::imshow("LaneDetectionParser", frame); + if(cv::waitKey(1) == 'q') break; + } + + return 0; +} diff --git a/examples/cpp/Beta/map_output_parser.cpp b/examples/cpp/Beta/map_output_parser.cpp new file mode 100644 index 0000000000..dfc4da74d9 --- /dev/null +++ b/examples/cpp/Beta/map_output_parser.cpp @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include + +#include "depthai/beta/datatypes.hpp" +#include "depthai/beta/nodes.hpp" +#include "depthai/depthai.hpp" + +int main() { + dai::Pipeline pipeline; + + const std::string modelSlug = "luxonis/dm-count:sha-426x240"; + dai::NNModelDescription modelDescription{modelSlug}; + modelDescription.platform = pipeline.getDefaultDevice()->getPlatformAsString(); + dai::NNArchive modelArchive(dai::getModelFromZoo(modelDescription)); + + auto cameraNode = pipeline.create()->build(); + auto neuralNetwork = pipeline.create()->build(cameraNode, modelArchive); + auto parserNode = pipeline.create()->build(neuralNetwork->out, modelArchive); + + auto frameQueue = neuralNetwork->passthrough.createOutputQueue(); + auto outputQueue = parserNode->out.createOutputQueue(); + auto configQueue = parserNode->inputConfig.createInputQueue(); + auto config = parserNode->initialConfig; + + pipeline.start(); + std::cout << "Controls: 't' toggle min/max scaling, 'q' quit." << std::endl; + + while(pipeline.isRunning()) { + auto frameMessage = frameQueue->get(); + auto parserOutput = outputQueue->get(); + cv::Mat frame = frameMessage->getCvFrame(); + const auto mapValues = parserOutput->getMap(); + cv::Mat map(static_cast(parserOutput->getHeight()), static_cast(parserOutput->getWidth()), CV_32F, const_cast(mapValues.data())); + cv::Mat normalizedMap; + cv::normalize(map, normalizedMap, 0, 255, cv::NORM_MINMAX); + normalizedMap.convertTo(normalizedMap, CV_8U); + cv::applyColorMap(normalizedMap, frame, cv::COLORMAP_INFERNO); + cv::resize(frame, frame, frameMessage->getCvFrame().size()); + + cv::imshow("MapOutputParser", frame); + const int key = cv::waitKey(1); + if(key == 'q') break; + if(key == 't') { + config->minMaxScaling = !config->minMaxScaling; + configQueue->send(config); + std::cout << "Min/max scaling: " << config->minMaxScaling << std::endl; + } + } + + return 0; +} diff --git a/examples/cpp/Beta/mlsd_parser.cpp b/examples/cpp/Beta/mlsd_parser.cpp new file mode 100644 index 0000000000..3c115435ad --- /dev/null +++ b/examples/cpp/Beta/mlsd_parser.cpp @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include +#include + +#include "depthai/beta/datatypes.hpp" +#include "depthai/beta/nodes.hpp" +#include "depthai/depthai.hpp" + +int main() { + dai::Pipeline pipeline; + + const std::string modelSlug = "luxonis/m-lsd:512x512"; + dai::NNModelDescription modelDescription{modelSlug}; + modelDescription.platform = pipeline.getDefaultDevice()->getPlatformAsString(); + dai::NNArchive modelArchive(dai::getModelFromZoo(modelDescription)); + + auto cameraNode = pipeline.create()->build(); + auto neuralNetwork = pipeline.create()->build(cameraNode, modelArchive); + auto parserNode = pipeline.create()->build(neuralNetwork->out, modelArchive); + + auto frameQueue = neuralNetwork->passthrough.createOutputQueue(); + auto outputQueue = parserNode->out.createOutputQueue(); + auto configQueue = parserNode->inputConfig.createInputQueue(); + auto config = parserNode->initialConfig; + + pipeline.start(); + std::cout << "Controls: '+' increase score threshold, '-' decrease it, 'q' quit." << std::endl; + + while(pipeline.isRunning()) { + auto frameMessage = frameQueue->get(); + auto parserOutput = outputQueue->get(); + cv::Mat frame = frameMessage->getCvFrame(); + for(const auto& line : parserOutput->lines) { + const cv::Point startPoint(static_cast(line.startPoint.x * frame.cols), static_cast(line.startPoint.y * frame.rows)); + const cv::Point endPoint(static_cast(line.endPoint.x * frame.cols), static_cast(line.endPoint.y * frame.rows)); + cv::line(frame, startPoint, endPoint, cv::Scalar(0, 255, 0), 2); + } + + cv::imshow("MLSDParser", frame); + const int key = cv::waitKey(1); + if(key == 'q') break; + if(key == '+') { + config->scoreThreshold = std::min(1.0f, config->scoreThreshold + 0.1f); + configQueue->send(config); + std::cout << "Score threshold: " << config->scoreThreshold << std::endl; + } else if(key == '-') { + config->scoreThreshold = std::max(0.0f, config->scoreThreshold - 0.1f); + configQueue->send(config); + std::cout << "Score threshold: " << config->scoreThreshold << std::endl; + } + } + + return 0; +} diff --git a/examples/cpp/Beta/mp_palm_detection_parser.cpp b/examples/cpp/Beta/mp_palm_detection_parser.cpp new file mode 100644 index 0000000000..86e50b8784 --- /dev/null +++ b/examples/cpp/Beta/mp_palm_detection_parser.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "depthai/beta/datatypes.hpp" +#include "depthai/beta/nodes.hpp" +#include "depthai/depthai.hpp" + +int main() { + dai::Pipeline pipeline; + + const std::string modelSlug = "luxonis/mediapipe-palm-detection:192x192"; + dai::NNModelDescription modelDescription{modelSlug}; + modelDescription.platform = pipeline.getDefaultDevice()->getPlatformAsString(); + dai::NNArchive modelArchive(dai::getModelFromZoo(modelDescription)); + + auto cameraNode = pipeline.create()->build(); + auto neuralNetwork = pipeline.create()->build(cameraNode, modelArchive); + auto parserNode = pipeline.create()->build(neuralNetwork->out, modelArchive); + + auto frameQueue = neuralNetwork->passthrough.createOutputQueue(); + auto outputQueue = parserNode->out.createOutputQueue(); + auto configQueue = parserNode->inputConfig.createInputQueue(); + auto config = parserNode->initialConfig; + + pipeline.start(); + std::cout << "Controls: '+' increase confidence threshold, '-' decrease it, 'q' quit." << std::endl; + + while(pipeline.isRunning()) { + auto frameMessage = frameQueue->get(); + auto parserOutput = outputQueue->get(); + cv::Mat frame = frameMessage->getCvFrame(); + for(const auto& detection : parserOutput->detections) { + const auto boundingBox = detection.getBoundingBox().denormalize(frame.cols, frame.rows); + std::vector points; + for(const auto& point : boundingBox.getPoints()) points.emplace_back(static_cast(point.x), static_cast(point.y)); + cv::polylines(frame, points, true, cv::Scalar(0, 255, 0), 2); + + const std::string label = detection.labelName.empty() ? std::to_string(detection.label) : detection.labelName; + std::ostringstream text; + text << label << ": " << std::fixed << std::setprecision(2) << detection.confidence; + cv::putText(frame, text.str(), points.front(), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 255, 0), 1); + for(const auto& point : detection.getKeypoints2f()) { + cv::circle(frame, cv::Point(static_cast(point.x * frame.cols), static_cast(point.y * frame.rows)), 3, cv::Scalar(0, 0, 255), -1); + } + } + + cv::imshow("MPPalmDetectionParser", frame); + const int key = cv::waitKey(1); + if(key == 'q') break; + if(key == '+') { + config->confidenceThreshold = std::min(1.0f, config->confidenceThreshold + 0.1f); + configQueue->send(config); + std::cout << "Confidence threshold: " << config->confidenceThreshold << std::endl; + } else if(key == '-') { + config->confidenceThreshold = std::max(0.0f, config->confidenceThreshold - 0.1f); + configQueue->send(config); + std::cout << "Confidence threshold: " << config->confidenceThreshold << std::endl; + } + } + + return 0; +} diff --git a/examples/cpp/Beta/pp_text_detection_parser.cpp b/examples/cpp/Beta/pp_text_detection_parser.cpp new file mode 100644 index 0000000000..cb88dbda5b --- /dev/null +++ b/examples/cpp/Beta/pp_text_detection_parser.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "depthai/beta/datatypes.hpp" +#include "depthai/beta/nodes.hpp" +#include "depthai/depthai.hpp" + +int main() { + dai::Pipeline pipeline; + + const std::string modelSlug = "luxonis/paddle-text-detection:256x256"; + dai::NNModelDescription modelDescription{modelSlug}; + modelDescription.platform = pipeline.getDefaultDevice()->getPlatformAsString(); + dai::NNArchive modelArchive(dai::getModelFromZoo(modelDescription)); + + auto cameraNode = pipeline.create()->build(); + auto neuralNetwork = pipeline.create()->build(cameraNode, modelArchive); + auto parserNode = pipeline.create()->build(neuralNetwork->out, modelArchive); + + auto frameQueue = neuralNetwork->passthrough.createOutputQueue(); + auto outputQueue = parserNode->out.createOutputQueue(); + auto configQueue = parserNode->inputConfig.createInputQueue(); + auto config = parserNode->initialConfig; + + pipeline.start(); + std::cout << "Controls: '+' increase confidence threshold, '-' decrease it, 'q' quit." << std::endl; + + while(pipeline.isRunning()) { + auto frameMessage = frameQueue->get(); + auto parserOutput = outputQueue->get(); + cv::Mat frame = frameMessage->getCvFrame(); + for(const auto& detection : parserOutput->detections) { + const auto boundingBox = detection.getBoundingBox().denormalize(frame.cols, frame.rows); + std::vector points; + for(const auto& point : boundingBox.getPoints()) points.emplace_back(static_cast(point.x), static_cast(point.y)); + cv::polylines(frame, points, true, cv::Scalar(0, 255, 0), 2); + + const std::string label = detection.labelName.empty() ? std::to_string(detection.label) : detection.labelName; + std::ostringstream text; + text << label << ": " << std::fixed << std::setprecision(2) << detection.confidence; + cv::putText(frame, text.str(), points.front(), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 255, 0), 1); + for(const auto& point : detection.getKeypoints2f()) { + cv::circle(frame, cv::Point(static_cast(point.x * frame.cols), static_cast(point.y * frame.rows)), 3, cv::Scalar(0, 0, 255), -1); + } + } + + cv::imshow("PPTextDetectionParser", frame); + const int key = cv::waitKey(1); + if(key == 'q') break; + if(key == '+') { + config->confidenceThreshold = std::min(1.0f, config->confidenceThreshold + 0.1f); + configQueue->send(config); + std::cout << "Confidence threshold: " << config->confidenceThreshold << std::endl; + } else if(key == '-') { + config->confidenceThreshold = std::max(0.0f, config->confidenceThreshold - 0.1f); + configQueue->send(config); + std::cout << "Confidence threshold: " << config->confidenceThreshold << std::endl; + } + } + + return 0; +} diff --git a/examples/cpp/Beta/scrfd_parser.cpp b/examples/cpp/Beta/scrfd_parser.cpp new file mode 100644 index 0000000000..3079f959ca --- /dev/null +++ b/examples/cpp/Beta/scrfd_parser.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "depthai/beta/datatypes.hpp" +#include "depthai/beta/nodes.hpp" +#include "depthai/depthai.hpp" + +int main() { + dai::Pipeline pipeline; + + const std::string modelSlug = "luxonis/scrfd-face-detection:10g-640x640"; + dai::NNModelDescription modelDescription{modelSlug}; + modelDescription.platform = pipeline.getDefaultDevice()->getPlatformAsString(); + dai::NNArchive modelArchive(dai::getModelFromZoo(modelDescription)); + + auto cameraNode = pipeline.create()->build(); + auto neuralNetwork = pipeline.create()->build(cameraNode, modelArchive); + auto parserNode = pipeline.create()->build(neuralNetwork->out, modelArchive); + + auto frameQueue = neuralNetwork->passthrough.createOutputQueue(); + auto outputQueue = parserNode->out.createOutputQueue(); + auto configQueue = parserNode->inputConfig.createInputQueue(); + auto config = parserNode->initialConfig; + + pipeline.start(); + std::cout << "Controls: '+' increase confidence threshold, '-' decrease it, 'q' quit." << std::endl; + + while(pipeline.isRunning()) { + auto frameMessage = frameQueue->get(); + auto parserOutput = outputQueue->get(); + cv::Mat frame = frameMessage->getCvFrame(); + for(const auto& detection : parserOutput->detections) { + const auto boundingBox = detection.getBoundingBox().denormalize(frame.cols, frame.rows); + std::vector points; + for(const auto& point : boundingBox.getPoints()) points.emplace_back(static_cast(point.x), static_cast(point.y)); + cv::polylines(frame, points, true, cv::Scalar(0, 255, 0), 2); + + const std::string label = detection.labelName.empty() ? std::to_string(detection.label) : detection.labelName; + std::ostringstream text; + text << label << ": " << std::fixed << std::setprecision(2) << detection.confidence; + cv::putText(frame, text.str(), points.front(), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 255, 0), 1); + for(const auto& point : detection.getKeypoints2f()) { + cv::circle(frame, cv::Point(static_cast(point.x * frame.cols), static_cast(point.y * frame.rows)), 3, cv::Scalar(0, 0, 255), -1); + } + } + + cv::imshow("SCRFDParser", frame); + const int key = cv::waitKey(1); + if(key == 'q') break; + if(key == '+') { + config->confidenceThreshold = std::min(1.0f, config->confidenceThreshold + 0.1f); + configQueue->send(config); + std::cout << "Confidence threshold: " << config->confidenceThreshold << std::endl; + } else if(key == '-') { + config->confidenceThreshold = std::max(0.0f, config->confidenceThreshold - 0.1f); + configQueue->send(config); + std::cout << "Confidence threshold: " << config->confidenceThreshold << std::endl; + } + } + + return 0; +} diff --git a/examples/cpp/Beta/superanimal_parser.cpp b/examples/cpp/Beta/superanimal_parser.cpp new file mode 100644 index 0000000000..9c2fe95b8f --- /dev/null +++ b/examples/cpp/Beta/superanimal_parser.cpp @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "depthai/beta/datatypes.hpp" +#include "depthai/beta/nodes.hpp" +#include "depthai/depthai.hpp" + +int main() { + dai::Pipeline pipeline; + + const std::string modelSlug = "luxonis/superanimal-landmarker:256x256"; + dai::NNModelDescription modelDescription{modelSlug}; + modelDescription.platform = pipeline.getDefaultDevice()->getPlatformAsString(); + dai::NNArchive modelArchive(dai::getModelFromZoo(modelDescription)); + + auto cameraNode = pipeline.create()->build(); + auto neuralNetwork = pipeline.create()->build(cameraNode, modelArchive); + auto parserNode = pipeline.create()->build(neuralNetwork->out, modelArchive); + + auto frameQueue = neuralNetwork->passthrough.createOutputQueue(); + auto outputQueue = parserNode->out.createOutputQueue(); + auto configQueue = parserNode->inputConfig.createInputQueue(); + auto config = parserNode->initialConfig; + + pipeline.start(); + std::cout << "Controls: '+' increase score threshold, '-' decrease it, 'q' quit." << std::endl; + + while(pipeline.isRunning()) { + auto frameMessage = frameQueue->get(); + auto parserOutput = outputQueue->get(); + cv::Mat frame = frameMessage->getCvFrame(); + std::vector points; + for(const auto& point : parserOutput->getPoints2f()) { + points.emplace_back(static_cast(point.x * frame.cols), static_cast(point.y * frame.rows)); + } + for(const auto& edge : parserOutput->getEdges()) { + cv::line(frame, points.at(edge[0]), points.at(edge[1]), cv::Scalar(0, 255, 0), 2); + } + for(const auto& point : points) cv::circle(frame, point, 3, cv::Scalar(0, 0, 255), -1); + + cv::imshow("SuperAnimalParser", frame); + const int key = cv::waitKey(1); + if(key == 'q') break; + if(key == '+') { + config->scoreThreshold = std::min(1.0f, config->scoreThreshold + 0.1f); + configQueue->send(config); + std::cout << "Score threshold: " << config->scoreThreshold << std::endl; + } else if(key == '-') { + config->scoreThreshold = std::max(0.0f, config->scoreThreshold - 0.1f); + configQueue->send(config); + std::cout << "Score threshold: " << config->scoreThreshold << std::endl; + } + } + + return 0; +} diff --git a/examples/cpp/Beta/yunet_parser.cpp b/examples/cpp/Beta/yunet_parser.cpp new file mode 100644 index 0000000000..443c3b9c46 --- /dev/null +++ b/examples/cpp/Beta/yunet_parser.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "depthai/beta/datatypes.hpp" +#include "depthai/beta/nodes.hpp" +#include "depthai/depthai.hpp" + +int main() { + dai::Pipeline pipeline; + + const std::string modelSlug = "luxonis/yunet:640x480"; + dai::NNModelDescription modelDescription{modelSlug}; + modelDescription.platform = pipeline.getDefaultDevice()->getPlatformAsString(); + dai::NNArchive modelArchive(dai::getModelFromZoo(modelDescription)); + + auto cameraNode = pipeline.create()->build(); + auto neuralNetwork = pipeline.create()->build(cameraNode, modelArchive); + auto parserNode = pipeline.create()->build(neuralNetwork->out, modelArchive); + + auto frameQueue = neuralNetwork->passthrough.createOutputQueue(); + auto outputQueue = parserNode->out.createOutputQueue(); + auto configQueue = parserNode->inputConfig.createInputQueue(); + auto config = parserNode->initialConfig; + + pipeline.start(); + std::cout << "Controls: '+' increase confidence threshold, '-' decrease it, 'q' quit." << std::endl; + + while(pipeline.isRunning()) { + auto frameMessage = frameQueue->get(); + auto parserOutput = outputQueue->get(); + cv::Mat frame = frameMessage->getCvFrame(); + for(const auto& detection : parserOutput->detections) { + const auto boundingBox = detection.getBoundingBox().denormalize(frame.cols, frame.rows); + std::vector points; + for(const auto& point : boundingBox.getPoints()) points.emplace_back(static_cast(point.x), static_cast(point.y)); + cv::polylines(frame, points, true, cv::Scalar(0, 255, 0), 2); + + const std::string label = detection.labelName.empty() ? std::to_string(detection.label) : detection.labelName; + std::ostringstream text; + text << label << ": " << std::fixed << std::setprecision(2) << detection.confidence; + cv::putText(frame, text.str(), points.front(), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 255, 0), 1); + for(const auto& point : detection.getKeypoints2f()) { + cv::circle(frame, cv::Point(static_cast(point.x * frame.cols), static_cast(point.y * frame.rows)), 3, cv::Scalar(0, 0, 255), -1); + } + } + + cv::imshow("YuNetParser", frame); + const int key = cv::waitKey(1); + if(key == 'q') break; + if(key == '+') { + config->confidenceThreshold = std::min(1.0f, config->confidenceThreshold + 0.1f); + configQueue->send(config); + std::cout << "Confidence threshold: " << config->confidenceThreshold << std::endl; + } else if(key == '-') { + config->confidenceThreshold = std::max(0.0f, config->confidenceThreshold - 0.1f); + configQueue->send(config); + std::cout << "Confidence threshold: " << config->confidenceThreshold << std::endl; + } + } + + return 0; +} diff --git a/examples/cpp/CMakeLists.txt b/examples/cpp/CMakeLists.txt index f0831cb338..af6848d136 100644 --- a/examples/cpp/CMakeLists.txt +++ b/examples/cpp/CMakeLists.txt @@ -170,6 +170,9 @@ add_subdirectory(NeuralAssistedStereo) add_subdirectory(GPUStereo) add_subdirectory(Gate) add_subdirectory(PointCloud) +if(DEPTHAI_BUILD_BETA) + add_subdirectory(Beta) +endif() if(DEPTHAI_DYNAMIC_CALIBRATION_SUPPORT) add_subdirectory(AutoCalibration) endif() diff --git a/examples/cpp/Camera/camera_multiple_outputs.cpp b/examples/cpp/Camera/camera_multiple_outputs.cpp index df6a126c90..e2332a4f08 100644 --- a/examples/cpp/Camera/camera_multiple_outputs.cpp +++ b/examples/cpp/Camera/camera_multiple_outputs.cpp @@ -121,9 +121,10 @@ int main(int argc, char* argv[]) { auto videoIn = queues[i]->tryGet(); if(videoIn != nullptr) { fpsCounters[i].tick(); - std::cout << "frame " << videoIn->getWidth() << "x" << videoIn->getHeight() << " | " << videoIn->getSequenceNum() - << ": exposure=" << videoIn->getExposureTime().count() - << "us, timestamp: " << videoIn->getTimestampDevice().time_since_epoch().count() << std::endl; + if (!(videoIn->getSequenceNum() % 60)) + std::cout << "frame " << videoIn->getWidth() << "x" << videoIn->getHeight() << " | " << videoIn->getSequenceNum() + << ": exposure=" << videoIn->getExposureTime().count() + << "us, timestamp: " << videoIn->getTimestampDevice().time_since_epoch().count() << std::endl; cv::Mat cvFrame = videoIn->getCvFrame(); diff --git a/examples/cpp/Depth/depth_rgb_align.cpp b/examples/cpp/Depth/depth_rgb_align.cpp index 85886717bf..ede8cd7e64 100644 --- a/examples/cpp/Depth/depth_rgb_align.cpp +++ b/examples/cpp/Depth/depth_rgb_align.cpp @@ -66,62 +66,6 @@ class FPSCounter { std::deque frameTimes; }; -cv::Mat colorizeDepth(const cv::Mat& frameDepth) { - if(frameDepth.empty() || frameDepth.channels() != 1) { - return cv::Mat::zeros(frameDepth.size(), CV_8UC3); - } - - cv::Mat depth32f; - frameDepth.convertTo(depth32f, CV_32F); - - const cv::Mat nonZeroMask = depth32f != 0.0f; - const int nz = cv::countNonZero(nonZeroMask); - if(nz == 0) { - return cv::Mat::zeros(frameDepth.size(), CV_8UC3); - } - - std::vector values; - values.reserve(static_cast(nz)); - for(int r = 0; r < depth32f.rows; ++r) { - const float* d = depth32f.ptr(r); - const uchar* m = nonZeroMask.ptr(r); - for(int c = 0; c < depth32f.cols; ++c) { - if(m[c]) { - values.push_back(d[c]); - } - } - } - - std::sort(values.begin(), values.end()); - auto pct = [&](double p) { - const size_t idx = static_cast(std::round((p / 100.0) * (values.size() - 1))); - return values[idx]; - }; - - const float minDepth = pct(3.0); - const float maxDepth = pct(95.0); - - cv::Mat logDepth; - depth32f.copyTo(logDepth); - logDepth.setTo(minDepth, ~nonZeroMask); - cv::log(logDepth, logDepth); - - const float logMinDepth = std::log(minDepth); - const float logMaxDepth = std::log(maxDepth); - - cv::min(logDepth, logMaxDepth, logDepth); - cv::max(logDepth, logMinDepth, logDepth); - logDepth = (logDepth - logMinDepth) * (255.0f / (logMaxDepth - logMinDepth)); - - cv::Mat depth8U; - logDepth.convertTo(depth8U, CV_8U); - - cv::Mat depthFrameColor; - cv::applyColorMap(depth8U, depthFrameColor, cv::COLORMAP_JET); - depthFrameColor.setTo(cv::Scalar::all(0), ~nonZeroMask); - return depthFrameColor; -} - float rgbWeight = 0.4f; float depthWeight = 0.6f; @@ -190,7 +134,7 @@ int main() { if(frameDepth != nullptr) { cv::Mat cvFrame = frameRgb->getCvFrame(); - cv::Mat alignedDepthColorized = colorizeDepth(frameDepth->getFrame()); + cv::Mat alignedDepthColorized = dai::utility::colorizeDepthFrame(*frameDepth).getCvFrame(); cv::imshow("Depth aligned", alignedDepthColorized); if(cvFrame.channels() == 1) { diff --git a/examples/cpp/Depth/unified_depth.cpp b/examples/cpp/Depth/unified_depth.cpp index 792042d0ca..c0a358cb6b 100644 --- a/examples/cpp/Depth/unified_depth.cpp +++ b/examples/cpp/Depth/unified_depth.cpp @@ -30,62 +30,6 @@ namespace { -cv::Mat colorizeDepth(const cv::Mat& frameDepth) { - if(frameDepth.empty() || frameDepth.channels() != 1) { - return cv::Mat::zeros(frameDepth.size(), CV_8UC3); - } - - cv::Mat depth32f; - frameDepth.convertTo(depth32f, CV_32F); - - const cv::Mat nonZeroMask = depth32f != 0.0f; - const int nz = cv::countNonZero(nonZeroMask); - if(nz == 0) { - return cv::Mat::zeros(frameDepth.size(), CV_8UC3); - } - - std::vector values; - values.reserve(static_cast(nz)); - for(int r = 0; r < depth32f.rows; ++r) { - const float* d = depth32f.ptr(r); - const uchar* m = nonZeroMask.ptr(r); - for(int c = 0; c < depth32f.cols; ++c) { - if(m[c]) { - values.push_back(d[c]); - } - } - } - - std::sort(values.begin(), values.end()); - auto pct = [&](double p) { - const size_t idx = static_cast(std::round((p / 100.0) * (values.size() - 1))); - return values[idx]; - }; - - const float minDepth = pct(3.0); - const float maxDepth = pct(95.0); - - cv::Mat logDepth; - depth32f.copyTo(logDepth); - logDepth.setTo(minDepth, ~nonZeroMask); - cv::log(logDepth, logDepth); - - const float logMinDepth = std::log(minDepth); - const float logMaxDepth = std::log(maxDepth); - - cv::min(logDepth, logMaxDepth, logDepth); - cv::max(logDepth, logMinDepth, logDepth); - logDepth = (logDepth - logMinDepth) * (255.0f / (logMaxDepth - logMinDepth)); - - cv::Mat depth8U; - logDepth.convertTo(depth8U, CV_8U); - - cv::Mat depthFrameColor; - cv::applyColorMap(depth8U, depthFrameColor, cv::COLORMAP_JET); - depthFrameColor.setTo(cv::Scalar::all(0), ~nonZeroMask); - return depthFrameColor; -} - cv::Mat colorizeConfidence(const cv::Mat& frame) { if(frame.empty() || frame.channels() != 1) { return cv::Mat::zeros(frame.size(), CV_8UC3); @@ -351,7 +295,7 @@ int main(int argc, char** argv) { auto confidenceFrame = confidenceQueue->get(); if(depthFrame != nullptr) { - cv::imshow("depth", colorizeDepth(depthFrame->getFrame())); + cv::imshow("depth", dai::utility::colorizeDepthFrame(*depthFrame).getCvFrame()); } if(confidenceFrame != nullptr) { cv::imshow("confidence", colorizeConfidence(confidenceFrame->getFrame())); diff --git a/examples/cpp/DetectionNetwork/detection_network.cpp b/examples/cpp/DetectionNetwork/detection_network.cpp index a74c2e83ec..595c7e7af7 100644 --- a/examples/cpp/DetectionNetwork/detection_network.cpp +++ b/examples/cpp/DetectionNetwork/detection_network.cpp @@ -48,6 +48,7 @@ int main() { cv::Scalar textColor(255, 255, 255); pipeline.start(); + auto lastPrintTime = std::chrono::steady_clock::now() - std::chrono::seconds(1); while(pipeline.isRunning() && !quitEvent) { auto inRgb = qRgb->get(); auto inDet = qDet->get(); @@ -90,7 +91,10 @@ int main() { auto currentTime = std::chrono::steady_clock::now(); float fps = counter / std::chrono::duration(currentTime - startTime).count(); - std::cout << "FPS: " << fps << std::endl; + if(currentTime - lastPrintTime >= std::chrono::seconds(1)) { + std::cout << "FPS: " << fps << std::endl; + lastPrintTime = currentTime; + } } if(cv::waitKey(1) == 'q') { @@ -99,4 +103,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/examples/cpp/DetectionNetwork/detection_network_remap.cpp b/examples/cpp/DetectionNetwork/detection_network_remap.cpp index d0c3a2be72..c89e913b53 100644 --- a/examples/cpp/DetectionNetwork/detection_network_remap.cpp +++ b/examples/cpp/DetectionNetwork/detection_network_remap.cpp @@ -1,5 +1,3 @@ -#include // Required for std::sort and std::unique -#include // Required for std::log, std::isnan, std::isinf #include #include #include @@ -7,8 +5,6 @@ #include #include "depthai/depthai.hpp" -#include "xtensor/containers/xadapt.hpp" -#include "xtensor/core/xmath.hpp" std::atomic quitEvent(false); @@ -16,69 +12,6 @@ void signalHandler(int) { quitEvent = true; } -cv::Mat colorizeDepth(cv::Mat frameDepth) { - cv::Mat invalidMask = frameDepth == 0; - cv::Mat depthFrameColor; - - try { - cv::Mat frameDepthFloat; - frameDepth.convertTo(frameDepthFloat, CV_32F); - xt::xtensor depth = - xt::adapt((float*)frameDepthFloat.data, {static_cast(frameDepthFloat.rows), static_cast(frameDepthFloat.cols)}); - - // Get valid depth values (non-zero) - std::vector validDepth; - validDepth.reserve(depth.size()); - std::copy_if(depth.begin(), depth.end(), std::back_inserter(validDepth), [](float x) { return x != 0; }); - - if(validDepth.size() == 0) { - return cv::Mat::zeros(frameDepth.rows, frameDepth.cols, CV_8UC3); - } - - // Calculate percentiles - std::sort(validDepth.begin(), validDepth.end()); - float minDepth = validDepth[static_cast(validDepth.size() * 0.03)]; - float maxDepth = validDepth[static_cast(validDepth.size() * 0.95)]; - - // Take log of depth values - auto logDepth = xt::eval(xt::log(depth)); - float logMinDepth = std::log(minDepth); - float logMaxDepth = std::log(maxDepth); - - // Replace invalid values with logMinDepth using a naive implementation - auto logDepthData = logDepth.data(); - auto depthData = depth.data(); - const size_t size = depth.size(); - for(size_t i = 0; i < size; i++) { - if(std::isnan(logDepthData[i]) || std::isinf(logDepthData[i]) || depthData[i] == 0.0f) { - logDepthData[i] = logMinDepth; - } - } - - // Clip values - logDepth = xt::clip(logDepth, logMinDepth, logMaxDepth); - - // Normalize to 0-255 range - auto normalizedDepth = (logDepth - logMinDepth) / (logMaxDepth - logMinDepth) * 255.0f; - - // Convert to CV_8UC1 - cv::Mat depthMat(frameDepth.rows, frameDepth.cols, CV_8UC1); - std::transform(normalizedDepth.begin(), normalizedDepth.end(), depthMat.data, [](float x) { return static_cast(x); }); - - // Apply colormap - cv::applyColorMap(depthMat, depthFrameColor, cv::COLORMAP_JET); - - // Set invalid pixels to black - depthFrameColor.setTo(cv::Scalar(0, 0, 0), invalidMask); - - } catch(const std::exception& e) { - std::cerr << "Error in colorizeDepth: " << e.what() << std::endl; - return cv::Mat::zeros(frameDepth.rows, frameDepth.cols, CV_8UC3); - } - - return depthFrameColor; -} - // Helper function to display frames with detections void displayFrame(const std::string& name, std::shared_ptr frame, @@ -88,7 +21,7 @@ void displayFrame(const std::string& name, cv::Mat cvFrame; if(frame->getType() == dai::ImgFrame::Type::RAW16) { - cvFrame = colorizeDepth(frame->getFrame()); + cvFrame = dai::utility::colorizeDepthFrame(*frame).getCvFrame(); } else { cvFrame = frame->getCvFrame(); } @@ -139,8 +72,10 @@ int main() { dai::Pipeline pipeline; + auto colorSockets = pipeline.getDefaultDevice()->getConnectedCameras(dai::CameraSensorType::COLOR); + auto colorSocket = colorSockets.empty() ? dai::CameraBoardSocket::CAM_A : colorSockets.front(); auto cameraNode = pipeline.create(); - cameraNode->build(); + cameraNode->build(colorSocket); auto detectionNetwork = pipeline.create(); dai::NNModelDescription modelDescription; @@ -148,26 +83,12 @@ int main() { detectionNetwork->build(cameraNode, modelDescription); auto labelMap = detectionNetwork->getClasses().value_or(std::vector{}); - auto monoLeft = pipeline.create(); - monoLeft->build(dai::CameraBoardSocket::CAM_B); - auto monoRight = pipeline.create(); - monoRight->build(dai::CameraBoardSocket::CAM_C); - auto stereo = pipeline.create(); - - // Linking - auto monoLeftOut = monoLeft->requestOutput(std::make_pair(1280, 720)); - auto monoRightOut = monoRight->requestOutput(std::make_pair(1280, 720)); - monoLeftOut->link(stereo->left); - monoRightOut->link(stereo->right); - - stereo->setRectification(true); - stereo->setExtendedDisparity(true); - stereo->setLeftRightCheck(true); - stereo->setSubpixel(true); + auto depth = pipeline.create(); + depth->build(dai::node::Depth::Algorithm::AUTO); auto qRgb = detectionNetwork->passthrough.createOutputQueue(); auto qDet = detectionNetwork->out.createOutputQueue(); - auto qDepth = stereo->disparity.createOutputQueue(); + auto qDepth = depth->depth().createOutputQueue(); pipeline.start(); @@ -194,4 +115,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/examples/cpp/DynamicCalibration/README.md b/examples/cpp/DynamicCalibration/README.md index dbc2bbb908..321baabd9b 100644 --- a/examples/cpp/DynamicCalibration/README.md +++ b/examples/cpp/DynamicCalibration/README.md @@ -47,11 +47,11 @@ This folder contains minimal, end-to-end **C++** examples that use **`dai::node: **Flow:** 1. Create mono cameras → request **full-res NV12** (unrectified) → link to: - `DynamicCalibration.left/right` - - `StereoDepth.left/right` (for live disparity view) + - `StereoDepth.left/right` (for live depth view) 2. Start the pipeline, give AE a moment to settle. 3. **Start calibration** by sending `DynamicCalibrationControl::Commands::StartCalibration{}`. 4. In the loop: - - Show `left`, `right`, and `disparity`. + - Show `left`, `right`, and `depth`. - Poll `coverageOutput` for progress. - Poll `calibrationOutput` for a result. 5. When a result arrives: @@ -110,10 +110,10 @@ This folder contains minimal, end-to-end **C++** examples that use **`dai::node: **File:** `calibration_integration.cpp` **What it does:** -Runs one loop that periodically refreshes coverage, executes calibration, and applies a new calibration automatically when the returned metrics indicate drift — while showing `left`, `right`, and a colorized `disparity` preview. +Runs one loop that periodically refreshes coverage, executes calibration, and applies a new calibration automatically when the returned metrics indicate drift — while showing `left`, `right`, and a colorized `depth` preview. **Flow:** -1. Create mono cameras → request **full-res NV12** (unrectified) → link to `dai::node::DynamicCalibration` and `dai::node::StereoDepth` for live disparity. Read the device’s current calibration as the baseline. +1. Create mono cameras → request **full-res NV12** (unrectified) → link to `dai::node::DynamicCalibration` and `dai::node::StereoDepth` for live depth. Read the device’s current calibration as the baseline. 2. On a fixed interval (e.g., ~3 seconds), send on the control queue: - `DynamicCalibrationControl::Commands::LoadImage{}` to compute coverage on the current frames, and - `DynamicCalibrationControl::Commands::Calibrate{true}` to compute a new candidate calibration and return metrics on `calibrationOutput`. @@ -125,7 +125,7 @@ Runs one loop that periodically refreshes coverage, executes calibration, and ap 5. Exit on `q` keypress or window close. **Notes & defaults:** -- Disparity preview can be auto-scaled to the observed maximum; zero disparity can be rendered black for clarity. +- Depth preview uses the shared colorization helper with a 500–12000 mm range and logarithmic scaling. - The 0.05 px Sampson threshold is a simple heuristic — tune to your tolerance and noise profile. **Example console output:** @@ -146,7 +146,7 @@ Mono CAM_B ──▶ [Camera] ── NV12 (full-res) ──▶ DynamicCalibratio Mono CAM_C ──▶ [Camera] ── NV12 (full-res) ──▶ DynamicCalibration.right │ - └───────────▶ StereoDepth.right ──▶ disparity + └───────────▶ StereoDepth.right ──▶ depth ``` --- @@ -230,7 +230,7 @@ If you previously read fields from `CalibrationQuality::qualityData`, read the s - **No quality data returned** Ensure the target is sharp, well-lit, and covers diverse parts of the image. Increase lighting or steady the rig. -- **Disparity looks worse after apply** +- **Depth preview looks worse after apply** Collect more diverse samples (tilt/translate the target), or try a performance mode tuned for robustness. Clean lenses; verify focus. - **Nothing happens after StartCalibration** diff --git a/examples/cpp/DynamicCalibration/calibration_dynamic.cpp b/examples/cpp/DynamicCalibration/calibration_dynamic.cpp index e95d3d6bc3..0c9888ad7b 100644 --- a/examples/cpp/DynamicCalibration/calibration_dynamic.cpp +++ b/examples/cpp/DynamicCalibration/calibration_dynamic.cpp @@ -1,5 +1,4 @@ // examples/cpp/DynamicCalibration/calibrate.cpp -#include #include #include #include @@ -33,7 +32,7 @@ int main() { // In-pipeline host queues auto leftSyncedQueue = stereo->syncedLeft.createOutputQueue(); auto rightSyncedQueue = stereo->syncedRight.createOutputQueue(); - auto disparityQueue = stereo->disparity.createOutputQueue(); + auto depthQueue = stereo->depth.createOutputQueue(); auto dynCalibOutQ = dynCalib->calibrationOutput.createOutputQueue(); auto dynCoverageOutQ = dynCalib->coverageOutput.createOutputQueue(); @@ -52,32 +51,15 @@ int main() { // Start calibration (optimize performance) dynCalibInputControl->send(DCC::startCalibration()); - double maxDisparity = 1.0; while(pipeline.isRunning()) { auto leftSynced = leftSyncedQueue->get(); auto rightSynced = rightSyncedQueue->get(); - auto disparity = disparityQueue->get(); + auto depth = depthQueue->get(); cv::imshow("left", leftSynced->getCvFrame()); cv::imshow("right", rightSynced->getCvFrame()); - cv::Mat npDisparity = disparity->getFrame(); - - double minVal = 0.0, curMax = 0.0; - cv::minMaxLoc(npDisparity, &minVal, &curMax); - maxDisparity = std::max(maxDisparity, curMax); - - // Normalize the disparity image to an 8-bit scale. - cv::Mat normalized; - npDisparity.convertTo(normalized, CV_8UC1, 255.0 / (maxDisparity > 0 ? maxDisparity : 1.0)); - - cv::Mat colorizedDisparity; - cv::applyColorMap(normalized, colorizedDisparity, cv::COLORMAP_JET); - - // Set pixels with zero disparity to black. - colorizedDisparity.setTo(cv::Scalar(0, 0, 0), normalized == 0); - - cv::imshow("disparity", colorizedDisparity); + cv::imshow("depth", dai::utility::colorizeDepthFrame(*depth).getCvFrame()); // Coverage (non-blocking) if(auto coverageMsg = dynCoverageOutQ->tryGet()) { diff --git a/examples/cpp/DynamicCalibration/calibration_integration.cpp b/examples/cpp/DynamicCalibration/calibration_integration.cpp index f4aa3859db..ed5131e364 100644 --- a/examples/cpp/DynamicCalibration/calibration_integration.cpp +++ b/examples/cpp/DynamicCalibration/calibration_integration.cpp @@ -45,7 +45,7 @@ int main() { // In-pipeline host queues auto leftSyncedQueue = stereo->syncedLeft.createOutputQueue(); auto rightSyncedQueue = stereo->syncedRight.createOutputQueue(); - auto disparityQueue = stereo->disparity.createOutputQueue(); + auto depthQueue = stereo->depth.createOutputQueue(); auto dynCoverageOutQ = dynCalib->coverageOutput.createOutputQueue(); auto dynCalibOutQ = dynCalib->calibrationOutput.createOutputQueue(); @@ -54,8 +54,6 @@ int main() { device->setCalibration(device->getCalibration()); - double maxDisparity = 1.0; - pipeline.start(); std::this_thread::sleep_for(std::chrono::seconds(1)); // wait for autoexposure to settle auto lastSent = std::chrono::steady_clock::now(); @@ -65,28 +63,12 @@ int main() { while(pipeline.isRunning()) { auto leftSynced = leftSyncedQueue->get(); auto rightSynced = rightSyncedQueue->get(); - auto disparity = disparityQueue->get(); + auto depth = depthQueue->get(); cv::imshow("left", leftSynced->getCvFrame()); cv::imshow("right", rightSynced->getCvFrame()); - cv::Mat npDisparity = disparity->getFrame(); - - double minVal, curMax; - cv::minMaxLoc(npDisparity, &minVal, &curMax); - maxDisparity = std::max(maxDisparity, curMax); - - // Normalize the disparity image to an 8-bit scale. - cv::Mat normalized; - npDisparity.convertTo(normalized, CV_8UC1, 255.0 / maxDisparity); - - cv::Mat colorizedDisparity; - cv::applyColorMap(normalized, colorizedDisparity, cv::COLORMAP_JET); - - // Set pixels with zero disparity to black. - colorizedDisparity.setTo(cv::Scalar(0, 0, 0), normalized == 0); - - cv::imshow("disparity", colorizedDisparity); + cv::imshow("depth", dai::utility::colorizeDepthFrame(*depth).getCvFrame()); // Wait for coverage info auto coverageMsg = dynCoverageOutQ->tryGet(); if(coverageMsg) { diff --git a/examples/cpp/IMU/imu_gyroscope_accelerometer.cpp b/examples/cpp/IMU/imu_gyroscope_accelerometer.cpp index 5438f34fd8..c18af95f78 100644 --- a/examples/cpp/IMU/imu_gyroscope_accelerometer.cpp +++ b/examples/cpp/IMU/imu_gyroscope_accelerometer.cpp @@ -47,26 +47,29 @@ int main() { // Set up output formatting std::cout << std::fixed << std::setprecision(6); + auto lastPrintTime = std::chrono::steady_clock::now() - std::chrono::seconds(1); while(pipeline.isRunning() && !quitEvent) { auto imuData = imuQueue->get(); - if(imuData == nullptr) continue; + if(imuData == nullptr || imuData->packets.empty()) continue; - for(const auto& imuPacket : imuData->packets) { - auto acceleroValues = imuPacket.acceleroMeter; - auto gyroValues = imuPacket.gyroscope; + const auto now = std::chrono::steady_clock::now(); - auto acceleroTs = acceleroValues.getTimestamp(); - auto gyroTs = gyroValues.getTimestamp(); + const auto& imuPacket = imuData->packets.back(); + auto acceleroValues = imuPacket.acceleroMeter; + auto gyroValues = imuPacket.gyroscope; - // Print accelerometer data - std::cout << "Accelerometer timestamp: " << acceleroTs.time_since_epoch().count() << std::endl; - std::cout << "Latency [ms]: " << timeDeltaToMilliS(std::chrono::steady_clock::now() - acceleroValues.getTimestamp()) << std::endl; - std::cout << "Accelerometer [m/s^2]: x: " << acceleroValues.x << " y: " << acceleroValues.y << " z: " << acceleroValues.z << std::endl; + auto acceleroTs = acceleroValues.getTimestamp(); + auto gyroTs = gyroValues.getTimestamp(); + if(now - lastPrintTime < std::chrono::seconds(1)) continue; + lastPrintTime = now; - // Print gyroscope data - std::cout << "Gyroscope timestamp: " << gyroTs.time_since_epoch().count() << std::endl; - std::cout << "Gyroscope [rad/s]: x: " << gyroValues.x << " y: " << gyroValues.y << " z: " << gyroValues.z << std::endl; - } + // Print the latest IMU sample at most once per second. + std::cout << "Accelerometer timestamp: " << acceleroTs.time_since_epoch().count() << std::endl; + std::cout << "Latency [ms]: " << timeDeltaToMilliS(std::chrono::steady_clock::now() - acceleroValues.getTimestamp()) << std::endl; + std::cout << "Accelerometer [m/s^2]: x: " << acceleroValues.x << " y: " << acceleroValues.y << " z: " << acceleroValues.z << std::endl; + + std::cout << "Gyroscope timestamp: " << gyroTs.time_since_epoch().count() << std::endl; + std::cout << "Gyroscope [rad/s]: x: " << gyroValues.x << " y: " << gyroValues.y << " z: " << gyroValues.z << std::endl; } // Cleanup diff --git a/examples/cpp/ImageAlign/depth_align.cpp b/examples/cpp/ImageAlign/depth_align.cpp index 4049e97934..f3ecce3297 100644 --- a/examples/cpp/ImageAlign/depth_align.cpp +++ b/examples/cpp/ImageAlign/depth_align.cpp @@ -44,45 +44,6 @@ class FPSCounter { std::deque frameTimes; }; -// Depth colorization function from detection_network_remap.cpp -cv::Mat colorizeDepth(cv::Mat frameDepth) { - try { - // Early exit if no valid pixels - if(cv::countNonZero(frameDepth) == 0) { - return cv::Mat::zeros(frameDepth.rows, frameDepth.cols, CV_8UC3); - } - - // Convert to float once - cv::Mat frameDepthFloat; - frameDepth.convertTo(frameDepthFloat, CV_32F); - - double minVal, maxVal; - cv::minMaxLoc(frameDepthFloat, &minVal, &maxVal, nullptr, nullptr, frameDepthFloat > 0); - - // Take log in-place - cv::log(frameDepthFloat, frameDepthFloat); - float logMinDepth = std::log(minVal); - float logMaxDepth = std::log(maxVal); - - frameDepthFloat = (frameDepthFloat - logMinDepth) * (255.0f / (logMaxDepth - logMinDepth)); - - cv::Mat normalizedDepth; - frameDepthFloat.convertTo(normalizedDepth, CV_8UC1); - - cv::Mat depthFrameColor; - cv::applyColorMap(normalizedDepth, depthFrameColor, cv::COLORMAP_JET); - - // Mask invalid pixels - depthFrameColor.setTo(0, frameDepth == 0); - - return depthFrameColor; - - } catch(const std::exception& e) { - std::cerr << "Error in colorizeDepth: " << e.what() << std::endl; - return cv::Mat::zeros(frameDepth.rows, frameDepth.cols, CV_8UC3); - } -} - // Global blend weights float rgbWeight = 0.4f; float depthWeight = 0.6f; @@ -164,7 +125,7 @@ int main() { cv::Mat cvFrame = frameRgb->getCvFrame(); // Colorize depth - cv::Mat alignedDepthColorized = colorizeDepth(frameDepth->getFrame()); + cv::Mat alignedDepthColorized = dai::utility::colorizeDepthFrame(*frameDepth).getCvFrame(); cv::imshow("Depth aligned", alignedDepthColorized); // Convert grayscale to BGR if needed diff --git a/examples/cpp/Misc/Bootloader/CMakeLists.txt b/examples/cpp/Misc/Bootloader/CMakeLists.txt new file mode 100644 index 0000000000..a6b75898b5 --- /dev/null +++ b/examples/cpp/Misc/Bootloader/CMakeLists.txt @@ -0,0 +1,5 @@ +project(bootloader_examples) +cmake_minimum_required(VERSION 3.10) + +## function: dai_add_example(example_name example_src enable_test use_pcl) +dai_add_example(bootloader_dump bootloader_dump.cpp OFF OFF) diff --git a/examples/cpp/Misc/Bootloader/bootloader_dump.cpp b/examples/cpp/Misc/Bootloader/bootloader_dump.cpp new file mode 100644 index 0000000000..53d5e805ab --- /dev/null +++ b/examples/cpp/Misc/Bootloader/bootloader_dump.cpp @@ -0,0 +1,30 @@ +#include +#include +#include +#include + +#include "depthai/depthai.hpp" + +int main() { + bool found = false; + dai::DeviceInfo deviceInfo; + std::tie(found, deviceInfo) = dai::DeviceBootloader::getFirstAvailableDevice(); + if(!found) { + throw std::runtime_error("No available device found"); + } + + std::string version; + bool userBootloader = false; + { + dai::DeviceBootloader bootloader(deviceInfo); + version = bootloader.getVersion().toString(); + userBootloader = bootloader.isUserBootloader(); + } + + const std::string bootloaderType = userBootloader ? "User flashed bootloader" : "Factory flashed bootloader"; + + std::cout << deviceInfo.toString() << '\n'; + std::cout << "Bootloader version: " << bootloaderType << ' ' << version << '\n'; + std::cout << "Embedded depthai bootloader version: " << dai::DeviceBootloader::getEmbeddedBootloaderVersion() << '\n'; + return 0; +} diff --git a/examples/cpp/Misc/CMakeLists.txt b/examples/cpp/Misc/CMakeLists.txt index 843806e7f7..18dda67f37 100644 --- a/examples/cpp/Misc/CMakeLists.txt +++ b/examples/cpp/Misc/CMakeLists.txt @@ -2,6 +2,7 @@ project(misc_examples) cmake_minimum_required(VERSION 3.10) add_subdirectory(AutoReconnect) +add_subdirectory(Bootloader) add_subdirectory(CrashDump) add_subdirectory(HealthCheck) add_subdirectory(Projectors) diff --git a/examples/cpp/Misc/PipelineDebugging/get_pipeline_state.cpp b/examples/cpp/Misc/PipelineDebugging/get_pipeline_state.cpp index 29f0df73fe..8ca9c8212a 100644 --- a/examples/cpp/Misc/PipelineDebugging/get_pipeline_state.cpp +++ b/examples/cpp/Misc/PipelineDebugging/get_pipeline_state.cpp @@ -22,29 +22,12 @@ int main() { stereo->setExtendedDisparity(true); stereo->setLeftRightCheck(true); - auto disparityQueue = stereo->disparity.createOutputQueue(); + auto depthQueue = stereo->depth.createOutputQueue(); - double maxDisparity = 1.0; pipeline.start(); while(pipeline.isRunning()) { - auto disparity = disparityQueue->get(); - cv::Mat npDisparity = disparity->getFrame(); - - double minVal, curMax; - cv::minMaxLoc(npDisparity, &minVal, &curMax); - maxDisparity = std::max(maxDisparity, curMax); - - // Normalize the disparity image to an 8-bit scale. - cv::Mat normalized; - npDisparity.convertTo(normalized, CV_8UC1, 255.0 / maxDisparity); - - cv::Mat colorizedDisparity; - cv::applyColorMap(normalized, colorizedDisparity, cv::COLORMAP_JET); - - // Set pixels with zero disparity to black. - colorizedDisparity.setTo(cv::Scalar(0, 0, 0), normalized == 0); - - cv::imshow("disparity", colorizedDisparity); + auto depth = depthQueue->get(); + cv::imshow("depth", dai::utility::colorizeDepthFrame(*depth).getCvFrame()); int key = cv::waitKey(1); if(key == 'q') { diff --git a/examples/cpp/Misc/PipelineDebugging/node_pipeline_events.cpp b/examples/cpp/Misc/PipelineDebugging/node_pipeline_events.cpp index e19dee459f..42ed9a8f25 100644 --- a/examples/cpp/Misc/PipelineDebugging/node_pipeline_events.cpp +++ b/examples/cpp/Misc/PipelineDebugging/node_pipeline_events.cpp @@ -22,32 +22,14 @@ int main() { stereo->setExtendedDisparity(true); stereo->setLeftRightCheck(true); - auto disparityQueue = stereo->disparity.createOutputQueue(); + auto depthQueue = stereo->depth.createOutputQueue(); auto monoLeftEventQueue = monoLeft->pipelineEventOutput.createOutputQueue(1, false); - double maxDisparity = 1.0; pipeline.start(); while(pipeline.isRunning()) { - auto disparity = disparityQueue->get(); + auto depth = depthQueue->get(); auto latestNodeEvent = monoLeftEventQueue->tryGet(); - - cv::Mat npDisparity = disparity->getFrame(); - - double minVal, curMax; - cv::minMaxLoc(npDisparity, &minVal, &curMax); - maxDisparity = std::max(maxDisparity, curMax); - - // Normalize the disparity image to an 8-bit scale. - cv::Mat normalized; - npDisparity.convertTo(normalized, CV_8UC1, 255.0 / maxDisparity); - - cv::Mat colorizedDisparity; - cv::applyColorMap(normalized, colorizedDisparity, cv::COLORMAP_JET); - - // Set pixels with zero disparity to black. - colorizedDisparity.setTo(cv::Scalar(0, 0, 0), normalized == 0); - - cv::imshow("disparity", colorizedDisparity); + cv::imshow("depth", dai::utility::colorizeDepthFrame(*depth).getCvFrame()); std::cout << "Latest event from MonoLeft camera node: " << (latestNodeEvent ? latestNodeEvent->str() : "No event"); diff --git a/examples/cpp/NeuralAssistedStereo/neural_assisted_stereo.cpp b/examples/cpp/NeuralAssistedStereo/neural_assisted_stereo.cpp index c958f5526b..597cab8cff 100644 --- a/examples/cpp/NeuralAssistedStereo/neural_assisted_stereo.cpp +++ b/examples/cpp/NeuralAssistedStereo/neural_assisted_stereo.cpp @@ -6,41 +6,6 @@ #include constexpr float FPS = 20.0f; -// Nicely visualize a depth map. -// The input depthFrame is assumed to be the raw disparity (CV_16UC1 or similar) -// received from the DepthAI pipeline. -void showDepth(const cv::Mat& depthFrameIn, - const std::string& windowName = "Depth", - int minDistance = 500, - int maxDistance = 5000, - int colormap = cv::COLORMAP_TURBO, - bool useLog = false) { - cv::Mat depthFrame = depthFrameIn.clone(); - - cv::Mat floatFrame; - depthFrame.convertTo(floatFrame, CV_32FC1); - - // # Optionally apply log scaling - if(useLog) { - // depthFrame = np.log(depthFrame + 1) - cv::log(floatFrame + 1, floatFrame); - } - - cv::Mat upperClamped; - cv::min(floatFrame, maxDistance, upperClamped); - - cv::Mat clippedFrame; - cv::max(upperClamped, minDistance, clippedFrame); - - double alpha = 255.0 / maxDistance; - clippedFrame.convertTo(clippedFrame, CV_8U, alpha); - - cv::Mat depthColor; - cv::applyColorMap(clippedFrame, depthColor, colormap); - - cv::imshow(windowName, depthColor); -} - int main() { // 1. Create device and pipeline auto device = std::make_shared(); @@ -59,12 +24,12 @@ int main() { auto neuralAssistedStereo = pipeline.create()->build(*monoLeftOut, *monoRightOut, dai::DeviceModelZoo::NEURAL_DEPTH_NANO); // 6. Get output queue - auto disparityQueue = neuralAssistedStereo->disparity.createOutputQueue(); + auto depthQueue = neuralAssistedStereo->depth.createOutputQueue(); pipeline.start(); while(pipeline.isRunning()) { - auto disparityPacket = disparityQueue->get(); - showDepth(disparityPacket->getCvFrame(), "Depth", 100, 6000, cv::COLORMAP_TURBO, false); + auto depthPacket = depthQueue->get(); + cv::imshow("Depth", dai::utility::colorizeDepthFrame(*depthPacket, 500.0f, 12000.0f, cv::COLORMAP_TURBO, true).getCvFrame()); int key = cv::waitKey(1); if(key == 'q') { break; diff --git a/examples/cpp/NeuralDepth/neural_depth.cpp b/examples/cpp/NeuralDepth/neural_depth.cpp index 3628bd3e0d..6762304490 100644 --- a/examples/cpp/NeuralDepth/neural_depth.cpp +++ b/examples/cpp/NeuralDepth/neural_depth.cpp @@ -40,7 +40,7 @@ int main() { // Create output queues auto confidenceQueue = neuralDepth->confidence.createOutputQueue(); auto edgeQueue = neuralDepth->edge.createOutputQueue(); - auto disparityQueue = neuralDepth->disparity.createOutputQueue(); + auto depthQueue = neuralDepth->depth.createOutputQueue(); // Create input queue for runtime configuration auto inputConfigQueue = neuralDepth->inputConfig.createInputQueue(); @@ -49,7 +49,6 @@ int main() { pipeline.start(); // Variables for visualization - double maxDisparity = 1.0; cv::Mat colorMap; cv::Mat gray(256, 1, CV_8UC1); for(int i = 0; i < 256; i++) { @@ -83,20 +82,8 @@ int main() { cv::applyColorMap(npEdge, colorizedEdge, colorMap); cv::imshow("edge", colorizedEdge); - // Get disparity data, normalize, and display it - auto disparityData = disparityQueue->get(); - cv::Mat npDisparity = disparityData->getFrame(); - - double minVal, curMax; - cv::minMaxLoc(npDisparity, &minVal, &curMax); - maxDisparity = std::max(maxDisparity, curMax); - - cv::Mat normalized; - npDisparity.convertTo(normalized, CV_8UC1, 255.0 / maxDisparity); - - cv::Mat colorizedDisparity; - cv::applyColorMap(normalized, colorizedDisparity, colorMap); - cv::imshow("disparity", colorizedDisparity); + auto depthData = depthQueue->get(); + cv::imshow("depth", dai::utility::colorizeDepthFrame(*depthData).getCvFrame()); // Check for keyboard input int key = cv::waitKey(1); diff --git a/examples/cpp/NeuralDepth/neural_depth_align.cpp b/examples/cpp/NeuralDepth/neural_depth_align.cpp index 44399f5287..e96a3dec0e 100644 --- a/examples/cpp/NeuralDepth/neural_depth_align.cpp +++ b/examples/cpp/NeuralDepth/neural_depth_align.cpp @@ -40,73 +40,6 @@ class FPSCounter { std::deque frameTimes; }; -// Function to colorize a depth frame for visualization -cv::Mat colorizeDepth(const cv::Mat& frameDepth) { - if(frameDepth.empty() || frameDepth.channels() != 1) { - return cv::Mat::zeros(frameDepth.size(), CV_8UC3); - } - - cv::Mat depth32f; - frameDepth.convertTo(depth32f, CV_32F); - - const cv::Mat nonZeroMask = depth32f != 0.0f; - const int nz = cv::countNonZero(nonZeroMask); - if(nz == 0) { - return cv::Mat::zeros(frameDepth.size(), CV_8UC3); - } - - // Extract non-zero depth values to calculate percentiles - std::vector values; - values.reserve(nz); - for(int r = 0; r < depth32f.rows; ++r) { - const float* d = depth32f.ptr(r); - const uchar* m = nonZeroMask.ptr(r); - for(int c = 0; c < depth32f.cols; ++c) { - if(m[c]) { - values.push_back(d[c]); - } - } - } - - std::sort(values.begin(), values.end()); - - // Lambda to calculate percentile - auto pct = [&](double p) { - if(values.empty()) return 0.0f; - size_t idx = static_cast(std::round((p / 100.0) * (values.size() - 1))); - return values[idx]; - }; - - const float minDepth = pct(3.0); - const float maxDepth = pct(95.0); - - // Apply logarithmic scaling - cv::Mat logDepth; - depth32f.copyTo(logDepth); - logDepth.setTo(minDepth, ~nonZeroMask); // Replace zeros to avoid log(0) - cv::log(logDepth, logDepth); - - const float logMinDepth = std::log(minDepth); - const float logMaxDepth = std::log(maxDepth); - - // Clip and linearly scale to the [0, 255] range - cv::min(logDepth, logMaxDepth, logDepth); - cv::max(logDepth, logMinDepth, logDepth); - if(logMaxDepth > logMinDepth) { - logDepth = (logDepth - logMinDepth) * (255.0f / (logMaxDepth - logMinDepth)); - } - - cv::Mat depth8U; - logDepth.convertTo(depth8U, CV_8U); - - // Apply color map and set invalid pixels to black - cv::Mat depthFrameColor; - cv::applyColorMap(depth8U, depthFrameColor, cv::COLORMAP_JET); - depthFrameColor.setTo(cv::Scalar::all(0), ~nonZeroMask); - - return depthFrameColor; -} - // Global variables for blending weights, controlled by the trackbar float rgbWeight = 0.4f; float depthWeight = 0.6f; @@ -178,7 +111,7 @@ int main() { cv::Mat cvFrame = frameRgb->getCvFrame(); // Colorize the aligned depth frame for visualization - cv::Mat alignedDepthColorized = colorizeDepth(frameDepth->getFrame()); + cv::Mat alignedDepthColorized = dai::utility::colorizeDepthFrame(*frameDepth).getCvFrame(); cv::imshow("Depth aligned", alignedDepthColorized); // Blend the RGB and colorized depth frames diff --git a/examples/cpp/NeuralDepth/neural_depth_minimal.cpp b/examples/cpp/NeuralDepth/neural_depth_minimal.cpp index 6470d63e3d..29656c603d 100644 --- a/examples/cpp/NeuralDepth/neural_depth_minimal.cpp +++ b/examples/cpp/NeuralDepth/neural_depth_minimal.cpp @@ -38,47 +38,15 @@ int main() { auto neuralDepth = pipeline.create(); neuralDepth->build(*leftOutput, *rightOutput, dai::DeviceModelZoo::NEURAL_DEPTH_LARGE); - // Create an output queue to get the disparity frames from the node - auto disparityQueue = neuralDepth->disparity.createOutputQueue(); + // Create an output queue to get the depth frames from the node + auto depthQueue = neuralDepth->depth.createOutputQueue(); // Start the pipeline pipeline.start(); - // Variables for visualization - double maxDisparity = 1.0; - cv::Mat colorMap; - - // Pre-generate the color map for efficiency - cv::Mat gray(256, 1, CV_8UC1); - for(int i = 0; i < 256; i++) { - gray.at(i) = i; - } - cv::applyColorMap(gray, colorMap, cv::COLORMAP_JET); - // Set the color for zero-disparity pixels to black, as in the Python example - colorMap.at(0) = cv::Vec3b(0, 0, 0); - while(!quitEvent && pipeline.isRunning()) { - // Get the disparity data from the queue - auto disparityData = disparityQueue->get(); - cv::Mat npDisparity = disparityData->getFrame(); - - // Find the current maximum disparity value to keep the visualization normalized - double minVal, currentMax; - cv::minMaxLoc(npDisparity, &minVal, ¤tMax); - if(currentMax > 0) { - maxDisparity = std::max(maxDisparity, currentMax); - } - - // Normalize the disparity image to a 0-255 scale for color mapping - cv::Mat normalized; - npDisparity.convertTo(normalized, CV_8UC1, 255.0 / maxDisparity); - - // Apply the color map to create a visual representation - cv::Mat colorizedDisparity; - cv::applyColorMap(normalized, colorizedDisparity, colorMap); - - // Display the colorized disparity map - cv::imshow("disparity", colorizedDisparity); + auto depthData = depthQueue->get(); + cv::imshow("depth", dai::utility::colorizeDepthFrame(*depthData).getCvFrame()); // Check for keyboard input to quit int key = cv::waitKey(1); diff --git a/examples/cpp/NeuralDepth/neural_depth_rgbd.cpp b/examples/cpp/NeuralDepth/neural_depth_rgbd.cpp index 76d11bf195..ef60489933 100644 --- a/examples/cpp/NeuralDepth/neural_depth_rgbd.cpp +++ b/examples/cpp/NeuralDepth/neural_depth_rgbd.cpp @@ -47,7 +47,7 @@ int main(int argc, char** argv) { // Color camera auto color = pipeline.create(); - color->build(dai::CameraBoardSocket::CAM_A, std::nullopt, FPS); + color->build(dai::CameraBoardSocket::AUTO, std::nullopt, FPS); // Left and right mono cameras for the stereo pair auto left = pipeline.create(); diff --git a/examples/cpp/NeuralNetwork/neural_network.cpp b/examples/cpp/NeuralNetwork/neural_network.cpp index e53d0af016..9498a740bd 100644 --- a/examples/cpp/NeuralNetwork/neural_network.cpp +++ b/examples/cpp/NeuralNetwork/neural_network.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -40,9 +41,14 @@ int main() { pipeline.start(); // Main loop + auto lastPrintTime = std::chrono::steady_clock::now() - std::chrono::seconds(1); while(pipeline.isRunning() && !quitEvent) { auto inNNData = qNNData->get(); auto tensor = inNNData->getFirstTensor(); + const auto now = std::chrono::steady_clock::now(); + if(now - lastPrintTime < std::chrono::seconds(1)) continue; + lastPrintTime = now; + std::cout << "Received NN data: " << tensor.shape()[0] << "x" << tensor.shape()[1] << std::endl; } @@ -50,4 +56,4 @@ int main() { pipeline.wait(); return 0; -} \ No newline at end of file +} diff --git a/examples/cpp/ObjectTracker/object_tracker.cpp b/examples/cpp/ObjectTracker/object_tracker.cpp index 538e604502..a4de65526a 100644 --- a/examples/cpp/ObjectTracker/object_tracker.cpp +++ b/examples/cpp/ObjectTracker/object_tracker.cpp @@ -8,25 +8,22 @@ int main() { bool fullFrameTracking = false; bool useSpatialAssociation = false; + float sensorFps = 20.0f; // Create pipeline dai::Pipeline pipeline; // Define sources and outputs - auto camRgb = pipeline.create()->build(dai::CameraBoardSocket::CAM_A); - auto monoLeft = pipeline.create()->build(dai::CameraBoardSocket::CAM_B); - auto monoRight = pipeline.create()->build(dai::CameraBoardSocket::CAM_C); + auto colorSockets = pipeline.getDefaultDevice()->getConnectedCameras(dai::CameraSensorType::COLOR); + auto colorSocket = colorSockets.empty() ? dai::CameraBoardSocket::CAM_A : colorSockets.front(); + auto camRgb = pipeline.create()->build(colorSocket, std::nullopt, sensorFps); - // Create stereo node - auto stereo = pipeline.create(); - auto leftOutput = monoLeft->requestOutput(std::make_pair(640, 400)); - auto rightOutput = monoRight->requestOutput(std::make_pair(640, 400)); - leftOutput->link(stereo->left); - rightOutput->link(stereo->right); + auto depth = pipeline.create(); + depth->build(dai::node::Depth::Algorithm::AUTO, sensorFps, std::make_pair(640u, 400u)); // Create spatial detection network dai::NNModelDescription modelDescription{"yolov6-nano"}; - auto spatialDetectionNetwork = pipeline.create()->build(camRgb, stereo, modelDescription); + auto spatialDetectionNetwork = pipeline.create()->build(camRgb, depth, modelDescription); spatialDetectionNetwork->setConfidenceThreshold(0.6f); spatialDetectionNetwork->input.setBlocking(false); spatialDetectionNetwork->setBoundingBoxScaleFactor(0.5f); diff --git a/examples/cpp/ObjectTracker/object_tracker_remap.cpp b/examples/cpp/ObjectTracker/object_tracker_remap.cpp index 084cb2badd..8c33361aed 100644 --- a/examples/cpp/ObjectTracker/object_tracker_remap.cpp +++ b/examples/cpp/ObjectTracker/object_tracker_remap.cpp @@ -1,76 +1,9 @@ -#include // Required for std::sort and std::unique -#include // Required for std::log, std::isnan, std::isinf #include #include #include #include #include "depthai/depthai.hpp" -#include "xtensor/containers/xadapt.hpp" -#include "xtensor/core/xmath.hpp" - -cv::Mat colorizeDepth(cv::Mat frameDepth) { - cv::Mat invalidMask = frameDepth == 0; - cv::Mat depthFrameColor; - - try { - cv::Mat frameDepthFloat; - frameDepth.convertTo(frameDepthFloat, CV_32F); - xt::xtensor depth = - xt::adapt((float*)frameDepthFloat.data, {static_cast(frameDepthFloat.rows), static_cast(frameDepthFloat.cols)}); - - // Get valid depth values (non-zero) - std::vector validDepth; - validDepth.reserve(depth.size()); - std::copy_if(depth.begin(), depth.end(), std::back_inserter(validDepth), [](float x) { return x != 0; }); - - if(validDepth.size() == 0) { - return cv::Mat::zeros(frameDepth.rows, frameDepth.cols, CV_8UC3); - } - - // Calculate percentiles - std::sort(validDepth.begin(), validDepth.end()); - float minDepth = validDepth[static_cast(validDepth.size() * 0.03)]; - float maxDepth = validDepth[static_cast(validDepth.size() * 0.95)]; - - // Take log of depth values - auto logDepth = xt::eval(xt::log(depth)); - float logMinDepth = std::log(minDepth); - float logMaxDepth = std::log(maxDepth); - - // Replace invalid values with logMinDepth using a naive implementation - auto logDepthData = logDepth.data(); - auto depthData = depth.data(); - const size_t size = depth.size(); - for(size_t i = 0; i < size; i++) { - if(std::isnan(logDepthData[i]) || std::isinf(logDepthData[i]) || depthData[i] == 0.0f) { - logDepthData[i] = logMinDepth; - } - } - - // Clip values - logDepth = xt::clip(logDepth, logMinDepth, logMaxDepth); - - // Normalize to 0-255 range - auto normalizedDepth = (logDepth - logMinDepth) / (logMaxDepth - logMinDepth) * 255.0f; - - // Convert to CV_8UC1 - cv::Mat depthMat(frameDepth.rows, frameDepth.cols, CV_8UC1); - std::transform(normalizedDepth.begin(), normalizedDepth.end(), depthMat.data, [](float x) { return static_cast(x); }); - - // Apply colormap - cv::applyColorMap(depthMat, depthFrameColor, cv::COLORMAP_JET); - - // Set invalid pixels to black - depthFrameColor.setTo(cv::Scalar(0, 0, 0), invalidMask); - - } catch(const std::exception& e) { - std::cerr << "Error in colorizeDepth: " << e.what() << std::endl; - return cv::Mat::zeros(frameDepth.rows, frameDepth.cols, CV_8UC3); - } - - return depthFrameColor; -} // Helper function to display frames with detections void displayFrame(const std::string& name, @@ -81,7 +14,7 @@ void displayFrame(const std::string& name, cv::Mat cvFrame; if(frame->getType() == dai::ImgFrame::Type::RAW16) { - cvFrame = colorizeDepth(frame->getFrame()); + cvFrame = dai::utility::colorizeDepthFrame(*frame).getCvFrame(); } else { cvFrame = frame->getCvFrame(); } @@ -133,8 +66,10 @@ void displayFrame(const std::string& name, int main() { dai::Pipeline pipeline; + auto colorSockets = pipeline.getDefaultDevice()->getConnectedCameras(dai::CameraSensorType::COLOR); + auto colorSocket = colorSockets.empty() ? dai::CameraBoardSocket::CAM_A : colorSockets.front(); auto cameraNode = pipeline.create(); - cameraNode->build(); + cameraNode->build(colorSocket); auto detectionNetwork = pipeline.create(); dai::NNModelDescription modelDescription; @@ -143,30 +78,16 @@ int main() { auto objectTracker = pipeline.create(); auto labelMap = detectionNetwork->getClasses().value_or(std::vector{}); - auto monoLeft = pipeline.create(); - monoLeft->build(dai::CameraBoardSocket::CAM_B); - auto monoRight = pipeline.create(); - monoRight->build(dai::CameraBoardSocket::CAM_C); - auto stereo = pipeline.create(); - - // Linking - auto monoLeftOut = monoLeft->requestOutput(std::make_pair(1280, 720)); - auto monoRightOut = monoRight->requestOutput(std::make_pair(1280, 720)); - monoLeftOut->link(stereo->left); - monoRightOut->link(stereo->right); + auto depth = pipeline.create(); + depth->build(dai::node::Depth::Algorithm::AUTO); detectionNetwork->out.link(objectTracker->inputDetections); detectionNetwork->passthrough.link(objectTracker->inputDetectionFrame); detectionNetwork->passthrough.link(objectTracker->inputTrackerFrame); - stereo->setRectification(true); - stereo->setExtendedDisparity(true); - stereo->setLeftRightCheck(true); - stereo->setSubpixel(true); - auto qRgb = detectionNetwork->passthrough.createOutputQueue(); auto qTrack = objectTracker->out.createOutputQueue(); - auto qDepth = stereo->disparity.createOutputQueue(); + auto qDepth = depth->depth().createOutputQueue(); pipeline.start(); diff --git a/examples/cpp/PointCloud/PointCloud.cpp b/examples/cpp/PointCloud/PointCloud.cpp index 05ac1bb7ed..c52377170a 100644 --- a/examples/cpp/PointCloud/PointCloud.cpp +++ b/examples/cpp/PointCloud/PointCloud.cpp @@ -7,34 +7,22 @@ int main() { dai::Pipeline pipeline; // Cameras - auto left = pipeline.create()->build(dai::CameraBoardSocket::CAM_B); - auto right = pipeline.create()->build(dai::CameraBoardSocket::CAM_C); - auto color = pipeline.create()->build(dai::CameraBoardSocket::CAM_A); - - // Stereo depth - auto stereo = pipeline.create(); - left->requestFullResolutionOutput()->link(stereo->left); - right->requestFullResolutionOutput()->link(stereo->right); + auto colorSockets = pipeline.getDefaultDevice()->getConnectedCameras(dai::CameraSensorType::COLOR); + auto colorSocket = colorSockets.empty() ? dai::CameraBoardSocket::CAM_A : colorSockets.front(); + auto color = pipeline.create()->build(colorSocket); // Color output aligned to depth auto colorOut = color->requestOutput(std::make_pair(640, 400), dai::ImgFrame::Type::RGB888i, dai::ImgResizeMode::CROP, std::nullopt, true); + auto depth = pipeline.create(); + depth->build(dai::node::Depth::Algorithm::AUTO, std::nullopt, std::make_pair(640u, 400u)); + depth->setAlignTo(*colorOut); + // Point cloud auto pc = pipeline.create(); pc->initialConfig->setLengthUnit(dai::LengthUnit::METER); - // Align depth to color on RVC4, or color to depth on RVC2/3 - auto platform = pipeline.getDefaultDevice()->getPlatform(); - if(platform == dai::Platform::RVC4) { - auto imageAlign = pipeline.create(); - stereo->depth.link(imageAlign->input); - colorOut->link(imageAlign->inputAlignTo); - imageAlign->outputAligned.link(pc->inputDepth); - } else { - colorOut->link(stereo->inputAlignTo); - stereo->depth.link(pc->inputDepth); - } - + depth->depth().link(pc->inputDepth); colorOut->link(pc->getColorInput()); auto q = pc->outputPointCloud.createOutputQueue(4, false); diff --git a/examples/cpp/PointCloud/PointCloudShowcase.cpp b/examples/cpp/PointCloud/PointCloudShowcase.cpp index 1559753f7b..666897765e 100644 --- a/examples/cpp/PointCloud/PointCloudShowcase.cpp +++ b/examples/cpp/PointCloud/PointCloudShowcase.cpp @@ -57,18 +57,19 @@ int main() { // ============================================================== dai::Pipeline pipeline(device); - auto left = pipeline.create()->build(dai::CameraBoardSocket::CAM_B); - auto right = pipeline.create()->build(dai::CameraBoardSocket::CAM_C); - auto color = pipeline.create()->build(dai::CameraBoardSocket::CAM_A); - auto stereo = pipeline.create(); - left->requestFullResolutionOutput()->link(stereo->left); - right->requestFullResolutionOutput()->link(stereo->right); + auto colorSockets = device->getConnectedCameras(dai::CameraSensorType::COLOR); + auto colorSocket = colorSockets.empty() ? dai::CameraBoardSocket::CAM_A : colorSockets.front(); + auto color = pipeline.create()->build(colorSocket); + auto* colorOut = color->requestOutput(std::make_pair(640, 400), dai::ImgFrame::Type::RGB888i, dai::ImgResizeMode::CROP, std::nullopt, true); + auto depth = pipeline.create(); + depth->build(dai::node::Depth::Algorithm::AUTO, std::nullopt, std::make_pair(640u, 400u)); + depth->setAlignTo(*colorOut); // ── 1. Filtered point cloud (METER) auto pcSparse = pipeline.create(); pcSparse->setRunOnHost(true); pcSparse->initialConfig->setLengthUnit(dai::LengthUnit::METER); - stereo->depth.link(pcSparse->inputDepth); + depth->depth().link(pcSparse->inputDepth); auto qSparse = pcSparse->outputPointCloud.createOutputQueue(); // ── 2. Organized point cloud (MILLIMETER) @@ -76,7 +77,7 @@ int main() { pcOrganized->setRunOnHost(true); pcOrganized->initialConfig->setLengthUnit(dai::LengthUnit::MILLIMETER); pcOrganized->initialConfig->setOrganized(true); - stereo->depth.link(pcOrganized->inputDepth); + depth->depth().link(pcOrganized->inputDepth); auto qOrganized = pcOrganized->outputPointCloud.createOutputQueue(); // ── 3. Transform pointcloud into another camera's coordinate system @@ -86,7 +87,7 @@ int main() { pcCam->initialConfig->setTargetCoordinateSystem(dai::CameraBoardSocket::CAM_A); // Or transform to a housing coordinate system instead, e.g.: // pcCam->initialConfig->setTargetCoordinateSystem(dai::HousingCoordinateSystem::VESA_A); - stereo->depth.link(pcCam->inputDepth); + depth->depth().link(pcCam->inputDepth); auto qCam = pcCam->outputPointCloud.createOutputQueue(); // ── 4. Custom 4×4 transform (90° Z rotation) + passthrough @@ -96,7 +97,7 @@ int main() { pcCustom->useCPU(); std::array, 4> transform = {{{{0.f, -1.f, 0.f, 0.f}}, {{1.f, 0.f, 0.f, 0.f}}, {{0.f, 0.f, 1.f, 0.f}}, {{0.f, 0.f, 0.f, 1.f}}}}; pcCustom->initialConfig->setTransformationMatrix(transform); - stereo->depth.link(pcCustom->inputDepth); + depth->depth().link(pcCustom->inputDepth); auto qCustom = pcCustom->outputPointCloud.createOutputQueue(); auto qDepth = pcCustom->passthroughDepth.createOutputQueue(); @@ -104,17 +105,7 @@ int main() { auto pcColor = pipeline.create(); pcColor->setRunOnHost(true); pcColor->initialConfig->setLengthUnit(dai::LengthUnit::METER); - auto* colorOut = color->requestOutput(std::make_pair(640, 400), dai::ImgFrame::Type::RGB888i, dai::ImgResizeMode::CROP, std::nullopt, true); - auto platform = device->getPlatform(); - if(platform == dai::Platform::RVC4) { - auto imageAlign = pipeline.create(); - stereo->depth.link(imageAlign->input); - colorOut->link(imageAlign->inputAlignTo); - imageAlign->outputAligned.link(pcColor->inputDepth); - } else { - colorOut->link(stereo->inputAlignTo); - stereo->depth.link(pcColor->inputDepth); - } + depth->depth().link(pcColor->inputDepth); colorOut->link(pcColor->getColorInput()); auto qColor = pcColor->outputPointCloud.createOutputQueue(); diff --git a/examples/cpp/PointCloud/PointCloudVisualizer.cpp b/examples/cpp/PointCloud/PointCloudVisualizer.cpp index 083b677e24..2374c51e11 100644 --- a/examples/cpp/PointCloud/PointCloudVisualizer.cpp +++ b/examples/cpp/PointCloud/PointCloudVisualizer.cpp @@ -38,30 +38,22 @@ int main() { const auto size = std::make_pair(640, 400); // ── Cameras ────────────────────────────────────────────────────── - auto left = pipeline.create(); - auto right = pipeline.create(); + auto colorSockets = device->getConnectedCameras(dai::CameraSensorType::COLOR); + auto colorSocket = colorSockets.empty() ? dai::CameraBoardSocket::CAM_A : colorSockets.front(); auto color = pipeline.create(); - - left->build(dai::CameraBoardSocket::CAM_B); - right->build(dai::CameraBoardSocket::CAM_C); - color->build(dai::CameraBoardSocket::CAM_A); - - // ── StereoDepth ────────────────────────────────────────────────── - auto stereo = pipeline.create(); - left->requestOutput(size)->link(stereo->left); - right->requestOutput(size)->link(stereo->right); + color->build(colorSocket); // ── Align depth to color camera ────────────────────────────────── auto colorOut = color->requestOutput(size, dai::ImgFrame::Type::RGB888i, dai::ImgResizeMode::CROP, std::nullopt, true); - auto align = pipeline.create(); - stereo->depth.link(align->input); - colorOut->link(align->inputAlignTo); + auto depth = pipeline.create(); + depth->build(dai::node::Depth::Algorithm::AUTO, std::nullopt, std::make_pair(640u, 400u)); + depth->setAlignTo(*colorOut); // ── PointCloud node ────────────────────────────────────────────── auto pc = pipeline.create(); pc->setRunOnHost(true); - align->outputAligned.link(pc->inputDepth); + depth->depth().link(pc->inputDepth); colorOut->link(pc->getColorInput()); // Publish the point cloud to the remote visualizer diff --git a/examples/cpp/RGBD/rgbd.cpp b/examples/cpp/RGBD/rgbd.cpp index d21f5cb75a..6b4665bc53 100644 --- a/examples/cpp/RGBD/rgbd.cpp +++ b/examples/cpp/RGBD/rgbd.cpp @@ -1,3 +1,5 @@ +#include + #include "depthai/capabilities/ImgFrameCapability.hpp" #include "depthai/depthai.hpp" #include "rerun.hpp" @@ -47,45 +49,17 @@ int main() { // Create pipeline dai::Pipeline pipeline; // Define sources and outputs - auto left = pipeline.create(); - auto right = pipeline.create(); - auto stereo = pipeline.create(); - auto rgbd = pipeline.create()->build(); + auto colorSockets = pipeline.getDefaultDevice()->getConnectedCameras(dai::CameraSensorType::COLOR); + auto colorSocket = colorSockets.empty() ? dai::CameraBoardSocket::CAM_A : colorSockets.front(); auto color = pipeline.create(); - std::shared_ptr align; + color->build(colorSocket); + auto depth = pipeline.create(); + depth->build(dai::node::Depth::Algorithm::AUTO, 30.0f, std::make_pair(640u, 400u)); auto rerun = pipeline.create(); - color->build(); - left->build(dai::CameraBoardSocket::CAM_B); - right->build(dai::CameraBoardSocket::CAM_C); - stereo->setSubpixel(true); - stereo->setExtendedDisparity(false); - stereo->setDefaultProfilePreset(dai::node::StereoDepth::PresetMode::DEFAULT); - stereo->setLeftRightCheck(true); - stereo->setRectifyEdgeFillColor(0); // black, to better see the cutout - stereo->enableDistortionCorrection(true); - stereo->initialConfig->setLeftRightCheckThreshold(10); - stereo->initialConfig->postProcessing.thresholdFilter.maxRange = 10000; + auto rgbd = pipeline.create()->build(color, depth, std::make_pair(640, 400), 30.0f); rgbd->setDepthUnit(dai::StereoDepthConfig::AlgorithmControl::DepthUnit::METER); - left->requestOutput(std::pair(640, 400))->link(stereo->left); - right->requestOutput(std::pair(640, 400))->link(stereo->right); - - auto platform = pipeline.getDefaultDevice()->getPlatform(); - if(platform == dai::Platform::RVC4) { - auto* out = color->requestOutput(std::pair(640, 400), dai::ImgFrame::Type::RGB888i, dai::ImgResizeMode::CROP, std::nullopt, true); - out->link(rgbd->inColor); - align = pipeline.create(); - stereo->depth.link(align->input); - out->link(align->inputAlignTo); - align->outputAligned.link(rgbd->inDepth); - } else { - auto* out = color->requestOutput(std::pair(640, 400), dai::ImgFrame::Type::RGB888i, dai::ImgResizeMode::CROP, 30, true); - out->link(rgbd->inColor); - out->link(stereo->inputAlignTo); - stereo->depth.link(rgbd->inDepth); - } - // Linking rgbd->pcl.link(rerun->inputPCL); rgbd->rgbd.link(rerun->inputRGBD); diff --git a/examples/cpp/RGBD/rgbd_pcl_processing.cpp b/examples/cpp/RGBD/rgbd_pcl_processing.cpp index ed968d4ba4..ea5638fb75 100644 --- a/examples/cpp/RGBD/rgbd_pcl_processing.cpp +++ b/examples/cpp/RGBD/rgbd_pcl_processing.cpp @@ -1,4 +1,5 @@ +#include #include #include #include @@ -56,7 +57,13 @@ int main() { dai::RemoteConnection remoteConnector(dai::RemoteConnection::DEFAULT_ADDRESS, webSocketPort, true, httpPort); // Create pipeline dai::Pipeline pipeline; - auto rgbd = pipeline.create()->build(true, dai::node::StereoDepth::PresetMode::DEFAULT); + auto colorSockets = pipeline.getDefaultDevice()->getConnectedCameras(dai::CameraSensorType::COLOR); + auto colorSocket = colorSockets.empty() ? dai::CameraBoardSocket::CAM_A : colorSockets.front(); + auto color = pipeline.create(); + color->build(colorSocket); + auto depth = pipeline.create(); + depth->build(dai::node::Depth::Algorithm::AUTO, std::nullopt, std::make_pair(640u, 400u)); + auto rgbd = pipeline.create()->build(color, depth); auto customNode = pipeline.create(); rgbd->pcl.link(customNode->inputPCL); diff --git a/examples/cpp/RGBD/visualizer_rgbd.cpp b/examples/cpp/RGBD/visualizer_rgbd.cpp index 504be9593a..0c5f13e73b 100644 --- a/examples/cpp/RGBD/visualizer_rgbd.cpp +++ b/examples/cpp/RGBD/visualizer_rgbd.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -62,32 +63,19 @@ int main(int argc, char** argv) { const std::pair size = std::make_pair(640, 400); // Create color camera + auto colorSockets = pipeline.getDefaultDevice()->getConnectedCameras(dai::CameraSensorType::COLOR); + auto colorSocket = colorSockets.empty() ? dai::CameraBoardSocket::CAM_A : colorSockets.front(); auto color = pipeline.create(); - color->build(dai::CameraBoardSocket::AUTO, std::nullopt, fps); + color->build(colorSocket, std::nullopt, fps); // Create depth source based on argument dai::node::DepthSource depthSource; if(depthSourceArg == "stereo") { - auto left = pipeline.create(); - auto right = pipeline.create(); - auto stereo = pipeline.create(); - - left->build(dai::CameraBoardSocket::CAM_B, std::nullopt, fps); - right->build(dai::CameraBoardSocket::CAM_C, std::nullopt, fps); - - stereo->setSubpixel(true); - stereo->setExtendedDisparity(false); - stereo->setDefaultProfilePreset(dai::node::StereoDepth::PresetMode::DEFAULT); - stereo->setLeftRightCheck(true); - stereo->setRectifyEdgeFillColor(0); // black, to better see the cutout - stereo->enableDistortionCorrection(true); - stereo->initialConfig->setLeftRightCheckThreshold(10); - - left->requestOutput(size, std::nullopt, dai::ImgResizeMode::CROP)->link(stereo->left); - right->requestOutput(size, std::nullopt, dai::ImgResizeMode::CROP)->link(stereo->right); + auto depth = pipeline.create(); + depth->build(dai::node::Depth::Algorithm::AUTO, fps, std::make_pair(640u, 400u)); - depthSource = stereo; + depthSource = depth; } else if(depthSourceArg == "neural") { auto left = pipeline.create(); auto right = pipeline.create(); diff --git a/examples/cpp/RGBD/visualizer_rgbd_autocreate.cpp b/examples/cpp/RGBD/visualizer_rgbd_autocreate.cpp index 306304d8bf..05a27bcb2b 100644 --- a/examples/cpp/RGBD/visualizer_rgbd_autocreate.cpp +++ b/examples/cpp/RGBD/visualizer_rgbd_autocreate.cpp @@ -1,12 +1,13 @@ +#include #include #include #include #include "depthai/depthai.hpp" -// NOTE: Using autocreate takes over the cameras cannot be used in complex pipelines, -// where cameras would be used in other nodes as well yet. +// NOTE: Using autocreate takes over the cameras and cannot yet be used in complex +// pipelines where the cameras would also be used by other nodes. // Signal handling for clean shutdown static bool isRunning = true; @@ -27,7 +28,13 @@ int main() { dai::RemoteConnection remoteConnector(dai::RemoteConnection::DEFAULT_ADDRESS, webSocketPort, true, httpPort); // Create pipeline dai::Pipeline pipeline; - auto rgbd = pipeline.create()->build(true, dai::node::StereoDepth::PresetMode::DEFAULT); + auto colorSockets = pipeline.getDefaultDevice()->getConnectedCameras(dai::CameraSensorType::COLOR); + auto colorSocket = colorSockets.empty() ? dai::CameraBoardSocket::CAM_A : colorSockets.front(); + auto color = pipeline.create(); + color->build(colorSocket); + auto depth = pipeline.create(); + depth->build(dai::node::Depth::Algorithm::AUTO, std::nullopt, std::make_pair(640u, 400u)); + auto rgbd = pipeline.create()->build(color, depth); remoteConnector.addTopic("pcl", rgbd->pcl); pipeline.start(); diff --git a/examples/cpp/Remapping/point_remapping.cpp b/examples/cpp/Remapping/point_remapping.cpp index 23a26b355d..4cfceda0ce 100644 --- a/examples/cpp/Remapping/point_remapping.cpp +++ b/examples/cpp/Remapping/point_remapping.cpp @@ -179,11 +179,7 @@ int main() { } } - cv::Mat depthScaled; - cv::convertScaleAbs(depthFrame->getFrame(), depthScaled, 0.05); - - cv::Mat depthColor; - cv::applyColorMap(depthScaled, depthColor, cv::COLORMAP_JET); + cv::Mat depthColor = dai::utility::colorizeDepthFrame(*depthFrame).getCvFrame(); drawPoint(leftFrame, originalPoint, sourceStatus, cv::Scalar(0, 255, 0)); drawPoint(rgbDisplay, remappedRgbPoint, rgbStatus, cv::Scalar(255, 255, 0)); diff --git a/examples/cpp/Script/script_simple.cpp b/examples/cpp/Script/script_simple.cpp index 9745c7f24c..7ac2d40278 100644 --- a/examples/cpp/Script/script_simple.cpp +++ b/examples/cpp/Script/script_simple.cpp @@ -50,12 +50,10 @@ int main() { while(pipeline.isRunning() && !quitEvent) { // Create and send a message auto message = std::make_shared(); - std::cout << "Sending a message" << std::endl; inputQueue->send(message); // Receive the message auto output = outputQueue->get(); - std::cout << "Received a message" << std::endl; // Sleep for 1 second std::this_thread::sleep_for(std::chrono::seconds(1)); diff --git a/examples/cpp/SpatialDetectionNetwork/spatial_detection.cpp b/examples/cpp/SpatialDetectionNetwork/spatial_detection.cpp index 2818c4ba9e..5902971506 100644 --- a/examples/cpp/SpatialDetectionNetwork/spatial_detection.cpp +++ b/examples/cpp/SpatialDetectionNetwork/spatial_detection.cpp @@ -43,35 +43,16 @@ class SpatialVisualizer : public dai::NodeCRTPget("detections"); auto rgbFrame = in->get("rgb"); - cv::Mat depthCv = depthFrame->getCvFrame(); cv::Mat rgbCv = rgbFrame->getCvFrame(); - cv::Mat depthFrameColor = processDepthFrame(depthCv); + cv::Mat depthFrameColor = processDepthFrame(*depthFrame); displayResults(rgbCv, depthFrameColor, detections->detections); return nullptr; } private: - cv::Mat processDepthFrame(const cv::Mat& depthFrame) { - // Downscale depth frame - cv::Mat depthDownscaled; - cv::resize(depthFrame, depthDownscaled, cv::Size(), 0.25, 0.25); - - // Find min and max depth values - double minDepth = 0, maxDepth = 0; - cv::Mat mask = (depthDownscaled != 0); - if(cv::countNonZero(mask) > 0) { - cv::minMaxLoc(depthDownscaled, &minDepth, &maxDepth, nullptr, nullptr, mask); - } - - // Normalize depth frame - cv::Mat depthFrameColor; - depthFrame.convertTo(depthFrameColor, CV_8UC1, 255.0 / (maxDepth - minDepth), -minDepth * 255.0 / (maxDepth - minDepth)); - - // Apply color map - cv::Mat colorized; - cv::applyColorMap(depthFrameColor, colorized, cv::COLORMAP_HOT); - return colorized; + cv::Mat processDepthFrame(const dai::ImgFrame& depthFrameImg) { + return dai::utility::colorizeDepthFrame(depthFrameImg, 500.0f, 12000.0f, cv::COLORMAP_HOT, true).getCvFrame(); } void displayResults(cv::Mat& rgbFrame, cv::Mat& depthFrameColor, const std::vector& detections) { @@ -150,8 +131,8 @@ int main(int argc, char** argv) { program.add_description("Spatial detection network example with configurable depth source"); program.add_argument("--depthSource").default_value(std::string("stereo")).help("Depth source: stereo, neural, tof"); - // Parse arguments try { + // Parse arguments program.parse_args(argc, argv); } catch(const std::runtime_error& err) { std::cerr << err.what() << '\n'; @@ -178,30 +159,20 @@ int main(int argc, char** argv) { // Create pipeline dai::Pipeline pipeline; - const std::pair size = {640, 400}; - // Define sources and outputs + auto colorSockets = pipeline.getDefaultDevice()->getConnectedCameras(dai::CameraSensorType::COLOR); + auto colorSocket = colorSockets.empty() ? dai::CameraBoardSocket::CAM_A : colorSockets.front(); auto camRgb = pipeline.create(); - camRgb->build(dai::CameraBoardSocket::CAM_A, std::nullopt, fps); - - auto platform = pipeline.getDefaultDevice()->getPlatform(); + camRgb->build(colorSocket, std::nullopt, fps); // Create depth source based on argument dai::node::DepthSource depthSource; if(depthSourceArg == "stereo") { - auto monoLeft = pipeline.create(); - auto monoRight = pipeline.create(); - auto stereo = pipeline.create(); - - monoLeft->build(dai::CameraBoardSocket::CAM_B, std::nullopt, fps); - monoRight->build(dai::CameraBoardSocket::CAM_C, std::nullopt, fps); - - stereo->setExtendedDisparity(true); - monoLeft->requestOutput(size, std::nullopt, dai::ImgResizeMode::CROP)->link(stereo->left); - monoRight->requestOutput(size, std::nullopt, dai::ImgResizeMode::CROP)->link(stereo->right); + auto depth = pipeline.create(); + depth->build(dai::node::Depth::Algorithm::AUTO, fps, std::make_pair(640u, 400u)); - depthSource = stereo; + depthSource = depth; } else if(depthSourceArg == "neural") { auto monoLeft = pipeline.create(); auto monoRight = pipeline.create(); diff --git a/examples/cpp/SpatialLocationCalculator/spatial_location_calculator.cpp b/examples/cpp/SpatialLocationCalculator/spatial_location_calculator.cpp index 76d9b9c319..4e7bcdafe1 100644 --- a/examples/cpp/SpatialLocationCalculator/spatial_location_calculator.cpp +++ b/examples/cpp/SpatialLocationCalculator/spatial_location_calculator.cpp @@ -21,19 +21,10 @@ int main() { dai::Pipeline pipeline; // Define sources and outputs - auto monoLeft = pipeline.create(); - monoLeft->build(dai::CameraBoardSocket::CAM_B); - - auto monoRight = pipeline.create(); - monoRight->build(dai::CameraBoardSocket::CAM_C); - - auto stereo = pipeline.create(); + auto depth = pipeline.create(); + depth->build(dai::node::Depth::Algorithm::AUTO, std::nullopt, std::make_pair(640u, 400u)); auto spatialLocationCalculator = pipeline.create(); - // Configure stereo - stereo->setRectification(true); - stereo->setExtendedDisparity(true); - // Initial ROI configuration dai::Point2f topLeft(0.4f, 0.4f); dai::Point2f bottomRight(0.6f, 0.6f); @@ -55,9 +46,7 @@ int main() { auto inputConfigQueue = spatialLocationCalculator->inputConfig.createInputQueue(); // Linking - monoLeft->requestOutput(std::make_pair(640, 400))->link(stereo->left); - monoRight->requestOutput(std::make_pair(640, 400))->link(stereo->right); - stereo->depth.link(spatialLocationCalculator->inputDepth); + depth->depth().link(spatialLocationCalculator->inputDepth); // Start pipeline pipeline.start(); @@ -88,10 +77,7 @@ int main() { } // Process depth frame for visualization - cv::Mat depthFrameColor; - cv::normalize(frameDepth, depthFrameColor, 255, 0, cv::NORM_INF, CV_8UC1); - cv::equalizeHist(depthFrameColor, depthFrameColor); - cv::applyColorMap(depthFrameColor, depthFrameColor, cv::COLORMAP_HOT); + cv::Mat depthFrameColor = dai::utility::colorizeDepthFrame(*outputDepthImage).getCvFrame(); // Draw spatial data for(const auto& depthData : spatialData->spatialLocations) { @@ -198,4 +184,4 @@ int main() { } return 0; -} \ No newline at end of file +} diff --git a/examples/cpp/SpatialLocationCalculator/spatial_segmentation.cpp b/examples/cpp/SpatialLocationCalculator/spatial_segmentation.cpp index 379924f8e8..ac8cd68c75 100644 --- a/examples/cpp/SpatialLocationCalculator/spatial_segmentation.cpp +++ b/examples/cpp/SpatialLocationCalculator/spatial_segmentation.cpp @@ -18,14 +18,6 @@ void signalHandler(int) { namespace { -cv::Mat colorizeDepth(const cv::Mat& depthFrame) { - cv::Mat depthAbs; - cv::Mat colorizedDepth; - cv::convertScaleAbs(depthFrame, depthAbs, 0.03); - cv::applyColorMap(depthAbs, colorizedDepth, cv::COLORMAP_JET); - return colorizedDepth; -} - void applySegmentationOverlay(cv::Mat& image, const cv::Mat& segmentationMask) { cv::Mat lut(1, 256, CV_8U); for(int i = 0; i < 256; ++i) { @@ -137,8 +129,7 @@ int main() { if(!inSpatialDet || !rgbFrame || !depthFrame) { continue; } - cv::Mat depthCv = depthFrame->getCvFrame(); - cv::Mat colorizedDepth = colorizeDepth(depthCv); + cv::Mat colorizedDepth = dai::utility::colorizeDepthFrame(*depthFrame).getCvFrame(); cv::Mat image = rgbFrame->getCvFrame(); diff --git a/examples/cpp/StereoDepth/depth_preview.cpp b/examples/cpp/StereoDepth/depth_preview.cpp index eb69365dd4..e358786a13 100644 --- a/examples/cpp/StereoDepth/depth_preview.cpp +++ b/examples/cpp/StereoDepth/depth_preview.cpp @@ -35,7 +35,7 @@ int main() { auto* lout = monoLeft->requestOutput({640, 400}); auto* rout = monoRight->requestOutput({640, 400}); - // Create a node that will produce the depth map (using disparity output as it's easier to visualize depth this way) + // Create a node that will produce the depth map. depth->build(*lout, *rout, dai::node::StereoDepth::PresetMode::DEFAULT); // Options: MEDIAN_OFF, KERNEL_3x3, KERNEL_5x5, KERNEL_7x7 (default) depth->initialConfig->setMedianFilter(dai::StereoDepthConfig::MedianFilter::KERNEL_7x7); @@ -43,24 +43,14 @@ int main() { depth->setExtendedDisparity(extended_disparity); depth->setSubpixel(subpixel); - // Output queue will be used to get the disparity frames from the outputs defined above - auto q = depth->disparity.createOutputQueue(); - auto qleft = lout->createOutputQueue(); + auto q = depth->depth.createOutputQueue(); pipeline.start(); while(pipeline.isRunning() && !quitEvent) { auto inDepth = q->get(); - auto inLeft = qleft->get(); - auto frame = inDepth->getFrame(); - // Normalization for better visualization - frame.convertTo(frame, CV_8UC1, 255 / depth->initialConfig->getMaxDisparity()); - - cv::imshow("disparity", frame); - - // Available color maps: https://docs.opencv.org/3.4/d3/d50/group__imgproc__colormap.html - cv::applyColorMap(frame, frame, cv::COLORMAP_JET); - cv::imshow("disparity_color", frame); + auto frame = dai::utility::colorizeDepthFrame(*inDepth).getCvFrame(); + cv::imshow("depth", frame); int key = cv::waitKey(1); if(key == 'q' || key == 'Q') { diff --git a/examples/cpp/StereoDepth/stereo.cpp b/examples/cpp/StereoDepth/stereo.cpp index 140879e6e4..e2e202b95a 100644 --- a/examples/cpp/StereoDepth/stereo.cpp +++ b/examples/cpp/StereoDepth/stereo.cpp @@ -31,29 +31,13 @@ int main() { stereo->setExtendedDisparity(true); stereo->setLeftRightCheck(true); - auto disparityQueue = stereo->disparity.createOutputQueue(); + auto depthQueue = stereo->depth.createOutputQueue(); - double maxDisparity = 1.0; pipeline.start(); while(pipeline.isRunning() && !quitEvent) { - auto disparity = disparityQueue->get(); - cv::Mat npDisparity = disparity->getFrame(); - - double minVal, curMax; - cv::minMaxLoc(npDisparity, &minVal, &curMax); - maxDisparity = std::max(maxDisparity, curMax); - - // Normalize the disparity image to an 8-bit scale. - cv::Mat normalized; - npDisparity.convertTo(normalized, CV_8UC1, 255.0 / maxDisparity); - - cv::Mat colorizedDisparity; - cv::applyColorMap(normalized, colorizedDisparity, cv::COLORMAP_JET); - - // Set pixels with zero disparity to black. - colorizedDisparity.setTo(cv::Scalar(0, 0, 0), normalized == 0); - - cv::imshow("disparity", colorizedDisparity); + auto depth = depthQueue->get(); + cv::Mat colorizedDepth = dai::utility::colorizeDepthFrame(*depth).getCvFrame(); + cv::imshow("depth", colorizedDepth); int key = cv::waitKey(1); if(key == 'q') { @@ -65,4 +49,4 @@ int main() { pipeline.wait(); return 0; -} \ No newline at end of file +} diff --git a/examples/cpp/StereoDepth/stereo_depth_remap.cpp b/examples/cpp/StereoDepth/stereo_depth_remap.cpp index 50052eb52e..79f3e6c79a 100644 --- a/examples/cpp/StereoDepth/stereo_depth_remap.cpp +++ b/examples/cpp/StereoDepth/stereo_depth_remap.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -31,44 +32,8 @@ void drawRotatedRectangle(cv::Mat& frame, const cv::Point2f& center, const cv::S } // Helper function to process depth frame -cv::Mat processDepthFrame(const cv::Mat& depthFrame) { - cv::Mat depth_downscaled; - cv::resize(depthFrame, depth_downscaled, cv::Size(), 0.25, 0.25); - - double min_depth = 0; - if(!cv::countNonZero(depth_downscaled == 0)) { - std::vector nonZeroDepth; - nonZeroDepth.reserve(depth_downscaled.rows * depth_downscaled.cols); - - for(int i = 0; i < depth_downscaled.rows; i++) { - for(int j = 0; j < depth_downscaled.cols; j++) { - uint16_t depth = depth_downscaled.at(i, j); - if(depth > 0) nonZeroDepth.push_back(depth); - } - } - - if(!nonZeroDepth.empty()) { - std::sort(nonZeroDepth.begin(), nonZeroDepth.end()); - min_depth = nonZeroDepth[static_cast(nonZeroDepth.size() * 0.01)]; // 1st percentile - } - } - - std::vector allDepth; - allDepth.reserve(depth_downscaled.rows * depth_downscaled.cols); - for(int i = 0; i < depth_downscaled.rows; i++) { - for(int j = 0; j < depth_downscaled.cols; j++) { - allDepth.push_back(depth_downscaled.at(i, j)); - } - } - std::sort(allDepth.begin(), allDepth.end()); - double max_depth = allDepth[static_cast(allDepth.size() * 0.99)]; // 99th percentile - - // Normalize and colorize - cv::Mat normalized; - cv::normalize(depthFrame, normalized, 0, 255, cv::NORM_MINMAX, CV_8UC1, depthFrame > min_depth); - cv::Mat colorized; - cv::applyColorMap(normalized, colorized, cv::COLORMAP_HOT); - return colorized; +cv::Mat processDepthFrame(const dai::ImgFrame& depthFrame) { + return dai::utility::colorizeDepthFrame(depthFrame, 500.0f, 12000.0f, cv::COLORMAP_HOT, true).getCvFrame(); } int main() { @@ -112,6 +77,7 @@ int main() { pipeline.start(); + auto lastPrintTime = std::chrono::steady_clock::now() - std::chrono::seconds(1); while(pipeline.isRunning() && !quitEvent) { auto colorFrame = colorOut->get(); auto stereoFrame = stereoOut->get(); @@ -121,22 +87,27 @@ int main() { // Validate transformations if(!colorFrame->validateTransformations() || !stereoFrame->validateTransformations()) { std::cerr << "Invalid transformations!" << std::endl; + throw std::runtime_error("Invalid transformations!"); continue; } // Get frames cv::Mat clr = colorFrame->getCvFrame(); - cv::Mat depth = processDepthFrame(stereoFrame->getCvFrame()); + cv::Mat depth = processDepthFrame(*stereoFrame); // Create and remap rectangle dai::RotatedRect rect(dai::Point2f(300, 200), dai::Size2f(200, 100), 10); auto remappedRect = colorFrame->transformation.remapRectTo(stereoFrame->transformation, rect); - // Print rectangle information - std::cout << "Original rect x: " << rect.center.x << " y: " << rect.center.y << " width: " << rect.size.width << " height: " << rect.size.height - << " angle: " << rect.angle << std::endl; - std::cout << "Remapped rect x: " << remappedRect.center.x << " y: " << remappedRect.center.y << " width: " << remappedRect.size.width - << " height: " << remappedRect.size.height << " angle: " << remappedRect.angle << std::endl; + const auto now = std::chrono::steady_clock::now(); + if(now - lastPrintTime >= std::chrono::seconds(1)) { + // Print rectangle information at most once per second. + std::cout << "Original rect x: " << rect.center.x << " y: " << rect.center.y << " width: " << rect.size.width << " height: " << rect.size.height + << " angle: " << rect.angle << std::endl; + std::cout << "Remapped rect x: " << remappedRect.center.x << " y: " << remappedRect.center.y << " width: " << remappedRect.size.width + << " height: " << remappedRect.size.height << " angle: " << remappedRect.angle << std::endl; + lastPrintTime = now; + } // Draw rectangles drawRotatedRectangle(clr, cv::Point2f(rect.center.x, rect.center.y), cv::Size2f(rect.size.width, rect.size.height), rect.angle, cv::Scalar(255, 0, 0)); @@ -160,4 +131,4 @@ int main() { pipeline.wait(); return 0; -} \ No newline at end of file +} diff --git a/examples/cpp/Sync/sync.cpp b/examples/cpp/Sync/sync.cpp index ed0bc4e4ef..89cc4cd692 100644 --- a/examples/cpp/Sync/sync.cpp +++ b/examples/cpp/Sync/sync.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -37,6 +38,7 @@ int main() { pipeline.start(); + auto lastPrintTime = std::chrono::steady_clock::now() - std::chrono::seconds(1); while(pipeline.isRunning() && !quitEvent) { auto messageGroup = outQueue->get(); if(messageGroup == nullptr) continue; @@ -44,9 +46,13 @@ int main() { auto leftMsg = messageGroup->get("left"); auto rightMsg = messageGroup->get("right"); - std::cout << "Timestamps, message group " << messageGroup->getTimestamp().time_since_epoch().count() << std::endl; - std::cout << "left " << leftMsg->getTimestamp().time_since_epoch().count() << std::endl; - std::cout << "right " << rightMsg->getTimestamp().time_since_epoch().count() << std::endl; + const auto now = std::chrono::steady_clock::now(); + if(now - lastPrintTime >= std::chrono::seconds(1)) { + std::cout << "Timestamps, message group " << messageGroup->getTimestamp().time_since_epoch().count() << std::endl; + std::cout << "left " << leftMsg->getTimestamp().time_since_epoch().count() << std::endl; + std::cout << "right " << rightMsg->getTimestamp().time_since_epoch().count() << std::endl; + lastPrintTime = now; + } if(cv::waitKey(1) == 'q') { break; @@ -57,4 +63,4 @@ int main() { pipeline.wait(); return 0; -} \ No newline at end of file +} diff --git a/examples/cpp/ToF/tof_align.cpp b/examples/cpp/ToF/tof_align.cpp index c8fa91ee37..291f594f94 100644 --- a/examples/cpp/ToF/tof_align.cpp +++ b/examples/cpp/ToF/tof_align.cpp @@ -1,200 +1,99 @@ +#include #include -#include -#include #include #include #include -#include #include "depthai/depthai.hpp" -// Constants from the Python script constexpr float FPS = 30.0f; -const dai::CameraBoardSocket RGB_SOCKET = dai::CameraBoardSocket::CAM_C; -const dai::CameraBoardSocket TOF_SOCKET = dai::CameraBoardSocket::CAM_A; -const cv::Size SIZE(640, 400); - -// FPSCounter class, similar to the one in the Python script -class FPSCounter { - public: - void tick() { - auto now = std::chrono::steady_clock::now(); - frameTimes.push_back(now); - // Keep the last 100 timestamps, similar to the Python example - while(frameTimes.size() > 100) { - frameTimes.pop_front(); - } - } - - double getFps() { - if(frameTimes.size() <= 1) { - return 0.0; - } - auto duration = std::chrono::duration_cast>(frameTimes.back() - frameTimes.front()).count(); - return (static_cast(frameTimes.size()) - 1.0) / duration; - } - - private: - std::deque frameTimes; -}; - -cv::Mat colorizeDepth(const cv::Mat& frameDepth) { - // ----------------------------------------------------------------------- - // 1. Basic checks & convert to CV_32F - // ----------------------------------------------------------------------- - if(frameDepth.empty() || frameDepth.channels() != 1) return cv::Mat::zeros(frameDepth.size(), CV_8UC3); - - cv::Mat depth32f; - frameDepth.convertTo(depth32f, CV_32F); // safe for any input type - - // ----------------------------------------------------------------------- - // 2. Build mask of valid (non-zero) pixels - // ----------------------------------------------------------------------- - const cv::Mat nonZeroMask = depth32f != 0.0f; - const int nz = cv::countNonZero(nonZeroMask); - if(nz == 0) return cv::Mat::zeros(frameDepth.size(), CV_8UC3); - - // ----------------------------------------------------------------------- - // 3. 3 % / 95 % percentiles (identical to Python version) - // ----------------------------------------------------------------------- - std::vector values; - values.reserve(nz); - for(int r = 0; r < depth32f.rows; ++r) { - const float* d = depth32f.ptr(r); - const uchar* m = nonZeroMask.ptr(r); - for(int c = 0; c < depth32f.cols; ++c) - if(m[c]) values.push_back(d[c]); - } +const cv::Size CAMERA_SIZE(640, 400); - std::sort(values.begin(), values.end()); - auto pct = [&](double p) { - size_t idx = static_cast(std::round((p / 100.0) * (values.size() - 1))); - return values[idx]; - }; - - const float minDepth = pct(3.0); - const float maxDepth = pct(95.0); - - // ----------------------------------------------------------------------- - // 4. Logarithm (zeros replaced by minDepth to avoid -inf) - // ----------------------------------------------------------------------- - cv::Mat logDepth; - depth32f.copyTo(logDepth); - logDepth.setTo(minDepth, ~nonZeroMask); // overwrite zeros - cv::log(logDepth, logDepth); - - const float logMinDepth = std::log(minDepth); - const float logMaxDepth = std::log(maxDepth); - - // ----------------------------------------------------------------------- - // 5. Clip & linearly scale to [0,255] (same as np.interp) - // ----------------------------------------------------------------------- - cv::min(logDepth, logMaxDepth, logDepth); - cv::max(logDepth, logMinDepth, logDepth); - logDepth = (logDepth - logMinDepth) * (255.0f / (logMaxDepth - logMinDepth)); - - cv::Mat depth8U; - logDepth.convertTo(depth8U, CV_8U); - - // ----------------------------------------------------------------------- - // 6. Colour map + set invalid pixels to black - // ----------------------------------------------------------------------- - cv::Mat depthFrameColor; - cv::applyColorMap(depth8U, depthFrameColor, cv::COLORMAP_JET); - depthFrameColor.setTo(cv::Scalar::all(0), ~nonZeroMask); - - return depthFrameColor; -} - -// Global variables for blending weights -float rgbWeight = 0.4f; -float depthWeight = 0.6f; +constexpr float MIN_DEPTH = 100.0f; +constexpr float MAX_DEPTH = 7000.0f; +float rgbWeight = 0.5f; +float depthWeight = 0.5f; -// Callback function for the trackbar void updateBlendWeights(int percentRgb, void*) { rgbWeight = static_cast(percentRgb) / 100.0f; depthWeight = 1.0f - rgbWeight; } -int main() { +int main(int argc, char** argv) { + argparse::ArgumentParser program("tof_align"); + program.add_description("Align ToF depth over left or right camera and show a blended overlay."); + program.add_argument("--camera") + .default_value(std::string("left")) + .choices("left", "right") + .help("Camera to align depth onto: left=CAM_B, right=CAM_C (default: left)"); + + try { + program.parse_args(argc, argv); + } catch(const std::runtime_error& err) { + std::cerr << err.what() << '\n'; + std::cerr << program; + return EXIT_FAILURE; + } + + const std::string cameraArg = program.get("--camera"); + const dai::CameraBoardSocket alignSocket = (cameraArg == "right") ? dai::CameraBoardSocket::CAM_C : dai::CameraBoardSocket::CAM_B; + std::cout << "Aligning ToF depth over " << cameraArg << " camera\n"; + dai::Pipeline pipeline; - // Define sources and outputs - auto camRgb = pipeline.create(); auto tof = pipeline.create(); - auto sync = pipeline.create(); + tof->build(dai::CameraBoardSocket::AUTO, dai::ToFConfig::Profile::MID_RANGE, FPS); + + auto cam = pipeline.create()->build(alignSocket); + auto camOut = cam->requestOutput(std::make_pair(CAMERA_SIZE.width, CAMERA_SIZE.height), std::nullopt, dai::ImgResizeMode::CROP, FPS, true); + auto align = pipeline.create(); align->setRunOnHost(true); + tof->depth.link(align->input); + camOut->link(align->inputAlignTo); - camRgb->build(RGB_SOCKET); - const auto profile = dai::ToFConfig::Profile::MID_RANGE; - tof->build(TOF_SOCKET, profile, FPS); - - // Set sync threshold - sync->setSyncThreshold(std::chrono::milliseconds(static_cast(500 / FPS))); + auto sync = pipeline.create(); + sync->setSyncThreshold(std::chrono::duration_cast(std::chrono::duration(0.5 / FPS))); sync->setRunOnHost(true); - - // Linking - auto cameraOutput = camRgb->requestOutput(std::make_pair(SIZE.width, SIZE.height), std::nullopt, dai::ImgResizeMode::CROP, FPS, true); - - cameraOutput->link(sync->inputs["rgb"]); - tof->depth.link(align->input); + camOut->link(sync->inputs["rgb"]); align->outputAligned.link(sync->inputs["depth_aligned"]); sync->inputs["rgb"].setBlocking(false); - cameraOutput->link(align->inputAlignTo); - auto syncQueue = sync->out.createOutputQueue(); - auto confFilter = pipeline.create(); - tof->depth.link(confFilter->depth); - tof->amplitude.link(confFilter->amplitude); - confFilter->setRunOnHost(true); + auto syncQueue = sync->out.createOutputQueue(); - auto filteredDepthQ = confFilter->filteredDepth.createOutputQueue(); + const std::string windowBlend = "tof-overlay-" + cameraArg; + const std::string windowDepth = "depth-aligned"; - // Start the pipeline pipeline.start(); + cv::namedWindow(windowBlend); + cv::namedWindow(windowDepth); + cv::createTrackbar("RGB Weight %", windowBlend, nullptr, 100, updateBlendWeights); + cv::setTrackbarPos("RGB Weight %", windowBlend, static_cast(rgbWeight * 100)); - // Configure windows and trackbar - const std::string rgbDepthWindowName = "rgb-depth"; - cv::namedWindow(rgbDepthWindowName); - cv::createTrackbar("RGB Weight %", rgbDepthWindowName, nullptr, 100, updateBlendWeights); - cv::setTrackbarPos("RGB Weight %", rgbDepthWindowName, static_cast(rgbWeight * 100)); - - FPSCounter fpsCounter; - - while(true) { + while(pipeline.isRunning()) { auto messageGroup = syncQueue->get(); if(messageGroup == nullptr) continue; - fpsCounter.tick(); - auto frameRgb = messageGroup->get("rgb"); auto frameDepth = messageGroup->get("depth_aligned"); - auto filteredDepthMsg = filteredDepthQ->get(); - if(filteredDepthMsg) { - cv::Mat filteredDepthMat = filteredDepthMsg->getCvFrame(); - // Display filtered depth map - cv::imshow("Filtered Depth", colorizeDepth(filteredDepthMat)); + cv::Mat cvFrame = frameRgb->getCvFrame(); + if(cvFrame.channels() == 1) { + cv::cvtColor(cvFrame, cvFrame, cv::COLOR_GRAY2BGR); } - if(frameRgb && frameDepth) { - cv::Mat cvFrame = frameRgb->getCvFrame(); - cv::Mat alignedDepthColorized = colorizeDepth(frameDepth->getFrame()); + cv::Mat depthColorized = dai::utility::colorizeDepthFrame(*frameDepth, MIN_DEPTH, MAX_DEPTH, cv::COLORMAP_JET, true).getCvFrame(); + if(depthColorized.size() != cvFrame.size()) { + cv::resize(depthColorized, depthColorized, cvFrame.size()); + } - // Add FPS text to the depth frame - std::string fpsText = "FPS: " + std::to_string(fpsCounter.getFps()); - cv::putText(alignedDepthColorized, fpsText, cv::Point(10, 30), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255, 255, 255), 2); - cv::imshow("depth", alignedDepthColorized); + cv::imshow(windowDepth, depthColorized); - // Blend the RGB and depth frames - cv::Mat blended; - cv::addWeighted(cvFrame, rgbWeight, alignedDepthColorized, depthWeight, 0, blended); - cv::imshow(rgbDepthWindowName, blended); - } + cv::Mat blended; + cv::addWeighted(cvFrame, rgbWeight, depthColorized, depthWeight, 0, blended); + cv::imshow(windowBlend, blended); - int key = cv::waitKey(1); - if(key == 'q' || key == 27) { // 'q' or ESC + if(cv::waitKey(1) == 'q') { break; } } diff --git a/examples/cpp/ToF/tof_all_queues.cpp b/examples/cpp/ToF/tof_all_queues.cpp index 5db5906212..8bd5d92866 100644 --- a/examples/cpp/ToF/tof_all_queues.cpp +++ b/examples/cpp/ToF/tof_all_queues.cpp @@ -1,46 +1,11 @@ -#include +#include #include #include #include #include "depthai/depthai.hpp" -cv::Mat colorizeDepth(const cv::Mat& frame, float minDepth, float maxDepth) { - cv::Mat depth32f; - frame.convertTo(depth32f, CV_32F); - - cv::Mat invalidMask = depth32f == 0.0f; - - try { - cv::Mat logDepth = depth32f + 1e-6f; - cv::log(logDepth, logDepth); - logDepth.setTo(0.0f, invalidMask); - - const float logMinDepth = std::log(minDepth + 1e-6f); - const float logMaxDepth = std::log(maxDepth + 1e-6f); - - cv::min(logDepth, logMaxDepth, logDepth); - cv::max(logDepth, logMinDepth, logDepth); - - cv::Mat validMask = invalidMask == 0; - double validMin = 0.0; - double validMax = 0.0; - cv::minMaxLoc(logDepth, &validMin, &validMax, nullptr, nullptr, validMask); - - if(validMax <= validMin) { - return cv::Mat::zeros(frame.size(), CV_8UC3); - } - - cv::Mat colored; - logDepth.convertTo(colored, CV_8U, 255.0 / (validMax - validMin), -validMin * 255.0 / (validMax - validMin)); - cv::applyColorMap(colored, colored, cv::COLORMAP_JET); - colored.setTo(cv::Scalar::all(0), invalidMask); - return colored; - } catch(const cv::Exception&) { - return cv::Mat::zeros(frame.size(), CV_8UC3); - } -} - +constexpr float FPS = 30.0f; cv::Mat normalizeFrame(const cv::Mat& frame) { cv::Mat normalized; cv::normalize(frame, normalized, 0, 255, cv::NORM_MINMAX, CV_8U); @@ -50,22 +15,27 @@ cv::Mat normalizeFrame(const cv::Mat& frame) { int main() { dai::Pipeline pipeline; - // show depth in range 0.1m - 7m constexpr float minDepth = 100.0f; constexpr float maxDepth = 7000.0f; - // choose one of profiles LOW_RANGE / MID_RANGE / HIGH_RANGE auto profile = dai::ToFConfig::Profile::MID_RANGE; - auto tof = pipeline.create()->build(dai::CameraBoardSocket::AUTO, profile); + auto tof = pipeline.create()->build(dai::CameraBoardSocket::AUTO, profile, FPS); + + bool isRVC2 = pipeline.getDefaultDevice()->getPlatform() == dai::Platform::RVC2; std::map> outputQueues = { {"depth", tof->depth.createOutputQueue(1, false)}, {"amplitude", tof->amplitude.createOutputQueue(1, false)}, {"intensity", tof->intensity.createOutputQueue(1, false)}, - // {"rawDepth", tof->rawDepth.createOutputQueue(1, false)}, // not supported on RVC4 - // {"confidence", tof->confidence.createOutputQueue(1, false)}, // not supported on RVC2 }; + if(isRVC2) { + outputQueues["rawDepth"] = tof->rawDepth.createOutputQueue(1, false); + } else { + outputQueues["confidence"] = tof->confidence.createOutputQueue(1, false); + } + + std::cout << "Detected " << (isRVC2 ? "RVC2" : "RVC4") << std::endl; pipeline.start(); while(pipeline.isRunning()) { @@ -78,7 +48,7 @@ int main() { cv::Mat displayFrame; if(name == "depth" || name == "rawDepth") { - displayFrame = colorizeDepth(frame->getCvFrame(), minDepth, maxDepth); + displayFrame = dai::utility::colorizeDepthFrame(*frame, minDepth, maxDepth, cv::COLORMAP_JET, true).getCvFrame(); } else { displayFrame = normalizeFrame(frame->getCvFrame()); } diff --git a/examples/cpp/ToF/tof_minimal.cpp b/examples/cpp/ToF/tof_minimal.cpp index 70821ed63a..acd1f4f5d5 100644 --- a/examples/cpp/ToF/tof_minimal.cpp +++ b/examples/cpp/ToF/tof_minimal.cpp @@ -1,63 +1,25 @@ -#include #include #include "depthai/depthai.hpp" -cv::Mat colorizeDepth(const cv::Mat& frame, float minDepth, float maxDepth) { - cv::Mat depth32f; - frame.convertTo(depth32f, CV_32F); - - cv::Mat invalidMask = depth32f == 0.0f; - - try { - cv::Mat logDepth = depth32f + 1e-6f; - cv::log(logDepth, logDepth); - logDepth.setTo(0.0f, invalidMask); - - const float logMinDepth = std::log(minDepth + 1e-6f); - const float logMaxDepth = std::log(maxDepth + 1e-6f); - - cv::min(logDepth, logMaxDepth, logDepth); - cv::max(logDepth, logMinDepth, logDepth); - - cv::Mat validMask = invalidMask == 0; - double validMin = 0.0; - double validMax = 0.0; - cv::minMaxLoc(logDepth, &validMin, &validMax, nullptr, nullptr, validMask); - - if(validMax <= validMin) { - return cv::Mat::zeros(frame.size(), CV_8UC3); - } - - cv::Mat colored; - logDepth.convertTo(colored, CV_8U, 255.0 / (validMax - validMin), -validMin * 255.0 / (validMax - validMin)); - cv::applyColorMap(colored, colored, cv::COLORMAP_JET); - colored.setTo(cv::Scalar::all(0), invalidMask); - return colored; - } catch(const cv::Exception&) { - return cv::Mat::zeros(frame.size(), CV_8UC3); - } -} - +constexpr float FPS = 30.0f; int main() { auto device = std::make_shared(); dai::Pipeline pipeline(device); - // Show depth in range 0.1 m to 7 m. constexpr float minDepth = 100.0f; constexpr float maxDepth = 7000.0f; - // Choose one of the profiles: LOW_RANGE, MID_RANGE, or HIGH_RANGE. auto profile = dai::ToFConfig::Profile::MID_RANGE; - auto tof = pipeline.create()->build(dai::CameraBoardSocket::AUTO, profile); + auto tof = pipeline.create()->build(dai::CameraBoardSocket::AUTO, profile, FPS); auto depthOutputQueue = tof->depth.createOutputQueue(); pipeline.start(); while(pipeline.isRunning()) { auto depth = depthOutputQueue->get(); - cv::imshow("depth", colorizeDepth(depth->getCvFrame(), minDepth, maxDepth)); + cv::imshow("depth", dai::utility::colorizeDepthFrame(*depth, minDepth, maxDepth, cv::COLORMAP_JET, true).getCvFrame()); if(cv::waitKey(1) == 'q') { break; diff --git a/examples/cpp/Vpp/virtual_patern_projection.cpp b/examples/cpp/Vpp/virtual_patern_projection.cpp index 54af653996..fcc426b8c3 100644 --- a/examples/cpp/Vpp/virtual_patern_projection.cpp +++ b/examples/cpp/Vpp/virtual_patern_projection.cpp @@ -12,40 +12,6 @@ └───────┘ └───────────────┘ --right_low_res--> └──────────────┘ --confidence--> └─────┘ **/ -// Nicely visualize a depth map. -// The input depthFrameIn is assumed to be the raw disparity (CV_16UC1 or similar) -// received from the DepthAI pipeline. -void showDepth(const cv::Mat& depthFrameIn, - const std::string& windowName = "Depth", - int minDistance = 500, - int maxDistance = 5000, - int colorMap = cv::COLORMAP_TURBO, - bool useLog = false) { - cv::Mat depthFrame = depthFrameIn.clone(); - - cv::Mat floatFrame; - depthFrame.convertTo(floatFrame, CV_32FC1); - - // Optionally apply log scaling - if(useLog) { - cv::log(floatFrame + 1, floatFrame); - } - - cv::Mat upperClamped; - cv::min(floatFrame, maxDistance, upperClamped); - - cv::Mat clippedFrame; - cv::max(upperClamped, minDistance, clippedFrame); - - double alpha = 255.0 / maxDistance; - clippedFrame.convertTo(clippedFrame, CV_8U, alpha); - - cv::Mat depthColor; - cv::applyColorMap(clippedFrame, depthColor, colorMap); - - cv::imshow(windowName, depthColor); -} - int main() { int fps = 20; dai::Pipeline pipeline; @@ -108,7 +74,7 @@ int main() { cv::imshow("vppLeft", vppLeftFrame->getCvFrame()); cv::imshow("vppRight", vppRightFrame->getCvFrame()); - showDepth(depthFrame->getCvFrame()); + cv::imshow("Depth", dai::utility::colorizeDepthFrame(*depthFrame, 500.0f, 12000.0f, cv::COLORMAP_TURBO, true).getCvFrame()); if(cv::waitKey(1) == 'q') { break; diff --git a/examples/python/AutoCalibration/auto_calibration_example.py b/examples/python/AutoCalibration/auto_calibration_example.py index abbea6bf42..5e88812a4a 100644 --- a/examples/python/AutoCalibration/auto_calibration_example.py +++ b/examples/python/AutoCalibration/auto_calibration_example.py @@ -1,47 +1,6 @@ import cv2 as cv import numpy as np import depthai as dai -import warnings - - -def showDepth(depthFrame, windowName="Depth", minDistance=500, maxDistance=5000, - colormap=cv.COLORMAP_TURBO, useLog=False): - """ - Nicely visualize a depth map. - - Args: - depthFrame (np.ndarray): Depth frame (in millimeters). - windowName (str): OpenCV window name. - minDistance (int): Minimum depth to display (in mm). - maxDistance (int): Maximum depth to display (in mm). - colormap (int): OpenCV colormap (e.g., cv.COLORMAP_JET, COLORMAP_TURBO, etc.). - useLog (bool): Apply logarithmic scaling for better visual contrast. - """ - if maxDistance <= minDistance: - warnings.warn( - f"Invalid distance range: maxDistance ({maxDistance}) <= minDistance ({minDistance})", - stacklevel=2, - ) - return - - # Convert to float for processing - depthFrame = depthFrame.astype(np.float32) - - # Optionally apply log scaling - if useLog: - depthFrame = np.log(depthFrame + 1) - minDistance = np.log(minDistance + 1) - maxDistance = np.log(maxDistance + 1) - - # Clip and normalize to [0, 255] - depthFrame = np.clip(depthFrame, minDistance, maxDistance) - depthFrame = np.uint8((depthFrame - minDistance) * (255.0 / (maxDistance - minDistance))) - - # Apply color map - depthColor = cv.applyColorMap(depthFrame, colormap) - - # Show in a window - cv.imshow(windowName, depthColor) def rotationMatrixToEulerAngles(rotationMatrix, vector=False): @@ -131,7 +90,7 @@ def botchCalibration(device : dai.Device): camLeft = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_B) camRight = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_C) - stereo = pipeline.create(dai.node.StereoDepth) + depthNode = pipeline.create(dai.node.Depth) dcWorker = pipeline.create(dai.node.AutoCalibration).build(camLeft, camRight) dcWorker.initialConfig.maxIterations = 2 @@ -142,13 +101,7 @@ def botchCalibration(device : dai.Device): dcWorker.initialConfig.dataConfidenceThreshold = 0.3 workerOutputQueue = dcWorker.output.createOutputQueue() - videoQueueLeft = camLeft.requestOutput((1280, 800), fps=30) - videoQueueRight = camRight.requestOutput((1280, 800), fps=30) - - videoQueueLeft.link(stereo.left) - videoQueueRight.link(stereo.right) - - stereoOut = stereo.depth.createOutputQueue() + stereoOut = depthNode.depth.createOutputQueue() pipeline.start() while pipeline.isRunning(): @@ -162,14 +115,7 @@ def botchCalibration(device : dai.Device): print("Did not pass") depth = stereoOut.get() - showDepth( - depth.getCvFrame(), - windowName="Depth", - minDistance=500, - maxDistance=5000, - colormap=cv.COLORMAP_TURBO, - useLog=False - ) + cv.imshow("Depth", dai.utility.colorizeDepthFrame(depth, 300, 12000, cv.COLORMAP_TURBO, useLog=True).getCvFrame()) if cv.waitKey(1) == ord("q"): break diff --git a/examples/python/Beta/classification_parser.py b/examples/python/Beta/classification_parser.py new file mode 100644 index 0000000000..813e1caf55 --- /dev/null +++ b/examples/python/Beta/classification_parser.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 + +import cv2 +import depthai as dai + + +def main() -> None: + with dai.Pipeline() as pipeline: + modelSlug = "luxonis/emotion-recognition:260x260" + modelDescription = dai.NNModelDescription( + modelSlug, + platform=pipeline.getDefaultDevice().getPlatformAsString(), + ) + modelArchive = dai.NNArchive(dai.getModelFromZoo(modelDescription)) + + cameraNode = pipeline.create(dai.node.Camera).build() + neuralNetwork = pipeline.create(dai.node.NeuralNetwork).build(cameraNode, modelArchive) + parserNode = pipeline.create(dai.beta.node.ClassificationParser).build( + neuralNetwork.out, + modelArchive, + ) + + frameQueue = neuralNetwork.passthrough.createOutputQueue() + outputQueue = parserNode.out.createOutputQueue() + + pipeline.start() + + while pipeline.isRunning(): + frameMessage = frameQueue.get() + parserOutput = outputQueue.get() + frame = frameMessage.getCvFrame() + for index, (label, score) in enumerate( + zip(parserOutput.classes[:5], parserOutput.scores[:5]) + ): + cv2.putText( + frame, + f"{label}: {score:.2f}", + (20, 35 + index * 25), + cv2.FONT_HERSHEY_SIMPLEX, + 0.65, + (0, 255, 0), + 2, + ) + + cv2.imshow("ClassificationParser", frame) + if cv2.waitKey(1) == ord("q"): + break + + +if __name__ == "__main__": + main() diff --git a/examples/python/Beta/classification_sequence_parser.py b/examples/python/Beta/classification_sequence_parser.py new file mode 100644 index 0000000000..fe8807d8d0 --- /dev/null +++ b/examples/python/Beta/classification_sequence_parser.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 + +import cv2 +import depthai as dai + + +def main() -> None: + with dai.Pipeline() as pipeline: + modelSlug = "luxonis/paddle-text-recognition:320x48" + modelDescription = dai.NNModelDescription( + modelSlug, + platform=pipeline.getDefaultDevice().getPlatformAsString(), + ) + modelArchive = dai.NNArchive(dai.getModelFromZoo(modelDescription)) + + cameraNode = pipeline.create(dai.node.Camera).build() + neuralNetwork = pipeline.create(dai.node.NeuralNetwork).build(cameraNode, modelArchive) + parserNode = pipeline.create(dai.beta.node.ClassificationSequenceParser).build( + neuralNetwork.out, + modelArchive, + ) + + frameQueue = neuralNetwork.passthrough.createOutputQueue() + outputQueue = parserNode.out.createOutputQueue() + configQueue = parserNode.inputConfig.createInputQueue() + config = parserNode.initialConfig + + pipeline.start() + print("Controls: 't' toggle remove duplicates, 'q' quit.") + + while pipeline.isRunning(): + frameMessage = frameQueue.get() + parserOutput = outputQueue.get() + frame = frameMessage.getCvFrame() + separator = "" if all(len(label) <= 1 for label in parserOutput.classes) else " " + decodedText = separator.join(parserOutput.classes) + cv2.putText( + frame, + decodedText, + (20, 35), + cv2.FONT_HERSHEY_SIMPLEX, + 0.65, + (0, 255, 0), + 2, + ) + + + cv2.imshow("ClassificationSequenceParser", frame) + key = cv2.waitKey(1) + if key == ord("q"): + break + if key == ord("t"): + config.removeDuplicates = not config.removeDuplicates + configQueue.send(config) + print(f"Remove duplicates: {config.removeDuplicates}") + + +if __name__ == "__main__": + main() diff --git a/examples/python/Beta/embeddings_parser.py b/examples/python/Beta/embeddings_parser.py new file mode 100644 index 0000000000..90f95cd438 --- /dev/null +++ b/examples/python/Beta/embeddings_parser.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 + +import cv2 +import depthai as dai +import numpy as np + + +def main() -> None: + with dai.Pipeline() as pipeline: + modelSlug = "luxonis/arcface:lfw-112x112" + modelDescription = dai.NNModelDescription( + modelSlug, + platform=pipeline.getDefaultDevice().getPlatformAsString(), + ) + modelArchive = dai.NNArchive(dai.getModelFromZoo(modelDescription)) + + cameraNode = pipeline.create(dai.node.Camera).build() + neuralNetwork = pipeline.create(dai.node.NeuralNetwork).build(cameraNode, modelArchive) + parserNode = pipeline.create(dai.beta.node.EmbeddingsParser).build( + neuralNetwork.out, + modelArchive, + ) + + frameQueue = neuralNetwork.passthrough.createOutputQueue() + outputQueue = parserNode.out.createOutputQueue() + + pipeline.start() + + while pipeline.isRunning(): + frameMessage = frameQueue.get() + parserOutput = outputQueue.get() + frame = frameMessage.getCvFrame() + layerName = parserOutput.getAllLayerNames()[0] + embedding = parserOutput.getTensor(layerName, True).reshape(-1) + cv2.putText( + frame, + f"Embedding size: {embedding.size}, norm: {np.linalg.norm(embedding):.2f}", + (20, 35), + cv2.FONT_HERSHEY_SIMPLEX, + 0.65, + (0, 255, 0), + 2, + ) + + cv2.imshow("EmbeddingsParser", frame) + if cv2.waitKey(1) == ord("q"): + break + + +if __name__ == "__main__": + main() diff --git a/examples/python/Beta/fastsam_parser.py b/examples/python/Beta/fastsam_parser.py new file mode 100644 index 0000000000..c4ca702f77 --- /dev/null +++ b/examples/python/Beta/fastsam_parser.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 + +import cv2 +import depthai as dai +import numpy as np + + +def main() -> None: + with dai.Pipeline() as pipeline: + modelSlug = "luxonis/fastsam-s:512x288" + modelDescription = dai.NNModelDescription( + modelSlug, + platform=pipeline.getDefaultDevice().getPlatformAsString(), + ) + modelArchive = dai.NNArchive(dai.getModelFromZoo(modelDescription)) + + cameraNode = pipeline.create(dai.node.Camera).build() + neuralNetwork = pipeline.create(dai.node.NeuralNetwork).build(cameraNode, modelArchive) + parserNode = pipeline.create(dai.beta.node.FastSAMParser).build( + neuralNetwork.out, + modelArchive, + ) + + frameQueue = neuralNetwork.passthrough.createOutputQueue() + outputQueue = parserNode.out.createOutputQueue() + configQueue = parserNode.inputConfig.createInputQueue() + config = parserNode.initialConfig + + pipeline.start() + print("Controls: '+' increase confidence threshold, '-' decrease it, 'q' quit.") + + while pipeline.isRunning(): + frameMessage = frameQueue.get() + parserOutput = outputQueue.get() + frame = frameMessage.getCvFrame() + mask = np.asarray(parserOutput.getCvMask(), dtype=np.uint8) + mask = cv2.resize(mask, (frame.shape[1], frame.shape[0]), interpolation=cv2.INTER_NEAREST) + coloredMask = cv2.applyColorMap(mask * 37, cv2.COLORMAP_TURBO) + coloredMask[mask == 255] = 0 + frame = cv2.addWeighted(frame, 0.6, coloredMask, 0.4, 0) + + + cv2.imshow("FastSAMParser", frame) + key = cv2.waitKey(1) + if key == ord("q"): + break + if key == ord("+"): + config.confidenceThreshold = min(1.0, config.confidenceThreshold + 0.1) + configQueue.send(config) + print(f"Confidence threshold: {config.confidenceThreshold:.1f}") + elif key == ord("-"): + config.confidenceThreshold = max(0.0, config.confidenceThreshold - 0.1) + configQueue.send(config) + print(f"Confidence threshold: {config.confidenceThreshold:.1f}") + + +if __name__ == "__main__": + main() diff --git a/examples/python/Beta/hrnet_parser.py b/examples/python/Beta/hrnet_parser.py new file mode 100644 index 0000000000..87bcf0d17b --- /dev/null +++ b/examples/python/Beta/hrnet_parser.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 + +import cv2 +import depthai as dai + + +def main() -> None: + with dai.Pipeline() as pipeline: + modelSlug = "luxonis/lite-hrnet:18-coco-288x384" + modelDescription = dai.NNModelDescription( + modelSlug, + platform=pipeline.getDefaultDevice().getPlatformAsString(), + ) + modelArchive = dai.NNArchive(dai.getModelFromZoo(modelDescription)) + + cameraNode = pipeline.create(dai.node.Camera).build() + neuralNetwork = pipeline.create(dai.node.NeuralNetwork).build(cameraNode, modelArchive) + parserNode = pipeline.create(dai.beta.node.HRNetParser).build( + neuralNetwork.out, + modelArchive, + ) + + frameQueue = neuralNetwork.passthrough.createOutputQueue() + outputQueue = parserNode.out.createOutputQueue() + configQueue = parserNode.inputConfig.createInputQueue() + config = parserNode.initialConfig + + pipeline.start() + print("Controls: '+' increase score threshold, '-' decrease it, 'q' quit.") + + while pipeline.isRunning(): + frameMessage = frameQueue.get() + parserOutput = outputQueue.get() + frame = frameMessage.getCvFrame() + points = [ + (int(point.x * frame.shape[1]), int(point.y * frame.shape[0])) + for point in parserOutput.getPoints2f() + ] + for edge in parserOutput.getEdges(): + cv2.line(frame, points[edge[0]], points[edge[1]], (0, 255, 0), 2) + for point in points: + cv2.circle(frame, point, 3, (0, 0, 255), -1) + + + cv2.imshow("HRNetParser", frame) + key = cv2.waitKey(1) + if key == ord("q"): + break + if key == ord("+"): + config.scoreThreshold = min(1.0, config.scoreThreshold + 0.1) + configQueue.send(config) + print(f"Score threshold: {config.scoreThreshold:.1f}") + elif key == ord("-"): + config.scoreThreshold = max(0.0, config.scoreThreshold - 0.1) + configQueue.send(config) + print(f"Score threshold: {config.scoreThreshold:.1f}") + + +if __name__ == "__main__": + main() diff --git a/examples/python/Beta/image_output_parser.py b/examples/python/Beta/image_output_parser.py new file mode 100644 index 0000000000..f54e977ec2 --- /dev/null +++ b/examples/python/Beta/image_output_parser.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 + +import cv2 +import depthai as dai + + +def main() -> None: + with dai.Pipeline() as pipeline: + modelSlug = "luxonis/dncnn3:320x240" + modelDescription = dai.NNModelDescription( + modelSlug, + platform=pipeline.getDefaultDevice().getPlatformAsString(), + ) + modelArchive = dai.NNArchive(dai.getModelFromZoo(modelDescription)) + + cameraNode = pipeline.create(dai.node.Camera).build() + neuralNetwork = pipeline.create(dai.node.NeuralNetwork).build(cameraNode, modelArchive) + parserNode = pipeline.create(dai.beta.node.ImageOutputParser).build( + neuralNetwork.out, + modelArchive, + ) + + frameQueue = neuralNetwork.passthrough.createOutputQueue() + outputQueue = parserNode.out.createOutputQueue() + + pipeline.start() + + while pipeline.isRunning(): + frameMessage = frameQueue.get() + parserOutput = outputQueue.get() + frame = frameMessage.getCvFrame() + frame = parserOutput.getCvFrame() + + cv2.imshow("ImageOutputParser", frame) + if cv2.waitKey(1) == ord("q"): + break + + +if __name__ == "__main__": + main() diff --git a/examples/python/Beta/img_detections_filter.py b/examples/python/Beta/img_detections_filter.py new file mode 100644 index 0000000000..e9d75ff5b2 --- /dev/null +++ b/examples/python/Beta/img_detections_filter.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 + +import time + +import depthai as dai + + +def make_detection(label: int, confidence: float) -> dai.ImgDetection: + detection = dai.ImgDetection() + detection.label = label + detection.confidence = confidence + return detection + + +def print_detections(detections: dai.ImgDetections) -> None: + for detection in detections.detections: + print(f" class={detection.label}, confidence={detection.confidence:.2f}") + + +def main() -> None: + device = dai.Device() + with dai.Pipeline(device) as pipeline: + detections_filter = pipeline.create(dai.beta.node.ImgDetectionsFilter) + input_queue = detections_filter.input.createInputQueue() + config_queue = detections_filter.inputConfig.createInputQueue() + output_queue = detections_filter.output.createOutputQueue() + + pipeline.start() + + config = dai.beta.ImgDetectionsFilterConfig() + config.confidenceThreshold = 0.7 + config_queue.send(config) + + sequence_num = 0 + + try: + while pipeline.isRunning(): + detections = dai.ImgDetections() + detections.setSequenceNum(sequence_num) + detections.detections = [ + make_detection(label=0, confidence=0.95), + make_detection(label=1, confidence=0.60), + ] + + input_queue.send(detections) + forwarded = output_queue.get(timeout=1.0) + + if forwarded is None: + raise RuntimeError("Timed out waiting for ImgDetectionsFilter output") + + print(f"Sequence number: {forwarded.getSequenceNum()}") + print_detections(forwarded) + print() + + sequence_num += 1 + time.sleep(0.1) + except KeyboardInterrupt: + print("\nStopped.") + + +if __name__ == "__main__": + main() diff --git a/examples/python/Beta/keypoint_parser.py b/examples/python/Beta/keypoint_parser.py new file mode 100644 index 0000000000..b5096f1a71 --- /dev/null +++ b/examples/python/Beta/keypoint_parser.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 + +import cv2 +import depthai as dai + + +def main() -> None: + with dai.Pipeline() as pipeline: + modelSlug = "luxonis/mediapipe-face-landmarker:192x192" + modelDescription = dai.NNModelDescription( + modelSlug, + platform=pipeline.getDefaultDevice().getPlatformAsString(), + ) + modelArchive = dai.NNArchive(dai.getModelFromZoo(modelDescription)) + + cameraNode = pipeline.create(dai.node.Camera).build() + neuralNetwork = pipeline.create(dai.node.NeuralNetwork).build(cameraNode, modelArchive) + parserNode = pipeline.create(dai.beta.node.KeypointParser).build( + neuralNetwork.out, + modelArchive, + ) + + frameQueue = neuralNetwork.passthrough.createOutputQueue() + outputQueue = parserNode.out.createOutputQueue() + + pipeline.start() + + while pipeline.isRunning(): + frameMessage = frameQueue.get() + parserOutput = outputQueue.get() + frame = frameMessage.getCvFrame() + points = [ + (int(point.x * frame.shape[1]), int(point.y * frame.shape[0])) + for point in parserOutput.getPoints2f() + ] + for edge in parserOutput.getEdges(): + cv2.line(frame, points[edge[0]], points[edge[1]], (0, 255, 0), 2) + for point in points: + cv2.circle(frame, point, 3, (0, 0, 255), -1) + + cv2.imshow("KeypointParser", frame) + if cv2.waitKey(1) == ord("q"): + break + + +if __name__ == "__main__": + main() diff --git a/examples/python/Beta/lane_detection_parser.py b/examples/python/Beta/lane_detection_parser.py new file mode 100644 index 0000000000..8cc456dfed --- /dev/null +++ b/examples/python/Beta/lane_detection_parser.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +import cv2 +import depthai as dai +import numpy as np + + +def main() -> None: + with dai.Pipeline() as pipeline: + modelSlug = "luxonis/ultra-fast-lane-detection:culane-800x288" + modelDescription = dai.NNModelDescription( + modelSlug, + platform=pipeline.getDefaultDevice().getPlatformAsString(), + ) + modelArchive = dai.NNArchive(dai.getModelFromZoo(modelDescription)) + + cameraNode = pipeline.create(dai.node.Camera).build() + neuralNetwork = pipeline.create(dai.node.NeuralNetwork).build(cameraNode, modelArchive) + parserNode = pipeline.create(dai.beta.node.LaneDetectionParser).build( + neuralNetwork.out, + modelArchive, + ) + + frameQueue = neuralNetwork.passthrough.createOutputQueue() + outputQueue = parserNode.out.createOutputQueue() + + pipeline.start() + + while pipeline.isRunning(): + frameMessage = frameQueue.get() + parserOutput = outputQueue.get() + frame = frameMessage.getCvFrame() + for cluster in parserOutput.clusters: + points = np.array( + [ + (point.x * frame.shape[1], point.y * frame.shape[0]) + for point in cluster.points + ], + dtype=np.int32, + ) + if len(points) > 1: + cv2.polylines(frame, [points], False, (0, 255, 0), 3) + + cv2.imshow("LaneDetectionParser", frame) + if cv2.waitKey(1) == ord("q"): + break + + +if __name__ == "__main__": + main() diff --git a/examples/python/Beta/map_output_parser.py b/examples/python/Beta/map_output_parser.py new file mode 100644 index 0000000000..74e0c37e16 --- /dev/null +++ b/examples/python/Beta/map_output_parser.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +import cv2 +import depthai as dai +import numpy as np + + +def main() -> None: + with dai.Pipeline() as pipeline: + modelSlug = "luxonis/dm-count:sha-426x240" + modelDescription = dai.NNModelDescription( + modelSlug, + platform=pipeline.getDefaultDevice().getPlatformAsString(), + ) + modelArchive = dai.NNArchive(dai.getModelFromZoo(modelDescription)) + + cameraNode = pipeline.create(dai.node.Camera).build() + neuralNetwork = pipeline.create(dai.node.NeuralNetwork).build(cameraNode, modelArchive) + parserNode = pipeline.create(dai.beta.node.MapOutputParser).build( + neuralNetwork.out, + modelArchive, + ) + + frameQueue = neuralNetwork.passthrough.createOutputQueue() + outputQueue = parserNode.out.createOutputQueue() + configQueue = parserNode.inputConfig.createInputQueue() + config = parserNode.initialConfig + + pipeline.start() + print("Controls: 't' toggle min/max scaling, 'q' quit.") + + while pipeline.isRunning(): + frameMessage = frameQueue.get() + parserOutput = outputQueue.get() + frame = frameMessage.getCvFrame() + mapValues = np.asarray(parserOutput.getMap(), dtype=np.float32) + normalizedMap = cv2.normalize(mapValues, None, 0, 255, cv2.NORM_MINMAX) + frame = cv2.applyColorMap(normalizedMap.astype(np.uint8), cv2.COLORMAP_INFERNO) + frame = cv2.resize(frame, (frameMessage.getWidth(), frameMessage.getHeight())) + + + cv2.imshow("MapOutputParser", frame) + key = cv2.waitKey(1) + if key == ord("q"): + break + if key == ord("t"): + config.minMaxScaling = not config.minMaxScaling + configQueue.send(config) + print(f"Min/max scaling: {config.minMaxScaling}") + + +if __name__ == "__main__": + main() diff --git a/examples/python/Beta/mlsd_parser.py b/examples/python/Beta/mlsd_parser.py new file mode 100644 index 0000000000..a5da82aecf --- /dev/null +++ b/examples/python/Beta/mlsd_parser.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 + +import cv2 +import depthai as dai + + +def main() -> None: + with dai.Pipeline() as pipeline: + modelSlug = "luxonis/m-lsd:512x512" + modelDescription = dai.NNModelDescription( + modelSlug, + platform=pipeline.getDefaultDevice().getPlatformAsString(), + ) + modelArchive = dai.NNArchive(dai.getModelFromZoo(modelDescription)) + + cameraNode = pipeline.create(dai.node.Camera).build() + neuralNetwork = pipeline.create(dai.node.NeuralNetwork).build(cameraNode, modelArchive) + parserNode = pipeline.create(dai.beta.node.MLSDParser).build( + neuralNetwork.out, + modelArchive, + ) + + frameQueue = neuralNetwork.passthrough.createOutputQueue() + outputQueue = parserNode.out.createOutputQueue() + configQueue = parserNode.inputConfig.createInputQueue() + config = parserNode.initialConfig + + pipeline.start() + print("Controls: '+' increase score threshold, '-' decrease it, 'q' quit.") + + while pipeline.isRunning(): + frameMessage = frameQueue.get() + parserOutput = outputQueue.get() + frame = frameMessage.getCvFrame() + for line in parserOutput.lines: + startPoint = ( + int(line.startPoint.x * frame.shape[1]), + int(line.startPoint.y * frame.shape[0]), + ) + endPoint = ( + int(line.endPoint.x * frame.shape[1]), + int(line.endPoint.y * frame.shape[0]), + ) + cv2.line(frame, startPoint, endPoint, (0, 255, 0), 2) + + + cv2.imshow("MLSDParser", frame) + key = cv2.waitKey(1) + if key == ord("q"): + break + if key == ord("+"): + config.scoreThreshold = min(1.0, config.scoreThreshold + 0.1) + configQueue.send(config) + print(f"Score threshold: {config.scoreThreshold:.1f}") + elif key == ord("-"): + config.scoreThreshold = max(0.0, config.scoreThreshold - 0.1) + configQueue.send(config) + print(f"Score threshold: {config.scoreThreshold:.1f}") + + +if __name__ == "__main__": + main() diff --git a/examples/python/Beta/mp_palm_detection_parser.py b/examples/python/Beta/mp_palm_detection_parser.py new file mode 100644 index 0000000000..8f537628ac --- /dev/null +++ b/examples/python/Beta/mp_palm_detection_parser.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 + +import cv2 +import depthai as dai +import numpy as np + + +def main() -> None: + with dai.Pipeline() as pipeline: + modelSlug = "luxonis/mediapipe-palm-detection:192x192" + modelDescription = dai.NNModelDescription( + modelSlug, + platform=pipeline.getDefaultDevice().getPlatformAsString(), + ) + modelArchive = dai.NNArchive(dai.getModelFromZoo(modelDescription)) + + cameraNode = pipeline.create(dai.node.Camera).build() + neuralNetwork = pipeline.create(dai.node.NeuralNetwork).build(cameraNode, modelArchive) + parserNode = pipeline.create(dai.beta.node.MPPalmDetectionParser).build( + neuralNetwork.out, + modelArchive, + ) + + frameQueue = neuralNetwork.passthrough.createOutputQueue() + outputQueue = parserNode.out.createOutputQueue() + configQueue = parserNode.inputConfig.createInputQueue() + config = parserNode.initialConfig + + pipeline.start() + print("Controls: '+' increase confidence threshold, '-' decrease it, 'q' quit.") + + while pipeline.isRunning(): + frameMessage = frameQueue.get() + parserOutput = outputQueue.get() + frame = frameMessage.getCvFrame() + for detection in parserOutput.detections: + boundingBox = detection.getBoundingBox().denormalize( + frame.shape[1], frame.shape[0] + ) + points = np.array( + [(point.x, point.y) for point in boundingBox.getPoints()], + dtype=np.int32, + ) + cv2.polylines(frame, [points], True, (0, 255, 0), 2) + label = detection.labelName or str(detection.label) + cv2.putText( + frame, + f"{label}: {detection.confidence:.2f}", + tuple(points[0]), + cv2.FONT_HERSHEY_SIMPLEX, + 0.5, + (0, 255, 0), + 1, + ) + for point in detection.getKeypoints2f(): + cv2.circle( + frame, + (int(point.x * frame.shape[1]), int(point.y * frame.shape[0])), + 3, + (0, 0, 255), + -1, + ) + + + cv2.imshow("MPPalmDetectionParser", frame) + key = cv2.waitKey(1) + if key == ord("q"): + break + if key == ord("+"): + config.confidenceThreshold = min(1.0, config.confidenceThreshold + 0.1) + configQueue.send(config) + print(f"Confidence threshold: {config.confidenceThreshold:.1f}") + elif key == ord("-"): + config.confidenceThreshold = max(0.0, config.confidenceThreshold - 0.1) + configQueue.send(config) + print(f"Confidence threshold: {config.confidenceThreshold:.1f}") + + +if __name__ == "__main__": + main() diff --git a/examples/python/Beta/pp_text_detection_parser.py b/examples/python/Beta/pp_text_detection_parser.py new file mode 100644 index 0000000000..ae8acc5190 --- /dev/null +++ b/examples/python/Beta/pp_text_detection_parser.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 + +import cv2 +import depthai as dai +import numpy as np + + +def main() -> None: + with dai.Pipeline() as pipeline: + modelSlug = "luxonis/paddle-text-detection:256x256" + modelDescription = dai.NNModelDescription( + modelSlug, + platform=pipeline.getDefaultDevice().getPlatformAsString(), + ) + modelArchive = dai.NNArchive(dai.getModelFromZoo(modelDescription)) + + cameraNode = pipeline.create(dai.node.Camera).build() + neuralNetwork = pipeline.create(dai.node.NeuralNetwork).build(cameraNode, modelArchive) + parserNode = pipeline.create(dai.beta.node.PPTextDetectionParser).build( + neuralNetwork.out, + modelArchive, + ) + + frameQueue = neuralNetwork.passthrough.createOutputQueue() + outputQueue = parserNode.out.createOutputQueue() + configQueue = parserNode.inputConfig.createInputQueue() + config = parserNode.initialConfig + + pipeline.start() + print("Controls: '+' increase confidence threshold, '-' decrease it, 'q' quit.") + + while pipeline.isRunning(): + frameMessage = frameQueue.get() + parserOutput = outputQueue.get() + frame = frameMessage.getCvFrame() + for detection in parserOutput.detections: + boundingBox = detection.getBoundingBox().denormalize( + frame.shape[1], frame.shape[0] + ) + points = np.array( + [(point.x, point.y) for point in boundingBox.getPoints()], + dtype=np.int32, + ) + cv2.polylines(frame, [points], True, (0, 255, 0), 2) + label = detection.labelName or str(detection.label) + cv2.putText( + frame, + f"{label}: {detection.confidence:.2f}", + tuple(points[0]), + cv2.FONT_HERSHEY_SIMPLEX, + 0.5, + (0, 255, 0), + 1, + ) + for point in detection.getKeypoints2f(): + cv2.circle( + frame, + (int(point.x * frame.shape[1]), int(point.y * frame.shape[0])), + 3, + (0, 0, 255), + -1, + ) + + + cv2.imshow("PPTextDetectionParser", frame) + key = cv2.waitKey(1) + if key == ord("q"): + break + if key == ord("+"): + config.confidenceThreshold = min(1.0, config.confidenceThreshold + 0.1) + configQueue.send(config) + print(f"Confidence threshold: {config.confidenceThreshold:.1f}") + elif key == ord("-"): + config.confidenceThreshold = max(0.0, config.confidenceThreshold - 0.1) + configQueue.send(config) + print(f"Confidence threshold: {config.confidenceThreshold:.1f}") + + +if __name__ == "__main__": + main() diff --git a/examples/python/Beta/scrfd_parser.py b/examples/python/Beta/scrfd_parser.py new file mode 100644 index 0000000000..b7fdd1e93e --- /dev/null +++ b/examples/python/Beta/scrfd_parser.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 + +import cv2 +import depthai as dai +import numpy as np + + +def main() -> None: + with dai.Pipeline() as pipeline: + modelSlug = "luxonis/scrfd-face-detection:10g-640x640" + modelDescription = dai.NNModelDescription( + modelSlug, + platform=pipeline.getDefaultDevice().getPlatformAsString(), + ) + modelArchive = dai.NNArchive(dai.getModelFromZoo(modelDescription)) + + cameraNode = pipeline.create(dai.node.Camera).build() + neuralNetwork = pipeline.create(dai.node.NeuralNetwork).build(cameraNode, modelArchive) + parserNode = pipeline.create(dai.beta.node.SCRFDParser).build( + neuralNetwork.out, + modelArchive, + ) + + frameQueue = neuralNetwork.passthrough.createOutputQueue() + outputQueue = parserNode.out.createOutputQueue() + configQueue = parserNode.inputConfig.createInputQueue() + config = parserNode.initialConfig + + pipeline.start() + print("Controls: '+' increase confidence threshold, '-' decrease it, 'q' quit.") + + while pipeline.isRunning(): + frameMessage = frameQueue.get() + parserOutput = outputQueue.get() + frame = frameMessage.getCvFrame() + for detection in parserOutput.detections: + boundingBox = detection.getBoundingBox().denormalize( + frame.shape[1], frame.shape[0] + ) + points = np.array( + [(point.x, point.y) for point in boundingBox.getPoints()], + dtype=np.int32, + ) + cv2.polylines(frame, [points], True, (0, 255, 0), 2) + label = detection.labelName or str(detection.label) + cv2.putText( + frame, + f"{label}: {detection.confidence:.2f}", + tuple(points[0]), + cv2.FONT_HERSHEY_SIMPLEX, + 0.5, + (0, 255, 0), + 1, + ) + for point in detection.getKeypoints2f(): + cv2.circle( + frame, + (int(point.x * frame.shape[1]), int(point.y * frame.shape[0])), + 3, + (0, 0, 255), + -1, + ) + + + cv2.imshow("SCRFDParser", frame) + key = cv2.waitKey(1) + if key == ord("q"): + break + if key == ord("+"): + config.confidenceThreshold = min(1.0, config.confidenceThreshold + 0.1) + configQueue.send(config) + print(f"Confidence threshold: {config.confidenceThreshold:.1f}") + elif key == ord("-"): + config.confidenceThreshold = max(0.0, config.confidenceThreshold - 0.1) + configQueue.send(config) + print(f"Confidence threshold: {config.confidenceThreshold:.1f}") + + +if __name__ == "__main__": + main() diff --git a/examples/python/Beta/superanimal_parser.py b/examples/python/Beta/superanimal_parser.py new file mode 100644 index 0000000000..8c786132f9 --- /dev/null +++ b/examples/python/Beta/superanimal_parser.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 + +import cv2 +import depthai as dai + + +def main() -> None: + with dai.Pipeline() as pipeline: + modelSlug = "luxonis/superanimal-landmarker:256x256" + modelDescription = dai.NNModelDescription( + modelSlug, + platform=pipeline.getDefaultDevice().getPlatformAsString(), + ) + modelArchive = dai.NNArchive(dai.getModelFromZoo(modelDescription)) + + cameraNode = pipeline.create(dai.node.Camera).build() + neuralNetwork = pipeline.create(dai.node.NeuralNetwork).build(cameraNode, modelArchive) + parserNode = pipeline.create(dai.beta.node.SuperAnimalParser).build( + neuralNetwork.out, + modelArchive, + ) + + frameQueue = neuralNetwork.passthrough.createOutputQueue() + outputQueue = parserNode.out.createOutputQueue() + configQueue = parserNode.inputConfig.createInputQueue() + config = parserNode.initialConfig + + pipeline.start() + print("Controls: '+' increase score threshold, '-' decrease it, 'q' quit.") + + while pipeline.isRunning(): + frameMessage = frameQueue.get() + parserOutput = outputQueue.get() + frame = frameMessage.getCvFrame() + points = [ + (int(point.x * frame.shape[1]), int(point.y * frame.shape[0])) + for point in parserOutput.getPoints2f() + ] + for edge in parserOutput.getEdges(): + cv2.line(frame, points[edge[0]], points[edge[1]], (0, 255, 0), 2) + for point in points: + cv2.circle(frame, point, 3, (0, 0, 255), -1) + + + cv2.imshow("SuperAnimalParser", frame) + key = cv2.waitKey(1) + if key == ord("q"): + break + if key == ord("+"): + config.scoreThreshold = min(1.0, config.scoreThreshold + 0.1) + configQueue.send(config) + print(f"Score threshold: {config.scoreThreshold:.1f}") + elif key == ord("-"): + config.scoreThreshold = max(0.0, config.scoreThreshold - 0.1) + configQueue.send(config) + print(f"Score threshold: {config.scoreThreshold:.1f}") + + +if __name__ == "__main__": + main() diff --git a/examples/python/Beta/yunet_parser.py b/examples/python/Beta/yunet_parser.py new file mode 100644 index 0000000000..a879e89050 --- /dev/null +++ b/examples/python/Beta/yunet_parser.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 + +import cv2 +import depthai as dai +import numpy as np + + +def main() -> None: + with dai.Pipeline() as pipeline: + modelSlug = "luxonis/yunet:640x480" + modelDescription = dai.NNModelDescription( + modelSlug, + platform=pipeline.getDefaultDevice().getPlatformAsString(), + ) + modelArchive = dai.NNArchive(dai.getModelFromZoo(modelDescription)) + + cameraNode = pipeline.create(dai.node.Camera).build() + neuralNetwork = pipeline.create(dai.node.NeuralNetwork).build(cameraNode, modelArchive) + parserNode = pipeline.create(dai.beta.node.YuNetParser).build( + neuralNetwork.out, + modelArchive, + ) + + frameQueue = neuralNetwork.passthrough.createOutputQueue() + outputQueue = parserNode.out.createOutputQueue() + configQueue = parserNode.inputConfig.createInputQueue() + config = parserNode.initialConfig + + pipeline.start() + print("Controls: '+' increase confidence threshold, '-' decrease it, 'q' quit.") + + while pipeline.isRunning(): + frameMessage = frameQueue.get() + parserOutput = outputQueue.get() + frame = frameMessage.getCvFrame() + for detection in parserOutput.detections: + boundingBox = detection.getBoundingBox().denormalize( + frame.shape[1], frame.shape[0] + ) + points = np.array( + [(point.x, point.y) for point in boundingBox.getPoints()], + dtype=np.int32, + ) + cv2.polylines(frame, [points], True, (0, 255, 0), 2) + label = detection.labelName or str(detection.label) + cv2.putText( + frame, + f"{label}: {detection.confidence:.2f}", + tuple(points[0]), + cv2.FONT_HERSHEY_SIMPLEX, + 0.5, + (0, 255, 0), + 1, + ) + for point in detection.getKeypoints2f(): + cv2.circle( + frame, + (int(point.x * frame.shape[1]), int(point.y * frame.shape[0])), + 3, + (0, 0, 255), + -1, + ) + + + cv2.imshow("YuNetParser", frame) + key = cv2.waitKey(1) + if key == ord("q"): + break + if key == ord("+"): + config.confidenceThreshold = min(1.0, config.confidenceThreshold + 0.1) + configQueue.send(config) + print(f"Confidence threshold: {config.confidenceThreshold:.1f}") + elif key == ord("-"): + config.confidenceThreshold = max(0.0, config.confidenceThreshold - 0.1) + configQueue.send(config) + print(f"Confidence threshold: {config.confidenceThreshold:.1f}") + + +if __name__ == "__main__": + main() diff --git a/examples/python/CMakeLists.txt b/examples/python/CMakeLists.txt index 1dbb3856e3..df2bbd5e47 100644 --- a/examples/python/CMakeLists.txt +++ b/examples/python/CMakeLists.txt @@ -149,6 +149,24 @@ function(dai_example_test_relax_fail_regex example_name) endif() endfunction() +if(DEPTHAI_BUILD_BETA) + add_python_example(beta_classification_parser Beta/classification_parser.py) + add_python_example(beta_classification_sequence_parser Beta/classification_sequence_parser.py) + add_python_example(beta_embeddings_parser Beta/embeddings_parser.py) + add_python_example(beta_fastsam_parser Beta/fastsam_parser.py) + add_python_example(beta_hrnet_parser Beta/hrnet_parser.py) + add_python_example(beta_image_output_parser Beta/image_output_parser.py) + add_python_example(beta_keypoint_parser Beta/keypoint_parser.py) + add_python_example(beta_lane_detection_parser Beta/lane_detection_parser.py) + add_python_example(beta_map_output_parser Beta/map_output_parser.py) + add_python_example(beta_mlsd_parser Beta/mlsd_parser.py) + add_python_example(beta_mp_palm_detection_parser Beta/mp_palm_detection_parser.py) + add_python_example(beta_pp_text_detection_parser Beta/pp_text_detection_parser.py) + add_python_example(beta_scrfd_parser Beta/scrfd_parser.py) + add_python_example(beta_superanimal_parser Beta/superanimal_parser.py) + add_python_example(beta_yunet_parser Beta/yunet_parser.py) +endif() + ## Camera output add_python_example(camera_output Camera/camera_output.py) diff --git a/examples/python/Calibration/calibration_load.py b/examples/python/Calibration/calibration_load.py index 5757e04458..f9004735dd 100755 --- a/examples/python/Calibration/calibration_load.py +++ b/examples/python/Calibration/calibration_load.py @@ -13,13 +13,11 @@ with dai.Pipeline() as pipeline: pipeline.setCalibrationData(calibData) # Define sources and output - monoLeft = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_B) - monoRight = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_C) resolution = (640, 480) - stereo = pipeline.create(dai.node.StereoDepth).build( - monoLeft.requestOutput(resolution), monoRight.requestOutput(resolution) + depth = pipeline.create(dai.node.Depth).build( + dai.node.Depth.Algorithm.STEREO, size=resolution ) - depthQueue = stereo.depth.createOutputQueue() + depthQueue = depth.depth.createOutputQueue() pipeline.start() while True: # blocking call, will wait until a new data has arrived diff --git a/examples/python/Camera/camera_multiple_outputs.py b/examples/python/Camera/camera_multiple_outputs.py index 83cb134828..5e84624b36 100755 --- a/examples/python/Camera/camera_multiple_outputs.py +++ b/examples/python/Camera/camera_multiple_outputs.py @@ -79,9 +79,10 @@ def getFps(self): if videoIn is not None: FPSCounters[index].tick() assert isinstance(videoIn, dai.ImgFrame) - print( - f"frame {videoIn.getWidth()}x{videoIn.getHeight()} | {videoIn.getSequenceNum()}: exposure={videoIn.getExposureTime()}us, timestamp: {videoIn.getTimestampDevice()}" - ) + if (videoIn.getSequenceNum() % 60) == 0: + print( + f"frame {videoIn.getWidth()}x{videoIn.getHeight()} | {videoIn.getSequenceNum()}: exposure={videoIn.getExposureTime()}us, timestamp: {videoIn.getTimestampDevice()}" + ) # Get BGR frame from NV12 encoded video frame to show with opencv # Visualizing the frame on slower hosts might have overhead cvFrame = videoIn.getCvFrame() diff --git a/examples/python/Depth/depth_rgb_align.py b/examples/python/Depth/depth_rgb_align.py index af1b79bf2f..574d304098 100644 --- a/examples/python/Depth/depth_rgb_align.py +++ b/examples/python/Depth/depth_rgb_align.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 -import numpy as np import cv2 import depthai as dai import time @@ -55,35 +54,6 @@ def getFps(self): queue = sync.out.createOutputQueue() -def colorizeDepth(frameDepth): - invalidMask = frameDepth == 0 - # Log the depth, minDepth and maxDepth - try: - minDepth = np.percentile(frameDepth[frameDepth != 0], 3) - maxDepth = np.percentile(frameDepth[frameDepth != 0], 95) - logDepth = np.zeros_like(frameDepth, dtype=np.float32) - np.log(frameDepth, where=frameDepth != 0, out=logDepth) - logMinDepth = np.log(minDepth) - logMaxDepth = np.log(maxDepth) - np.nan_to_num(logDepth, copy=False, nan=logMinDepth) - # Clip the values to be in the 0-255 range - logDepth = np.clip(logDepth, logMinDepth, logMaxDepth) - - # Interpolate only valid logDepth values, setting the rest based on the mask - depthFrameColor = np.interp(logDepth, (logMinDepth, logMaxDepth), (0, 255)) - depthFrameColor = np.nan_to_num(depthFrameColor) - depthFrameColor = depthFrameColor.astype(np.uint8) - depthFrameColor = cv2.applyColorMap(depthFrameColor, cv2.COLORMAP_JET) - # Set invalid depth pixels to black - depthFrameColor[invalidMask] = 0 - except IndexError: - # Frame is likely empty - depthFrameColor = np.zeros((frameDepth.shape[0], frameDepth.shape[1], 3), dtype=np.uint8) - except Exception as e: - raise e - return depthFrameColor - - rgbWeight = 0.4 depthWeight = 0.6 @@ -134,7 +104,7 @@ def updateBlendWeights(percentRgb): if frameDepth is not None: cvFrame = frameRgb.getCvFrame() # Colorize the aligned depth - alignedDepthColorized = colorizeDepth(frameDepth.getFrame()) + alignedDepthColorized = dai.utility.colorizeDepthFrame(frameDepth).getCvFrame() cv2.imshow("Depth aligned", alignedDepthColorized) if len(cvFrame.shape) == 2: diff --git a/examples/python/Depth/unified_depth.py b/examples/python/Depth/unified_depth.py index bdd29b4353..93eafc14a0 100644 --- a/examples/python/Depth/unified_depth.py +++ b/examples/python/Depth/unified_depth.py @@ -34,27 +34,6 @@ } -def colorizeDepth(frameDepth: np.ndarray) -> np.ndarray: - """Log-scaled depth colorization with adaptive 3rd..95th percentile clipping (zero = invalid).""" - invalidMask = frameDepth == 0 - try: - minDepth = np.percentile(frameDepth[frameDepth != 0], 3) - maxDepth = np.percentile(frameDepth[frameDepth != 0], 95) - logDepth = np.log(frameDepth, where=frameDepth != 0) - logMinDepth = np.log(minDepth) - logMaxDepth = np.log(maxDepth) - np.nan_to_num(logDepth, copy=False, nan=logMinDepth) - logDepth = np.clip(logDepth, logMinDepth, logMaxDepth) - - depthFrameColor = np.interp(logDepth, (logMinDepth, logMaxDepth), (0, 255)) - depthFrameColor = np.nan_to_num(depthFrameColor).astype(np.uint8) - depthFrameColor = cv2.applyColorMap(depthFrameColor, cv2.COLORMAP_JET) - depthFrameColor[invalidMask] = 0 - except IndexError: - depthFrameColor = np.zeros((frameDepth.shape[0], frameDepth.shape[1], 3), dtype=np.uint8) - return depthFrameColor - - def colorizeConfidence(frame: np.ndarray) -> np.ndarray: if frame.dtype == np.uint16: vmax = int(np.max(frame)) @@ -201,7 +180,7 @@ def main() -> int: while pipeline.isRunning(): depthFrame = depthQueue.get() assert isinstance(depthFrame, dai.ImgFrame) - cv2.imshow("depth", colorizeDepth(depthFrame.getFrame())) + cv2.imshow("depth", dai.utility.colorizeDepthFrame(depthFrame).getCvFrame()) confidenceFrame = confidenceQueue.get() assert isinstance(confidenceFrame, dai.ImgFrame) diff --git a/examples/python/DetectionNetwork/detection_network.py b/examples/python/DetectionNetwork/detection_network.py index 27218c39e9..669a26364f 100644 --- a/examples/python/DetectionNetwork/detection_network.py +++ b/examples/python/DetectionNetwork/detection_network.py @@ -56,6 +56,7 @@ def displayFrame(name, frame): # Show the frame cv2.imshow(name, frame) + lastPrintTime = 0.0 while pipeline.isRunning(): inRgb: dai.ImgFrame = qRgb.get() inDet: dai.ImgDetections = qDet.get() @@ -76,7 +77,10 @@ def displayFrame(name, frame): if frame is not None: displayFrame("rgb", frame) - print("FPS: {:.2f}".format(counter / (time.monotonic() - startTime))) + now = time.monotonic() + if now - lastPrintTime >= 1.0: + print("FPS: {:.2f}".format(counter / (now - startTime))) + lastPrintTime = now if cv2.waitKey(1) == ord("q"): pipeline.stop() break diff --git a/examples/python/DetectionNetwork/detection_network_remap.py b/examples/python/DetectionNetwork/detection_network_remap.py index dac2c4597a..9c12277944 100644 --- a/examples/python/DetectionNetwork/detection_network_remap.py +++ b/examples/python/DetectionNetwork/detection_network_remap.py @@ -2,67 +2,29 @@ import cv2 import depthai as dai -import numpy as np - -def colorizeDepth(frameDepth): - invalidMask = frameDepth == 0 - # Log the depth, minDepth and maxDepth - try: - minDepth = np.percentile(frameDepth[frameDepth != 0], 3) - maxDepth = np.percentile(frameDepth[frameDepth != 0], 95) - logDepth = np.log(frameDepth, where=frameDepth != 0) - logMinDepth = np.log(minDepth) - logMaxDepth = np.log(maxDepth) - np.nan_to_num(logDepth, copy=False, nan=logMinDepth) - # Clip the values to be in the 0-255 range - logDepth = np.clip(logDepth, logMinDepth, logMaxDepth) - - # Interpolate only valid logDepth values, setting the rest based on the mask - depthFrameColor = np.interp(logDepth, (logMinDepth, logMaxDepth), (0, 255)) - depthFrameColor = np.nan_to_num(depthFrameColor) - depthFrameColor = depthFrameColor.astype(np.uint8) - depthFrameColor = cv2.applyColorMap(depthFrameColor, cv2.COLORMAP_JET) - # Set invalid depth pixels to black - depthFrameColor[invalidMask] = 0 - except IndexError: - # Frame is likely empty - depthFrameColor = np.zeros((frameDepth.shape[0], frameDepth.shape[1], 3), dtype=np.uint8) - except Exception as e: - raise e - return depthFrameColor # Create pipeline with dai.Pipeline() as pipeline: - cameraNode = pipeline.create(dai.node.Camera).build() + colorSockets = pipeline.getDefaultDevice().getConnectedCameras(dai.CameraSensorType.COLOR) + colorSocket = colorSockets[0] if colorSockets else dai.CameraBoardSocket.CAM_A + cameraNode = pipeline.create(dai.node.Camera).build(colorSocket) detectionNetwork = pipeline.create(dai.node.DetectionNetwork).build(cameraNode, dai.NNModelDescription("yolov6-nano")) labelMap = detectionNetwork.getClasses() - monoLeft = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_B) - monoRight = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_C) - stereo = pipeline.create(dai.node.StereoDepth) - - # Linking - monoLeftOut = monoLeft.requestOutput((1280, 720)) - monoRightOut = monoRight.requestOutput((1280, 720)) - monoLeftOut.link(stereo.left) - monoRightOut.link(stereo.right) - - stereo.setRectification(True) - stereo.setExtendedDisparity(True) - stereo.setLeftRightCheck(True) - stereo.setSubpixel(True) + depth = pipeline.create(dai.node.Depth).build(dai.node.Depth.Algorithm.AUTO, None) qRgb = detectionNetwork.passthrough.createOutputQueue() qDet = detectionNetwork.out.createOutputQueue() - qDepth = stereo.disparity.createOutputQueue() + qDepth = depth.depth.createOutputQueue() pipeline.start() def displayFrame(name: str, frame: dai.ImgFrame, imgDetections: dai.ImgDetections): color = (0, 255, 0) assert imgDetections.getTransformation() is not None - cvFrame = frame.getFrame() if frame.getType() == dai.ImgFrame.Type.RAW16 else frame.getCvFrame() if(frame.getType() == dai.ImgFrame.Type.RAW16): - cvFrame = colorizeDepth(cvFrame) + cvFrame = dai.utility.colorizeDepthFrame(frame).getCvFrame() + else: + cvFrame = frame.getCvFrame() for detection in imgDetections.detections: # Get the shape of the frame from which the detections originated for denormalization normShape = imgDetections.getTransformation().getSize() @@ -108,4 +70,3 @@ def displayFrame(name: str, frame: dai.ImgFrame, imgDetections: dai.ImgDetection if cv2.waitKey(1) == ord("q"): pipeline.stop() break - diff --git a/examples/python/DynamicCalibration/README.md b/examples/python/DynamicCalibration/README.md index 9506a8fcbc..ecbaf06b40 100644 --- a/examples/python/DynamicCalibration/README.md +++ b/examples/python/DynamicCalibration/README.md @@ -47,7 +47,7 @@ This folder contains minimal, end-to-end examples that use **`dai.node.DynamicCa **Flow:** 1. Create mono cameras → request **full-res NV12** (unrectified) → link to: - `DynamicCalibration.left/right` - - `StereoDepth.left/right` (for live disparity view) + - `StereoDepth.left/right` (for live depth view) 2. Start the pipeline, give AE a moment to settle. 3. **Start calibration** with: ```python @@ -56,7 +56,7 @@ This folder contains minimal, end-to-end examples that use **`dai.node.DynamicCa ) ``` 4. In the loop: - - Show `left`, `right`, and `disparity`. + - Show `left`, `right`, and `depth`. - Poll `coverageOutput` for progress (`meanCoverage`, `dataAcquired`). - Poll `calibrationOutput` for a result. 5. When a result arrives: @@ -110,7 +110,7 @@ Mean Sampson error current = 0.38 px **Flow:** 1. Same camera / StereoDepth / DynamicCalibration setup as above. 2. In the loop: - - Show `left`, `right`, and `disparity`. + - Show `left`, `right`, and `depth`. - Ask for **coverage** on demand: ```python dynCalibInputControl.send(dai.DynamicCalibrationControl(dai.DynamicCalibrationControl.Commands.LoadImage())) @@ -144,10 +144,10 @@ Use this only if you need a legacy reference while migrating old code. **Script:** `calibration_integration.py` **What it does:** -Runs one loop that periodically refreshes coverage, executes calibration, and applies a new calibration automatically when the returned metrics indicate drift — while showing `left`, `right`, and colorized `disparity` previews. +Runs one loop that periodically refreshes coverage, executes calibration, and applies a new calibration automatically when the returned metrics indicate drift — while showing `left`, `right`, and colorized `depth` previews. **Flow:** -1. Create mono cameras → request **full-res NV12** → link to `DynamicCalibration` and `StereoDepth` for live disparity. Read the device’s current calibration as baseline. +1. Create mono cameras → request **full-res NV12** → link to `DynamicCalibration` and `StereoDepth` for live depth. Read the device’s current calibration as baseline. 2. On a fixed interval (for example, every ~3 seconds), send: - `LoadImage()` to compute coverage on the current frames, and - `dai.DynamicCalibrationControl.calibrate(True)` (or equivalent) to compute a new candidate calibration and return metrics on `calibrationOutput`. @@ -159,7 +159,7 @@ Runs one loop that periodically refreshes coverage, executes calibration, and ap 5. Press **`q`** to exit. **Notes & defaults:** -- Disparity preview is auto-scaled to the observed maximum; **zero disparity appears black** for clarity. +- Depth preview uses the shared colorization helper with a 500–12000 mm range and logarithmic scaling. - The 0.05 px Sampson threshold is a simple heuristic — adjust per your tolerance. **Example console output:** @@ -181,7 +181,7 @@ Mono CAM_B ──▶ [Camera] ── NV12 (full-res) ──▶ DynamicCalibratio Mono CAM_C ──▶ [Camera] ── NV12 (full-res) ──▶ DynamicCalibration.right │ - └───────────▶ StereoDepth.right ──▶ disparity + └───────────▶ StereoDepth.right ──▶ depth ``` --- @@ -262,7 +262,7 @@ If you previously read fields from `CalibrationQuality.qualityData`, read the sa - **No quality data returned** Ensure the pattern is visible, not motion-blurred, and covers diverse regions of the image. Increase lighting, adjust exposure, or hold the rig steady. -- **Disparity looks worse after apply** +- **Depth preview looks worse after apply** Re-run to collect more diverse views (tilt/translate the target). - **Typos in prints** diff --git a/examples/python/DynamicCalibration/calibration_dynamic.py b/examples/python/DynamicCalibration/calibration_dynamic.py index 36011eb528..33ae19dde3 100644 --- a/examples/python/DynamicCalibration/calibration_dynamic.py +++ b/examples/python/DynamicCalibration/calibration_dynamic.py @@ -20,7 +20,7 @@ monoLeftOut.link(dynCalib.left) monoRightOut.link(dynCalib.right) - # Stereo (for disparity + synced previews) + # Stereo (for depth + synced previews) stereo = pipeline.create(dai.node.StereoDepth) monoLeftOut.link(stereo.left) monoRightOut.link(stereo.right) @@ -28,7 +28,7 @@ # Output queues syncedLeftQueue = stereo.syncedLeft.createOutputQueue() syncedRightQueue = stereo.syncedRight.createOutputQueue() - disparityQueue = stereo.disparity.createOutputQueue() + depthQueue = stereo.depth.createOutputQueue() # Initialize the command output queues for calibration and coverage dynCalibCalibrationQueue = dynCalib.calibrationOutput.createOutputQueue() @@ -40,11 +40,6 @@ device = pipeline.getDefaultDevice() device.setCalibration(device.getCalibration()) - # Setup the colormap for visualization - colorMap = cv2.applyColorMap(np.arange(256, dtype=np.uint8), cv2.COLORMAP_JET) - colorMap[0] = [0, 0, 0] # to make zero-disparity pixels black - maxDisparity = 1.0 - pipeline.start() time.sleep(1) # wait for auto exposure to settle @@ -63,23 +58,13 @@ while pipeline.isRunning(): leftSynced = syncedLeftQueue.get() rightSynced = syncedRightQueue.get() - disparity = disparityQueue.get() + depth = depthQueue.get() cv2.imshow("left", leftSynced.getCvFrame()) cv2.imshow("right", rightSynced.getCvFrame()) - # --- Disparity visualization --- - npDisparity = disparity.getFrame() - curMax = float(np.max(npDisparity)) - if curMax > 0: - maxDisparity = max(maxDisparity, curMax) - - # Normalize to [0,255] and colorize; keep zero-disparity as black - denom = maxDisparity if maxDisparity > 0 else 1.0 - normalized = (npDisparity / denom * 255.0).astype(np.uint8) - colorizedDisparity = cv2.applyColorMap(normalized, cv2.COLORMAP_JET) - colorizedDisparity[normalized == 0] = (0, 0, 0) - cv2.imshow("disparity", colorizedDisparity) + colorizedDepth = dai.utility.colorizeDepthFrame(depth).getCvFrame() + cv2.imshow("depth", colorizedDepth) # --- Coverage (non-blocking) --- coverage = dynCalibCoverageQueue.tryGet() diff --git a/examples/python/DynamicCalibration/calibration_integration.py b/examples/python/DynamicCalibration/calibration_integration.py index 7efe48f701..a7ff7b14a7 100644 --- a/examples/python/DynamicCalibration/calibration_integration.py +++ b/examples/python/DynamicCalibration/calibration_integration.py @@ -34,7 +34,7 @@ def print_metrics(metrics: dai.CalibrationQualityData) -> None: syncedLeftQueue = stereo.syncedLeft.createOutputQueue() syncedRightQueue = stereo.syncedRight.createOutputQueue() - disparityQueue = stereo.disparity.createOutputQueue() + depthQueue = stereo.depth.createOutputQueue() # Initialize the command output queues for coverage and calibration output dynCalibCoverageQueue = dynCalib.coverageOutput.createOutputQueue() @@ -46,11 +46,6 @@ def print_metrics(metrics: dai.CalibrationQualityData) -> None: device = pipeline.getDefaultDevice() device.setCalibration(device.getCalibration()) - # Setup the colormap for visualization - colorMap = cv2.applyColorMap(np.arange(256, dtype=np.uint8), cv2.COLORMAP_JET) - colorMap[0] = [0, 0, 0] # to make zero-disparity pixels black - maxDisparity = 1 - pipeline.start() time.sleep(1) # wait for auto exposure to settle start = time.time() @@ -59,16 +54,12 @@ def print_metrics(metrics: dai.CalibrationQualityData) -> None: leftSynced = syncedLeftQueue.get() rightSynced = syncedRightQueue.get() - disparity = disparityQueue.get() + depth = depthQueue.get() cv2.imshow("left", leftSynced.getCvFrame()) cv2.imshow("right", rightSynced.getCvFrame()) - npDisparity = disparity.getFrame() - maxDisparity = max(maxDisparity, np.max(npDisparity)) - colorizedDisparity = cv2.applyColorMap( - ((npDisparity / maxDisparity) * 255).astype(np.uint8), colorMap - ) + colorizedDepth = dai.utility.colorizeDepthFrame(depth).getCvFrame() coverage = dynCalibCoverageQueue.tryGet() if coverage is not None: @@ -99,7 +90,7 @@ def print_metrics(metrics: dai.CalibrationQualityData) -> None: dynCalibInputControl.send(dai.DynamicCalibrationControl.resetData()) - cv2.imshow("disparity", colorizedDisparity) + cv2.imshow("depth", colorizedDepth) key = cv2.waitKey(1) if key == ord("q"): pipeline.stop() diff --git a/examples/python/DynamicCalibration/calibration_quality_dynamic.py b/examples/python/DynamicCalibration/calibration_quality_dynamic.py index c4c25adeab..3d2d686e96 100644 --- a/examples/python/DynamicCalibration/calibration_quality_dynamic.py +++ b/examples/python/DynamicCalibration/calibration_quality_dynamic.py @@ -34,7 +34,7 @@ def print_metrics(metrics: dai.CalibrationQualityData) -> None: # Queues syncedLeftQueue = stereo.syncedLeft.createOutputQueue() syncedRightQueue = stereo.syncedRight.createOutputQueue() - disparityQueue = stereo.disparity.createOutputQueue() + depthQueue = stereo.depth.createOutputQueue() # Initialize the command output queues for coverage and calibration output dynCalibCoverageQueue = dynCalib.coverageOutput.createOutputQueue() @@ -46,31 +46,19 @@ def print_metrics(metrics: dai.CalibrationQualityData) -> None: device = pipeline.getDefaultDevice() device.setCalibration(device.getCalibration()) - # Setup the colormap for visualization - colorMap = cv2.applyColorMap(np.arange(256, dtype=np.uint8), cv2.COLORMAP_JET) - colorMap[0] = [0, 0, 0] # to make zero-disparity pixels black - maxDisparity = 1 - pipeline.start() time.sleep(1) # wait for auto exposure to settle while pipeline.isRunning(): leftSynced = syncedLeftQueue.get() rightSynced = syncedRightQueue.get() - disparity = disparityQueue.get() + depth = depthQueue.get() cv2.imshow("left", leftSynced.getCvFrame()) cv2.imshow("right", rightSynced.getCvFrame()) - # --- Disparity visualization --- - npDisparity = disparity.getFrame() - curMax = float(np.max(npDisparity)) - if curMax > 0: - maxDisparity = max(maxDisparity, curMax) - normalized = (npDisparity / (maxDisparity if maxDisparity > 0 else 1.0) * 255.0).astype(np.uint8) - colorizedDisparity = cv2.applyColorMap(normalized, cv2.COLORMAP_JET) - colorizedDisparity[normalized == 0] = (0, 0, 0) - cv2.imshow("disparity", colorizedDisparity) + colorizedDepth = dai.utility.colorizeDepthFrame(depth).getCvFrame() + cv2.imshow("depth", colorizedDepth) # --- Load one frame into calibration & read coverage dynCalibInputControl.send(dai.DynamicCalibrationControl.loadImage()) diff --git a/examples/python/IMU/imu_gyroscope_accelerometer.py b/examples/python/IMU/imu_gyroscope_accelerometer.py index 0698ef4b5e..f7847f12bc 100755 --- a/examples/python/IMU/imu_gyroscope_accelerometer.py +++ b/examples/python/IMU/imu_gyroscope_accelerometer.py @@ -1,4 +1,6 @@ #!/usr/bin/env python3 +import time + import depthai as dai @@ -27,6 +29,7 @@ def timeDeltaToMilliS(delta) -> float: pipeline.start() baseTs = None + lastPrintTime = 0.0 while pipeline.isRunning(): try: imuData = imuQueue.get() @@ -34,19 +37,26 @@ def timeDeltaToMilliS(delta) -> float: break assert isinstance(imuData, dai.IMUData) imuPackets = imuData.packets - for imuPacket in imuPackets: - acceleroValues = imuPacket.acceleroMeter - gyroValues = imuPacket.gyroscope - - acceleroTs = acceleroValues.getTimestamp() - gyroTs = gyroValues.getTimestamp() - - imuF = "{:.06f}" - tsF = "{:.03f}" - - print(f"Accelerometer timestamp: {acceleroTs}") - print(f"Latency [ms]: {dai.Clock.now() - acceleroValues.getTimestamp()}") - print(f"Accelerometer [m/s^2]: x: {imuF.format(acceleroValues.x)} y: {imuF.format(acceleroValues.y)} z: {imuF.format(acceleroValues.z)}") - print(f"Gyroscope timestamp: {gyroTs}") - print(f"Gyroscope [rad/s]: x: {imuF.format(gyroValues.x)} y: {imuF.format(gyroValues.y)} z: {imuF.format(gyroValues.z)} ") - print() + if not imuPackets: + continue + + now = time.monotonic() + if now - lastPrintTime < 1.0: + continue + lastPrintTime = now + + imuPacket = imuPackets[-1] + acceleroValues = imuPacket.acceleroMeter + gyroValues = imuPacket.gyroscope + + acceleroTs = acceleroValues.getTimestamp() + gyroTs = gyroValues.getTimestamp() + + imuF = "{:.06f}" + + print(f"Accelerometer timestamp: {acceleroTs}") + print(f"Latency [ms]: {dai.Clock.now() - acceleroValues.getTimestamp()}") + print(f"Accelerometer [m/s^2]: x: {imuF.format(acceleroValues.x)} y: {imuF.format(acceleroValues.y)} z: {imuF.format(acceleroValues.z)}") + print(f"Gyroscope timestamp: {gyroTs}") + print(f"Gyroscope [rad/s]: x: {imuF.format(gyroValues.x)} y: {imuF.format(gyroValues.y)} z: {imuF.format(gyroValues.z)} ") + print() diff --git a/examples/python/ImageAlign/depth_align.py b/examples/python/ImageAlign/depth_align.py index 00fa3d73a3..173e678ebb 100755 --- a/examples/python/ImageAlign/depth_align.py +++ b/examples/python/ImageAlign/depth_align.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 -import numpy as np import cv2 import depthai as dai import time @@ -59,35 +58,6 @@ def getFps(self): queue = sync.out.createOutputQueue() -def colorizeDepth(frameDepth): - invalidMask = frameDepth == 0 - # Log the depth, minDepth and maxDepth - try: - minDepth = np.percentile(frameDepth[frameDepth != 0], 3) - maxDepth = np.percentile(frameDepth[frameDepth != 0], 95) - logDepth = np.zeros_like(frameDepth, dtype=np.float32) - np.log(frameDepth, where=frameDepth != 0, out=logDepth) - logMinDepth = np.log(minDepth) - logMaxDepth = np.log(maxDepth) - np.nan_to_num(logDepth, copy=False, nan=logMinDepth) - # Clip the values to be in the 0-255 range - logDepth = np.clip(logDepth, logMinDepth, logMaxDepth) - - # Interpolate only valid logDepth values, setting the rest based on the mask - depthFrameColor = np.interp(logDepth, (logMinDepth, logMaxDepth), (0, 255)) - depthFrameColor = np.nan_to_num(depthFrameColor) - depthFrameColor = depthFrameColor.astype(np.uint8) - depthFrameColor = cv2.applyColorMap(depthFrameColor, cv2.COLORMAP_JET) - # Set invalid depth pixels to black - depthFrameColor[invalidMask] = 0 - except IndexError: - # Frame is likely empty - depthFrameColor = np.zeros((frameDepth.shape[0], frameDepth.shape[1], 3), dtype=np.uint8) - except Exception as e: - raise e - return depthFrameColor - - rgbWeight = 0.4 depthWeight = 0.6 @@ -135,7 +105,7 @@ def updateBlendWeights(percentRgb): if frameDepth is not None: cvFrame = frameRgb.getCvFrame() # Colorize the aligned depth - alignedDepthColorized = colorizeDepth(frameDepth.getFrame()) + alignedDepthColorized = dai.utility.colorizeDepthFrame(frameDepth).getCvFrame() # Resize depth to match the rgb frame cv2.imshow("Depth aligned", alignedDepthColorized) diff --git a/examples/python/Misc/Bootloader/bootloader_dump.py b/examples/python/Misc/Bootloader/bootloader_dump.py new file mode 100644 index 0000000000..61942100dd --- /dev/null +++ b/examples/python/Misc/Bootloader/bootloader_dump.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 + +import depthai as dai + + +found, deviceInfo = dai.DeviceBootloader.getFirstAvailableDevice() +if not found: + raise RuntimeError("No available device found") + +deviceState = str(deviceInfo.state).replace("XLinkDeviceState.", "") + +with dai.DeviceBootloader(deviceInfo) as bootloader: + version = bootloader.getVersion() + userBootloader = bootloader.isUserBootloader() + +bootloaderType = "Factory flashed bootloader" if not userBootloader else "User flashed bootloader" + +print(deviceInfo) +print(f"Bootloader version: {bootloaderType} {version}") +print(f"Embedded depthai bootloader version: {dai.DeviceBootloader.getEmbeddedBootloaderVersion()}") diff --git a/examples/python/Misc/MultiDevice/multi_device_frame_sync.py b/examples/python/Misc/MultiDevice/multi_device_frame_sync.py index 7178b7ae54..3802cbefa9 100644 --- a/examples/python/Misc/MultiDevice/multi_device_frame_sync.py +++ b/examples/python/Misc/MultiDevice/multi_device_frame_sync.py @@ -12,6 +12,11 @@ import signal import threading +try: + import av +except ImportError: + av = None + from typing import Optional, Dict from enum import Enum @@ -58,12 +63,19 @@ def createCameraOutputs(pipeline: dai.Pipeline, socket: dai.CameraBoardSocket, s .build(socket) ) - output = ( + rawOutput = ( cam.requestOutput( - (640, 480), dai.ImgFrame.Type.NV12, dai.ImgResizeMode.STRETCH + (1920, 1080), dai.ImgFrame.Type.NV12, dai.ImgResizeMode.STRETCH ) ) + encoder = pipeline.create(dai.node.VideoEncoder).build( + rawOutput, + frameRate=sensorFps, + profile=dai.VideoEncoderProperties.Profile.H265_MAIN, + ) + output = encoder.out + if syncType == SyncType.PTP: cam.initialControl.setFrameSyncMode(dai.CameraControl.FrameSyncMode.TIME_PTP) print(f"Setting PTP for {socket.name}") @@ -225,6 +237,9 @@ def interruptHandler(sig, frame): group.add_argument("--ptp-sync", action="store_true", help="Use PTP sync") args = parser.parse_args() +if av is None: + raise RuntimeError("PyAV is required for H.265 display. Install it with: pip install av") + # if user did not specify device IPs, use all available devices if len(args.devices) == 0: deviceInfos = dai.Device.getAllAvailableDevices() @@ -245,6 +260,28 @@ def interruptHandler(sig, frame): print("Master camera does not match PTP master, instead it signifies the sync pipeline") else: raise RuntimeError("Must specify sync type") +# One persistent H.265 decoder is required per stream. +decoders = {} +lastDecodedFrames = {} + +def decodeH265(outputName, msg): + decoder = decoders.get(outputName) + if decoder is None: + decoder = av.CodecContext.create("hevc", "r") + decoders[outputName] = decoder + + try: + frames = decoder.decode(av.Packet(bytes(msg.getData()))) + except Exception: + return lastDecodedFrames.get(outputName) + + if frames: + frame = frames[-1].to_ndarray(format="bgr24") + lastDecodedFrames[outputName] = frame + return frame + + return lastDecodedFrames.get(outputName) + # --------------------------------------------------------------------------- # Main # --------------------------------------------------------------------------- @@ -299,10 +336,13 @@ def data_collector(deviceName, socketName): # Send frames from slave output queues to sync node input queues camOutputQueue = slaveQueues[deviceName][socketName] while running: - if camOutputQueue.has(): - inputQueues[f"slave_{deviceName}_{socketName}"].send(camOutputQueue.get()) - else: - time.sleep(0.001) + try: + if camOutputQueue.has(): + inputQueues[f"slave_{deviceName}_{socketName}"].send(camOutputQueue.get()) + else: + time.sleep(0.001) + except dai.MessageQueue.QueueException: + break threads = {} @@ -336,7 +376,7 @@ def data_collector(deviceName, socketName): if latestFrameGroup is not None and latestFrameGroup.getNumMessages() == len(outputNames): tsValues = {} for name in outputNames: - tsValues[name] = latestFrameGroup[name].getTimestamp(dai.CameraExposureOffset.END).total_seconds() + tsValues[name] = latestFrameGroup[name].getTimestamp().total_seconds() # Build individual image arrays for each camera socket, displayed side-by-side imgs = [] @@ -381,7 +421,10 @@ def data_collector(deviceName, socketName): # Get frame for this output msg = latestFrameGroup[outputName] - frame = msg.getCvFrame() + frame = decodeH265(outputName, msg) + if frame is None: + continue + frame = frame.copy() # Add output name to frame cv2.putText( @@ -410,6 +453,8 @@ def data_collector(deviceName, socketName): # Add absolute maximum time difference between all frames for i, img in enumerate(imgs): + if not img: + continue cv2.putText( imgs[i][0], f"{syncStatusStr} | delta = {delta*1e3:.3f} ms", @@ -423,6 +468,8 @@ def data_collector(deviceName, socketName): # Show the frame for i, img in enumerate(imgs): + if not img: + continue cv2.imshow(f"synced_view_{camSockets[i]}", cv2.hconcat(imgs[i])) latestFrameGroup = None # Wait for next batch @@ -431,7 +478,7 @@ def data_collector(deviceName, socketName): running = False break - for t in threads.keys(): - threads[t].join() + for t in threads.values(): + t.join() -cv2.destroyAllWindows() +cv2.destroyAllWindows() \ No newline at end of file diff --git a/examples/python/Misc/PipelineDebugging/get_pipeline_state.py b/examples/python/Misc/PipelineDebugging/get_pipeline_state.py index e30277a378..4258158d91 100644 --- a/examples/python/Misc/PipelineDebugging/get_pipeline_state.py +++ b/examples/python/Misc/PipelineDebugging/get_pipeline_state.py @@ -1,5 +1,4 @@ import depthai as dai -import numpy as np import cv2 with dai.Pipeline() as pipeline: @@ -21,20 +20,14 @@ stereo.setExtendedDisparity(True) stereo.setLeftRightCheck(True) - disparityQueue = stereo.disparity.createOutputQueue() - - colorMap = cv2.applyColorMap(np.arange(256, dtype=np.uint8), cv2.COLORMAP_JET) - colorMap[0] = [0, 0, 0] # to make zero-disparity pixels black + depthQueue = stereo.depth.createOutputQueue() pipeline.start() - maxDisparity = 1 while pipeline.isRunning(): - disparity = disparityQueue.get() - assert isinstance(disparity, dai.ImgFrame) - npDisparity = disparity.getFrame() - maxDisparity = max(maxDisparity, np.max(npDisparity)) - colorizedDisparity = cv2.applyColorMap(((npDisparity / maxDisparity) * 255).astype(np.uint8), colorMap) - cv2.imshow("disparity", colorizedDisparity) + depth = depthQueue.get() + assert isinstance(depth, dai.ImgFrame) + colorizedDepth = dai.utility.colorizeDepthFrame(depth).getCvFrame() + cv2.imshow("depth", colorizedDepth) key = cv2.waitKey(1) if key == ord('q'): pipeline.stop() diff --git a/examples/python/Misc/PipelineDebugging/node_pipeline_events.py b/examples/python/Misc/PipelineDebugging/node_pipeline_events.py index f932b6abcd..0848e617c9 100644 --- a/examples/python/Misc/PipelineDebugging/node_pipeline_events.py +++ b/examples/python/Misc/PipelineDebugging/node_pipeline_events.py @@ -1,5 +1,6 @@ +import time + import depthai as dai -import numpy as np import cv2 with dai.Pipeline() as pipeline: @@ -21,22 +22,16 @@ stereo.setExtendedDisparity(True) stereo.setLeftRightCheck(True) - disparityQueue = stereo.disparity.createOutputQueue() + depthQueue = stereo.depth.createOutputQueue() monoLeftEventQueue = monoLeft.pipelineEventOutput.createOutputQueue() # Supported on core and rvc4 only - colorMap = cv2.applyColorMap(np.arange(256, dtype=np.uint8), cv2.COLORMAP_JET) - colorMap[0] = [0, 0, 0] # to make zero-disparity pixels black - pipeline.start() - maxDisparity = 1 while pipeline.isRunning(): - disparity = disparityQueue.get() + depth = depthQueue.get() latestEvent = monoLeftEventQueue.tryGet() - assert isinstance(disparity, dai.ImgFrame) - npDisparity = disparity.getFrame() - maxDisparity = max(maxDisparity, np.max(npDisparity)) - colorizedDisparity = cv2.applyColorMap(((npDisparity / maxDisparity) * 255).astype(np.uint8), colorMap) - cv2.imshow("disparity", colorizedDisparity) + assert isinstance(depth, dai.ImgFrame) + colorizedDepth = dai.utility.colorizeDepthFrame(depth).getCvFrame() + cv2.imshow("depth", colorizedDepth) print(f"Latest event from MonoLeft camera node: {latestEvent if latestEvent is not None else 'No event'}") key = cv2.waitKey(1) if key == ord('q'): diff --git a/examples/python/NeuralAssistedStereo/neural_assisted_stereo.py b/examples/python/NeuralAssistedStereo/neural_assisted_stereo.py index 070e88db1c..bedb9a55bd 100644 --- a/examples/python/NeuralAssistedStereo/neural_assisted_stereo.py +++ b/examples/python/NeuralAssistedStereo/neural_assisted_stereo.py @@ -1,42 +1,8 @@ -import numpy as np import cv2 as cv import depthai as dai FPS = 20 -def showDepth(depthFrame, windowName="Depth", minDistance=500, maxDistance=5000, - colormap=cv.COLORMAP_TURBO, useLog=False): - """ - Nicely visualize a depth map. - - Args: - depthFrame (np.ndarray): Depth frame (in millimeters). - windowName (str): OpenCV window name. - minDistance (int): Minimum depth to display (in mm). - maxDistance (int): Maximum depth to display (in mm). - colormap (int): OpenCV colormap (e.g., cv.COLORMAP_JET, COLORMAP_TURBO, etc.). - useLog (bool): Apply logarithmic scaling for better visual contrast. - - Example: - frame = depth.getCvFrame() - showDepth(frame) - """ - # Convert to float for processing - depthFrame = depthFrame.astype(np.float32) - - # Optionally apply log scaling - if useLog: - depthFrame = np.log(depthFrame + 1) - - # Clip to defined range (avoid far-out values) - depthFrame = np.uint8(np.clip(depthFrame, minDistance, maxDistance) / maxDistance * 255) - - # Apply color map - depthColor = cv.applyColorMap(depthFrame, colormap) - - # Show in a window - cv.imshow(windowName, depthColor) - if __name__ == "__main__": device = dai.Device() pipeline = dai.Pipeline(device) @@ -52,13 +18,13 @@ def showDepth(depthFrame, windowName="Depth", minDistance=500, maxDistance=5000, neuralAssistedStereo = pipeline.create(dai.node.NeuralAssistedStereo).build(monoLeftOut, monoRightOut, neuralModel=dai.DeviceModelZoo.NEURAL_DEPTH_NANO) - disparityQueue = neuralAssistedStereo.disparity.createOutputQueue() + depthQueue = neuralAssistedStereo.depth.createOutputQueue() with pipeline: pipeline.start() while pipeline.isRunning(): - disparity = disparityQueue.get() - showDepth(disparity.getCvFrame(), minDistance=100, maxDistance=6000, useLog=False) + depth = depthQueue.get() + cv.imshow("Depth", dai.utility.colorizeDepthFrame(depth, 500, 12000, cv.COLORMAP_TURBO, useLog=True).getCvFrame()) key = cv.waitKey(1) if key == ord('q'): diff --git a/examples/python/NeuralDepth/neural_depth.py b/examples/python/NeuralDepth/neural_depth.py index 3f8cee01b7..4cfb838dc8 100644 --- a/examples/python/NeuralDepth/neural_depth.py +++ b/examples/python/NeuralDepth/neural_depth.py @@ -17,7 +17,6 @@ confidenceQueue = neuralDepth.confidence.createOutputQueue() edgeQueue = neuralDepth.edge.createOutputQueue() - disparityQueue = neuralDepth.disparity.createOutputQueue() depthQueue = neuralDepth.depth.createOutputQueue() inputConfigQueue = neuralDepth.inputConfig.createInputQueue() @@ -26,7 +25,6 @@ # Connect to device and start pipeline pipeline.start() - maxDisparity = 1 colorMap = cv2.applyColorMap(np.arange(256, dtype=np.uint8), cv2.COLORMAP_JET) colorMap[0] = [0, 0, 0] # to make zero-disparity pixels black print("For adjusting thresholds, use keys:") @@ -53,19 +51,9 @@ cv2.imshow("edge", colorizedEdge) - disparityData = disparityQueue.get() - assert isinstance(disparityData, dai.ImgFrame) - npDisparity = disparityData.getFrame() - maxDisparity = max(maxDisparity, np.max(npDisparity)) - colorizedDisparity = cv2.applyColorMap(((npDisparity / maxDisparity) * 255).astype(np.uint8), colorMap) - cv2.imshow("disparity", colorizedDisparity) - depthData = depthQueue.get() assert isinstance(depthData, dai.ImgFrame) - npDepth = depthData.getFrame() - maxRange = max(currentConfig.postProcessing.thresholdFilter.maxRange, 1) - depthFrame = np.clip((npDepth / maxRange) * 255, 0, 255).astype(np.uint8) - colorizedDepth = cv2.applyColorMap(depthFrame, colorMap) + colorizedDepth = dai.utility.colorizeDepthFrame(depthData).getCvFrame() cv2.imshow("depth", colorizedDepth) key = cv2.waitKey(1) diff --git a/examples/python/NeuralDepth/neural_depth_align.py b/examples/python/NeuralDepth/neural_depth_align.py index 4b359a744a..c13e7fb911 100755 --- a/examples/python/NeuralDepth/neural_depth_align.py +++ b/examples/python/NeuralDepth/neural_depth_align.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 -import numpy as np import cv2 import depthai as dai import time @@ -51,34 +50,6 @@ def getFps(self): queue = sync.out.createOutputQueue() -def colorizeDepth(frameDepth): - invalidMask = frameDepth == 0 - # Log the depth, minDepth and maxDepth - try: - minDepth = np.percentile(frameDepth[frameDepth != 0], 3) - maxDepth = np.percentile(frameDepth[frameDepth != 0], 95) - logDepth = np.log(frameDepth, where=frameDepth != 0) - logMinDepth = np.log(minDepth) - logMaxDepth = np.log(maxDepth) - np.nan_to_num(logDepth, copy=False, nan=logMinDepth) - # Clip the values to be in the 0-255 range - logDepth = np.clip(logDepth, logMinDepth, logMaxDepth) - - # Interpolate only valid logDepth values, setting the rest based on the mask - depthFrameColor = np.interp(logDepth, (logMinDepth, logMaxDepth), (0, 255)) - depthFrameColor = np.nan_to_num(depthFrameColor) - depthFrameColor = depthFrameColor.astype(np.uint8) - depthFrameColor = cv2.applyColorMap(depthFrameColor, cv2.COLORMAP_JET) - # Set invalid depth pixels to black - depthFrameColor[invalidMask] = 0 - except IndexError: - # Frame is likely empty - depthFrameColor = np.zeros((frameDepth.shape[0], frameDepth.shape[1], 3), dtype=np.uint8) - except Exception as e: - raise e - return depthFrameColor - - rgbWeight = 0.4 depthWeight = 0.6 @@ -126,7 +97,7 @@ def updateBlendWeights(percentRgb): if frameDepth is not None: cvFrame = frameRgb.getCvFrame() # Colorize the aligned depth - alignedDepthColorized = colorizeDepth(frameDepth.getFrame()) + alignedDepthColorized = dai.utility.colorizeDepthFrame(frameDepth).getCvFrame() # Resize depth to match the rgb frame cv2.imshow("Depth aligned", alignedDepthColorized) @@ -150,4 +121,4 @@ def updateBlendWeights(percentRgb): key = cv2.waitKey(1) if key == ord("q"): - break \ No newline at end of file + break diff --git a/examples/python/NeuralDepth/neural_depth_minimal.py b/examples/python/NeuralDepth/neural_depth_minimal.py index d7d90d4c6b..6ceba2df28 100644 --- a/examples/python/NeuralDepth/neural_depth_minimal.py +++ b/examples/python/NeuralDepth/neural_depth_minimal.py @@ -2,7 +2,6 @@ import cv2 import depthai as dai -import numpy as np FPS = 25 @@ -15,21 +14,15 @@ neuralDepth = pipeline.create(dai.node.NeuralDepth).build(leftOutput, rightOutput, dai.DeviceModelZoo.NEURAL_DEPTH_LARGE) - disparityQueue = neuralDepth.disparity.createOutputQueue() + depthQueue = neuralDepth.depth.createOutputQueue() # Connect to device and start pipeline pipeline.start() - maxDisparity = 1 - colorMap = cv2.applyColorMap(np.arange(256, dtype=np.uint8), cv2.COLORMAP_JET) - colorMap[0] = [0, 0, 0] # to make zero-disparity pixels black - while pipeline.isRunning(): - disparityData = disparityQueue.get() - assert isinstance(disparityData, dai.ImgFrame) - npDisparity = disparityData.getFrame() - maxDisparity = max(maxDisparity, np.max(npDisparity)) - colorizedDisparity = cv2.applyColorMap(((npDisparity / maxDisparity) * 255).astype(np.uint8), colorMap) - cv2.imshow("disparity", colorizedDisparity) + depthData = depthQueue.get() + assert isinstance(depthData, dai.ImgFrame) + colorizedDepth = dai.utility.colorizeDepthFrame(depthData).getCvFrame() + cv2.imshow("depth", colorizedDepth) key = cv2.waitKey(1) if key == ord('q'): diff --git a/examples/python/NeuralNetwork/neural_network.py b/examples/python/NeuralNetwork/neural_network.py index cb053c84da..1ec1c93fc6 100644 --- a/examples/python/NeuralNetwork/neural_network.py +++ b/examples/python/NeuralNetwork/neural_network.py @@ -17,9 +17,12 @@ pipeline.start() - + lastPrintTime = 0.0 while pipeline.isRunning(): inNNData: dai.NNData = qNNData.get() tensor = inNNData.getFirstTensor() assert(isinstance(tensor, np.ndarray)) - print(f"Received NN data: {tensor.shape}") + now = time.monotonic() + if now - lastPrintTime >= 1.0: + print(f"Received NN data: {tensor.shape}") + lastPrintTime = now diff --git a/examples/python/ObjectTracker/object_tracker.py b/examples/python/ObjectTracker/object_tracker.py index 26990b0f34..574d8e6d86 100644 --- a/examples/python/ObjectTracker/object_tracker.py +++ b/examples/python/ObjectTracker/object_tracker.py @@ -7,21 +7,18 @@ fullFrameTracking = False useSpatialAssociation = False +fps = 20 # Create pipeline with dai.Pipeline() as pipeline: # Define sources and outputs - camRgb = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_A) - monoLeft = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_B) - monoRight = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_C) + colorSockets = pipeline.getDefaultDevice().getConnectedCameras(dai.CameraSensorType.COLOR) + colorSocket = colorSockets[0] if colorSockets else dai.CameraBoardSocket.CAM_A + camRgb = pipeline.create(dai.node.Camera).build(colorSocket, sensorFps=fps) - stereo = pipeline.create(dai.node.StereoDepth) - leftOutput = monoLeft.requestOutput((640, 400)) - rightOutput = monoRight.requestOutput((640, 400)) - leftOutput.link(stereo.left) - rightOutput.link(stereo.right) + depth = pipeline.create(dai.node.Depth).build(dai.node.Depth.Algorithm.AUTO, fps, (640, 400)) - spatialDetectionNetwork = pipeline.create(dai.node.SpatialDetectionNetwork).build(camRgb, stereo, "yolov6-nano") + spatialDetectionNetwork = pipeline.create(dai.node.SpatialDetectionNetwork).build(camRgb, depth, "yolov6-nano") objectTracker = pipeline.create(dai.node.ObjectTracker) spatialDetectionNetwork.setConfidenceThreshold(0.6) diff --git a/examples/python/ObjectTracker/object_tracker_remap.py b/examples/python/ObjectTracker/object_tracker_remap.py index d172e86741..e378565aa0 100644 --- a/examples/python/ObjectTracker/object_tracker_remap.py +++ b/examples/python/ObjectTracker/object_tracker_remap.py @@ -2,74 +2,34 @@ import cv2 import depthai as dai -import numpy as np - -def colorizeDepth(frameDepth): - invalidMask = frameDepth == 0 - # Log the depth, minDepth and maxDepth - try: - minDepth = np.percentile(frameDepth[frameDepth != 0], 3) - maxDepth = np.percentile(frameDepth[frameDepth != 0], 95) - logDepth = np.zeros_like(frameDepth, dtype=np.float32) - np.log(frameDepth, where=frameDepth != 0, out=logDepth) - logMinDepth = np.log(minDepth) - logMaxDepth = np.log(maxDepth) - np.nan_to_num(logDepth, copy=False, nan=logMinDepth) - # Clip the values to be in the 0-255 range - logDepth = np.clip(logDepth, logMinDepth, logMaxDepth) - - # Interpolate only valid logDepth values, setting the rest based on the mask - depthFrameColor = np.interp(logDepth, (logMinDepth, logMaxDepth), (0, 255)) - depthFrameColor = np.nan_to_num(depthFrameColor) - depthFrameColor = depthFrameColor.astype(np.uint8) - depthFrameColor = cv2.applyColorMap(depthFrameColor, cv2.COLORMAP_JET) - # Set invalid depth pixels to black - depthFrameColor[invalidMask] = 0 - except IndexError: - # Frame is likely empty - depthFrameColor = np.zeros((frameDepth.shape[0], frameDepth.shape[1], 3), dtype=np.uint8) - except Exception as e: - raise e - return depthFrameColor # Create pipeline with dai.Pipeline() as pipeline: - cameraNode = pipeline.create(dai.node.Camera).build() + colorSockets = pipeline.getDefaultDevice().getConnectedCameras(dai.CameraSensorType.COLOR) + colorSocket = colorSockets[0] if colorSockets else dai.CameraBoardSocket.CAM_A + cameraNode = pipeline.create(dai.node.Camera).build(colorSocket) detectionNetwork = pipeline.create(dai.node.DetectionNetwork).build(cameraNode, dai.NNModelDescription("yolov6-nano")) objectTracker = pipeline.create(dai.node.ObjectTracker) labelMap = detectionNetwork.getClasses() - monoLeft = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_B) - monoRight = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_C) - stereo = pipeline.create(dai.node.StereoDepth) - - # Linking - monoLeftOut = monoLeft.requestOutput((1280, 720)) - monoRightOut = monoRight.requestOutput((1280, 720)) - monoLeftOut.link(stereo.left) - monoRightOut.link(stereo.right) + depth = pipeline.create(dai.node.Depth).build(dai.node.Depth.Algorithm.AUTO) detectionNetwork.out.link(objectTracker.inputDetections) detectionNetwork.passthrough.link(objectTracker.inputDetectionFrame) detectionNetwork.passthrough.link(objectTracker.inputTrackerFrame) - stereo.setRectification(True) - stereo.setExtendedDisparity(True) - stereo.setLeftRightCheck(True) - stereo.setSubpixel(True) - - qRgb = detectionNetwork.passthrough.createOutputQueue() qTrack = objectTracker.out.createOutputQueue() - qDepth = stereo.disparity.createOutputQueue() + qDepth = depth.depth.createOutputQueue() pipeline.start() def displayFrame(name: str, frame: dai.ImgFrame, tracklets: dai.Tracklets): color = (0, 255, 0) assert tracklets.getTransformation() is not None - cvFrame = frame.getFrame() if frame.getType() == dai.ImgFrame.Type.RAW16 else frame.getCvFrame() if(frame.getType() == dai.ImgFrame.Type.RAW16): - cvFrame = colorizeDepth(cvFrame) + cvFrame = dai.utility.colorizeDepthFrame(frame).getCvFrame() + else: + cvFrame = frame.getCvFrame() for tracklet in tracklets.tracklets: # Get the shape of the frame from which the detections originated for denormalization normShape = tracklets.getTransformation().getSize() diff --git a/examples/python/PointCloud/point_cloud.py b/examples/python/PointCloud/point_cloud.py index 5cbc55df6e..8e2b58d924 100644 --- a/examples/python/PointCloud/point_cloud.py +++ b/examples/python/PointCloud/point_cloud.py @@ -6,34 +6,22 @@ pipeline = dai.Pipeline() # Cameras -left = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_B) -right = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_C) -color = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_A) - -# Stereo depth -stereo = pipeline.create(dai.node.StereoDepth) -left.requestFullResolutionOutput().link(stereo.left) -right.requestFullResolutionOutput().link(stereo.right) +colorSockets = pipeline.getDefaultDevice().getConnectedCameras(dai.CameraSensorType.COLOR) +colorSocket = colorSockets[0] if colorSockets else dai.CameraBoardSocket.CAM_A +color = pipeline.create(dai.node.Camera).build(colorSocket) # Color output aligned to depth colorOut = color.requestOutput((640, 400), type=dai.ImgFrame.Type.RGB888i, resizeMode=dai.ImgResizeMode.CROP, enableUndistortion=True) +depth = pipeline.create(dai.node.Depth).build(dai.node.Depth.Algorithm.AUTO, None, (640, 400)) +depth.setAlignTo(colorOut) + # Point cloud pc = pipeline.create(dai.node.PointCloud) pc.initialConfig.setLengthUnit(dai.LengthUnit.METER) -# Align depth to color on RVC4 -platform = pipeline.getDefaultDevice().getPlatform() -if platform == dai.Platform.RVC4: - imageAlign = pipeline.create(dai.node.ImageAlign) - stereo.depth.link(imageAlign.input) - colorOut.link(imageAlign.inputAlignTo) - imageAlign.outputAligned.link(pc.inputDepth) -else: - colorOut.link(stereo.inputAlignTo) - stereo.depth.link(pc.inputDepth) - +depth.depth.link(pc.inputDepth) colorOut.link(pc.inputColor) q = pc.outputPointCloud.createOutputQueue(maxSize=4, blocking=False) diff --git a/examples/python/PointCloud/point_cloud_showcase.py b/examples/python/PointCloud/point_cloud_showcase.py index a2543714c1..fd86398a07 100644 --- a/examples/python/PointCloud/point_cloud_showcase.py +++ b/examples/python/PointCloud/point_cloud_showcase.py @@ -51,18 +51,22 @@ def main() -> None: # nodes configured differently. # ------------------------------------------------------------------ with dai.Pipeline(device) as pipeline: - left = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_B) - right = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_C) - color = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_A) - stereo = pipeline.create(dai.node.StereoDepth) - left.requestFullResolutionOutput().link(stereo.left) - right.requestFullResolutionOutput().link(stereo.right) + colorSockets = device.getConnectedCameras(dai.CameraSensorType.COLOR) + colorSocket = colorSockets[0] if colorSockets else dai.CameraBoardSocket.CAM_A + color = pipeline.create(dai.node.Camera).build(colorSocket) + colorOut = color.requestOutput( + (640, 400), type=dai.ImgFrame.Type.RGB888i, + resizeMode=dai.ImgResizeMode.CROP, enableUndistortion=True, + ) + + depth = pipeline.create(dai.node.Depth).build(dai.node.Depth.Algorithm.AUTO, None, (640, 400)) + depth.setAlignTo(colorOut) # ── 1. Filtered point cloud (METER) ──── pcSparse = pipeline.create(dai.node.PointCloud) pcSparse.setRunOnHost(True) pcSparse.initialConfig.setLengthUnit(dai.LengthUnit.METER) - stereo.depth.link(pcSparse.inputDepth) + depth.depth.link(pcSparse.inputDepth) qSparse = pcSparse.outputPointCloud.createOutputQueue(maxSize=4, blocking=False) # ── 2. Organized point cloud (MILLIMETER) ─────── @@ -70,7 +74,7 @@ def main() -> None: pcOrganized.setRunOnHost(True) pcOrganized.initialConfig.setLengthUnit(dai.LengthUnit.MILLIMETER) pcOrganized.initialConfig.setOrganized(True) - stereo.depth.link(pcOrganized.inputDepth) + depth.depth.link(pcOrganized.inputDepth) qOrganized = pcOrganized.outputPointCloud.createOutputQueue(maxSize=4, blocking=False) # ── 3. Transform pointcloud into another device's coordinate system ─── @@ -80,7 +84,7 @@ def main() -> None: pcCam.initialConfig.setTargetCoordinateSystem(dai.CameraBoardSocket.CAM_A) # Or transform to a housing coordinate system instead, e.g.: # pcCam.initialConfig.setTargetCoordinateSystem(dai.HousingCoordinateSystem.VESA_A) - stereo.depth.link(pcCam.inputDepth) + depth.depth.link(pcCam.inputDepth) qCam = pcCam.outputPointCloud.createOutputQueue(maxSize=4, blocking=False) # ── 4. Custom 4×4 transform (90° Z rotation) + passthrough ────── @@ -95,7 +99,7 @@ def main() -> None: [0.0, 0.0, 0.0, 1.0], ] pcCustom.initialConfig.setTransformationMatrix(transform) - stereo.depth.link(pcCustom.inputDepth) + depth.depth.link(pcCustom.inputDepth) qCustom = pcCustom.outputPointCloud.createOutputQueue(maxSize=4, blocking=False) qDepth = pcCustom.passthroughDepth.createOutputQueue(maxSize=4, blocking=False) @@ -103,19 +107,7 @@ def main() -> None: pcColorized = pipeline.create(dai.node.PointCloud) pcColorized.setRunOnHost(True) pcColorized.initialConfig.setLengthUnit(dai.LengthUnit.METER) - colorOut = color.requestOutput( - (640, 400), type=dai.ImgFrame.Type.RGB888i, - resizeMode=dai.ImgResizeMode.CROP, enableUndistortion=True, - ) - platform = pipeline.getDefaultDevice().getPlatform() - if platform == dai.Platform.RVC4: - imageAlign = pipeline.create(dai.node.ImageAlign) - stereo.depth.link(imageAlign.input) - colorOut.link(imageAlign.inputAlignTo) - imageAlign.outputAligned.link(pcColorized.inputDepth) - else: - colorOut.link(stereo.inputAlignTo) - stereo.depth.link(pcColorized.inputDepth) + depth.depth.link(pcColorized.inputDepth) colorOut.link(pcColorized.inputColor) qColorized = pcColorized.outputPointCloud.createOutputQueue(maxSize=4, blocking=False) diff --git a/examples/python/PointCloud/point_cloud_visualizer.py b/examples/python/PointCloud/point_cloud_visualizer.py index c0835f8320..e7b26c26d0 100644 --- a/examples/python/PointCloud/point_cloud_visualizer.py +++ b/examples/python/PointCloud/point_cloud_visualizer.py @@ -19,19 +19,6 @@ import depthai as dai -def colorizeDepth(frame: np.ndarray) -> np.ndarray: - """Normalize a uint16 depth frame and apply a colormap for display.""" - downscaled = frame[::4, ::4] - nonZero = downscaled[downscaled != 0] - if nonZero.size == 0: - minD, maxD = 0, 1 - else: - minD = np.percentile(nonZero, 1) - maxD = np.percentile(nonZero, 99) - colored = np.interp(frame, (minD, maxD), (0, 255)).astype(np.uint8) - return cv2.applyColorMap(colored, cv2.COLORMAP_HOT) - - def main() -> None: print("PointCloud Visualizer") print("=====================") @@ -41,34 +28,23 @@ def main() -> None: print(f"Device: {device.getDeviceName()} (ID: {device.getDeviceId()})\n") with dai.Pipeline(device) as pipeline: - # ── Camera + StereoDepth ────────────────────────────────────── - left = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_B) - right = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_C) - color = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_A) - stereo = pipeline.create(dai.node.StereoDepth) - left.requestOutput((640, 400)).link(stereo.left) - right.requestOutput((640, 400)).link(stereo.right) - - # Align depth to color camera - platform = pipeline.getDefaultDevice().getPlatform() + # ── Camera + Depth ───────────────────────────────────── + colorSockets = device.getConnectedCameras(dai.CameraSensorType.COLOR) + colorSocket = colorSockets[0] if colorSockets else dai.CameraBoardSocket.CAM_A + color = pipeline.create(dai.node.Camera).build(colorSocket) colorOut = color.requestOutput( (640, 400), type=dai.ImgFrame.Type.RGB888i, resizeMode=dai.ImgResizeMode.CROP, enableUndistortion=True, ) - if platform == dai.Platform.RVC4: - align = pipeline.create(dai.node.ImageAlign) - stereo.depth.link(align.input) - colorOut.link(align.inputAlignTo) - alignedDepth = align.outputAligned - else: - colorOut.link(stereo.inputAlignTo) - alignedDepth = stereo.depth + + depth = pipeline.create(dai.node.Depth).build(dai.node.Depth.Algorithm.AUTO, None, (640, 400)) + depth.setAlignTo(colorOut) # ── PointCloud node ─────────────────────────────────────────── pc = pipeline.create(dai.node.PointCloud) pc.setRunOnHost(True) pc.initialConfig.setLengthUnit(dai.LengthUnit.METER) - alignedDepth.link(pc.inputDepth) + depth.depth.link(pc.inputDepth) colorOut.link(pc.inputColor) queue = pc.outputPointCloud.createOutputQueue(maxSize=4, blocking=False) @@ -124,7 +100,7 @@ def main() -> None: # Show colorized depth in an OpenCV window depthMsg = qDepth.tryGet() if depthMsg is not None: - cv2.imshow("Depth", colorizeDepth(depthMsg.getCvFrame())) + cv2.imshow("Depth", dai.utility.colorizeDepthFrame(depthMsg, 300, 12000, cv2.COLORMAP_HOT, useLog=True).getCvFrame()) if cv2.waitKey(1) == ord("q"): break diff --git a/examples/python/RGBD/rgbd.py b/examples/python/RGBD/rgbd.py index 3153191107..9802a39ad0 100644 --- a/examples/python/RGBD/rgbd.py +++ b/examples/python/RGBD/rgbd.py @@ -35,41 +35,15 @@ def run(self): with dai.Pipeline() as p: fps = 30 # Define sources and outputs - left = p.create(dai.node.Camera) - right = p.create(dai.node.Camera) - color = p.create(dai.node.Camera) - stereo = p.create(dai.node.StereoDepth) - rgbd = p.create(dai.node.RGBD).build() - align = None - color.build() + colorSockets = p.getDefaultDevice().getConnectedCameras(dai.CameraSensorType.COLOR) + colorSocket = colorSockets[0] if colorSockets else dai.CameraBoardSocket.CAM_A + color = p.create(dai.node.Camera).build(colorSocket) + depth = p.create(dai.node.Depth).build(dai.node.Depth.Algorithm.AUTO, fps, (640, 400)) rerunViewer = p.create(RerunNode) - left.build(dai.CameraBoardSocket.CAM_B) - right.build(dai.CameraBoardSocket.CAM_C) - out = None - stereo.setRectifyEdgeFillColor(0) - stereo.enableDistortionCorrection(True) - stereo.setDefaultProfilePreset(dai.node.StereoDepth.PresetMode.DEFAULT) - stereo.initialConfig.postProcessing.thresholdFilter.maxRange = 10000 + rgbd = p.create(dai.node.RGBD).build(color, depth, (640, 400), fps) rgbd.setDepthUnits(dai.StereoDepthConfig.AlgorithmControl.DepthUnit.METER) - # Linking - left.requestOutput((640, 400)).link(stereo.left) - right.requestOutput((640, 400)).link(stereo.right) - platform = p.getDefaultDevice().getPlatform() - - if platform == dai.Platform.RVC4: - out = color.requestOutput((640,400), dai.ImgFrame.Type.RGB888i, enableUndistortion=True) - align = p.create(dai.node.ImageAlign) - stereo.depth.link(align.input) - out.link(align.inputAlignTo) - align.outputAligned.link(rgbd.inDepth) - else: - out = color.requestOutput((640,400), dai.ImgFrame.Type.RGB888i, dai.ImgResizeMode.CROP, 30, True) - stereo.depth.link(rgbd.inDepth) - out.link(stereo.inputAlignTo) - out.link(rgbd.inColor) - rgbd.pcl.link(rerunViewer.inputPCL) p.start() diff --git a/examples/python/RGBD/rgbd_o3d.py b/examples/python/RGBD/rgbd_o3d.py index 57ffc2bc85..d8651fc877 100644 --- a/examples/python/RGBD/rgbd_o3d.py +++ b/examples/python/RGBD/rgbd_o3d.py @@ -59,39 +59,13 @@ def key_callback(vis, action, mods): with dai.Pipeline() as p: fps = 30 # Define sources and outputs - left = p.create(dai.node.Camera) - right = p.create(dai.node.Camera) - color = p.create(dai.node.Camera) - stereo = p.create(dai.node.StereoDepth) - rgbd = p.create(dai.node.RGBD).build() - align = None - color.build() + colorSockets = p.getDefaultDevice().getConnectedCameras(dai.CameraSensorType.COLOR) + colorSocket = colorSockets[0] if colorSockets else dai.CameraBoardSocket.CAM_A + color = p.create(dai.node.Camera).build(colorSocket) + depth = p.create(dai.node.Depth).build(dai.node.Depth.Algorithm.AUTO, fps, (640, 400)) o3dViewer = p.create(O3DNode) - left.build(dai.CameraBoardSocket.CAM_B) - right.build(dai.CameraBoardSocket.CAM_C) - out = None - stereo.setDefaultProfilePreset(dai.node.StereoDepth.PresetMode.DEFAULT) - stereo.setRectifyEdgeFillColor(0) - stereo.enableDistortionCorrection(True) - - # Linking - left.requestOutput((640, 400)).link(stereo.left) - right.requestOutput((640, 400)).link(stereo.right) - platform = p.getDefaultDevice().getPlatform() - if platform == dai.Platform.RVC4: - out = color.requestOutput((640, 400), dai.ImgFrame.Type.RGB888i, enableUndistortion=True) - align = p.create(dai.node.ImageAlign) - stereo.depth.link(align.input) - out.link(align.inputAlignTo) - align.outputAligned.link(rgbd.inDepth) - else: - out = color.requestOutput( - (640, 400), dai.ImgFrame.Type.RGB888i, dai.ImgResizeMode.CROP, 30, True - ) - stereo.depth.link(rgbd.inDepth) - out.link(stereo.inputAlignTo) - out.link(rgbd.inColor) + rgbd = p.create(dai.node.RGBD).build(color, depth, (640, 400), fps) rgbd.pcl.link(o3dViewer.inputPCL) diff --git a/examples/python/RGBD/rgbd_pcl_processing.py b/examples/python/RGBD/rgbd_pcl_processing.py index f372aa1886..4c21e3a14b 100644 --- a/examples/python/RGBD/rgbd_pcl_processing.py +++ b/examples/python/RGBD/rgbd_pcl_processing.py @@ -36,7 +36,11 @@ def run(self): remoteConnector = dai.RemoteConnection( webSocketPort=args.webSocketPort, httpPort=args.httpPort ) - rgbd = p.create(dai.node.RGBD).build(True, dai.node.StereoDepth.PresetMode.DEFAULT) + colorSockets = p.getDefaultDevice().getConnectedCameras(dai.CameraSensorType.COLOR) + colorSocket = colorSockets[0] if colorSockets else dai.CameraBoardSocket.CAM_A + color = p.create(dai.node.Camera).build(colorSocket) + depth = p.create(dai.node.Depth).build(dai.node.Depth.Algorithm.AUTO, None, (640, 400)) + rgbd = p.create(dai.node.RGBD).build(color, depth) customNode = p.create(CustomPCLProcessingNode) # Link rgbd.pcl to the input of CustomPCLProcessingNode diff --git a/examples/python/RGBD/visualizer_rgbd.py b/examples/python/RGBD/visualizer_rgbd.py index 3ca5b23b80..d0af4d48e6 100644 --- a/examples/python/RGBD/visualizer_rgbd.py +++ b/examples/python/RGBD/visualizer_rgbd.py @@ -1,16 +1,12 @@ -import time import depthai as dai from argparse import ArgumentParser -NEURAL_FPS = 8 -STEREO_DEFAULT_FPS = 30 -TOF_DEFAULT_FPS = 30 +fps = 30 parser = ArgumentParser() parser.add_argument("--webSocketPort", type=int, default=8765) parser.add_argument("--httpPort", type=int, default=8082) -parser.add_argument("--depthSource", type=str, default="stereo", choices=["stereo", "neural", "tof"]) args = parser.parse_args() with dai.Pipeline() as p: @@ -19,42 +15,18 @@ ) size = (640, 400) - if args.depthSource == "neural": - fps = NEURAL_FPS - elif args.depthSource == "tof": - fps = TOF_DEFAULT_FPS - else: - fps = STEREO_DEFAULT_FPS - - if args.depthSource == "stereo": - color = p.create(dai.node.Camera).build(sensorFps=fps) - left = p.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_B, sensorFps=fps) - right = p.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_C, sensorFps=fps) - depthSource = p.create(dai.node.StereoDepth) - depthSource.setDefaultProfilePreset(dai.node.StereoDepth.PresetMode.DEFAULT) - depthSource.setRectifyEdgeFillColor(0) - depthSource.enableDistortionCorrection(True) - left.requestOutput(size).link(depthSource.left) - right.requestOutput(size).link(depthSource.right) - elif args.depthSource == "neural": - color = p.create(dai.node.Camera).build(sensorFps=fps) - left = p.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_B, sensorFps=fps) - right = p.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_C, sensorFps=fps) - depthSource = p.create(dai.node.NeuralDepth).build(left.requestOutput(size), right.requestOutput(size), dai.DeviceModelZoo.NEURAL_DEPTH_LARGE) - elif args.depthSource == "tof": - color = p.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_C, sensorFps=fps) - socket, profile = dai.CameraBoardSocket.AUTO, dai.ToFConfig.Profile.MID_RANGE - depthSource = p.create(dai.node.ToF).build(socket, profile) - else: - raise ValueError(f"Invalid depth source: {args.depthSource}") - - rgbd = p.create(dai.node.RGBD).build(color, depthSource, size, fps) + colorSockets = p.getDefaultDevice().getConnectedCameras(dai.CameraSensorType.COLOR) + colorSocket = colorSockets[0] if colorSockets else dai.CameraBoardSocket.CAM_A + color = p.create(dai.node.Camera).build(colorSocket, sensorFps=fps) + depth = p.create(dai.node.Depth).build(dai.node.Depth.Algorithm.AUTO, fps, size) + + rgbd = p.create(dai.node.RGBD).build(color, depth, size, fps) remoteConnector.addTopic("pcl", rgbd.pcl, "common") p.start() remoteConnector.registerPipeline(p) - print("Pipeline started with depth source: ", args.depthSource) + print("Pipeline started") while p.isRunning(): key = remoteConnector.waitKey(1) diff --git a/examples/python/RGBD/visualizer_rgbd_autocreate.py b/examples/python/RGBD/visualizer_rgbd_autocreate.py index 7015d5f606..631799477d 100644 --- a/examples/python/RGBD/visualizer_rgbd_autocreate.py +++ b/examples/python/RGBD/visualizer_rgbd_autocreate.py @@ -13,7 +13,11 @@ remoteConnector = dai.RemoteConnection( webSocketPort=args.webSocketPort, httpPort=args.httpPort ) - rgbd = p.create(dai.node.RGBD).build(True, dai.node.StereoDepth.PresetMode.DEFAULT) + colorSockets = p.getDefaultDevice().getConnectedCameras(dai.CameraSensorType.COLOR) + colorSocket = colorSockets[0] if colorSockets else dai.CameraBoardSocket.CAM_A + color = p.create(dai.node.Camera).build(colorSocket) + depth = p.create(dai.node.Depth).build(dai.node.Depth.Algorithm.AUTO, None, (640, 400)) + rgbd = p.create(dai.node.RGBD).build(color, depth) remoteConnector.addTopic("pcl", rgbd.pcl, "common") p.start() diff --git a/examples/python/Remapping/point_remapping.py b/examples/python/Remapping/point_remapping.py index 1b4face666..b0297084e3 100644 --- a/examples/python/Remapping/point_remapping.py +++ b/examples/python/Remapping/point_remapping.py @@ -114,7 +114,7 @@ def sampleDepth(point, depthFrame, patchRadius=2): rightStatus = f"R projection failed: {exc}" rgbStatus = f"RGB projection failed: {exc}" - depthColor = cv2.applyColorMap(cv2.convertScaleAbs(depthFrame.getFrame(), alpha=0.05), cv2.COLORMAP_JET) + depthColor = dai.utility.colorizeDepthFrame(depthFrame).getCvFrame() drawPoint(leftFrame, originalPoint, f"{sourceStatus}", (0, 255, 0)) drawPoint(rgbDisplay, remappedRgbPoint, f"{rgbStatus}", (255, 255, 0)) drawPoint(depthColor, remappedDepthPoint, f"{depthStatus}", (0, 0, 255)) diff --git a/examples/python/Script/script_simple.py b/examples/python/Script/script_simple.py index 02d42082ce..7ac5df5312 100644 --- a/examples/python/Script/script_simple.py +++ b/examples/python/Script/script_simple.py @@ -25,8 +25,6 @@ with pipeline: while pipeline.isRunning(): message = dai.ImgFrame() - print("Sending a message") inputQueue.send(message) output = outputQueue.get() - print("Received a message") time.sleep(1) diff --git a/examples/python/SpatialDetectionNetwork/spatial_detection.py b/examples/python/SpatialDetectionNetwork/spatial_detection.py index a0439c5e1d..58d6a34882 100644 --- a/examples/python/SpatialDetectionNetwork/spatial_detection.py +++ b/examples/python/SpatialDetectionNetwork/spatial_detection.py @@ -1,28 +1,10 @@ #!/usr/bin/env python3 -import argparse -from pathlib import Path import cv2 import depthai as dai -import numpy as np -NEURAL_FPS = 8 -STEREO_DEFAULT_FPS = 20 - -parser = argparse.ArgumentParser() -parser.add_argument( - "--depthSource", type=str, default="stereo", choices=["stereo", "neural"] -) -args = parser.parse_args() -# For better results on OAK4, use a segmentation model like "luxonis/yolov8-instance-segmentation-large:coco-640x480" -# for depth estimation over the objects mask instead of the full bounding box. +fps = 20 modelDescription = dai.NNModelDescription("yolov6-nano") -size = (640, 400) - -if args.depthSource == "stereo": - fps = STEREO_DEFAULT_FPS -else: - fps = NEURAL_FPS class SpatialVisualizer(dai.node.HostNode): def __init__(self): @@ -32,20 +14,12 @@ def build(self, depth:dai.Node.Output, detections: dai.Node.Output, rgb: dai.Nod self.link_args(depth, detections, rgb) # Must match the inputs to the process method def process(self, depthPreview, detections, rgbPreview): - depthPreview = depthPreview.getCvFrame() rgbPreview = rgbPreview.getCvFrame() depthFrameColor = self.processDepthFrame(depthPreview) self.displayResults(rgbPreview, depthFrameColor, detections.detections) def processDepthFrame(self, depthFrame): - depthDownscaled = depthFrame[::4] - if np.all(depthDownscaled == 0): - minDepth = 0 - else: - minDepth = np.percentile(depthDownscaled[depthDownscaled != 0], 1) - maxDepth = np.percentile(depthDownscaled, 99) - depthFrameColor = np.interp(depthFrame, (minDepth, maxDepth), (0, 255)).astype(np.uint8) - return cv2.applyColorMap(depthFrameColor, cv2.COLORMAP_HOT) + return dai.utility.colorizeDepthFrame(depthFrame, colormap=cv2.COLORMAP_HOT).getCvFrame() def displayResults(self, rgbFrame, depthFrameColor, detections): height, width, _ = rgbFrame.shape @@ -83,27 +57,13 @@ def drawDetections(self, frame, detection, frameWidth, frameHeight): # Creates the pipeline and a default device implicitly with dai.Pipeline() as p: # Define sources and outputs - platform = p.getDefaultDevice().getPlatform() - - camRgb = p.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_A, sensorFps=fps) - monoLeft = p.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_B, sensorFps=fps) - monoRight = p.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_C, sensorFps=fps) - if args.depthSource == "stereo": - depthSource = p.create(dai.node.StereoDepth) - depthSource.setExtendedDisparity(True) - monoLeft.requestOutput(size).link(depthSource.left) - monoRight.requestOutput(size).link(depthSource.right) - elif args.depthSource == "neural": - depthSource = p.create(dai.node.NeuralDepth).build( - monoLeft.requestFullResolutionOutput(), - monoRight.requestFullResolutionOutput(), - dai.DeviceModelZoo.NEURAL_DEPTH_LARGE, - ) - else: - raise ValueError(f"Invalid depth source: {args.depthSource}") + colorSockets = p.getDefaultDevice().getConnectedCameras(dai.CameraSensorType.COLOR) + colorSocket = colorSockets[0] if colorSockets else dai.CameraBoardSocket.CAM_A + camRgb = p.create(dai.node.Camera).build(colorSocket, sensorFps=fps) + depth = p.create(dai.node.Depth).build(dai.node.Depth.Algorithm.AUTO, fps, (640, 400)) spatialDetectionNetwork = p.create(dai.node.SpatialDetectionNetwork).build( - camRgb, depthSource, modelDescription + camRgb, depth, modelDescription ) visualizer = p.create(SpatialVisualizer) @@ -118,6 +78,6 @@ def drawDetections(self, frame, detection, frameWidth, frameHeight): spatialDetectionNetwork.passthrough, ) - print("Starting pipeline with depth source: ", args.depthSource) + print("Starting pipeline") p.run() diff --git a/examples/python/SpatialLocationCalculator/spatial_keypoints.py b/examples/python/SpatialLocationCalculator/spatial_keypoints.py index 4044e68fc9..5c0cb1993e 100644 --- a/examples/python/SpatialLocationCalculator/spatial_keypoints.py +++ b/examples/python/SpatialLocationCalculator/spatial_keypoints.py @@ -99,8 +99,7 @@ assert isinstance(passthrough, dai.ImgFrame) assert isinstance(depthFrame, dai.ImgFrame) - depthImg = depthFrame.getCvFrame() - colorizedDepth = cv2.applyColorMap(cv2.convertScaleAbs(depthImg, alpha=0.03), cv2.COLORMAP_JET) + colorizedDepth = dai.utility.colorizeDepthFrame(depthFrame).getCvFrame() image = passthrough.getCvFrame() filterKeypoints = [0, 3, 4, 7, 8, 13, 14, 15, 16] # filter out nose, ears, elbows, knees, ankles diff --git a/examples/python/SpatialLocationCalculator/spatial_location_calculator.py b/examples/python/SpatialLocationCalculator/spatial_location_calculator.py index 8c8caaa690..3c102fb1df 100755 --- a/examples/python/SpatialLocationCalculator/spatial_location_calculator.py +++ b/examples/python/SpatialLocationCalculator/spatial_location_calculator.py @@ -14,20 +14,9 @@ bottomRight = dai.Point2f(0.6, 0.6) # Define sources and outputs -monoLeft = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_B) -monoRight = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_C) -stereo = pipeline.create(dai.node.StereoDepth) +depth = pipeline.create(dai.node.Depth).build(dai.node.Depth.Algorithm.AUTO, None, (640, 400)) spatialLocationCalculator = pipeline.create(dai.node.SpatialLocationCalculator) -# Linking -monoLeftOut = monoLeft.requestOutput((640, 400)) -monoRightOut = monoRight.requestOutput((640, 400)) -monoLeftOut.link(stereo.left) -monoRightOut.link(stereo.right) - -stereo.setRectification(True) -stereo.setExtendedDisparity(True) - stepSize = 0.05 config = dai.SpatialLocationCalculatorConfigData() @@ -43,7 +32,7 @@ xoutSpatialQueue = spatialLocationCalculator.out.createOutputQueue() outputDepthQueue = spatialLocationCalculator.passthroughDepth.createOutputQueue() -stereo.depth.link(spatialLocationCalculator.inputDepth) +depth.depth.link(spatialLocationCalculator.inputDepth) inputConfigQueue = spatialLocationCalculator.inputConfig.createInputQueue() @@ -56,13 +45,10 @@ print("Use WASD keys to move ROI!") outputDepthIMage : dai.ImgFrame = outputDepthQueue.get() - frameDepth = outputDepthIMage.getCvFrame() frameDepth = outputDepthIMage.getFrame() print("Median depth value: ", np.median(frameDepth)) - depthFrameColor = cv2.normalize(frameDepth, None, 255, 0, cv2.NORM_INF, cv2.CV_8UC1) - depthFrameColor = cv2.equalizeHist(depthFrameColor) - depthFrameColor = cv2.applyColorMap(depthFrameColor, cv2.COLORMAP_HOT) + depthFrameColor = dai.utility.colorizeDepthFrame(outputDepthIMage).getCvFrame() for depthData in spatialData: roi = depthData.config.roi roi = roi.denormalize(width=depthFrameColor.shape[1], height=depthFrameColor.shape[0]) diff --git a/examples/python/SpatialLocationCalculator/spatial_segmentation.py b/examples/python/SpatialLocationCalculator/spatial_segmentation.py index 0fc15f625f..164d371077 100644 --- a/examples/python/SpatialLocationCalculator/spatial_segmentation.py +++ b/examples/python/SpatialLocationCalculator/spatial_segmentation.py @@ -62,8 +62,7 @@ def addTopPanel(image: np.ndarray, useSegmentation: bool) -> np.ndarray: assert isinstance(rgbFrame, dai.ImgFrame) assert isinstance(depthFrame, dai.ImgFrame) - depthImg = depthFrame.getCvFrame() - colorizedDepth = cv2.applyColorMap(cv2.convertScaleAbs(depthImg, alpha=0.03), cv2.COLORMAP_JET) + colorizedDepth = dai.utility.colorizeDepthFrame(depthFrame).getCvFrame() image = rgbFrame.getCvFrame() segmentationMask = cv2.Mat(np.zeros((spatialDetections.getSegmentationMaskHeight(), spatialDetections.getSegmentationMaskWidth()), dtype=np.uint8)) segmentationMask = spatialDetections.getCvSegmentationMask() diff --git a/examples/python/StereoDepth/stereo.py b/examples/python/StereoDepth/stereo.py index 14d3113ac7..9592372f5e 100644 --- a/examples/python/StereoDepth/stereo.py +++ b/examples/python/StereoDepth/stereo.py @@ -2,7 +2,6 @@ import cv2 import depthai as dai -import numpy as np pipeline = dai.Pipeline() monoLeft = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_B) @@ -19,21 +18,15 @@ stereo.setExtendedDisparity(True) stereo.setLeftRightCheck(True) -disparityQueue = stereo.disparity.createOutputQueue() - -colorMap = cv2.applyColorMap(np.arange(256, dtype=np.uint8), cv2.COLORMAP_JET) -colorMap[0] = [0, 0, 0] # to make zero-disparity pixels black +depthQueue = stereo.depth.createOutputQueue() with pipeline: pipeline.start() - maxDisparity = 1 while pipeline.isRunning(): - disparity = disparityQueue.get() - assert isinstance(disparity, dai.ImgFrame) - npDisparity = disparity.getFrame() - maxDisparity = max(maxDisparity, np.max(npDisparity)) - colorizedDisparity = cv2.applyColorMap(((npDisparity / maxDisparity) * 255).astype(np.uint8), colorMap) - cv2.imshow("disparity", colorizedDisparity) + depth = depthQueue.get() + assert isinstance(depth, dai.ImgFrame) + colorizedDepth = dai.utility.colorizeDepthFrame(depth).getCvFrame() + cv2.imshow("depth", colorizedDepth) key = cv2.waitKey(1) if key == ord('q'): pipeline.stop() diff --git a/examples/python/StereoDepth/stereo_depth_from_host.py b/examples/python/StereoDepth/stereo_depth_from_host.py index bb8744e5dd..513c21c9ba 100644 --- a/examples/python/StereoDepth/stereo_depth_from_host.py +++ b/examples/python/StereoDepth/stereo_depth_from_host.py @@ -1125,9 +1125,6 @@ def __init__(self, config): def convertToCv2Frame(name, image, config): maxDisp = config.getMaxDisparity() - subpixelLevels = pow(2, config.algorithmControl.subpixelFractionalBits) - subpixel = config.algorithmControl.enableSubpixel - dispIntegerLevels = maxDisp if not subpixel else maxDisp / subpixelLevels frame = image.getFrame() @@ -1140,8 +1137,9 @@ def convertToCv2Frame(name, image, config): if np.isnan(frame).any() or np.isinf(frame).any(): frame = np.nan_to_num(frame, nan=0, posinf=0, neginf=0) - frame = np.clip(frame * 255. / dispIntegerLevels, 0, 255).astype(np.uint8) - frame = cv2.applyColorMap(frame, cv2.COLORMAP_HOT) + depthImg = dai.ImgFrame() + depthImg.setCvFrame(frame.astype(np.float32), dai.ImgFrame.Type.GRAYF16) + frame = dai.utility.colorizeDepthFrame(depthImg,colormap=cv2.COLORMAP_HOT).getCvFrame() elif "confidence_map" in name: pass elif name == "disparity_cost_dump": diff --git a/examples/python/StereoDepth/stereo_depth_remap.py b/examples/python/StereoDepth/stereo_depth_remap.py index 9cdfd89d63..1d80df6b6b 100644 --- a/examples/python/StereoDepth/stereo_depth_remap.py +++ b/examples/python/StereoDepth/stereo_depth_remap.py @@ -1,6 +1,7 @@ import depthai as dai import cv2 import numpy as np +import time def draw_rotated_rectangle(frame, center, size, angle, color, thickness=2): """ @@ -24,15 +25,8 @@ def draw_rotated_rectangle(frame, center, size, angle, color, thickness=2): # Draw the rectangle on the frame cv2.polylines(frame, [box], isClosed=True, color=color, thickness=thickness) -def processDepthFrame(depthFrame): - depth_downscaled = depthFrame[::4] - if np.all(depth_downscaled == 0): - min_depth = 0 - else: - min_depth = np.percentile(depth_downscaled[depth_downscaled != 0], 1) - max_depth = np.percentile(depth_downscaled, 99) - depthFrameColor = np.interp(depthFrame, (min_depth, max_depth), (0, 255)).astype(np.uint8) - return cv2.applyColorMap(depthFrameColor, cv2.COLORMAP_HOT) +def processDepthFrame(depthFrame: dai.ImgFrame): + return dai.utility.colorizeDepthFrame(depthFrame, colormap=cv2.COLORMAP_HOT).getCvFrame() with dai.Pipeline() as pipeline: color = pipeline.create(dai.node.Camera).build(dai.CameraBoardSocket.CAM_A) @@ -57,6 +51,7 @@ def processDepthFrame(depthFrame): stereoOut = stereo.depth.createOutputQueue() pipeline.start() + lastPrintTime = 0.0 while pipeline.isRunning(): colorFrame = colorOut.get() stereoFrame = stereoOut.get() @@ -65,13 +60,16 @@ def processDepthFrame(depthFrame): assert stereoFrame.validateTransformations() clr = colorFrame.getCvFrame() - depth = processDepthFrame(stereoFrame.getCvFrame()) + depth = processDepthFrame(stereoFrame) rect = dai.RotatedRect(dai.Point2f(300, 200), dai.Size2f(200, 100), 10) remappedRect = colorFrame.getTransformation().remapRectTo(stereoFrame.getTransformation(), rect) - print(f"Original rect x: {rect.center.x} y: {rect.center.y} width: {rect.size.width} height: {rect.size.height} angle: {rect.angle}") - print(f"Remapped rect x: {remappedRect.center.x} y: {remappedRect.center.y} width: {remappedRect.size.width} height: {remappedRect.size.height} angle: {remappedRect.angle}") + now = time.monotonic() + if now - lastPrintTime >= 1.0: + print(f"Original rect x: {rect.center.x} y: {rect.center.y} width: {rect.size.width} height: {rect.size.height} angle: {rect.angle}") + print(f"Remapped rect x: {remappedRect.center.x} y: {remappedRect.center.y} width: {remappedRect.size.width} height: {remappedRect.size.height} angle: {remappedRect.angle}") + lastPrintTime = now draw_rotated_rectangle(clr, (rect.center.x, rect.center.y), (rect.size.width, rect.size.height), rect.angle, (255, 0, 0)) draw_rotated_rectangle(depth, (remappedRect.center.x, remappedRect.center.y), (remappedRect.size.width, remappedRect.size.height), remappedRect.angle, (255, 0, 0)) diff --git a/examples/python/StereoDepth/stereo_runtime_calibration_update.py b/examples/python/StereoDepth/stereo_runtime_calibration_update.py index ac5db4a7a5..4d7b9a41d0 100755 --- a/examples/python/StereoDepth/stereo_runtime_calibration_update.py +++ b/examples/python/StereoDepth/stereo_runtime_calibration_update.py @@ -23,27 +23,21 @@ rectifiedLeftQueue = stereo.rectifiedLeft.createOutputQueue() rectifiedRightQueue = stereo.rectifiedRight.createOutputQueue() -disparityQueue = stereo.disparity.createOutputQueue() - -colorMap = cv2.applyColorMap(np.arange(256, dtype=np.uint8), cv2.COLORMAP_JET) -colorMap[0] = [0, 0, 0] # to make zero-disparity pixels black +depthQueue = stereo.depth.createOutputQueue() with pipeline: pipeline.start() - maxDisparity = 1 while pipeline.isRunning(): leftRectified = rectifiedLeftQueue.get() rightRectified = rectifiedRightQueue.get() - disparity = disparityQueue.get() + depth = depthQueue.get() assert isinstance(leftRectified, dai.ImgFrame) assert isinstance(rightRectified, dai.ImgFrame) - assert isinstance(disparity, dai.ImgFrame) + assert isinstance(depth, dai.ImgFrame) cv2.imshow("left", leftRectified.getCvFrame()) cv2.imshow("right", rightRectified.getCvFrame()) - npDisparity = disparity.getFrame() - maxDisparity = max(maxDisparity, np.max(npDisparity)) - colorizedDisparity = cv2.applyColorMap(((npDisparity / maxDisparity) * 255).astype(np.uint8), colorMap) - cv2.imshow("disparity", colorizedDisparity) + colorizedDepth = dai.utility.colorizeDepthFrame(depth).getCvFrame() + cv2.imshow("depth", colorizedDepth) key = cv2.waitKey(1) if key == ord('q'): pipeline.stop() @@ -62,4 +56,3 @@ print("Updated distortion coefficients: ", distortionCoeffs) except: pass - diff --git a/examples/python/Sync/sync.py b/examples/python/Sync/sync.py index bc09ce7822..72126f2725 100644 --- a/examples/python/Sync/sync.py +++ b/examples/python/Sync/sync.py @@ -1,3 +1,5 @@ +import time + import depthai as dai pipeline = dai.Pipeline() @@ -13,9 +15,13 @@ outQueue = sync.out.createOutputQueue() pipeline.start() +lastPrintTime = 0.0 while pipeline.isRunning(): messageGroup : dai.MessageGroup = outQueue.get() left = messageGroup["left"] right = messageGroup["right"] - print(f"Timestamps, message group {messageGroup.getTimestamp()}, left {left.getTimestamp()}, right {right.getTimestamp()}") \ No newline at end of file + now = time.monotonic() + if now - lastPrintTime >= 1.0: + print(f"Timestamps, message group {messageGroup.getTimestamp()}, left {left.getTimestamp()}, right {right.getTimestamp()}") + lastPrintTime = now diff --git a/examples/python/ToF/tof_align.py b/examples/python/ToF/tof_align.py index fada11eb8c..65a3aa3c1d 100644 --- a/examples/python/ToF/tof_align.py +++ b/examples/python/ToF/tof_align.py @@ -2,8 +2,8 @@ """Align ToF depth over left or right camera and show a blended overlay. Usage: - python tof_align_overlay.py --camera left # align over CAM_B (default) - python tof_align_overlay.py --camera right # align over CAM_C + python tof_align.py --camera left + python tof_align.py --camera right """ import argparse @@ -11,31 +11,13 @@ import cv2 import depthai as dai -import numpy as np -FPS = 10.0 +FPS = 30.0 CAMERA_SIZE = (640, 400) -# show depth in range 0.1m - 7m -MIN_DEPTH = 100 -MAX_DEPTH = 7000 - - -def colorizeDepth(frameDepth: np.ndarray, minDepth: float, maxDepth: float) -> np.ndarray: - invalidMask = frameDepth == 0 - try: - logDepth = np.log(frameDepth.astype(np.float32) + 1e-6) - logDepth[invalidMask] = 0.0 - logDepth = np.clip(logDepth, np.log(minDepth + 1e-6), np.log(maxDepth + 1e-6)) - depthFrameColor = np.interp(logDepth, (logDepth[~invalidMask].min(), logDepth[~invalidMask].max()), (0, 255)) - depthFrameColor = depthFrameColor.astype(np.uint8) - depthFrameColor = cv2.applyColorMap(depthFrameColor, cv2.COLORMAP_JET) - depthFrameColor[invalidMask] = 0 - except (IndexError, ValueError): - depthFrameColor = np.zeros((frameDepth.shape[0], frameDepth.shape[1], 3), dtype=np.uint8) - return depthFrameColor - +MIN_DEPTH = 100.0 +MAX_DEPTH = 7000.0 rgbWeight = 0.5 depthWeight = 0.5 @@ -111,7 +93,7 @@ def main(): if len(cvFrame.shape) == 2: cvFrame = cv2.cvtColor(cvFrame, cv2.COLOR_GRAY2BGR) - depthColorized = colorizeDepth(frameDepth.getFrame(), MIN_DEPTH, MAX_DEPTH) + depthColorized = dai.utility.colorizeDepthFrame(frameDepth, MIN_DEPTH, MAX_DEPTH, useLog=True).getCvFrame() if depthColorized.shape[:2] != cvFrame.shape[:2]: depthColorized = cv2.resize( depthColorized, (cvFrame.shape[1], cvFrame.shape[0]) diff --git a/examples/python/ToF/tof_all_queues.py b/examples/python/ToF/tof_all_queues.py index eaffdda00e..f55dd0eec3 100644 --- a/examples/python/ToF/tof_all_queues.py +++ b/examples/python/ToF/tof_all_queues.py @@ -8,42 +8,27 @@ """ import cv2 -import numpy as np import depthai as dai +FPS = 30.0 -def colorizeDepth(frame: np.ndarray, minDepth: float, maxDepth: float) -> np.ndarray: - invalidMask = frame == 0 - try: - logDepth = np.log(frame.astype(np.float32) + 1e-6) - logDepth[invalidMask] = 0.0 - logDepth = np.clip(logDepth, np.log(minDepth + 1e-6), np.log(maxDepth + 1e-6)) - colored = np.interp(logDepth, (logDepth[~invalidMask].min(), logDepth[~invalidMask].max()), (0, 255)) - colored = colored.astype(np.uint8) - colored = cv2.applyColorMap(colored, cv2.COLORMAP_JET) - colored[invalidMask] = 0 - except (IndexError, ValueError): - colored = np.zeros((*frame.shape, 3), dtype=np.uint8) - return colored - -def normalizeFrame(frame: np.ndarray) -> np.ndarray: +def normalizeFrame(frame): return cv2.normalize(frame, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U) def main(): pipeline = dai.Pipeline() - # show depth in range 0.1m - 7m - minDepth = 100 - maxDepth = 7000 + minDepth = 100.0 + maxDepth = 7000.0 - # choose one of profiles LOW_RANGE / MID_RANGE / HIGH_RANGE profile = dai.ToFConfig.Profile.MID_RANGE tof = pipeline.create(dai.node.ToF).build( boardSocket=dai.CameraBoardSocket.AUTO, - profile=profile + profile=profile, + fps=FPS, ) with pipeline as p: @@ -56,10 +41,8 @@ def main(): "intensity": tof.intensity.createOutputQueue(maxSize=1, blocking=False), } if isRVC2: - # rawDepth are only supported on RVC2 outputQueues["rawDepth"] = tof.rawDepth.createOutputQueue(maxSize=1, blocking=False) else: - # confidence is only supported on RVC4 outputQueues["confidence"] = tof.confidence.createOutputQueue(maxSize=1, blocking=False) platformName = "RVC2" if isRVC2 else "RVC4" @@ -73,7 +56,7 @@ def main(): continue if name in {"depth", "rawDepth"}: - display = colorizeDepth(frame.getCvFrame(), minDepth, maxDepth) + display = dai.utility.colorizeDepthFrame(frame, minDepth, maxDepth, useLog=True).getCvFrame() else: display = normalizeFrame(frame.getCvFrame()) cv2.imshow(name, display) diff --git a/examples/python/ToF/tof_minimal.py b/examples/python/ToF/tof_minimal.py index b5a5604837..b20fce8dd2 100644 --- a/examples/python/ToF/tof_minimal.py +++ b/examples/python/ToF/tof_minimal.py @@ -8,38 +8,23 @@ """ import cv2 -import numpy as np import depthai as dai - -def colorizeDepth(frame: np.ndarray, minDepth: float, maxDepth: float) -> np.ndarray: - invalidMask = frame == 0 - try: - logDepth = np.log(frame.astype(np.float32) + 1e-6) - logDepth[invalidMask] = 0.0 - logDepth = np.clip(logDepth, np.log(minDepth + 1e-6), np.log(maxDepth + 1e-6)) - colored = np.interp(logDepth, (logDepth[~invalidMask].min(), logDepth[~invalidMask].max()), (0, 255)) - colored = colored.astype(np.uint8) - colored = cv2.applyColorMap(colored, cv2.COLORMAP_JET) - colored[invalidMask] = 0 - except (IndexError, ValueError): - colored = np.zeros((*frame.shape, 3), dtype=np.uint8) - return colored +FPS = 30.0 def main(): pipeline = dai.Pipeline() - # show depth in range 0.1m - 7m - minDepth = 100 - maxDepth = 7000 + minDepth = 100.0 + maxDepth = 7000.0 - # choose one of profiles LOW_RANGE / MID_RANGE / HIGH_RANGE profile = dai.ToFConfig.Profile.MID_RANGE tof = pipeline.create(dai.node.ToF).build( boardSocket=dai.CameraBoardSocket.AUTO, - profile=profile + profile=profile, + fps=FPS, ) depthOutputQueue = tof.depth.createOutputQueue() @@ -48,7 +33,7 @@ def main(): p.start() while p.isRunning(): depth = depthOutputQueue.get() - cv2.imshow("depth", colorizeDepth(depth.getCvFrame(), minDepth, maxDepth)) + cv2.imshow("depth", dai.utility.colorizeDepthFrame(depth, minDepth, maxDepth, useLog=True).getCvFrame()) if cv2.waitKey(1) == ord("q"): break diff --git a/examples/python/ToF/tof_pointcloud.py b/examples/python/ToF/tof_pointcloud.py index 47830002a1..9b4acc6388 100644 --- a/examples/python/ToF/tof_pointcloud.py +++ b/examples/python/ToF/tof_pointcloud.py @@ -6,7 +6,7 @@ import depthai as dai -FPS = 10.0 +FPS = 30.0 SIZE = (640, 400) with dai.Pipeline() as p: @@ -18,7 +18,7 @@ RGB_SOCKET = dai.CameraBoardSocket.CAM_A color = p.create(dai.node.Camera).build(RGB_SOCKET, sensorFps=FPS) - colorOut = color.requestOutput(SIZE, fps=FPS, type=dai.ImgFrame.Type.RGB888i, enableUndistortion =True) + colorOut = color.requestOutput(SIZE, fps=FPS, type=dai.ImgFrame.Type.RGB888i, enableUndistortion=True) tof = p.create(dai.node.ToF) tof.build( @@ -27,7 +27,6 @@ fps=FPS ) - # Align depth into colour frame so both inputs to PointCloud share the same dimensions align = p.create(dai.node.ImageAlign) align.setRunOnHost(True) tof.depth.link(align.input) diff --git a/examples/python/Vpp/virtual_patern_projection.py b/examples/python/Vpp/virtual_patern_projection.py index a7ed1f954f..1785e4799b 100644 --- a/examples/python/Vpp/virtual_patern_projection.py +++ b/examples/python/Vpp/virtual_patern_projection.py @@ -1,4 +1,3 @@ -import numpy as np import cv2 as cv import depthai as dai @@ -13,36 +12,6 @@ """ -def showDepth(depthFrame, windowName="Depth", minDistance=500, maxDistance=5000, - colormap=cv.COLORMAP_TURBO, useLog=False): - """ - Nicely visualize a depth map. - - Args: - depthFrame (np.ndarray): Depth frame (in millimeters). - window_name (str): OpenCV window name. - minDistance (int): Minimum depth to display (in mm). - maxDistance (int): Maximum depth to display (in mm). - colormap (int): OpenCV colormap (e.g., cv.COLORMAP_JET, COLORMAP_TURBO, etc.). - use_log (bool): Apply logarithmic scaling for better visual contrast. - """ - # Convert to float for processing - depthFrame = depthFrame.astype(np.float32) - - # Optionally apply log scaling - if useLog: - depthFrame = np.log(depthFrame + 1) - - # Clip to defined range (avoid far-out values) - depthFrame = np.uint8(np.clip(depthFrame, minDistance, maxDistance) / maxDistance * 255) - - # Apply color map - depthColor = cv.applyColorMap(depthFrame, colormap) - - # Show in a window - cv.imshow(windowName, depthColor) - - if __name__ == "__main__": fps = 20 @@ -114,14 +83,7 @@ def showDepth(depthFrame, windowName="Depth", minDistance=500, maxDistance=5000, cv.imshow("vpp_left", vpp_out_left.getCvFrame()) cv.imshow("vpp_right", vpp_out_right.getCvFrame()) - showDepth( - depth.getCvFrame(), - windowName="Depth", - minDistance=500, - maxDistance=5000, - colormap=cv.COLORMAP_TURBO, - useLog=False - ) + cv.imshow("Depth", dai.utility.colorizeDepthFrame(depth, colormap=cv.COLORMAP_TURBO).getCvFrame()) key = cv.waitKey(1) if key == ord('q'): diff --git a/include/depthai/beta/BetaNode.hpp b/include/depthai/beta/BetaNode.hpp new file mode 100644 index 0000000000..ae25b1049b --- /dev/null +++ b/include/depthai/beta/BetaNode.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "depthai/pipeline/DeviceNode.hpp" + +namespace dai { +namespace beta { + +class BetaNode : public DeviceNode, public HostRunnable { + public: + virtual ~BetaNode() = default; + + virtual void setRunOnHost(bool runOnHost) = 0; + + protected: + using DeviceNode::DeviceNode; + + void buildStage1() override; +}; + +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/README.md b/include/depthai/beta/README.md new file mode 100644 index 0000000000..0a3622795a --- /dev/null +++ b/include/depthai/beta/README.md @@ -0,0 +1,26 @@ +# Beta namespace + +The `beta` namespace is a staging area for experimental DepthAI features. It +allows new features to be developed and iterated on quickly before they are +promoted to the main `depthai` namespace. + +Beta features are well-developed, but minor API and behavioral changes may occur between DepthAI releases without notice. + +## Usage + +In C++, beta nodes are available under `dai::beta::node`: + +```cpp +auto node = pipeline.create(); +``` + +In Python, they are available under `dai.beta.node`: + +```python +node = pipeline.create(dai.beta.node.ImgDetectionsFilter) +``` + +## Device support + +On-device execution of Beta nodes is supported only on RVC4. If running Beta nodes on RVC2, DepthAI +automatically configures beta nodes to run on the host. diff --git a/include/depthai/beta/datatype/ClassificationSequenceParserConfig.hpp b/include/depthai/beta/datatype/ClassificationSequenceParserConfig.hpp new file mode 100644 index 0000000000..b21322fa17 --- /dev/null +++ b/include/depthai/beta/datatype/ClassificationSequenceParserConfig.hpp @@ -0,0 +1,87 @@ +#pragma once + +#include +#include + +#include "depthai/pipeline/datatype/Buffer.hpp" + +namespace dai { +namespace beta { + +/** + * Runtime configuration for ClassificationSequenceParser. + */ +class ClassificationSequenceParserConfig : public Buffer { + public: + std::vector ignoredIndexes; + + bool removeDuplicates = false; + + bool concatenateClasses = false; + + ClassificationSequenceParserConfig() = default; + + ~ClassificationSequenceParserConfig() override; + + /** + * Sets the class indexes ignored while decoding the sequence. + * @param indexes Nonnegative class indexes to ignore + */ + void setIgnoredIndexes(const std::vector& indexes); + + /** + * Gets the class indexes ignored while decoding the sequence. + * @return Ignored class indexes + */ + std::vector getIgnoredIndexes() const; + + /** + * Sets whether consecutive duplicate classes are removed. + * @param removeDuplicates Whether duplicate classes are removed + */ + void setRemoveDuplicates(bool removeDuplicates); + + /** + * Gets whether consecutive duplicate classes are removed. + * @return Whether duplicate classes are removed + */ + bool getRemoveDuplicates() const; + + /** + * Sets whether decoded class labels are concatenated. + * @param concatenateClasses Whether class labels are concatenated + */ + void setConcatenateClasses(bool concatenateClasses); + + /** + * Gets whether decoded class labels are concatenated. + * @return Whether class labels are concatenated + */ + bool getConcatenateClasses() const; + + /** + * Validates this configuration. + * @return True if all ignored indexes are nonnegative + */ + bool validate() const; + + /** + * Serializes this configuration into message metadata. + * @param metadata Destination metadata buffer + * @param datatype Datatype identifier written during serialization + */ + void serialize(std::vector& metadata, DatatypeEnum& datatype) const override; + + /** + * Returns the datatype identifier for this configuration. + * @return Configuration datatype identifier + */ + DatatypeEnum getDatatype() const override { + return DatatypeEnum::ClassificationSequenceParserConfig; + } + + DEPTHAI_SERIALIZE(ClassificationSequenceParserConfig, ignoredIndexes, removeDuplicates, concatenateClasses); +}; + +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/datatype/Classifications.hpp b/include/depthai/beta/datatype/Classifications.hpp new file mode 100644 index 0000000000..b0c5a7717e --- /dev/null +++ b/include/depthai/beta/datatype/Classifications.hpp @@ -0,0 +1,101 @@ +#pragma once + +#include +#include + +#include "depthai/common/ImgTransformations.hpp" +#include "depthai/common/optional.hpp" +#include "depthai/pipeline/datatype/Buffer.hpp" +#include "depthai/pipeline/datatype/Transformable.hpp" + +namespace dai { +namespace beta { + +/** + * Classifications message. Carries classification class names and their corresponding scores. + * + * The classes and scores vectors are index-aligned. Parsers emit them sorted in descending + * order of score, so the first entry is the most probable class. + */ +class Classifications : public Buffer, public TransformableCRTP { + protected: + /** + * Internal transform hook used by transformTo(). + * + * Classification results carry no spatial data, so only the transformation metadata is + * replaced with the target transformation. + */ + void transformToInternal(const ImgTransformation& target) override; + + public: + using Buffer::sequenceNum; + using Buffer::ts; + using Buffer::tsDevice; + using Buffer::tsSystem; + using Transformable::transformation; + + friend class TransformableCRTP; + + /** + * Construct Classifications message. + */ + Classifications() = default; + ~Classifications() override; + + /** + * Class names, index-aligned with the scores vector. + */ + std::vector classes; + + /** + * Classification scores, index-aligned with the classes vector. + */ + std::vector scores; + + /** + * Returns the most probable class name. + * + * Assumes the classes are sorted in descending order of score, which holds for + * parser-emitted messages. + * + * @throws std::runtime_error if the message contains no classes. + */ + std::string getTopClass() const; + + /** + * Returns the score of the most probable class. + * + * Assumes the scores are sorted in descending order, which holds for parser-emitted + * messages. + * + * @throws std::runtime_error if the message contains no scores. + */ + float getTopScore() const; + + /** + * Returns a new Classifications message with the transformation metadata replaced by the + * target transformation. Classification results carry no spatial data, so classes and + * scores are unchanged. + * + * @param target Target image transformation. + */ + Classifications transformTo(const ImgTransformation& target) const; + + /** + * Returns an ImgAnnotations visualization with up to the top five classes and their + * scores, or std::monostate when no transformation metadata is available to derive the + * annotation layout from. + */ + dai::VisualizeType getVisualizationMessage() const override; + + void serialize(std::vector& metadata, DatatypeEnum& datatype) const override; + + DatatypeEnum getDatatype() const override { + return DatatypeEnum::Classifications; + } + + DEPTHAI_SERIALIZE(Classifications, sequenceNum, ts, tsDevice, tsSystem, transformation, classes, scores); +}; + +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/datatype/Clusters.hpp b/include/depthai/beta/datatype/Clusters.hpp new file mode 100644 index 0000000000..d825bb9650 --- /dev/null +++ b/include/depthai/beta/datatype/Clusters.hpp @@ -0,0 +1,96 @@ +#pragma once + +#include +#include + +#include "depthai/common/ImgTransformations.hpp" +#include "depthai/common/Point2f.hpp" +#include "depthai/common/optional.hpp" +#include "depthai/pipeline/datatype/Buffer.hpp" +#include "depthai/pipeline/datatype/Transformable.hpp" +#include "depthai/utility/Serialization.hpp" + +namespace dai { +namespace beta { + +/** + * Cluster of 2D points. Serialized value type contained by the Clusters message. + */ +struct Cluster { + /** + * Label of the cluster. + */ + std::int32_t label = 0; + + /** + * Points in the cluster. + */ + std::vector points; + + DEPTHAI_SERIALIZE(Cluster, label, points); +}; + +/** + * Clusters message. Carries clusters of 2D points, each cluster with an integer label. + * + * Parsers emit clusters with sequential labels starting at 0 and point image coordinates + * normalized to [0, 1]. Clusters may be empty, e.g. lanes without enough detected points. + */ +class Clusters : public Buffer, public TransformableCRTP { + protected: + /** + * Internal transform hook used by transformTo(). + * + * Remaps the point image coordinates of every cluster from the source transformation + * carried by this message into the target transformation. + */ + void transformToInternal(const ImgTransformation& target) override; + + public: + using Buffer::sequenceNum; + using Buffer::ts; + using Buffer::tsDevice; + using Buffer::tsSystem; + using Transformable::transformation; + + friend class TransformableCRTP; + + /** + * Construct Clusters message. + */ + Clusters() = default; + ~Clusters() override; + + /** + * Detected clusters of points. + */ + std::vector clusters; + + /** + * Returns a new Clusters message with the cluster point image coordinates remapped from + * this message's transformation into the target transformation. + * + * @param target Target image transformation. + * @throws std::runtime_error if this message carries no transformation metadata. + */ + Clusters transformTo(const ImgTransformation& target) const; + + /** + * Returns an ImgAnnotations visualization with each cluster drawn as points in a distinct + * color sampled from a rainbow colormap. + * + * @throws std::runtime_error if the message contains more than 255 clusters. + */ + dai::VisualizeType getVisualizationMessage() const override; + + void serialize(std::vector& metadata, DatatypeEnum& datatype) const override; + + DatatypeEnum getDatatype() const override { + return DatatypeEnum::Clusters; + } + + DEPTHAI_SERIALIZE(Clusters, sequenceNum, ts, tsDevice, tsSystem, transformation, clusters); +}; + +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/datatype/FastSAMParserConfig.hpp b/include/depthai/beta/datatype/FastSAMParserConfig.hpp new file mode 100644 index 0000000000..1ef68bfbec --- /dev/null +++ b/include/depthai/beta/datatype/FastSAMParserConfig.hpp @@ -0,0 +1,154 @@ +#pragma once + +#include +#include +#include +#include + +#include "depthai/common/optional.hpp" +#include "depthai/pipeline/datatype/Buffer.hpp" + +namespace dai { +namespace beta { + +/** + * Runtime configuration for FastSAMParser. + */ +class FastSAMParserConfig : public Buffer { + public: + /** Prompt mode used to select emitted segmentation masks. */ + enum class Prompt : std::int32_t { + EVERYTHING, ///< Keep all detected instances. + POINT, ///< Select instances using a point and point label. + BOUNDING_BOX ///< Select an instance using a bounding box. + }; + + float confidenceThreshold = 0.5f; + + float iouThreshold = 0.5f; + + float maskConfidence = 0.5f; + + Prompt prompt = Prompt::EVERYTHING; + + std::optional> points; + + std::optional pointLabel; + + std::optional> boundingBox; + + FastSAMParserConfig() = default; + + ~FastSAMParserConfig() override; + + /** + * Sets the minimum detection confidence. + * @param threshold Confidence threshold in the range [0, 1] + */ + void setConfidenceThreshold(float threshold); + + /** + * Gets the minimum detection confidence. + * @return Confidence threshold + */ + float getConfidenceThreshold() const; + + /** + * Sets the intersection-over-union threshold used by non-maximum suppression. + * @param threshold IoU threshold in the range [0, 1] + */ + void setIouThreshold(float threshold); + + /** + * Gets the intersection-over-union threshold. + * @return IoU threshold + */ + float getIouThreshold() const; + + /** + * Sets the threshold used to binarize instance masks. + * @param threshold Mask confidence threshold in the range [0, 1] + */ + void setMaskConfidence(float threshold); + + /** + * Gets the threshold used to binarize instance masks. + * @return Mask confidence threshold + */ + float getMaskConfidence() const; + + /** + * Sets the prompt mode. Required point or bounding-box data must already be present. + * @param prompt Prompt mode + */ + void setPrompt(Prompt prompt); + + /** + * Gets the prompt mode. + * @return Prompt mode + */ + Prompt getPrompt() const; + + /** + * Sets the prompt point. + * @param x Point x coordinate + * @param y Point y coordinate + */ + void setPoints(std::int32_t x, std::int32_t y); + + /** + * Gets the prompt point. + * @return Optional prompt point as (x, y) + */ + std::optional> getPoints() const; + + /** + * Sets the prompt point label. + * @param label Point label, 0 for negative or 1 for positive + */ + void setPointLabel(std::int32_t label); + + /** + * Gets the prompt point label. + * @return Optional point label + */ + std::optional getPointLabel() const; + + /** + * Sets the prompt bounding box. + * @param boundingBox Bounding box as {x1, y1, x2, y2}, with ordered coordinates and positive x2/y2 + */ + void setBoundingBox(const std::array& boundingBox); + + /** + * Gets the prompt bounding box. + * @return Optional bounding box as {x1, y1, x2, y2} + */ + std::optional> getBoundingBox() const; + + /** + * Validates thresholds, prompt payload, point label, and bounding-box coordinates. + * @return True if the complete configuration is valid + */ + bool validate() const; + + /** + * Serializes this configuration into message metadata. + * @param metadata Destination metadata buffer + * @param datatype Datatype identifier written during serialization + */ + void serialize(std::vector& metadata, DatatypeEnum& datatype) const override; + + /** + * Returns the datatype identifier for this configuration. + * @return Configuration datatype identifier + */ + DatatypeEnum getDatatype() const override { + return DatatypeEnum::FastSAMParserConfig; + } + + DEPTHAI_SERIALIZE(FastSAMParserConfig, confidenceThreshold, iouThreshold, maskConfidence, prompt, points, pointLabel, boundingBox); +}; + +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/datatype/HRNetParserConfig.hpp b/include/depthai/beta/datatype/HRNetParserConfig.hpp new file mode 100644 index 0000000000..6f6cead047 --- /dev/null +++ b/include/depthai/beta/datatype/HRNetParserConfig.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include "depthai/pipeline/datatype/Buffer.hpp" + +namespace dai { +namespace beta { + +/** + * Runtime configuration for HRNetParser. + */ +class HRNetParserConfig : public Buffer { + public: + float scoreThreshold = 0.5f; + + HRNetParserConfig() = default; + + ~HRNetParserConfig() override; + + /** + * Sets the minimum keypoint score. + * @param threshold Score threshold in the range [0, 1] + */ + void setScoreThreshold(float threshold); + + /** + * Gets the minimum keypoint score. + * @return Score threshold + */ + float getScoreThreshold() const; + + /** + * Validates this configuration. + * @return True if the score threshold is in the range [0, 1] + */ + bool validate() const; + + /** + * Serializes this configuration into message metadata. + * @param metadata Destination metadata buffer + * @param datatype Datatype identifier written during serialization + */ + void serialize(std::vector& metadata, DatatypeEnum& datatype) const override; + + /** + * Returns the datatype identifier for this configuration. + * @return Configuration datatype identifier + */ + DatatypeEnum getDatatype() const override { + return DatatypeEnum::HRNetParserConfig; + } + + DEPTHAI_SERIALIZE(HRNetParserConfig, scoreThreshold); +}; + +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/datatype/ImgDetectionsFilterConfig.hpp b/include/depthai/beta/datatype/ImgDetectionsFilterConfig.hpp new file mode 100644 index 0000000000..d1f05e6656 --- /dev/null +++ b/include/depthai/beta/datatype/ImgDetectionsFilterConfig.hpp @@ -0,0 +1,102 @@ +#pragma once + +#include +#include +#include + +#include "depthai/common/optional.hpp" +#include "depthai/pipeline/datatype/Buffer.hpp" + +namespace dai { +namespace beta { + +/** + * Runtime configuration for ImgDetectionsFilter. + * + * The default configuration is a no-op: it keeps every detection in its + * original order. + */ +class ImgDetectionsFilterConfig : public Buffer { + public: + ~ImgDetectionsFilterConfig() override; + + void serialize(std::vector& metadata, DatatypeEnum& datatype) const override; + + DatatypeEnum getDatatype() const override { + return DatatypeEnum::ImgDetectionsFilterConfig; + } + + /** + * If set, only detections with one of these labels are kept. + * Takes precedence over labelsToReject when both are set. + */ + std::optional> labelsToKeep; + + /** + * If set, detections with one of these labels are removed. + */ + std::optional> labelsToReject; + + /** + * If set, detections below this confidence are removed. + */ + std::optional confidenceThreshold; + + /** + * If set, detections with a normalized bounding-box area below this value + * are removed. + */ + std::optional minArea; + + /** + * Disable non-maximum suppression. Disabled by default to preserve all + * detections. + */ + bool nmsDisabled = true; + + /** + * Confidence threshold applied by non-maximum suppression. + */ + float nmsConfidenceThreshold = 0.3f; + + /** + * Intersection-over-union threshold applied by non-maximum suppression. + */ + float nmsIouThreshold = 0.4f; + + /** + * Disable confidence sorting. Disabled by default to preserve input order. + */ + bool sortingDisabled = true; + + /** + * Sort detections in descending confidence order when sorting is enabled. + */ + bool sortDescending = true; + + /** + * If set, retain at most this many detections after filtering and sorting. + */ + std::optional firstK; + + /** + * Returns true when this configuration preserves every detection and its + * order. + */ + bool isNoOp() const; + + DEPTHAI_SERIALIZE(ImgDetectionsFilterConfig, + labelsToKeep, + labelsToReject, + confidenceThreshold, + minArea, + nmsDisabled, + nmsConfidenceThreshold, + nmsIouThreshold, + sortingDisabled, + sortDescending, + firstK); +}; + +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/datatype/Keypoints.hpp b/include/depthai/beta/datatype/Keypoints.hpp new file mode 100644 index 0000000000..2484642b5b --- /dev/null +++ b/include/depthai/beta/datatype/Keypoints.hpp @@ -0,0 +1,122 @@ +#pragma once + +#include + +#include "depthai/common/ImgTransformations.hpp" +#include "depthai/common/Keypoint.hpp" +#include "depthai/common/optional.hpp" +#include "depthai/pipeline/datatype/Buffer.hpp" +#include "depthai/pipeline/datatype/Transformable.hpp" + +namespace dai { +namespace beta { + +/** + * Keypoints message. Streamable wrapper around the native dai::KeypointsList, carrying 2D or 3D + * keypoints together with optional skeleton edges connecting them. + * + * Keypoint image coordinates are normalized to [0, 1] by the keypoint parsers. 2D keypoints carry + * a z coordinate of 0. + */ +class Keypoints : public Buffer, public TransformableCRTP { + protected: + /** + * Internal transform hook used by transformTo(). + * + * Remaps the keypoint image coordinates from the source transformation carried by this + * message into the target transformation. + */ + void transformToInternal(const ImgTransformation& target) override; + + public: + using Buffer::sequenceNum; + using Buffer::ts; + using Buffer::tsDevice; + using Buffer::tsSystem; + using Transformable::transformation; + + friend class TransformableCRTP; + + /** + * Construct Keypoints message. + */ + Keypoints() = default; + ~Keypoints() override; + + /** + * Native keypoints list carrying the keypoints and the skeleton edges connecting them. + */ + KeypointsList keypointsList; + + /** + * Returns the keypoints. + */ + std::vector getKeypoints() const; + + /** + * Sets the keypoints. + * + * @param keypoints Keypoints to set. + * @note This clears any existing keypoints and edges. + */ + void setKeypoints(const std::vector& keypoints); + + /** + * Sets the keypoints together with the skeleton edges connecting them. + * + * @param keypoints Keypoints to set. + * @param edges Pairs of keypoint indices to connect. Example: {{0, 1}, {1, 2}} connects + * keypoint 0 to keypoint 1 and keypoint 1 to keypoint 2. + * @throws std::invalid_argument if an edge index is out of range or an edge is a self-loop. + */ + void setKeypoints(const std::vector& keypoints, const std::vector& edges); + + /** + * Returns the skeleton edges as pairs of keypoint indices. + */ + std::vector getEdges() const; + + /** + * Sets the skeleton edges. + * + * @param edges Pairs of keypoint indices to connect. + * @throws std::invalid_argument if an edge index is out of range or an edge is a self-loop. + */ + void setEdges(const std::vector& edges); + + /** + * Returns the 2D image coordinates of the keypoints, dropping the z axis values. + */ + std::vector getPoints2f() const; + + /** + * Returns the 3D image coordinates of the keypoints. 2D keypoints carry a z coordinate of 0. + */ + std::vector getPoints3f() const; + + /** + * Returns a new Keypoints message with the keypoint image coordinates remapped from this + * message's transformation into the target transformation. + * + * @param target Target image transformation. + * @throws std::runtime_error if this message carries no transformation metadata. + */ + Keypoints transformTo(const ImgTransformation& target) const; + + /** + * Returns an ImgAnnotations visualization with the keypoints drawn as points and the skeleton + * edges drawn as lines. + */ + dai::VisualizeType getVisualizationMessage() const override; + + void serialize(std::vector& metadata, DatatypeEnum& datatype) const override; + + DatatypeEnum getDatatype() const override { + return DatatypeEnum::Keypoints; + } + + DEPTHAI_SERIALIZE(Keypoints, sequenceNum, ts, tsDevice, tsSystem, transformation, keypointsList); +}; + +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/datatype/Lines.hpp b/include/depthai/beta/datatype/Lines.hpp new file mode 100644 index 0000000000..7a7abcda9b --- /dev/null +++ b/include/depthai/beta/datatype/Lines.hpp @@ -0,0 +1,99 @@ +#pragma once + +#include +#include + +#include "depthai/common/ImgTransformations.hpp" +#include "depthai/common/Point2f.hpp" +#include "depthai/common/optional.hpp" +#include "depthai/pipeline/datatype/Buffer.hpp" +#include "depthai/pipeline/datatype/Transformable.hpp" +#include "depthai/utility/Serialization.hpp" + +namespace dai { +namespace beta { + +/** + * Detected line segment. Serialized value type contained by the Lines message. + */ +struct Line { + /** + * Start point of the line with x and y coordinate. + */ + Point2f startPoint; + + /** + * End point of the line with x and y coordinate. + */ + Point2f endPoint; + + /** + * Confidence of the line, in [0, 1]. + */ + float confidence = 0.0f; + + DEPTHAI_SERIALIZE(Line, startPoint, endPoint, confidence); +}; + +/** + * Lines message. Carries detected line segments, each with a start point, an end point and a + * confidence score. + * + * Parsers emit line point image coordinates normalized to [0, 1] and confidences clipped to + * [0, 1]. The message may carry no lines when nothing passes the detection thresholds. + */ +class Lines : public Buffer, public TransformableCRTP { + protected: + /** + * Internal transform hook used by transformTo(). + * + * Remaps the start and end point image coordinates of every line from the source + * transformation carried by this message into the target transformation. + */ + void transformToInternal(const ImgTransformation& target) override; + + public: + using Buffer::sequenceNum; + using Buffer::ts; + using Buffer::tsDevice; + using Buffer::tsSystem; + using Transformable::transformation; + + friend class TransformableCRTP; + + /** + * Construct Lines message. + */ + Lines() = default; + ~Lines() override; + + /** + * Detected lines. + */ + std::vector lines; + + /** + * Returns a new Lines message with the line point image coordinates remapped from this + * message's transformation into the target transformation. + * + * @param target Target image transformation. + * @throws std::runtime_error if this message carries no transformation metadata. + */ + Lines transformTo(const ImgTransformation& target) const; + + /** + * Returns an ImgAnnotations visualization with each line drawn as a two-point line strip. + */ + dai::VisualizeType getVisualizationMessage() const override; + + void serialize(std::vector& metadata, DatatypeEnum& datatype) const override; + + DatatypeEnum getDatatype() const override { + return DatatypeEnum::Lines; + } + + DEPTHAI_SERIALIZE(Lines, sequenceNum, ts, tsDevice, tsSystem, transformation, lines); +}; + +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/datatype/MLSDParserConfig.hpp b/include/depthai/beta/datatype/MLSDParserConfig.hpp new file mode 100644 index 0000000000..c9c965b6ea --- /dev/null +++ b/include/depthai/beta/datatype/MLSDParserConfig.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include "depthai/pipeline/datatype/Buffer.hpp" + +namespace dai { +namespace beta { + +/** + * Runtime configuration for MLSDParser. + */ +class MLSDParserConfig : public Buffer { + public: + int topK = 200; + + float scoreThreshold = 0.10f; + + float distanceThreshold = 20.0f; + + MLSDParserConfig() = default; + + ~MLSDParserConfig() override; + + /** + * Sets the number of highest-scoring candidates retained for decoding. + * @param topK Positive candidate count + */ + void setTopK(int topK); + + /** + * Gets the number of highest-scoring candidates retained for decoding. + * @return Candidate count + */ + int getTopK() const; + + /** + * Sets the minimum candidate score. + * @param threshold Score threshold in the range [0, 1] + */ + void setScoreThreshold(float threshold); + + /** + * Gets the minimum candidate score. + * @return Score threshold + */ + float getScoreThreshold() const; + + /** + * Sets the distance threshold used while decoding line segments. + * @param threshold Nonnegative distance threshold + */ + void setDistanceThreshold(float threshold); + + /** + * Gets the distance threshold used while decoding line segments. + * @return Distance threshold + */ + float getDistanceThreshold() const; + + /** + * Validates this configuration. + * @return True if topK is positive, the score threshold is in [0, 1], and the distance threshold is nonnegative + */ + bool validate() const; + + /** + * Serializes this configuration into message metadata. + * @param metadata Destination metadata buffer + * @param datatype Datatype identifier written during serialization + */ + void serialize(std::vector& metadata, DatatypeEnum& datatype) const override; + + /** + * Returns the datatype identifier for this configuration. + * @return Configuration datatype identifier + */ + DatatypeEnum getDatatype() const override { + return DatatypeEnum::MLSDParserConfig; + } + + DEPTHAI_SERIALIZE(MLSDParserConfig, topK, scoreThreshold, distanceThreshold); +}; + +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/datatype/MPPalmDetectionParserConfig.hpp b/include/depthai/beta/datatype/MPPalmDetectionParserConfig.hpp new file mode 100644 index 0000000000..308c25247c --- /dev/null +++ b/include/depthai/beta/datatype/MPPalmDetectionParserConfig.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include "depthai/pipeline/datatype/Buffer.hpp" + +namespace dai { +namespace beta { + +/** + * Runtime configuration for MPPalmDetectionParser. + */ +class MPPalmDetectionParserConfig : public Buffer { + public: + float confidenceThreshold = 0.5f; + + float iouThreshold = 0.5f; + + int maxDetections = 100; + + MPPalmDetectionParserConfig() = default; + + ~MPPalmDetectionParserConfig() override; + + /** + * Sets the minimum detection confidence. + * @param threshold Confidence threshold in the range [0, 1] + */ + void setConfidenceThreshold(float threshold); + + /** + * Gets the minimum detection confidence. + * @return Confidence threshold + */ + float getConfidenceThreshold() const; + + /** + * Sets the intersection-over-union threshold used by non-maximum suppression. + * @param threshold IoU threshold in the range [0, 1] + */ + void setIouThreshold(float threshold); + + /** + * Gets the intersection-over-union threshold. + * @return IoU threshold + */ + float getIouThreshold() const; + + /** + * Sets the maximum number of emitted detections. + * @param maxDetections Positive maximum detection count + */ + void setMaxDetections(int maxDetections); + + /** + * Gets the maximum number of emitted detections. + * @return Maximum detection count + */ + int getMaxDetections() const; + + /** + * Validates this configuration. + * @return True if both thresholds are in [0, 1] and maxDetections is positive + */ + bool validate() const; + + /** + * Serializes this configuration into message metadata. + * @param metadata Destination metadata buffer + * @param datatype Datatype identifier written during serialization + */ + void serialize(std::vector& metadata, DatatypeEnum& datatype) const override; + + /** + * Returns the datatype identifier for this configuration. + * @return Configuration datatype identifier + */ + DatatypeEnum getDatatype() const override { + return DatatypeEnum::MPPalmDetectionParserConfig; + } + + DEPTHAI_SERIALIZE(MPPalmDetectionParserConfig, confidenceThreshold, iouThreshold, maxDetections); +}; + +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/datatype/Map2D.hpp b/include/depthai/beta/datatype/Map2D.hpp new file mode 100644 index 0000000000..c0514ee7bb --- /dev/null +++ b/include/depthai/beta/datatype/Map2D.hpp @@ -0,0 +1,126 @@ +#pragma once + +#include +#include + +#include "depthai/common/ImgTransformations.hpp" +#include "depthai/common/optional.hpp" +#include "depthai/pipeline/datatype/Buffer.hpp" +#include "depthai/pipeline/datatype/Transformable.hpp" +#include "depthai/utility/span.hpp" + +namespace dai { +namespace beta { + +/** + * Map2D message. Carries a dense 2D map of 32-bit floats, such as a depth map, a density map or a + * heat map, together with image transformation metadata. + * + * The map values are stored row-major in the buffer payload; the map dimensions are carried in the + * serialized metadata. + */ +class Map2D : public Buffer, public TransformableCRTP { + protected: + /** + * Internal transform hook used by transformTo(). + * + * The map carries no remappable spatial data, so only the transformation metadata is replaced + * with the target transformation. + */ + void transformToInternal(const ImgTransformation& target) override; + + private: + size_t width = 0; + size_t height = 0; + + public: + using Buffer::sequenceNum; + using Buffer::ts; + using Buffer::tsDevice; + using Buffer::tsSystem; + using Transformable::transformation; + + friend class TransformableCRTP; + + /** + * Construct Map2D message. + */ + Map2D(); + + /** + * Construct Map2D message with the given map values and dimensions. + * + * @param map Map values in row-major order, of size width * height. + * @param width Map width in values per row. + * @param height Map height in rows. + * @throws std::runtime_error if the map size does not equal width * height. + */ + Map2D(const std::vector& map, size_t width, size_t height); + ~Map2D() override; + + /** + * Sets the 2D map. The values are copied into the buffer payload. + * + * @param map Map values in row-major order, of size width * height. + * @param width Map width in values per row. + * @param height Map height in rows. + * @throws std::runtime_error if the map size does not equal width * height. + */ + void setMap(const std::vector& map, size_t width, size_t height); + + /** + * Sets the 2D map from a float span without an extra temporary vector. The values are copied + * into the buffer payload. + * + * @param map Map values in row-major order, of size width * height. + * @param width Map width in values per row. + * @param height Map height in rows. + * @throws std::runtime_error if the map size does not equal width * height. + */ + void setMap(span map, size_t width, size_t height); + + /** + * Returns a copy of the 2D map values in row-major order. If no map is set, returns an empty + * vector. + */ + std::vector getMap() const; + + /** + * Returns the width of the 2D map. + */ + size_t getWidth() const; + + /** + * Returns the height of the 2D map. + */ + size_t getHeight() const; + + /** + * Returns a new Map2D message with the transformation metadata replaced by the target + * transformation. The map values and dimensions are unchanged. + * + * @param target Target image transformation. + * @throws std::runtime_error if this message carries no transformation metadata. + */ + Map2D transformTo(const ImgTransformation& target) const; + + /** + * Returns an ImgFrame visualization of the map colored with a plasma colormap. + * + * When any map value is below 1 the values are scaled by 255, so maps normalized to [0, 1] + * use the full colormap range. The values are then truncated to 8-bit indices into the + * colormap and emitted as an interleaved BGR frame. + */ + dai::VisualizeType getVisualizationMessage() const override; + + void serialize(std::vector& metadata, DatatypeEnum& datatype) const override; + + DatatypeEnum getDatatype() const override { + return DatatypeEnum::Map2D; + } + + DEPTHAI_SERIALIZE(Map2D, sequenceNum, ts, tsDevice, tsSystem, transformation, width, height); +}; + +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/datatype/MapOutputParserConfig.hpp b/include/depthai/beta/datatype/MapOutputParserConfig.hpp new file mode 100644 index 0000000000..0c622363f6 --- /dev/null +++ b/include/depthai/beta/datatype/MapOutputParserConfig.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include "depthai/pipeline/datatype/Buffer.hpp" + +namespace dai { +namespace beta { + +/** + * Runtime configuration for MapOutputParser. + */ +class MapOutputParserConfig : public Buffer { + public: + bool minMaxScaling = false; + + MapOutputParserConfig() = default; + + ~MapOutputParserConfig() override; + + /** + * Sets whether output values are scaled using their minimum and maximum. + * @param enabled Whether min-max scaling is enabled + */ + void setMinMaxScaling(bool enabled); + + /** + * Gets whether output values are scaled using their minimum and maximum. + * @return Whether min-max scaling is enabled + */ + bool getMinMaxScaling() const; + + /** + * Validates this configuration. + * @return True because every value of the boolean option is valid + */ + bool validate() const; + + /** + * Serializes this configuration into message metadata. + * @param metadata Destination metadata buffer + * @param datatype Datatype identifier written during serialization + */ + void serialize(std::vector& metadata, DatatypeEnum& datatype) const override; + + /** + * Returns the datatype identifier for this configuration. + * @return Configuration datatype identifier + */ + DatatypeEnum getDatatype() const override { + return DatatypeEnum::MapOutputParserConfig; + } + + DEPTHAI_SERIALIZE(MapOutputParserConfig, minMaxScaling); +}; + +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/datatype/PPTextDetectionParserConfig.hpp b/include/depthai/beta/datatype/PPTextDetectionParserConfig.hpp new file mode 100644 index 0000000000..d100df1fe4 --- /dev/null +++ b/include/depthai/beta/datatype/PPTextDetectionParserConfig.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include "depthai/pipeline/datatype/Buffer.hpp" + +namespace dai { +namespace beta { + +/** + * Runtime configuration for PPTextDetectionParser. + */ +class PPTextDetectionParserConfig : public Buffer { + public: + float confidenceThreshold = 0.5f; + + float maskThreshold = 0.25f; + + int maxDetections = 100; + + PPTextDetectionParserConfig() = default; + + ~PPTextDetectionParserConfig() override; + + /** + * Sets the minimum detection confidence. + * @param threshold Confidence threshold in the range [0, 1] + */ + void setConfidenceThreshold(float threshold); + + /** + * Gets the minimum detection confidence. + * @return Confidence threshold + */ + float getConfidenceThreshold() const; + + /** + * Sets the threshold applied to the text probability mask. + * @param threshold Mask threshold in the range [0, 1] + */ + void setMaskThreshold(float threshold); + + /** + * Gets the threshold applied to the text probability mask. + * @return Mask threshold + */ + float getMaskThreshold() const; + + /** + * Sets the maximum number of emitted detections. + * @param maxDetections Positive maximum detection count + */ + void setMaxDetections(int maxDetections); + + /** + * Gets the maximum number of emitted detections. + * @return Maximum detection count + */ + int getMaxDetections() const; + + /** + * Validates this configuration. + * @return True if both thresholds are in [0, 1] and maxDetections is positive + */ + bool validate() const; + + /** + * Serializes this configuration into message metadata. + * @param metadata Destination metadata buffer + * @param datatype Datatype identifier written during serialization + */ + void serialize(std::vector& metadata, DatatypeEnum& datatype) const override; + + /** + * Returns the datatype identifier for this configuration. + * @return Configuration datatype identifier + */ + DatatypeEnum getDatatype() const override { + return DatatypeEnum::PPTextDetectionParserConfig; + } + + DEPTHAI_SERIALIZE(PPTextDetectionParserConfig, confidenceThreshold, maskThreshold, maxDetections); +}; + +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/datatype/Predictions.hpp b/include/depthai/beta/datatype/Predictions.hpp new file mode 100644 index 0000000000..a7422e4022 --- /dev/null +++ b/include/depthai/beta/datatype/Predictions.hpp @@ -0,0 +1,96 @@ +#pragma once + +#include +#include + +#include "depthai/common/ImgTransformations.hpp" +#include "depthai/common/optional.hpp" +#include "depthai/pipeline/datatype/Buffer.hpp" +#include "depthai/pipeline/datatype/Transformable.hpp" +#include "depthai/utility/Serialization.hpp" + +namespace dai { +namespace beta { + +/** + * Single predicted value. Serialized value type contained by the Predictions message. + */ +struct Prediction { + /** + * The predicted value. + */ + float prediction = 0.0f; + + DEPTHAI_SERIALIZE(Prediction, prediction); +}; + +/** + * Predictions message. Carries the predicted value(s) of a regression model in the order the + * model emitted them. + * + * The message may carry no predictions when the parsed tensor is empty. + */ +class Predictions : public Buffer, public TransformableCRTP { + protected: + /** + * Internal transform hook used by transformTo(). + * + * Regression results carry no spatial data, so only the transformation metadata is + * replaced with the target transformation. + */ + void transformToInternal(const ImgTransformation& target) override; + + public: + using Buffer::sequenceNum; + using Buffer::ts; + using Buffer::tsDevice; + using Buffer::tsSystem; + using Transformable::transformation; + + friend class TransformableCRTP; + + /** + * Construct Predictions message. + */ + Predictions() = default; + ~Predictions() override; + + /** + * Predicted values, in the order the model emitted them. + */ + std::vector predictions; + + /** + * Returns the first predicted value. Useful for single-prediction models. + * + * @throws std::runtime_error if the message contains no predictions. + */ + float getFirstPrediction() const; + + /** + * Returns a new Predictions message with the transformation metadata replaced by the + * target transformation. Regression results carry no spatial data, so the predictions + * are unchanged. + * + * @param target Target image transformation. + */ + Predictions transformTo(const ImgTransformation& target) const; + + /** + * Returns an ImgAnnotations visualization with each predicted value drawn as text, one + * below the other, or std::monostate when no transformation metadata is available to + * derive the annotation layout from. + */ + dai::VisualizeType getVisualizationMessage() const override; + + void serialize(std::vector& metadata, DatatypeEnum& datatype) const override; + + DatatypeEnum getDatatype() const override { + return DatatypeEnum::Predictions; + } + + DEPTHAI_SERIALIZE(Predictions, sequenceNum, ts, tsDevice, tsSystem, transformation, predictions); +}; + +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/datatype/RFDETRParserConfig.hpp b/include/depthai/beta/datatype/RFDETRParserConfig.hpp new file mode 100644 index 0000000000..cb2283a55b --- /dev/null +++ b/include/depthai/beta/datatype/RFDETRParserConfig.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include "depthai/pipeline/datatype/Buffer.hpp" + +namespace dai { +namespace beta { + +/** + * Runtime configuration for RFDETRParser. + */ +class RFDETRParserConfig : public Buffer { + public: + float confidenceThreshold = 0.5f; + + int maxDetections = 300; + + float maskConfidence = 0.5f; + + RFDETRParserConfig() = default; + + ~RFDETRParserConfig() override; + + /** + * Set the minimum detection confidence. + * @param threshold Confidence threshold in the inclusive range [0, 1] + */ + void setConfidenceThreshold(float threshold); + + /** + * Get the minimum detection confidence. + * @return Confidence threshold in the inclusive range [0, 1] + */ + float getConfidenceThreshold() const; + + /** + * Set the maximum number of detections to retain. + * @param maxDetections Maximum detection count, which must be positive + */ + void setMaxDetections(int maxDetections); + + /** + * Get the maximum number of detections to retain. + * @return Maximum detection count + */ + int getMaxDetections() const; + + /** + * Set the minimum per-pixel confidence used when creating instance masks. + * @param threshold Mask confidence threshold in the inclusive range [0, 1] + */ + void setMaskConfidence(float threshold); + + /** + * Get the minimum per-pixel confidence used when creating instance masks. + * @return Mask confidence threshold in the inclusive range [0, 1] + */ + float getMaskConfidence() const; + + /** + * Check whether all configuration values are valid. + * @return True when both confidence thresholds are in the inclusive range [0, 1] and maxDetections is positive + */ + bool validate() const; + + /** + * Serialize this configuration into stream metadata. + * @param metadata Output buffer that receives the serialized configuration + * @param datatype Output datatype identifier, set to RFDETRParserConfig + */ + void serialize(std::vector& metadata, DatatypeEnum& datatype) const override; + + /** + * Get the datatype identifier for RFDETRParserConfig. + * @return DatatypeEnum::RFDETRParserConfig + */ + DatatypeEnum getDatatype() const override { + return DatatypeEnum::RFDETRParserConfig; + } + + DEPTHAI_SERIALIZE(RFDETRParserConfig, confidenceThreshold, maxDetections, maskConfidence); +}; + +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/datatype/SCRFDParserConfig.hpp b/include/depthai/beta/datatype/SCRFDParserConfig.hpp new file mode 100644 index 0000000000..22a5b9898d --- /dev/null +++ b/include/depthai/beta/datatype/SCRFDParserConfig.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include "depthai/pipeline/datatype/Buffer.hpp" + +namespace dai { +namespace beta { + +/** + * Runtime configuration for SCRFDParser. + */ +class SCRFDParserConfig : public Buffer { + public: + float confidenceThreshold = 0.5f; + + float iouThreshold = 0.5f; + + int maxDetections = 100; + + SCRFDParserConfig() = default; + + ~SCRFDParserConfig() override; + + /** + * Set the minimum detection confidence. + * @param threshold Confidence threshold in the inclusive range [0, 1] + */ + void setConfidenceThreshold(float threshold); + + /** + * Get the minimum detection confidence. + * @return Confidence threshold in the inclusive range [0, 1] + */ + float getConfidenceThreshold() const; + + /** + * Set the non-maximum suppression intersection-over-union threshold. + * @param threshold Intersection-over-union threshold in the inclusive range [0, 1] + */ + void setIouThreshold(float threshold); + + /** + * Get the non-maximum suppression intersection-over-union threshold. + * @return Intersection-over-union threshold in the inclusive range [0, 1] + */ + float getIouThreshold() const; + + /** + * Set the maximum number of post-suppression detections to retain. + * @param maxDetections Maximum detection count, which must be positive + */ + void setMaxDetections(int maxDetections); + + /** + * Get the maximum number of post-suppression detections to retain. + * @return Maximum post-suppression detection count + */ + int getMaxDetections() const; + + /** + * Check whether all configuration values are valid. + * @return True when both thresholds are in the inclusive range [0, 1] and maxDetections is positive + */ + bool validate() const; + + /** + * Serialize this configuration into stream metadata. + * @param metadata Output buffer that receives the serialized configuration + * @param datatype Output datatype identifier, set to SCRFDParserConfig + */ + void serialize(std::vector& metadata, DatatypeEnum& datatype) const override; + + /** + * Get the datatype identifier for SCRFDParserConfig. + * @return DatatypeEnum::SCRFDParserConfig + */ + DatatypeEnum getDatatype() const override { + return DatatypeEnum::SCRFDParserConfig; + } + + DEPTHAI_SERIALIZE(SCRFDParserConfig, confidenceThreshold, iouThreshold, maxDetections); +}; + +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/datatype/SuperAnimalParserConfig.hpp b/include/depthai/beta/datatype/SuperAnimalParserConfig.hpp new file mode 100644 index 0000000000..0b0d25675e --- /dev/null +++ b/include/depthai/beta/datatype/SuperAnimalParserConfig.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include "depthai/pipeline/datatype/Buffer.hpp" + +namespace dai { +namespace beta { + +/** + * Runtime configuration for SuperAnimalParser. + */ +class SuperAnimalParserConfig : public Buffer { + public: + float scoreThreshold = 0.5f; + + SuperAnimalParserConfig() = default; + + ~SuperAnimalParserConfig() override; + + /** + * Set the minimum keypoint score. + * @param threshold Score threshold in the inclusive range [0, 1] + */ + void setScoreThreshold(float threshold); + + /** + * Get the minimum keypoint score. + * @return Score threshold in the inclusive range [0, 1] + */ + float getScoreThreshold() const; + + /** + * Check whether all configuration values are valid. + * @return True when scoreThreshold is in the inclusive range [0, 1] + */ + bool validate() const; + + /** + * Serialize this configuration into stream metadata. + * @param metadata Output buffer that receives the serialized configuration + * @param datatype Output datatype identifier, set to SuperAnimalParserConfig + */ + void serialize(std::vector& metadata, DatatypeEnum& datatype) const override; + + /** + * Get the datatype identifier for SuperAnimalParserConfig. + * @return DatatypeEnum::SuperAnimalParserConfig + */ + DatatypeEnum getDatatype() const override { + return DatatypeEnum::SuperAnimalParserConfig; + } + + DEPTHAI_SERIALIZE(SuperAnimalParserConfig, scoreThreshold); +}; + +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/datatype/XFeatMonoParserConfig.hpp b/include/depthai/beta/datatype/XFeatMonoParserConfig.hpp new file mode 100644 index 0000000000..a971039bb6 --- /dev/null +++ b/include/depthai/beta/datatype/XFeatMonoParserConfig.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include "depthai/pipeline/datatype/Buffer.hpp" + +namespace dai { +namespace beta { + +/** + * Runtime configuration for XFeatMonoParser. + */ +class XFeatMonoParserConfig : public Buffer { + public: + int maxKeypoints = 4096; + + XFeatMonoParserConfig() = default; + + ~XFeatMonoParserConfig() override; + + /** + * Set the maximum number of keypoints to retain per frame. + * @param maxKeypoints Maximum keypoint count, which must be positive + */ + void setMaxKeypoints(int maxKeypoints); + + /** + * Get the maximum number of keypoints to retain per frame. + * @return Maximum keypoint count + */ + int getMaxKeypoints() const; + + /** + * Check whether all configuration values are valid. + * @return True when maxKeypoints is positive + */ + bool validate() const; + + /** + * Serialize this configuration into stream metadata. + * @param metadata Output buffer that receives the serialized configuration + * @param datatype Output datatype identifier, set to XFeatMonoParserConfig + */ + void serialize(std::vector& metadata, DatatypeEnum& datatype) const override; + + /** + * Get the datatype identifier for XFeatMonoParserConfig. + * @return DatatypeEnum::XFeatMonoParserConfig + */ + DatatypeEnum getDatatype() const override { + return DatatypeEnum::XFeatMonoParserConfig; + } + + DEPTHAI_SERIALIZE(XFeatMonoParserConfig, maxKeypoints); +}; + +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/datatype/XFeatStereoParserConfig.hpp b/include/depthai/beta/datatype/XFeatStereoParserConfig.hpp new file mode 100644 index 0000000000..6347a5c41b --- /dev/null +++ b/include/depthai/beta/datatype/XFeatStereoParserConfig.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include "depthai/pipeline/datatype/Buffer.hpp" + +namespace dai { +namespace beta { + +/** + * Runtime configuration for XFeatStereoParser. + */ +class XFeatStereoParserConfig : public Buffer { + public: + int maxKeypoints = 4096; + + XFeatStereoParserConfig() = default; + + ~XFeatStereoParserConfig() override; + + /** + * Set the maximum number of keypoints to retain from each frame in the stereo pair. + * @param maxKeypoints Maximum keypoint count, which must be positive + */ + void setMaxKeypoints(int maxKeypoints); + + /** + * Get the maximum number of keypoints to retain from each frame in the stereo pair. + * @return Maximum keypoint count applied to each frame + */ + int getMaxKeypoints() const; + + /** + * Check whether all configuration values are valid. + * @return True when maxKeypoints is positive + */ + bool validate() const; + + /** + * Serialize this configuration into stream metadata. + * @param metadata Output buffer that receives the serialized configuration + * @param datatype Output datatype identifier, set to XFeatStereoParserConfig + */ + void serialize(std::vector& metadata, DatatypeEnum& datatype) const override; + + /** + * Get the datatype identifier for XFeatStereoParserConfig. + * @return DatatypeEnum::XFeatStereoParserConfig + */ + DatatypeEnum getDatatype() const override { + return DatatypeEnum::XFeatStereoParserConfig; + } + + DEPTHAI_SERIALIZE(XFeatStereoParserConfig, maxKeypoints); +}; + +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/datatype/YuNetParserConfig.hpp b/include/depthai/beta/datatype/YuNetParserConfig.hpp new file mode 100644 index 0000000000..add5aa7c98 --- /dev/null +++ b/include/depthai/beta/datatype/YuNetParserConfig.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include "depthai/pipeline/datatype/Buffer.hpp" + +namespace dai { +namespace beta { + +/** + * Runtime configuration for YuNetParser. + */ +class YuNetParserConfig : public Buffer { + public: + float confidenceThreshold = 0.8f; + + float iouThreshold = 0.3f; + + int maxDetections = 5000; + + YuNetParserConfig() = default; + + ~YuNetParserConfig() override; + + /** + * Set the minimum face detection confidence. + * @param threshold Confidence threshold in the inclusive range [0, 1] + */ + void setConfidenceThreshold(float threshold); + + /** + * Get the minimum face detection confidence. + * @return Confidence threshold in the inclusive range [0, 1] + */ + float getConfidenceThreshold() const; + + /** + * Set the non-maximum suppression intersection-over-union threshold. + * @param threshold Intersection-over-union threshold in the inclusive range [0, 1] + */ + void setIouThreshold(float threshold); + + /** + * Get the non-maximum suppression intersection-over-union threshold. + * @return Intersection-over-union threshold in the inclusive range [0, 1] + */ + float getIouThreshold() const; + + /** + * Set the maximum number of detections to retain. + * @param maxDetections Maximum detection count; a value less than or equal to zero means unlimited + */ + void setMaxDetections(int maxDetections); + + /** + * Get the maximum number of detections to retain. + * @return Maximum detection count; a value less than or equal to zero means unlimited + */ + int getMaxDetections() const; + + /** + * Check whether all configuration values are valid. + * @return True when confidenceThreshold and iouThreshold are in the inclusive range [0, 1]; maxDetections may have any integer value + */ + bool validate() const; + + /** + * Serialize this configuration into stream metadata. + * @param metadata Output buffer that receives the serialized configuration + * @param datatype Output datatype identifier, set to YuNetParserConfig + */ + void serialize(std::vector& metadata, DatatypeEnum& datatype) const override; + + /** + * Get the datatype identifier for YuNetParserConfig. + * @return DatatypeEnum::YuNetParserConfig + */ + DatatypeEnum getDatatype() const override { + return DatatypeEnum::YuNetParserConfig; + } + + DEPTHAI_SERIALIZE(YuNetParserConfig, confidenceThreshold, iouThreshold, maxDetections); +}; + +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/datatypes.hpp b/include/depthai/beta/datatypes.hpp new file mode 100644 index 0000000000..fe5e1fbd28 --- /dev/null +++ b/include/depthai/beta/datatypes.hpp @@ -0,0 +1,26 @@ +#pragma once + +// IWYU pragma: begin_exports + +#include "datatype/ClassificationSequenceParserConfig.hpp" +#include "datatype/Classifications.hpp" +#include "datatype/Clusters.hpp" +#include "datatype/FastSAMParserConfig.hpp" +#include "datatype/HRNetParserConfig.hpp" +#include "datatype/ImgDetectionsFilterConfig.hpp" +#include "datatype/Keypoints.hpp" +#include "datatype/Lines.hpp" +#include "datatype/MLSDParserConfig.hpp" +#include "datatype/MPPalmDetectionParserConfig.hpp" +#include "datatype/Map2D.hpp" +#include "datatype/MapOutputParserConfig.hpp" +#include "datatype/PPTextDetectionParserConfig.hpp" +#include "datatype/Predictions.hpp" +#include "datatype/RFDETRParserConfig.hpp" +#include "datatype/SCRFDParserConfig.hpp" +#include "datatype/SuperAnimalParserConfig.hpp" +#include "datatype/XFeatMonoParserConfig.hpp" +#include "datatype/XFeatStereoParserConfig.hpp" +#include "datatype/YuNetParserConfig.hpp" + +// IWYU pragma: end_exports diff --git a/include/depthai/beta/node/ClassificationParser.hpp b/include/depthai/beta/node/ClassificationParser.hpp new file mode 100644 index 0000000000..d042a1a5bb --- /dev/null +++ b/include/depthai/beta/node/ClassificationParser.hpp @@ -0,0 +1,141 @@ +#pragma once + +#include +#include +#include +#include + +#include "depthai/beta/BetaNode.hpp" +#include "depthai/beta/datatype/Classifications.hpp" +#include "depthai/beta/properties/ClassificationParserProperties.hpp" +#include "depthai/modelzoo/Zoo.hpp" +#include "depthai/nn_archive/NNArchive.hpp" +#include "depthai/nn_archive/v1/Head.hpp" +#include "depthai/pipeline/datatype/NNData.hpp" + +namespace dai { +namespace beta { +namespace node { + +/** + * @brief ClassificationParser node. Parses the raw output of a classification neural network into a dai::beta::Classifications message with class names and + * scores sorted in descending order of score. + * + * The parser consumes a single output tensor. When the incoming NNData contains exactly one tensor, it is selected automatically; otherwise the output layer + * name must be configured explicitly or through an NNArchive head. Raw scores are dequantized and flattened; when the model output is not already softmaxed, + * the parser applies softmax to convert the scores to probabilities. + */ +class ClassificationParser : public DeviceNodeCRTP { + public: + constexpr static const char* NAME = "ClassificationParser"; + using DeviceNodeCRTP::DeviceNodeCRTP; + using Model = std::variant; + + /** + * Input NN results with classification data to parse. + */ + Input input{*this, {"input", DEFAULT_GROUP, DEFAULT_BLOCKING, DEFAULT_QUEUE_SIZE, {{{DatatypeEnum::NNData, true}}}, DEFAULT_WAIT_FOR_MESSAGE}}; + + /** + * Outputs Classifications message with classes and scores sorted in descending order of score. + */ + Output out{*this, {"out", DEFAULT_GROUP, {{{DatatypeEnum::Classifications, false}}}}}; + + /** + * @brief Build ClassificationParser node. Links the supplied output to this node's input and configures the parser from the model's classification head. + * @param nnInput: Output to link + * @param model: Neural network model + */ + std::shared_ptr build(Node::Output& nnInput, const Model& model); + + /** + * @brief Build ClassificationParser node with the specific head from an NNArchive. Useful when the model has multiple classification heads. + * @param nnInput: Output to link + * @param head: Specific head from NNArchive to use for this parser + */ + std::shared_ptr build(Node::Output& nnInput, const dai::nn_archive::v1::Head& head); + + /** + * @brief Set NNArchive for this Node. The archive must contain exactly one ClassificationParser head; use setNNArchiveHead() to select a specific head + * from a multi-head archive. + * + * @param nnArchive: NNArchive to set + */ + void setNNArchive(const NNArchive& nnArchive); + + /** + * @brief Set NNArchive head for this Node. The head must be a ClassificationParser head with exactly one output layer. + * + * @param head: NNArchive head to set + */ + void setNNArchiveHead(const dai::nn_archive::v1::Head& head); + + /** + * Sets the name of the model output layer to parse. + * + * When left empty, the parser selects the tensor automatically if the incoming NNData contains exactly one tensor and fails otherwise. + * + * @param outputLayerName Name of the output layer + */ + void setOutputLayerName(const std::string& outputLayerName); + + /** + * Returns the name of the model output layer to parse. + */ + std::string getOutputLayerName() const; + + /** + * Sets the class names to link with the classification scores. + * + * The class names are expected to be in the same order as the neural network's output. The number of class names must match the number of scores produced + * by the model. + * + * @param classes Vector of class names + */ + void setClasses(const std::vector& classes); + + /** + * Returns the class names to link with the classification scores. + */ + std::vector getClasses() const; + + /** + * Sets whether the model output is already softmaxed. + * + * When false, the parser applies softmax to convert the raw scores to probabilities. + * + * @param isSoftmax True when the model output is already softmaxed + */ + void setSoftmax(bool isSoftmax); + + /** + * Returns whether the model output is treated as already softmaxed. + */ + bool getSoftmax() const; + + /** + * Select whether the node runs on the host or device. + */ + void setRunOnHost(bool runOnHost); + + /** + * Returns true when this node runs on the host. + * + * Host-only pipelines always run the node on the host. + */ + bool runOnHost() const override; + + void run() override; + + private: + void setConfig(const dai::NNArchiveVersionedConfig& config); + void setConfig(const dai::nn_archive::v1::Head& head); + NNArchive decodeModel(const Model& model); + NNArchive createNNArchive(NNModelDescription& modelDesc); + + bool runOnHostVar = false; +}; + +} // namespace node +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/node/ClassificationSequenceParser.hpp b/include/depthai/beta/node/ClassificationSequenceParser.hpp new file mode 100644 index 0000000000..cc0f3ec094 --- /dev/null +++ b/include/depthai/beta/node/ClassificationSequenceParser.hpp @@ -0,0 +1,210 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "depthai/beta/BetaNode.hpp" +#include "depthai/beta/datatype/Classifications.hpp" +#include "depthai/beta/properties/ClassificationSequenceParserProperties.hpp" +#include "depthai/modelzoo/Zoo.hpp" +#include "depthai/nn_archive/NNArchive.hpp" +#include "depthai/nn_archive/v1/Head.hpp" +#include "depthai/pipeline/datatype/NNData.hpp" + +namespace dai { +namespace beta { +namespace node { + +/** + * @brief ClassificationSequenceParser node. Parses the raw output of a classification sequence neural network into a dai::beta::Classifications message with + * class names and scores ordered by their position in the sequence. + * + * The model predicts the classes multiple times and returns a list of predicted classes, where each item corresponds to the relative step in the sequence. In + * addition to time series classification, this parser can also be used for text recognition models where words can be interpreted as a sequence of characters + * (classes). + * + * The parser consumes a single output tensor of shape (sequenceLength, nClasses), (1, sequenceLength, nClasses) or (sequenceLength, nClasses, 1). When the + * incoming NNData contains exactly one tensor, it is selected automatically; otherwise the output layer name must be configured explicitly or through an + * NNArchive head. Raw scores are dequantized; when the model output is not already softmaxed, the parser applies softmax along each sequence step to convert + * the scores to probabilities. + */ +class ClassificationSequenceParser : public DeviceNodeCRTP { + protected: + Properties& getProperties() override; + + public: + constexpr static const char* NAME = "ClassificationSequenceParser"; + using DeviceNodeCRTP::DeviceNodeCRTP; + using Model = std::variant; + + ClassificationSequenceParser() = default; + ClassificationSequenceParser(std::unique_ptr props); + + /** Configuration used until a message is received on inputConfig. */ + std::shared_ptr initialConfig = std::make_shared(); + + /** + * Runtime parser configuration. When synchronized, one configuration is consumed per frame; + * otherwise all queued configurations are drained and the newest valid one is used. + */ + Input inputConfig{*this, {"inputConfig", DEFAULT_GROUP, false, 4, {{{DatatypeEnum::ClassificationSequenceParserConfig, false}}}, DEFAULT_WAIT_FOR_MESSAGE}}; + + /** + * Input NN results with classification sequence data to parse. + */ + Input input{*this, {"input", DEFAULT_GROUP, DEFAULT_BLOCKING, DEFAULT_QUEUE_SIZE, {{{DatatypeEnum::NNData, true}}}, DEFAULT_WAIT_FOR_MESSAGE}}; + + /** + * Outputs Classifications message with classes and scores ordered by their position in the sequence. + */ + Output out{*this, {"out", DEFAULT_GROUP, {{{DatatypeEnum::Classifications, false}}}}}; + + /** + * @brief Build ClassificationSequenceParser node. Links the supplied output to this node's input and configures the parser from the model's classification + * sequence head. + * @param nnInput: Output to link + * @param model: Neural network model + */ + std::shared_ptr build(Node::Output& nnInput, const Model& model); + + /** + * @brief Build ClassificationSequenceParser node with the specific head from an NNArchive. Useful when the model has multiple classification sequence + * heads. + * @param nnInput: Output to link + * @param head: Specific head from NNArchive to use for this parser + */ + std::shared_ptr build(Node::Output& nnInput, const dai::nn_archive::v1::Head& head); + + /** + * @brief Set NNArchive for this Node. The archive must contain exactly one ClassificationSequenceParser head; use setNNArchiveHead() to select a specific + * head from a multi-head archive. + * + * @param nnArchive: NNArchive to set + */ + void setNNArchive(const NNArchive& nnArchive); + + /** + * @brief Set NNArchive head for this Node. The head must be a ClassificationSequenceParser head with exactly one output layer. + * + * @param head: NNArchive head to set + */ + void setNNArchiveHead(const dai::nn_archive::v1::Head& head); + + /** + * Sets the name of the model output layer to parse. + * + * When left empty, the parser selects the tensor automatically if the incoming NNData contains exactly one tensor and fails otherwise. + * + * @param outputLayerName Name of the output layer + */ + void setOutputLayerName(const std::string& outputLayerName); + + /** + * Returns the name of the model output layer to parse. + */ + std::string getOutputLayerName() const; + + /** + * Sets the class names to link with the per-step classification scores. + * + * The class names are expected to be in the same order as the neural network's output. The number of class names must match the number of scores produced + * by the model at each sequence step. + * + * @param classes Vector of class names + */ + void setClasses(const std::vector& classes); + + /** + * Returns the class names to link with the per-step classification scores. + */ + std::vector getClasses() const; + + /** + * Sets whether the model output is already softmaxed. + * + * When false, the parser applies softmax along each sequence step to convert the raw scores to probabilities. + * + * @param isSoftmax True when the model output is already softmaxed + */ + void setSoftmax(bool isSoftmax); + + /** + * Returns whether the model output is treated as already softmaxed. + */ + bool getSoftmax() const; + + /** + * Sets the class indexes to ignore during classification sequence generation (e.g. background class, blank space). + * + * Sequence steps whose most probable class index is listed here are dropped from the output. Every index must be within [0, nClasses - 1]. + * + * @param ignoredIndexes Vector of class indexes to ignore + * @note Configures startup behavior. Send ClassificationSequenceParserConfig to inputConfig after the pipeline starts. + */ + void setIgnoredIndexes(const std::vector& ignoredIndexes); + + /** + * Returns the class indexes ignored during classification sequence generation. + */ + std::vector getIgnoredIndexes() const; + + /** + * Sets whether consecutive duplicate classes are removed from the sequence. + * + * Only consecutive duplicates are removed; repeated classes separated by other classes are kept. + * + * @param removeDuplicates True to remove consecutive duplicates from the sequence + * @note Configures startup behavior. Send ClassificationSequenceParserConfig to inputConfig after the pipeline starts. + */ + void setRemoveDuplicates(bool removeDuplicates); + + /** + * Returns whether consecutive duplicate classes are removed from the sequence. + */ + bool getRemoveDuplicates() const; + + /** + * Sets whether the remaining classes are concatenated. Used mostly for text processing. + * + * When true and more than one class remains: when all remaining class names are at most one character long, they are joined and split on whitespace into + * words with a per-word mean score; otherwise all class names are joined into a single string with a " " separator and one mean score. + * + * @param concatenateClasses True to concatenate the remaining classes + * @note Configures startup behavior. Send ClassificationSequenceParserConfig to inputConfig after the pipeline starts. + */ + void setConcatenateClasses(bool concatenateClasses); + + /** + * Returns whether the remaining classes are concatenated. + */ + bool getConcatenateClasses() const; + + /** + * Select whether the node runs on the host or device. + */ + void setRunOnHost(bool runOnHost); + + /** + * Returns true when this node runs on the host. + * + * Host-only pipelines always run the node on the host. + */ + bool runOnHost() const override; + + void run() override; + + private: + void setConfig(const dai::NNArchiveVersionedConfig& config); + void setConfig(const dai::nn_archive::v1::Head& head); + NNArchive decodeModel(const Model& model); + NNArchive createNNArchive(NNModelDescription& modelDesc); + + bool runOnHostVar = false; +}; + +} // namespace node +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/node/EmbeddingsParser.hpp b/include/depthai/beta/node/EmbeddingsParser.hpp new file mode 100644 index 0000000000..48ec084772 --- /dev/null +++ b/include/depthai/beta/node/EmbeddingsParser.hpp @@ -0,0 +1,109 @@ +#pragma once + +#include +#include +#include + +#include "depthai/beta/BetaNode.hpp" +#include "depthai/beta/properties/EmbeddingsParserProperties.hpp" +#include "depthai/modelzoo/Zoo.hpp" +#include "depthai/nn_archive/NNArchive.hpp" +#include "depthai/nn_archive/v1/Head.hpp" +#include "depthai/pipeline/datatype/NNData.hpp" + +namespace dai { +namespace beta { +namespace node { + +/** + * @brief EmbeddingsParser node. Validates the raw output of an embeddings neural network model head and forwards it unchanged as a dai::NNData message. + * + * The parser expects a single output tensor carrying the embedding vector. When the output layer name is left unconfigured, every incoming NNData must contain + * exactly one tensor; otherwise the message is rejected. The message itself is forwarded without modification, so all tensors, sequence number, timestamps, + * and image transformation metadata are preserved. + */ +class EmbeddingsParser : public DeviceNodeCRTP { + public: + constexpr static const char* NAME = "EmbeddingsParser"; + using DeviceNodeCRTP::DeviceNodeCRTP; + using Model = std::variant; + + /** + * Input NN results with embeddings data to validate and forward. + */ + Input input{*this, {"input", DEFAULT_GROUP, DEFAULT_BLOCKING, DEFAULT_QUEUE_SIZE, {{{DatatypeEnum::NNData, true}}}, DEFAULT_WAIT_FOR_MESSAGE}}; + + /** + * Outputs the unchanged NNData message containing the embeddings output layer. + */ + Output out{*this, {"out", DEFAULT_GROUP, {{{DatatypeEnum::NNData, false}}}}}; + + /** + * @brief Build EmbeddingsParser node. Links the supplied output to this node's input and configures the parser from the model's embeddings head. + * @param nnInput: Output to link + * @param model: Neural network model + */ + std::shared_ptr build(Node::Output& nnInput, const Model& model); + + /** + * @brief Build EmbeddingsParser node with the specific head from an NNArchive. Useful when the model has multiple embeddings heads. + * @param nnInput: Output to link + * @param head: Specific head from NNArchive to use for this parser + */ + std::shared_ptr build(Node::Output& nnInput, const dai::nn_archive::v1::Head& head); + + /** + * @brief Set NNArchive for this Node. The archive must contain exactly one EmbeddingsParser head; use setNNArchiveHead() to select a specific head from a + * multi-head archive. + * + * @param nnArchive: NNArchive to set + */ + void setNNArchive(const NNArchive& nnArchive); + + /** + * @brief Set NNArchive head for this Node. The head must be an EmbeddingsParser head with exactly one output layer. + * + * @param head: NNArchive head to set + */ + void setNNArchiveHead(const dai::nn_archive::v1::Head& head); + + /** + * Sets the name of the model output layer carrying the embeddings. + * + * When left empty, the parser requires the incoming NNData to contain exactly one tensor and fails otherwise. + * + * @param outputLayerName Name of the output layer + */ + void setOutputLayerName(const std::string& outputLayerName); + + /** + * Returns the name of the model output layer carrying the embeddings. + */ + std::string getOutputLayerName() const; + + /** + * Select whether the node runs on the host or device. + */ + void setRunOnHost(bool runOnHost); + + /** + * Returns true when this node runs on the host. + * + * Host-only pipelines always run the node on the host. + */ + bool runOnHost() const override; + + void run() override; + + private: + void setConfig(const dai::NNArchiveVersionedConfig& config); + void setConfig(const dai::nn_archive::v1::Head& head); + NNArchive decodeModel(const Model& model); + NNArchive createNNArchive(NNModelDescription& modelDesc); + + bool runOnHostVar = false; +}; + +} // namespace node +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/node/FastSAMParser.hpp b/include/depthai/beta/node/FastSAMParser.hpp new file mode 100644 index 0000000000..632be93754 --- /dev/null +++ b/include/depthai/beta/node/FastSAMParser.hpp @@ -0,0 +1,278 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "depthai/beta/BetaNode.hpp" +#include "depthai/beta/properties/FastSAMParserProperties.hpp" +#include "depthai/modelzoo/Zoo.hpp" +#include "depthai/nn_archive/NNArchive.hpp" +#include "depthai/nn_archive/v1/Head.hpp" +#include "depthai/pipeline/datatype/NNData.hpp" +#include "depthai/pipeline/datatype/SegmentationMask.hpp" + +namespace dai { +namespace beta { +namespace node { + +/** + * @brief FastSAMParser node. Parses the output of the FastSAM segmentation model + * (https://github.com/CASIA-IVA-Lab/FastSAM) into a dai::SegmentationMask message where each pixel holds the index of the instance it belongs to and 255 + * marks background. + * + * The parser consumes the model's YOLO detection outputs (NCHW tensors of shape (1, numClasses + 5, gridH, gridW), sorted by layer name and decoded + * anchorless with strides 8/16/32), the per-head mask-coefficient outputs (NCHW tensors of shape (1, numPrototypes, gridH, gridW), sorted by layer name) and + * the prototype masks output (NCHW tensor of shape (1, numPrototypes, protoH, protoW)). The model input size is derived from the first (stride-8) YOLO + * output's grid times 8; the number of prototypes from the protos tensor's channel count. Boxes pass confidence filtering and non-maximum suppression, boxes + * within 20 pixels of the image border are snapped to it, and a box overlapping the full image with IoU > 0.9 is replaced by the full-image box. Each kept + * detection's mask is combined from the prototypes, resized to the model input size with nearest-neighbor interpolation, cropped to its box and binarized + * with the mask confidence threshold. + * + * The prompt selects the emitted instances: "everything" keeps all detections (later, lower-confidence instances overwrite earlier ones on overlapping + * pixels), "bbox" keeps the single mask with the highest IoU against the prompt bounding box, and "point" combines the masks containing the prompt point + * (added for point label 1, subtracted for 0). With no detections, a fully-background mask is emitted. + * + */ +class FastSAMParser : public DeviceNodeCRTP { + protected: + Properties& getProperties() override; + + public: + constexpr static const char* NAME = "FastSAMParser"; + using DeviceNodeCRTP::DeviceNodeCRTP; + using Model = std::variant; + + FastSAMParser() = default; + FastSAMParser(std::unique_ptr props); + + /** Configuration used until a message is received on inputConfig. */ + std::shared_ptr initialConfig = std::make_shared(); + + /** + * Runtime parser configuration. When synchronized, one configuration is consumed per frame; + * otherwise all queued configurations are drained and the newest valid one is used. + */ + Input inputConfig{*this, {"inputConfig", DEFAULT_GROUP, false, 4, {{{DatatypeEnum::FastSAMParserConfig, false}}}, DEFAULT_WAIT_FOR_MESSAGE}}; + + /** + * Input NN results with FastSAM data to parse. + */ + Input input{*this, {"input", DEFAULT_GROUP, DEFAULT_BLOCKING, DEFAULT_QUEUE_SIZE, {{{DatatypeEnum::NNData, true}}}, DEFAULT_WAIT_FOR_MESSAGE}}; + + /** + * Outputs SegmentationMask message with the resulting segmentation masks given the prompt. + */ + Output out{*this, {"out", DEFAULT_GROUP, {{{DatatypeEnum::SegmentationMask, false}}}}}; + + /** + * @brief Build FastSAMParser node. Links the supplied output to this node's input and configures the parser from the model's FastSAMParser head. + * @param nnInput: Output to link + * @param model: Neural network model + */ + std::shared_ptr build(Node::Output& nnInput, const Model& model); + + /** + * @brief Build FastSAMParser node with the specific head from an NNArchive. Useful when the model has multiple heads. + * @param nnInput: Output to link + * @param head: Specific head from NNArchive to use for this parser + */ + std::shared_ptr build(Node::Output& nnInput, const dai::nn_archive::v1::Head& head); + + /** + * @brief Set NNArchive for this Node. The archive must contain exactly one FastSAMParser head; use setNNArchiveHead() to select a specific head from a + * multi-head archive. + * + * @param nnArchive: NNArchive to set + */ + void setNNArchive(const NNArchive& nnArchive); + + /** + * @brief Set NNArchive head for this Node. The head's output layer names containing "_yolo" configure the YOLO output layers and those containing + * "_masks" the mask output layers (each only when at least one matches). The confidence threshold, number of classes, NMS threshold, mask confidence, + * prompt, points, point label and bounding box are read from the head metadata when present. + * + * @param head: NNArchive head to set + */ + void setNNArchiveHead(const dai::nn_archive::v1::Head& head); + + /** + * Sets the confidence score threshold for detected objects. Detections whose score is strictly greater than the threshold are kept. Defaults to 0.5. + * + * @param threshold Confidence score threshold, must be between 0 and 1 + * @note Configures startup behavior. Send FastSAMParserConfig to inputConfig after the pipeline starts. + */ + void setConfidenceThreshold(float threshold); + + /** + * Returns the confidence score threshold for detected objects. + */ + float getConfidenceThreshold() const; + + /** + * Sets the number of classes in the model. The YOLO output tensors must have numClasses + 5 channels. Defaults to 1. + * + * @param numClasses Number of classes, must be greater than 0 + */ + void setNumClasses(std::int32_t numClasses); + + /** + * Returns the number of classes in the model. + */ + std::int32_t getNumClasses() const; + + /** + * Sets the non-maximum suppression overlap threshold. Boxes whose overlap with a kept box is strictly greater than the threshold are suppressed. + * Defaults to 0.5. + * + * @param iouThreshold Overlap threshold, must be between 0 and 1 + * @note Configures startup behavior. Send FastSAMParserConfig to inputConfig after the pipeline starts. + */ + void setIouThreshold(float iouThreshold); + + /** + * Returns the non-maximum suppression overlap threshold. + */ + float getIouThreshold() const; + + /** + * Sets the mask confidence threshold used to binarize instance masks. Mask pixels with a sigmoid probability strictly greater than the threshold belong + * to the instance. Defaults to 0.5. + * + * @param maskConfidence Mask confidence threshold, must be between 0 and 1 + * @note Configures startup behavior. Send FastSAMParserConfig to inputConfig after the pipeline starts. + */ + void setMaskConfidence(float maskConfidence); + + /** + * Returns the mask confidence threshold. + */ + float getMaskConfidence() const; + + /** + * Sets the prompt type: "everything" emits every detected instance, "bbox" the single instance mask with the highest IoU against the prompt bounding + * box (see setBoundingBox()), and "point" the combination of the instance masks containing the prompt point (see setPoints() and setPointLabel()). + * Defaults to "everything". + * + * @param prompt Prompt type, one of "everything", "bbox" or "point" + * @note Configures startup behavior. Send FastSAMParserConfig to inputConfig after the pipeline starts. + */ + void setPrompt(const std::string& prompt); + + /** + * Returns the prompt type. + */ + std::string getPrompt() const; + + /** + * Sets the prompt point as (x, y) in model-input pixels, used by the "point" prompt. Unset by default; the "point" prompt requires it. + * + * @param x Point x coordinate + * @param y Point y coordinate + * @note Configures startup behavior. Send FastSAMParserConfig to inputConfig after the pipeline starts. + */ + void setPoints(std::int32_t x, std::int32_t y); + + /** + * Returns the prompt point as (x, y), or std::nullopt when it is not set. + */ + std::optional> getPoints() const; + + /** + * Sets the prompt point label, used by the "point" prompt: 1 adds the instance masks containing the point, 0 subtracts them. Unset by default; the + * "point" prompt requires it. + * + * @param pointLabel Point label + * @note Configures startup behavior. Send FastSAMParserConfig to inputConfig after the pipeline starts. + */ + void setPointLabel(std::int32_t pointLabel); + + /** + * Returns the prompt point label, or std::nullopt when it is not set. + */ + std::optional getPointLabel() const; + + /** + * Sets the prompt bounding box as (x1, y1, x2, y2) in model-input pixels, used by the "bbox" prompt. Unset by default; the "bbox" prompt requires it and + * its x2 and y2 coordinates must not be 0. + * + * @param bbox Bounding box as (x1, y1, x2, y2) + * @note Configures startup behavior. Send FastSAMParserConfig to inputConfig after the pipeline starts. + */ + void setBoundingBox(const std::array& bbox); + + /** + * Returns the prompt bounding box as (x1, y1, x2, y2), or std::nullopt when it is not set. + */ + std::optional> getBoundingBox() const; + + /** + * Sets the names of the model's YOLO output layers. The layers are processed sorted by name, so the stride-8 head must come first in sort order. + * Defaults to ["output1_yolov8", "output2_yolov8", "output3_yolov8"]. + * + * @param yoloOutputs Names of the YOLO output layers + */ + void setYoloOutputs(const std::vector& yoloOutputs); + + /** + * Returns the names of the model's YOLO output layers. + */ + std::vector getYoloOutputs() const; + + /** + * Sets the names of the model's mask-coefficient output layers. Only names containing "mask" are used, sorted by name and index-aligned with the sorted + * YOLO output layers; when empty, all layer names of the incoming NNData containing "mask" are used. Defaults to ["output1_masks", "output2_masks", + * "output3_masks"]. + * + * @param maskOutputs Names of the mask output layers + */ + void setMaskOutputs(const std::vector& maskOutputs); + + /** + * Returns the names of the model's mask-coefficient output layers. + */ + std::vector getMaskOutputs() const; + + /** + * Sets the name of the model's prototype-masks output layer; when empty, "protos_output" is used. Defaults to "protos_output". + * + * @param protosOutput Name of the protos output layer + */ + void setProtosOutput(const std::string& protosOutput); + + /** + * Returns the name of the model's prototype-masks output layer. + */ + std::string getProtosOutput() const; + + /** + * Select whether the node runs on the host or device. + */ + void setRunOnHost(bool runOnHost); + + /** + * Returns true when this node runs on the host. + * + * Host-only pipelines always run the node on the host. + */ + bool runOnHost() const override; + + void run() override; + + private: + void setConfig(const dai::NNArchiveVersionedConfig& config); + void setConfig(const dai::nn_archive::v1::Head& head); + NNArchive decodeModel(const Model& model); + NNArchive createNNArchive(NNModelDescription& modelDesc); + + bool runOnHostVar = false; +}; + +} // namespace node +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/node/HRNetParser.hpp b/include/depthai/beta/node/HRNetParser.hpp new file mode 100644 index 0000000000..d62b3b3d28 --- /dev/null +++ b/include/depthai/beta/node/HRNetParser.hpp @@ -0,0 +1,169 @@ +#pragma once + +#include +#include +#include +#include + +#include "depthai/beta/BetaNode.hpp" +#include "depthai/beta/datatype/Keypoints.hpp" +#include "depthai/beta/properties/HRNetParserProperties.hpp" +#include "depthai/modelzoo/Zoo.hpp" +#include "depthai/nn_archive/NNArchive.hpp" +#include "depthai/nn_archive/v1/Head.hpp" +#include "depthai/pipeline/datatype/NNData.hpp" + +namespace dai { +namespace beta { +namespace node { + +/** + * @brief HRNetParser node. Parses the heatmap output of an HRNet pose estimation neural network into a dai::beta::Keypoints message. The decoding is inspired + * by https://github.com/ibaiGorordo/ONNX-HRNET-Human-Pose-Estimation. + * + * The parser consumes a single output tensor. When the incoming NNData contains exactly one tensor, it is selected automatically; otherwise the output layer + * name must be configured explicitly or through an NNArchive head. The tensor is read in NCHW orientation regardless of its stored order; after squeezing a + * leading batch dimension of 1 it must be a 3D tensor of shape (numKeypoints, height, width). The number of keypoints and the heatmap size are derived from + * the tensor shape. Per heatmap, the keypoint is the position of the maximum value normalized by the heatmap size and the keypoint's score is the maximum + * value clipped to [0, 1]. Keypoints with a score below the score threshold are dropped and the skeleton edges are remapped to the kept keypoints. + * + */ +class HRNetParser : public DeviceNodeCRTP { + protected: + Properties& getProperties() override; + + public: + constexpr static const char* NAME = "HRNetParser"; + using DeviceNodeCRTP::DeviceNodeCRTP; + using Model = std::variant; + + HRNetParser() = default; + HRNetParser(std::unique_ptr props); + + /** Configuration used until a message is received on inputConfig. */ + std::shared_ptr initialConfig = std::make_shared(); + + /** + * Runtime parser configuration. When synchronized, one configuration is consumed per frame; + * otherwise all queued configurations are drained and the newest valid one is used. + */ + Input inputConfig{*this, {"inputConfig", DEFAULT_GROUP, false, 4, {{{DatatypeEnum::HRNetParserConfig, false}}}, DEFAULT_WAIT_FOR_MESSAGE}}; + + /** + * Input NN results with heatmaps data to parse. + */ + Input input{*this, {"input", DEFAULT_GROUP, DEFAULT_BLOCKING, DEFAULT_QUEUE_SIZE, {{{DatatypeEnum::NNData, true}}}, DEFAULT_WAIT_FOR_MESSAGE}}; + + /** + * Outputs Keypoints message with the detected body keypoints. + */ + Output out{*this, {"out", DEFAULT_GROUP, {{{DatatypeEnum::Keypoints, false}}}}}; + + /** + * @brief Build HRNetParser node. Links the supplied output to this node's input and configures the parser from the model's HRNetParser head. + * @param nnInput: Output to link + * @param model: Neural network model + */ + std::shared_ptr build(Node::Output& nnInput, const Model& model); + + /** + * @brief Build HRNetParser node with the specific head from an NNArchive. Useful when the model has multiple heads. + * @param nnInput: Output to link + * @param head: Specific head from NNArchive to use for this parser + */ + std::shared_ptr build(Node::Output& nnInput, const dai::nn_archive::v1::Head& head); + + /** + * @brief Set NNArchive for this Node. The archive must contain exactly one HRNetParser head; use setNNArchiveHead() to select a specific head from a + * multi-head archive. + * + * @param nnArchive: NNArchive to set + */ + void setNNArchive(const NNArchive& nnArchive); + + /** + * @brief Set NNArchive head for this Node. The head must be an HRNetParser head with exactly one output layer. + * + * @param head: NNArchive head to set + */ + void setNNArchiveHead(const dai::nn_archive::v1::Head& head); + + /** + * Sets the name of the model output layer to parse. + * + * When left empty, the parser selects the tensor automatically if the incoming NNData contains exactly one tensor and fails otherwise. + * + * @param outputLayerName Name of the output layer + */ + void setOutputLayerName(const std::string& outputLayerName); + + /** + * Returns the name of the model output layer to parse. + */ + std::string getOutputLayerName() const; + + /** + * Sets the confidence score threshold for detected keypoints. Keypoints with a score strictly below the threshold are dropped. + * + * @param threshold Confidence score threshold, must be between 0 and 1 + * @note Configures startup behavior. Send HRNetParserConfig to inputConfig after the pipeline starts. + */ + void setScoreThreshold(float threshold); + + /** + * Returns the confidence score threshold for detected keypoints. + */ + float getScoreThreshold() const; + + /** + * Sets the label names for the keypoints, indexed by keypoint index. + * + * @param labelNames Vector of label names + */ + void setLabelNames(const std::vector& labelNames); + + /** + * Returns the label names for the keypoints. + */ + std::vector getLabelNames() const; + + /** + * Sets the skeleton edges as pairs of keypoint indices used for visualizing the skeleton. + * + * Example: {{0, 1}, {1, 2}, {2, 3}, {3, 0}} connects keypoint 0 to keypoint 1, keypoint 1 to keypoint 2, etc. + * + * @param edges Vector of keypoint index pairs + */ + void setEdges(const std::vector& edges); + + /** + * Returns the skeleton edges as pairs of keypoint indices. + */ + std::vector getEdges() const; + + /** + * Select whether the node runs on the host or device. + */ + void setRunOnHost(bool runOnHost); + + /** + * Returns true when this node runs on the host. + * + * Host-only pipelines always run the node on the host. + */ + bool runOnHost() const override; + + void run() override; + + private: + void setConfig(const dai::NNArchiveVersionedConfig& config); + void setConfig(const dai::nn_archive::v1::Head& head); + NNArchive decodeModel(const Model& model); + NNArchive createNNArchive(NNModelDescription& modelDesc); + + bool runOnHostVar = false; +}; + +} // namespace node +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/node/ImageOutputParser.hpp b/include/depthai/beta/node/ImageOutputParser.hpp new file mode 100644 index 0000000000..024108a31c --- /dev/null +++ b/include/depthai/beta/node/ImageOutputParser.hpp @@ -0,0 +1,130 @@ +#pragma once + +#include +#include +#include + +#include "depthai/beta/BetaNode.hpp" +#include "depthai/beta/properties/ImageOutputParserProperties.hpp" +#include "depthai/modelzoo/Zoo.hpp" +#include "depthai/nn_archive/NNArchive.hpp" +#include "depthai/nn_archive/v1/Head.hpp" +#include "depthai/pipeline/datatype/NNData.hpp" + +namespace dai { +namespace beta { +namespace node { + +/** + * @brief ImageOutputParser node. Parses the output of image-to-image models (e.g. DnCNN3, zero-dce) where the output is a modified image (denoised, enhanced + * etc.) into a dai::ImgFrame message. + * + * The parser consumes a single output tensor. When the incoming NNData contains exactly one tensor, it is selected automatically; otherwise the output layer + * name must be configured explicitly or through an NNArchive head. The tensor is read in its stored order; after squeezing a leading batch dimension of 1 it + * must be a 3D image tensor in CHW or HWC orientation, with the channel dimension equal to 1 (grayscale) or 3 (color). All dimensions are derived from the + * runtime tensor descriptor. The values are min-max normalized and scaled to the [0, 255] 8-bit range. + * + * A grayscale image is emitted as a GRAY8 frame. A color image is emitted as a BGR888p frame when the pipeline's default device platform is RVC2 and as a + * BGR888i frame otherwise, including in a device-less pipeline. The model output is treated as RGB and converted to BGR unless the BGR-output flag marks it + * as already BGR. + * + */ +class ImageOutputParser : public DeviceNodeCRTP { + public: + constexpr static const char* NAME = "ImageOutputParser"; + using DeviceNodeCRTP::DeviceNodeCRTP; + using Model = std::variant; + + /** + * Input NN results with image tensor data to parse. + */ + Input input{*this, {"input", DEFAULT_GROUP, DEFAULT_BLOCKING, DEFAULT_QUEUE_SIZE, {{{DatatypeEnum::NNData, true}}}, DEFAULT_WAIT_FOR_MESSAGE}}; + + /** + * Outputs ImgFrame message with the model output image, e.g. a denoised or enhanced image. + */ + Output out{*this, {"out", DEFAULT_GROUP, {{{DatatypeEnum::ImgFrame, false}}}}}; + + /** + * @brief Build ImageOutputParser node. Links the supplied output to this node's input and configures the parser from the model's ImageOutputParser head. + * @param nnInput: Output to link + * @param model: Neural network model + */ + std::shared_ptr build(Node::Output& nnInput, const Model& model); + + /** + * @brief Build ImageOutputParser node with the specific head from an NNArchive. Useful when the model has multiple heads. + * @param nnInput: Output to link + * @param head: Specific head from NNArchive to use for this parser + */ + std::shared_ptr build(Node::Output& nnInput, const dai::nn_archive::v1::Head& head); + + /** + * @brief Set NNArchive for this Node. The archive must contain exactly one ImageOutputParser head; use setNNArchiveHead() to select a specific head from a + * multi-head archive. + * + * @param nnArchive: NNArchive to set + */ + void setNNArchive(const NNArchive& nnArchive); + + /** + * @brief Set NNArchive head for this Node. The head must be an ImageOutputParser head with exactly one output layer. + * + * @param head: NNArchive head to set + */ + void setNNArchiveHead(const dai::nn_archive::v1::Head& head); + + /** + * Sets the name of the model output layer to parse. + * + * When left empty, the parser selects the tensor automatically if the incoming NNData contains exactly one tensor and fails otherwise. + * + * @param outputLayerName Name of the output layer + */ + void setOutputLayerName(const std::string& outputLayerName); + + /** + * Returns the name of the model output layer to parse. + */ + std::string getOutputLayerName() const; + + /** + * Sets the flag indicating whether the model output image is in BGR (Blue-Green-Red) channel order. + * + * When false (the default), a color model output is treated as RGB and its channels are swapped to BGR before being emitted. + * + * @param outputIsBGR True when the model output image is already BGR, defaults to true + */ + void setBGROutput(bool outputIsBGR = true); + + /** + * Returns the flag indicating whether the model output image is in BGR (Blue-Green-Red) channel order. + */ + bool getBGROutput() const; + + /** + * Select whether the node runs on the host or device. + */ + void setRunOnHost(bool runOnHost); + + /** + * Returns true when this node runs on the host. + * + * Host-only pipelines always run the node on the host. + */ + bool runOnHost() const override; + + void run() override; + + private: + void setConfig(const dai::NNArchiveVersionedConfig& config); + void setConfig(const dai::nn_archive::v1::Head& head); + NNArchive decodeModel(const Model& model); + NNArchive createNNArchive(NNModelDescription& modelDesc); + + bool runOnHostVar = false; +}; + +} // namespace node +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/node/ImgDetectionsFilter.hpp b/include/depthai/beta/node/ImgDetectionsFilter.hpp new file mode 100644 index 0000000000..65b1e8c59e --- /dev/null +++ b/include/depthai/beta/node/ImgDetectionsFilter.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include + +#include "depthai/beta/BetaNode.hpp" +#include "depthai/beta/properties/ImgDetectionsFilterProperties.hpp" +#include "depthai/pipeline/datatype/ImgDetections.hpp" + +namespace dai { +namespace beta { +namespace node { + +/** + * @brief Experimental node for filtering image detections. + */ +class ImgDetectionsFilter : public DeviceNodeCRTP { + protected: + Properties& getProperties() override { + properties.initialConfig = *initialConfig; + return properties; + } + + public: + constexpr static const char* NAME = "ImgDetectionsFilter"; + using DeviceNodeCRTP::DeviceNodeCRTP; + + ImgDetectionsFilter() = default; + ImgDetectionsFilter(std::unique_ptr props); + ~ImgDetectionsFilter() override; + + /** + * Configuration used until a message is received on inputConfig. + * + * The default configuration forwards detections unchanged. + */ + std::shared_ptr initialConfig = std::make_shared(); + + /** + * Image detections to filter. + */ + Input input{*this, {"input", DEFAULT_GROUP, DEFAULT_BLOCKING, DEFAULT_QUEUE_SIZE, {{{DatatypeEnum::ImgDetections, false}}}, DEFAULT_WAIT_FOR_MESSAGE}}; + + /** + * Runtime filter configuration. The most recently received configuration + * is reused for subsequent detection messages. + */ + Input inputConfig{*this, {"inputConfig", DEFAULT_GROUP, false, 4, {{{DatatypeEnum::ImgDetectionsFilterConfig, false}}}, false}}; + + /** + * Filtered image detections. + */ + Output output{*this, {"output", DEFAULT_GROUP, {{{DatatypeEnum::ImgDetections, false}}}}}; + + /** + * Select whether the node runs on the host or device. + */ + void setRunOnHost(bool runOnHost); + + /** + * Returns true when this node runs on the host. + * + * Host-only pipelines always run the node on the host. + */ + bool runOnHost() const override; + + void run() override; + + private: + bool runOnHostVar = false; +}; + +} // namespace node +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/node/KeypointParser.hpp b/include/depthai/beta/node/KeypointParser.hpp new file mode 100644 index 0000000000..0583a4bcc1 --- /dev/null +++ b/include/depthai/beta/node/KeypointParser.hpp @@ -0,0 +1,179 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "depthai/beta/BetaNode.hpp" +#include "depthai/beta/datatype/Keypoints.hpp" +#include "depthai/beta/properties/KeypointParserProperties.hpp" +#include "depthai/modelzoo/Zoo.hpp" +#include "depthai/nn_archive/NNArchive.hpp" +#include "depthai/nn_archive/v1/Head.hpp" +#include "depthai/pipeline/datatype/NNData.hpp" + +namespace dai { +namespace beta { +namespace node { + +/** + * @brief KeypointParser node. Parses the raw output of a 2D or 3D keypoints neural network into a dai::beta::Keypoints message. + * + * The parser consumes a single output tensor. When the incoming NNData contains exactly one tensor, it is selected automatically; otherwise the output layer + * name must be configured explicitly or through an NNArchive head. The number of keypoints must be configured before the pipeline starts. The number of + * coordinates per keypoint (2 or 3) is derived from the tensor size and the configured number of keypoints. Keypoint coordinates are divided by the configured + * scale factor and clipped to [0, 1]. + * + */ +class KeypointParser : public DeviceNodeCRTP { + public: + constexpr static const char* NAME = "KeypointParser"; + using DeviceNodeCRTP::DeviceNodeCRTP; + using Model = std::variant; + + /** + * Input NN results with keypoints data to parse. + */ + Input input{*this, {"input", DEFAULT_GROUP, DEFAULT_BLOCKING, DEFAULT_QUEUE_SIZE, {{{DatatypeEnum::NNData, true}}}, DEFAULT_WAIT_FOR_MESSAGE}}; + + /** + * Outputs Keypoints message with the parsed 2D or 3D keypoints. + */ + Output out{*this, {"out", DEFAULT_GROUP, {{{DatatypeEnum::Keypoints, false}}}}}; + + /** + * @brief Build KeypointParser node. Links the supplied output to this node's input and configures the parser from the model's keypoints head. + * @param nnInput: Output to link + * @param model: Neural network model + */ + std::shared_ptr build(Node::Output& nnInput, const Model& model); + + /** + * @brief Build KeypointParser node with the specific head from an NNArchive. Useful when the model has multiple heads. + * @param nnInput: Output to link + * @param head: Specific head from NNArchive to use for this parser + */ + std::shared_ptr build(Node::Output& nnInput, const dai::nn_archive::v1::Head& head); + + /** + * @brief Set NNArchive for this Node. The archive must contain exactly one KeypointParser head; use setNNArchiveHead() to select a specific head from a + * multi-head archive. + * + * @param nnArchive: NNArchive to set + */ + void setNNArchive(const NNArchive& nnArchive); + + /** + * @brief Set NNArchive head for this Node. The head must be a KeypointParser head with exactly one output layer. + * + * @param head: NNArchive head to set + */ + void setNNArchiveHead(const dai::nn_archive::v1::Head& head); + + /** + * Sets the name of the model output layer to parse. + * + * When left empty, the parser selects the tensor automatically if the incoming NNData contains exactly one tensor and fails otherwise. + * + * @param outputLayerName Name of the output layer + */ + void setOutputLayerName(const std::string& outputLayerName); + + /** + * Returns the name of the model output layer to parse. + */ + std::string getOutputLayerName() const; + + /** + * Sets the scale factor to divide the keypoint coordinates by. + * + * @param scaleFactor Scale factor, must be greater than 0 + */ + void setScaleFactor(float scaleFactor); + + /** + * Returns the scale factor to divide the keypoint coordinates by. + */ + float getScaleFactor() const; + + /** + * Sets the number of keypoints the model detects. + * + * Must be configured before the pipeline starts, either explicitly or through an NNArchive head. + * + * @param nKeypoints Number of keypoints, must be greater than 0 + */ + void setNumKeypoints(std::int64_t nKeypoints); + + /** + * Returns the number of keypoints the model detects, or std::nullopt when not configured. + */ + std::optional getNumKeypoints() const; + + /** + * Sets the confidence score threshold for detected keypoints. + * + * @param threshold Confidence score threshold, must be between 0 and 1 + */ + void setScoreThreshold(float threshold); + + /** + * Returns the confidence score threshold for detected keypoints, or std::nullopt when not configured. + */ + std::optional getScoreThreshold() const; + + /** + * Sets the label names for the keypoints, indexed by keypoint index. + * + * @param labelNames Vector of label names + */ + void setLabelNames(const std::vector& labelNames); + + /** + * Returns the label names for the keypoints. + */ + std::vector getLabelNames() const; + + /** + * Sets the skeleton edges as pairs of keypoint indices used for visualizing the skeleton. + * + * Example: {{0, 1}, {1, 2}, {2, 3}, {3, 0}} connects keypoint 0 to keypoint 1, keypoint 1 to keypoint 2, etc. + * + * @param edges Vector of keypoint index pairs + */ + void setEdges(const std::vector& edges); + + /** + * Returns the skeleton edges as pairs of keypoint indices. + */ + std::vector getEdges() const; + + /** + * Select whether the node runs on the host or device. + */ + void setRunOnHost(bool runOnHost); + + /** + * Returns true when this node runs on the host. + * + * Host-only pipelines always run the node on the host. + */ + bool runOnHost() const override; + + void run() override; + + private: + void setConfig(const dai::NNArchiveVersionedConfig& config); + void setConfig(const dai::nn_archive::v1::Head& head); + NNArchive decodeModel(const Model& model); + NNArchive createNNArchive(NNModelDescription& modelDesc); + + bool runOnHostVar = false; +}; + +} // namespace node +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/node/LaneDetectionParser.hpp b/include/depthai/beta/node/LaneDetectionParser.hpp new file mode 100644 index 0000000000..74a55f272f --- /dev/null +++ b/include/depthai/beta/node/LaneDetectionParser.hpp @@ -0,0 +1,181 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "depthai/beta/BetaNode.hpp" +#include "depthai/beta/datatype/Clusters.hpp" +#include "depthai/beta/properties/LaneDetectionParserProperties.hpp" +#include "depthai/modelzoo/Zoo.hpp" +#include "depthai/nn_archive/NNArchive.hpp" +#include "depthai/nn_archive/v1/Head.hpp" +#include "depthai/pipeline/datatype/NNData.hpp" + +namespace dai { +namespace beta { +namespace node { + +/** + * @brief LaneDetectionParser node. Parses the output of an Ultra-Fast-Lane-Detection (UFLD) neural network, e.g. the CULane and TuSimple variants, into a + * dai::beta::Clusters message with one cluster of normalized points per lane, including empty clusters for lanes without enough detected points. + * + * The parser consumes a single output tensor. When the incoming NNData contains exactly one tensor, it is selected automatically; otherwise the output layer + * name must be configured explicitly or through an NNArchive head. The tensor is read in its stored order and must be a 4D tensor of shape + * (batch, gridingNum + 1, clsNumPerLane, numLanes); the first batch entry is decoded. The row anchors, griding number and number of points per lane must be + * configured before the pipeline starts, either explicitly or through an NNArchive head. The input size must also be configured before the pipeline starts: + * building from a full NNArchive derives it from the model input's declared shape and layout (NHWC or NCHW), while building from a specific head requires + * setInputSize() because a head carries no model input metadata. + * + */ +class LaneDetectionParser : public DeviceNodeCRTP { + public: + constexpr static const char* NAME = "LaneDetectionParser"; + using DeviceNodeCRTP::DeviceNodeCRTP; + using Model = std::variant; + + /** + * Input NN results with lane detection data to parse. + */ + Input input{*this, {"input", DEFAULT_GROUP, DEFAULT_BLOCKING, DEFAULT_QUEUE_SIZE, {{{DatatypeEnum::NNData, true}}}, DEFAULT_WAIT_FOR_MESSAGE}}; + + /** + * Outputs Clusters message with the detected lanes represented as clusters of points. + */ + Output out{*this, {"out", DEFAULT_GROUP, {{{DatatypeEnum::Clusters, false}}}}}; + + /** + * @brief Build LaneDetectionParser node. Links the supplied output to this node's input and configures the parser from the model's LaneDetectionParser + * head and the model input's declared shape and layout. + * @param nnInput: Output to link + * @param model: Neural network model + */ + std::shared_ptr build(Node::Output& nnInput, const Model& model); + + /** + * @brief Build LaneDetectionParser node with the specific head from an NNArchive. Useful when the model has multiple heads. + * @param nnInput: Output to link + * @param head: Specific head from NNArchive to use for this parser + * @note A head carries no model input metadata, so the input size must additionally be configured with setInputSize(). + */ + std::shared_ptr build(Node::Output& nnInput, const dai::nn_archive::v1::Head& head); + + /** + * @brief Set NNArchive for this Node. The archive must contain exactly one LaneDetectionParser head and exactly one model input; use setNNArchiveHead() + * to select a specific head from a multi-head archive. The input size is derived from the model input's declared shape and layout (NHWC or NCHW). + * + * @param nnArchive: NNArchive to set + */ + void setNNArchive(const NNArchive& nnArchive); + + /** + * @brief Set NNArchive head for this Node. The head must be a LaneDetectionParser head with exactly one output layer. + * + * @param head: NNArchive head to set + * @note A head carries no model input metadata, so the input size must additionally be configured with setInputSize(). + */ + void setNNArchiveHead(const dai::nn_archive::v1::Head& head); + + /** + * Sets the name of the model output layer to parse. + * + * When left empty, the parser selects the tensor automatically if the incoming NNData contains exactly one tensor and fails otherwise. + * + * @param outputLayerName Name of the output layer + */ + void setOutputLayerName(const std::string& outputLayerName); + + /** + * Returns the name of the model output layer to parse. + */ + std::string getOutputLayerName() const; + + /** + * Sets the row anchors, the image rows at which the model predicts lane positions. + * + * Must be configured before the pipeline starts, either explicitly or through an NNArchive head, and must contain at least as many entries as the number + * of points per lane. + * + * @param rowAnchors Row anchors, must not be empty + */ + void setRowAnchors(const std::vector& rowAnchors); + + /** + * Returns the row anchors, or an empty vector when not configured. + */ + std::vector getRowAnchors() const; + + /** + * Sets the griding number, the number of column samples the model predicts lane positions over. + * + * Must be configured before the pipeline starts, either explicitly or through an NNArchive head. + * + * @param gridingNum Griding number, must be greater than 1 + */ + void setGridingNum(std::int64_t gridingNum); + + /** + * Returns the griding number, or std::nullopt when not configured. + */ + std::optional getGridingNum() const; + + /** + * Sets the number of points per lane. + * + * Must be configured before the pipeline starts, either explicitly or through an NNArchive head. + * + * @param clsNumPerLane Number of points per lane, must be greater than 0 + */ + void setClsNumPerLane(std::int64_t clsNumPerLane); + + /** + * Returns the number of points per lane, or std::nullopt when not configured. + */ + std::optional getClsNumPerLane() const; + + /** + * Sets the model input image size the emitted points are computed against and normalized by. + * + * Must be configured before the pipeline starts. Configuring from a full NNArchive derives it from the model input's declared shape and layout; the most + * recent configuration wins. + * + * @param width Input image width, must be greater than 0 + * @param height Input image height, must be greater than 0 + */ + void setInputSize(std::uint32_t width, std::uint32_t height); + + /** + * Returns the model input image size as (width, height), or std::nullopt when not configured. + */ + std::optional> getInputSize() const; + + /** + * Select whether the node runs on the host or device. + */ + void setRunOnHost(bool runOnHost); + + /** + * Returns true when this node runs on the host. + * + * Host-only pipelines always run the node on the host. + */ + bool runOnHost() const override; + + void run() override; + + private: + void setConfig(const dai::NNArchiveVersionedConfig& config); + void setConfig(const dai::nn_archive::v1::Head& head); + NNArchive decodeModel(const Model& model); + NNArchive createNNArchive(NNModelDescription& modelDesc); + + bool runOnHostVar = false; +}; + +} // namespace node +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/node/MLSDParser.hpp b/include/depthai/beta/node/MLSDParser.hpp new file mode 100644 index 0000000000..fa4fb9fae4 --- /dev/null +++ b/include/depthai/beta/node/MLSDParser.hpp @@ -0,0 +1,210 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "depthai/beta/BetaNode.hpp" +#include "depthai/beta/datatype/Lines.hpp" +#include "depthai/beta/properties/MLSDParserProperties.hpp" +#include "depthai/modelzoo/Zoo.hpp" +#include "depthai/nn_archive/NNArchive.hpp" +#include "depthai/nn_archive/v1/Head.hpp" +#include "depthai/pipeline/datatype/NNData.hpp" + +namespace dai { +namespace beta { +namespace node { + +/** + * @brief MLSDParser node. Parses the output of the M-LSD line segment detection model into a dai::beta::Lines message with the detected lines and their + * confidence scores, ordered by descending score. + * + * The parser consumes two output tensors that must be configured before the pipeline starts, either explicitly or through an NNArchive head: the tpMap tensor, + * read in NCHW orientation as a 4D tensor of shape (batch, channels, height, width) whose channels 1 to 4 hold the line displacement maps of the first batch + * entry, and the heat tensor, flattened to one score per (height, width) grid position. The topK highest-scoring grid positions are decoded into candidate + * lines and kept when their score and length are strictly above the score and distance thresholds. Ties between equal heat scores are ordered following + * numpy's portable argpartition/argsort semantics. The emitted line coordinates are normalized by the model input size, which defaults to 512x512 (the input + * size of all known M-LSD models, hard-coded by the source parser); building from a full NNArchive derives it from the model input's declared shape and + * layout (NHWC or NCHW), and setInputSize() overrides it. + * + */ +class MLSDParser : public DeviceNodeCRTP { + protected: + Properties& getProperties() override; + + public: + constexpr static const char* NAME = "MLSDParser"; + using DeviceNodeCRTP::DeviceNodeCRTP; + using Model = std::variant; + + MLSDParser() = default; + MLSDParser(std::unique_ptr props); + + /** Configuration used until a message is received on inputConfig. */ + std::shared_ptr initialConfig = std::make_shared(); + + /** + * Runtime parser configuration. When synchronized, one configuration is consumed per frame; + * otherwise all queued configurations are drained and the newest valid one is used. + */ + Input inputConfig{*this, {"inputConfig", DEFAULT_GROUP, false, 4, {{{DatatypeEnum::MLSDParserConfig, false}}}, DEFAULT_WAIT_FOR_MESSAGE}}; + + /** + * Input NN results with line detection data to parse. + */ + Input input{*this, {"input", DEFAULT_GROUP, DEFAULT_BLOCKING, DEFAULT_QUEUE_SIZE, {{{DatatypeEnum::NNData, true}}}, DEFAULT_WAIT_FOR_MESSAGE}}; + + /** + * Outputs Lines message with the detected lines and confidence scores. + */ + Output out{*this, {"out", DEFAULT_GROUP, {{{DatatypeEnum::Lines, false}}}}}; + + /** + * @brief Build MLSDParser node. Links the supplied output to this node's input and configures the parser from the model's MLSDParser head and the model + * input's declared shape and layout. + * @param nnInput: Output to link + * @param model: Neural network model + */ + std::shared_ptr build(Node::Output& nnInput, const Model& model); + + /** + * @brief Build MLSDParser node with the specific head from an NNArchive. Useful when the model has multiple heads. + * @param nnInput: Output to link + * @param head: Specific head from NNArchive to use for this parser + * @note A head carries no model input metadata, so the input size keeps its current value (512x512 by default); use setInputSize() for models with a + * different input size. + */ + std::shared_ptr build(Node::Output& nnInput, const dai::nn_archive::v1::Head& head); + + /** + * @brief Set NNArchive for this Node. The archive must contain exactly one MLSDParser head and exactly one model input; use setNNArchiveHead() to select a + * specific head from a multi-head archive. The input size is derived from the model input's declared shape and layout (NHWC or NCHW). + * + * @param nnArchive: NNArchive to set + */ + void setNNArchive(const NNArchive& nnArchive); + + /** + * @brief Set NNArchive head for this Node. The head must be an MLSDParser head with exactly two output layers; the layer whose name contains "tpMap" is + * used as the tpMap layer and the layer whose name contains "heat" as the heat layer. + * + * @param head: NNArchive head to set + * @note A head carries no model input metadata, so the input size keeps its current value (512x512 by default); use setInputSize() for models with a + * different input size. + */ + void setNNArchiveHead(const dai::nn_archive::v1::Head& head); + + /** + * Sets the name of the output layer containing the tpMap tensor. + * + * Must be configured before the pipeline starts, either explicitly or through an NNArchive head. + * + * @param outputLayerTPMap Name of the output layer containing the tpMap tensor + */ + void setOutputLayerTPMap(const std::string& outputLayerTPMap); + + /** + * Returns the name of the output layer containing the tpMap tensor, or an empty string when not configured. + */ + std::string getOutputLayerTPMap() const; + + /** + * Sets the name of the output layer containing the heat tensor. + * + * Must be configured before the pipeline starts, either explicitly or through an NNArchive head. + * + * @param outputLayerHeat Name of the output layer containing the heat tensor + */ + void setOutputLayerHeat(const std::string& outputLayerHeat); + + /** + * Returns the name of the output layer containing the heat tensor, or an empty string when not configured. + */ + std::string getOutputLayerHeat() const; + + /** + * Sets the number of top candidates to keep. + * + * The number of candidates is capped at the heat map size when decoding. + * + * @param topK Number of top candidates to keep, must be positive + * @note Configures startup behavior. Send MLSDParserConfig to inputConfig after the pipeline starts. + */ + void setTopK(int topK); + + /** + * Returns the number of top candidates to keep. + */ + int getTopK() const; + + /** + * Sets the confidence score threshold for detected lines. Candidates with a heat score strictly above the threshold are kept. + * + * @param scoreThreshold Confidence score threshold + * @note Configures startup behavior. Send MLSDParserConfig to inputConfig after the pipeline starts. + */ + void setScoreThreshold(float scoreThreshold); + + /** + * Returns the confidence score threshold for detected lines. + */ + float getScoreThreshold() const; + + /** + * Sets the distance threshold for detected lines. Candidates whose length in heat map grid units is strictly above the threshold are kept. + * + * @param distanceThreshold Distance threshold + * @note Configures startup behavior. Send MLSDParserConfig to inputConfig after the pipeline starts. + */ + void setDistanceThreshold(float distanceThreshold); + + /** + * Returns the distance threshold for detected lines. + */ + float getDistanceThreshold() const; + + /** + * Sets the model input image size the emitted line coordinates are normalized by, x coordinates by the width and y coordinates by the height. + * + * Defaults to 512x512, the input size of all known M-LSD models. Configuring from a full NNArchive derives it from the model input's declared shape and + * layout; the most recent configuration wins. + * + * @param width Input image width, must be greater than 0 + * @param height Input image height, must be greater than 0 + */ + void setInputSize(std::uint32_t width, std::uint32_t height); + + /** + * Returns the model input image size as (width, height). + */ + std::pair getInputSize() const; + + /** + * Select whether the node runs on the host or device. + */ + void setRunOnHost(bool runOnHost); + + /** + * Returns true when this node runs on the host. + * + * Host-only pipelines always run the node on the host. + */ + bool runOnHost() const override; + + void run() override; + + private: + void setConfig(const dai::NNArchiveVersionedConfig& config); + void setConfig(const dai::nn_archive::v1::Head& head); + NNArchive decodeModel(const Model& model); + NNArchive createNNArchive(NNModelDescription& modelDesc); + + bool runOnHostVar = false; +}; + +} // namespace node +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/node/MPPalmDetectionParser.hpp b/include/depthai/beta/node/MPPalmDetectionParser.hpp new file mode 100644 index 0000000000..87fb8d33fe --- /dev/null +++ b/include/depthai/beta/node/MPPalmDetectionParser.hpp @@ -0,0 +1,197 @@ +#pragma once + +#include +#include +#include +#include + +#include "depthai/beta/BetaNode.hpp" +#include "depthai/beta/properties/MPPalmDetectionParserProperties.hpp" +#include "depthai/modelzoo/Zoo.hpp" +#include "depthai/nn_archive/NNArchive.hpp" +#include "depthai/nn_archive/v1/Head.hpp" +#include "depthai/pipeline/datatype/ImgDetections.hpp" +#include "depthai/pipeline/datatype/NNData.hpp" + +namespace dai { +namespace beta { +namespace node { + +/** + * @brief MPPalmDetectionParser node. Parses the output of the MediaPipe palm detection model into a dai::ImgDetections message containing the rotated bounding + * boxes, labels and confidence scores of the detected hands. The decoding is based on https://github.com/geaxgx/depthai_hand_tracker (MIT License). + * + * The parser consumes two output tensors and identifies them by their last dimension: the tensor with the larger last dimension holds the raw bounding boxes + * and is reshaped to (numAnchors, 18) rows of bounding box center/size plus 7 palm keypoint coordinate pairs; the tensor with the smaller last dimension holds + * the raw scores and is flattened to (numAnchors,). The scores are passed through a sigmoid and filtered with the confidence threshold, the kept rows are + * decoded against the model's SSD anchors generated from the configured scale (the model input size), converted to rectangles rotated to align the wrist to + * middle-finger direction with the rectangle's y-axis and expanded to squares, and non-maximum suppression keeps at most the configured maximum number of + * detections. The emitted bounding boxes are normalized to [0, 1]. + * + */ +class MPPalmDetectionParser : public DeviceNodeCRTP { + protected: + Properties& getProperties() override; + + public: + constexpr static const char* NAME = "MPPalmDetectionParser"; + using DeviceNodeCRTP::DeviceNodeCRTP; + using Model = std::variant; + + MPPalmDetectionParser() = default; + MPPalmDetectionParser(std::unique_ptr props); + + /** + * Configuration used when the parser starts. + */ + std::shared_ptr initialConfig = std::make_shared(); + + /** + * Runtime parser configuration. In synchronized mode one configuration is consumed per input frame; + * otherwise all queued configurations are drained and the newest valid one is retained. + */ + Input inputConfig{*this, {"inputConfig", DEFAULT_GROUP, false, 4, {{{DatatypeEnum::MPPalmDetectionParserConfig, false}}}, DEFAULT_WAIT_FOR_MESSAGE}}; + + /** + * Input NN results with palm detection data to parse. + */ + Input input{*this, {"input", DEFAULT_GROUP, DEFAULT_BLOCKING, DEFAULT_QUEUE_SIZE, {{{DatatypeEnum::NNData, true}}}, DEFAULT_WAIT_FOR_MESSAGE}}; + + /** + * Outputs ImgDetections message with the rotated bounding boxes, labels and confidence scores of the detected hands. + */ + Output out{*this, {"out", DEFAULT_GROUP, {{{DatatypeEnum::ImgDetections, false}}}}}; + + /** + * @brief Build MPPalmDetectionParser node. Links the supplied output to this node's input and configures the parser from the model's + * MPPalmDetectionParser head. + * @param nnInput: Output to link + * @param model: Neural network model + */ + std::shared_ptr build(Node::Output& nnInput, const Model& model); + + /** + * @brief Build MPPalmDetectionParser node with the specific head from an NNArchive. Useful when the model has multiple heads. + * @param nnInput: Output to link + * @param head: Specific head from NNArchive to use for this parser + */ + std::shared_ptr build(Node::Output& nnInput, const dai::nn_archive::v1::Head& head); + + /** + * @brief Set NNArchive for this Node. The archive must contain exactly one MPPalmDetectionParser head; use setNNArchiveHead() to select a specific head + * from a multi-head archive. + * + * @param nnArchive: NNArchive to set + */ + void setNNArchive(const NNArchive& nnArchive); + + /** + * @brief Set NNArchive head for this Node. The head must be an MPPalmDetectionParser head with exactly two output layers. + * + * @param head: NNArchive head to set + */ + void setNNArchiveHead(const dai::nn_archive::v1::Head& head); + + /** + * Sets the names of the model output layers relevant to the parser. Exactly two layer names are required. + * + * @param outputLayerNames Names of the output layers + */ + void setOutputLayerNames(const std::vector& outputLayerNames); + + /** + * Returns the names of the model output layers relevant to the parser. + */ + std::vector getOutputLayerNames() const; + + /** + * Sets the confidence score threshold for detected hands. Detections with a sigmoid score strictly above the threshold are kept. + * + * @param threshold Confidence score threshold + * @note Configures startup behavior. Send MPPalmDetectionParserConfig to inputConfig after the pipeline starts. + */ + void setConfidenceThreshold(float threshold); + + /** + * Returns the confidence score threshold for detected hands. + */ + float getConfidenceThreshold() const; + + /** + * Sets the non-maximum suppression (IoU) threshold. + * + * @param threshold Non-maximum suppression threshold + * @note Configures startup behavior. Send MPPalmDetectionParserConfig to inputConfig after the pipeline starts. + */ + void setIouThreshold(float threshold); + + /** + * Returns the non-maximum suppression (IoU) threshold. + */ + float getIouThreshold() const; + + /** + * Sets the maximum number of detections to keep. + * + * @param maxDetections Maximum number of detections to keep + * @note Configures startup behavior. Send MPPalmDetectionParserConfig to inputConfig after the pipeline starts. + */ + void setMaxDetections(int maxDetections); + + /** + * Returns the maximum number of detections to keep. + */ + int getMaxDetections() const; + + /** + * Sets the scale of the model input image in pixels (e.g. 192 for a 192x192 model). The SSD anchors used for decoding are generated from the scale; a + * scale that does not match the model input size fails decoding with an anchor count mismatch. + * + * @param scale Scale of the input image + */ + void setScale(int scale); + + /** + * Returns the scale of the model input image in pixels. + */ + int getScale() const; + + /** + * Sets the label names for the detected hands. The first label name is assigned to every detection (all detections carry label 0). When empty, no label + * name is assigned. + * + * @param labelNames List of label names + */ + void setLabelNames(const std::vector& labelNames); + + /** + * Returns the label names for the detected hands. + */ + std::vector getLabelNames() const; + + /** + * Select whether the node runs on the host or device. + */ + void setRunOnHost(bool runOnHost); + + /** + * Returns true when this node runs on the host. + * + * Host-only pipelines always run the node on the host. + */ + bool runOnHost() const override; + + void run() override; + + private: + void setConfig(const dai::NNArchiveVersionedConfig& config); + void setConfig(const dai::nn_archive::v1::Head& head); + NNArchive decodeModel(const Model& model); + NNArchive createNNArchive(NNModelDescription& modelDesc); + + bool runOnHostVar = false; +}; + +} // namespace node +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/node/MapOutputParser.hpp b/include/depthai/beta/node/MapOutputParser.hpp new file mode 100644 index 0000000000..4490b773a9 --- /dev/null +++ b/include/depthai/beta/node/MapOutputParser.hpp @@ -0,0 +1,145 @@ +#pragma once + +#include +#include +#include + +#include "depthai/beta/BetaNode.hpp" +#include "depthai/beta/datatype/Map2D.hpp" +#include "depthai/beta/properties/MapOutputParserProperties.hpp" +#include "depthai/modelzoo/Zoo.hpp" +#include "depthai/nn_archive/NNArchive.hpp" +#include "depthai/nn_archive/v1/Head.hpp" +#include "depthai/pipeline/datatype/NNData.hpp" + +namespace dai { +namespace beta { +namespace node { + +/** + * @brief MapOutputParser node. Parses the output of models that produce map outputs, such as depth maps (e.g. DepthAnything), density maps (e.g. DM-Count), + * heat maps, and similar, into a dai::beta::Map2D message. + * + * The parser consumes a single output tensor. When the incoming NNData contains exactly one tensor, it is selected automatically; otherwise the output layer + * name must be configured explicitly or through an NNArchive head. The tensor is read in its stored order; leading dimensions of 1 are squeezed and the + * tensor must then be a 2D HW map, or a 3D HWN map with a singleton trailing dimension that is squeezed as well. All map dimensions are derived from the + * runtime tensor descriptor. + * + * When min-max scaling is enabled, the map values are scaled to the [0, 1] range; a constant map is left unchanged. + * + */ +class MapOutputParser : public DeviceNodeCRTP { + protected: + Properties& getProperties() override; + + public: + constexpr static const char* NAME = "MapOutputParser"; + using DeviceNodeCRTP::DeviceNodeCRTP; + using Model = std::variant; + + MapOutputParser() = default; + MapOutputParser(std::unique_ptr props); + + /** Configuration used until a message is received on inputConfig. */ + std::shared_ptr initialConfig = std::make_shared(); + + /** + * Runtime parser configuration. When synchronized, one configuration is consumed per frame; + * otherwise all queued configurations are drained and the newest valid one is used. + */ + Input inputConfig{*this, {"inputConfig", DEFAULT_GROUP, false, 4, {{{DatatypeEnum::MapOutputParserConfig, false}}}, DEFAULT_WAIT_FOR_MESSAGE}}; + + /** + * Input NN results with map tensor data to parse. + */ + Input input{*this, {"input", DEFAULT_GROUP, DEFAULT_BLOCKING, DEFAULT_QUEUE_SIZE, {{{DatatypeEnum::NNData, true}}}, DEFAULT_WAIT_FOR_MESSAGE}}; + + /** + * Outputs Map2D message with the parsed 2D map, e.g. a depth or density map. + */ + Output out{*this, {"out", DEFAULT_GROUP, {{{DatatypeEnum::Map2D, false}}}}}; + + /** + * @brief Build MapOutputParser node. Links the supplied output to this node's input and configures the parser from the model's MapOutputParser head. + * @param nnInput: Output to link + * @param model: Neural network model + */ + std::shared_ptr build(Node::Output& nnInput, const Model& model); + + /** + * @brief Build MapOutputParser node with the specific head from an NNArchive. Useful when the model has multiple heads. + * @param nnInput: Output to link + * @param head: Specific head from NNArchive to use for this parser + */ + std::shared_ptr build(Node::Output& nnInput, const dai::nn_archive::v1::Head& head); + + /** + * @brief Set NNArchive for this Node. The archive must contain exactly one MapOutputParser head; use setNNArchiveHead() to select a specific head from a + * multi-head archive. + * + * @param nnArchive: NNArchive to set + */ + void setNNArchive(const NNArchive& nnArchive); + + /** + * @brief Set NNArchive head for this Node. The head must be a MapOutputParser head with exactly one output layer. + * + * @param head: NNArchive head to set + */ + void setNNArchiveHead(const dai::nn_archive::v1::Head& head); + + /** + * Sets the name of the model output layer to parse. + * + * When left empty, the parser selects the tensor automatically if the incoming NNData contains exactly one tensor and fails otherwise. + * + * @param outputLayerName Name of the output layer + */ + void setOutputLayerName(const std::string& outputLayerName); + + /** + * Returns the name of the model output layer to parse. + */ + std::string getOutputLayerName() const; + + /** + * Sets the flag indicating whether the map is scaled to the [0, 1] range. + * + * When true, the map values are min-max scaled to [0, 1]; a constant map is left unchanged. Defaults to false. + * + * @param minMaxScaling True to scale the map to the [0, 1] range, defaults to true + * @note Configures startup behavior. Send MapOutputParserConfig to inputConfig after the pipeline starts. + */ + void setMinMaxScaling(bool minMaxScaling = true); + + /** + * Returns the flag indicating whether the map is scaled to the [0, 1] range. + */ + bool getMinMaxScaling() const; + + /** + * Select whether the node runs on the host or device. + */ + void setRunOnHost(bool runOnHost); + + /** + * Returns true when this node runs on the host. + * + * Host-only pipelines always run the node on the host. + */ + bool runOnHost() const override; + + void run() override; + + private: + void setConfig(const dai::NNArchiveVersionedConfig& config); + void setConfig(const dai::nn_archive::v1::Head& head); + NNArchive decodeModel(const Model& model); + NNArchive createNNArchive(NNModelDescription& modelDesc); + + bool runOnHostVar = false; +}; + +} // namespace node +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/node/PPTextDetectionParser.hpp b/include/depthai/beta/node/PPTextDetectionParser.hpp new file mode 100644 index 0000000000..80bd9bcfac --- /dev/null +++ b/include/depthai/beta/node/PPTextDetectionParser.hpp @@ -0,0 +1,174 @@ +#pragma once + +#include +#include +#include + +#include "depthai/beta/BetaNode.hpp" +#include "depthai/beta/properties/PPTextDetectionParserProperties.hpp" +#include "depthai/modelzoo/Zoo.hpp" +#include "depthai/nn_archive/NNArchive.hpp" +#include "depthai/nn_archive/v1/Head.hpp" +#include "depthai/pipeline/datatype/ImgDetections.hpp" +#include "depthai/pipeline/datatype/NNData.hpp" + +namespace dai { +namespace beta { +namespace node { + +/** + * @brief PPTextDetectionParser node. Parses the output of the PaddlePaddle OCR text detection model into a dai::ImgDetections message containing the rotated + * bounding boxes and confidence scores of the detected text. + * + * The parser consumes a single probability-map output tensor of shape (1, 1, H, W) or (1, H, W, 1). The map is thresholded with the mask threshold into a + * binary text mask, the mask is dilated and its contours become rotated-rectangle candidates; when more contours than the maximum number of detections + * remain, the largest by area are kept. Rectangles smaller than 8 pixels on their smaller side are dropped, each candidate is scored with the mean probability + * inside its (slightly shrunk) corner polygon, candidates scoring below the confidence threshold are dropped, and the kept rectangles are expanded by sqrt(2) + * in both dimensions. The emitted bounding boxes are normalized to [0, 1] with angles in degrees rounded to whole numbers; the detections carry no labels. + * + */ +class PPTextDetectionParser : public DeviceNodeCRTP { + protected: + Properties& getProperties() override; + + public: + constexpr static const char* NAME = "PPTextDetectionParser"; + using DeviceNodeCRTP::DeviceNodeCRTP; + using Model = std::variant; + + PPTextDetectionParser() = default; + PPTextDetectionParser(std::unique_ptr props); + + /** + * Configuration used when the parser starts. + */ + std::shared_ptr initialConfig = std::make_shared(); + + /** + * Runtime parser configuration. In synchronized mode one configuration is consumed per input frame; + * otherwise all queued configurations are drained and the newest valid one is retained. + */ + Input inputConfig{*this, {"inputConfig", DEFAULT_GROUP, false, 4, {{{DatatypeEnum::PPTextDetectionParserConfig, false}}}, DEFAULT_WAIT_FOR_MESSAGE}}; + + /** + * Input NN results with text detection probability map to parse. + */ + Input input{*this, {"input", DEFAULT_GROUP, DEFAULT_BLOCKING, DEFAULT_QUEUE_SIZE, {{{DatatypeEnum::NNData, true}}}, DEFAULT_WAIT_FOR_MESSAGE}}; + + /** + * Outputs ImgDetections message with the rotated bounding boxes and confidence scores of the detected text. + */ + Output out{*this, {"out", DEFAULT_GROUP, {{{DatatypeEnum::ImgDetections, false}}}}}; + + /** + * @brief Build PPTextDetectionParser node. Links the supplied output to this node's input and configures the parser from the model's + * PPTextDetectionParser head. + * @param nnInput: Output to link + * @param model: Neural network model + */ + std::shared_ptr build(Node::Output& nnInput, const Model& model); + + /** + * @brief Build PPTextDetectionParser node with the specific head from an NNArchive. Useful when the model has multiple heads. + * @param nnInput: Output to link + * @param head: Specific head from NNArchive to use for this parser + */ + std::shared_ptr build(Node::Output& nnInput, const dai::nn_archive::v1::Head& head); + + /** + * @brief Set NNArchive for this Node. The archive must contain exactly one PPTextDetectionParser head; use setNNArchiveHead() to select a specific head + * from a multi-head archive. + * + * @param nnArchive: NNArchive to set + */ + void setNNArchive(const NNArchive& nnArchive); + + /** + * @brief Set NNArchive head for this Node. The head configures the confidence threshold, the mask threshold and the maximum number of detections when + * present in its metadata. The head's declared output names are not consumed: the parser resolves the single runtime output tensor by itself (or uses the + * explicitly configured output layer name), mirroring the source parser; archives whose head output name differs from the model's declared output name + * therefore parse correctly. + * + * @param head: NNArchive head to set + */ + void setNNArchiveHead(const dai::nn_archive::v1::Head& head); + + /** + * Sets the name of the model output layer holding the text probability map. When empty (the default), the layer is resolved automatically from + * single-tensor NN results; multi-tensor results require an explicit name. + * + * @param outputLayerName Name of the output layer + */ + void setOutputLayerName(const std::string& outputLayerName); + + /** + * Returns the name of the model output layer holding the text probability map. + */ + std::string getOutputLayerName() const; + + /** + * Sets the confidence score threshold for the detected text bounding boxes. Candidates with a score strictly below the threshold are dropped. + * + * @param threshold Confidence score threshold + * @note Configures startup behavior. Send PPTextDetectionParserConfig to inputConfig after the pipeline starts. + */ + void setConfidenceThreshold(float threshold); + + /** + * Returns the confidence score threshold for the detected text bounding boxes. + */ + float getConfidenceThreshold() const; + + /** + * Sets the mask threshold for creating the binary text mask from the model output probabilities. Probabilities strictly above the threshold belong to the + * mask. + * + * @param maskThreshold Mask threshold + * @note Configures startup behavior. Send PPTextDetectionParserConfig to inputConfig after the pipeline starts. + */ + void setMaskThreshold(float maskThreshold); + + /** + * Returns the mask threshold for creating the binary text mask from the model output probabilities. + */ + float getMaskThreshold() const; + + /** + * Sets the maximum number of candidate bounding boxes. When more candidate contours are found, only the largest by area are kept. + * + * @param maxDetections Maximum number of candidate bounding boxes + * @note Configures startup behavior. Send PPTextDetectionParserConfig to inputConfig after the pipeline starts. + */ + void setMaxDetections(int maxDetections); + + /** + * Returns the maximum number of candidate bounding boxes. + */ + int getMaxDetections() const; + + /** + * Select whether the node runs on the host or device. + */ + void setRunOnHost(bool runOnHost); + + /** + * Returns true when this node runs on the host. + * + * Host-only pipelines always run the node on the host. + */ + bool runOnHost() const override; + + void run() override; + + private: + void setConfig(const dai::NNArchiveVersionedConfig& config); + void setConfig(const dai::nn_archive::v1::Head& head); + NNArchive decodeModel(const Model& model); + NNArchive createNNArchive(NNModelDescription& modelDesc); + + bool runOnHostVar = false; +}; + +} // namespace node +} // namespace beta +} // namespace dai diff --git a/include/depthai/beta/node/RFDETRParser.hpp b/include/depthai/beta/node/RFDETRParser.hpp new file mode 100644 index 0000000000..eacdf49a6d --- /dev/null +++ b/include/depthai/beta/node/RFDETRParser.hpp @@ -0,0 +1,218 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "depthai/beta/BetaNode.hpp" +#include "depthai/beta/properties/RFDETRParserProperties.hpp" +#include "depthai/modelzoo/Zoo.hpp" +#include "depthai/nn_archive/NNArchive.hpp" +#include "depthai/nn_archive/v1/Head.hpp" +#include "depthai/pipeline/datatype/ImgDetections.hpp" +#include "depthai/pipeline/datatype/NNData.hpp" + +namespace dai { +namespace beta { +namespace node { + +/** + * @brief RFDETRParser node. Parses the output of RF-DETR object detection models + * (https://github.com/roboflow/rf-detr) into a dai::ImgDetections message containing the bounding boxes, labels, confidence scores and, in segmentation mode, + * an instance segmentation mask, everything normalized to [0, 1]. + * + * The parser consumes 2 output tensors for detection (boxes, class logits) or 3 for instance segmentation (boxes, class logits, mask logits), in that order. + * When no output layer names are configured, all layer names of the incoming NNData are used in their reported order. The boxes tensor squeezes to (N, 4) with + * normalized (xCenter, yCenter, width, height) boxes, the logits tensor is (1, N, C) and the mask logits tensor squeezes to (N, maskHeight, maskWidth). Class + * probabilities are the sigmoid of the logits; per query the maximum probability is the score and its class the label. Detections are ordered by descending + * score, truncated to the maximum number of detections and kept when their score is strictly greater than the confidence threshold. + * + * In segmentation mode at most 255 instances fit into the mask, so the truncation is additionally capped at 255. Each detection's mask logits are passed + * through a sigmoid, cropped to its bounding box, binarized with the mask confidence threshold and resized to the model input size with nearest-neighbor + * interpolation; the pixels not claimed by an earlier (higher-scoring) detection receive the detection's index, with 255 marking background. + * + */ +class RFDETRParser : public DeviceNodeCRTP { + protected: + Properties& getProperties() override; + + public: + constexpr static const char* NAME = "RFDETRParser"; + using DeviceNodeCRTP::DeviceNodeCRTP; + using Model = std::variant; + + RFDETRParser() = default; + RFDETRParser(std::unique_ptr props); + + /** + * Configuration used when the parser starts. + */ + std::shared_ptr initialConfig = std::make_shared(); + + /** + * Runtime parser configuration. In synchronized mode one configuration is consumed per input frame; + * otherwise all queued configurations are drained and the newest valid one is retained. + */ + Input inputConfig{*this, {"inputConfig", DEFAULT_GROUP, false, 4, {{{DatatypeEnum::RFDETRParserConfig, false}}}, DEFAULT_WAIT_FOR_MESSAGE}}; + + /** + * Input NN results with RF-DETR detection data to parse. + */ + Input input{*this, {"input", DEFAULT_GROUP, DEFAULT_BLOCKING, DEFAULT_QUEUE_SIZE, {{{DatatypeEnum::NNData, true}}}, DEFAULT_WAIT_FOR_MESSAGE}}; + + /** + * Outputs ImgDetections message with the bounding boxes, labels and confidence scores of the detected objects and, in segmentation mode, the instance + * segmentation mask. + */ + Output out{*this, {"out", DEFAULT_GROUP, {{{DatatypeEnum::ImgDetections, false}}}}}; + + /** + * @brief Build RFDETRParser node. Links the supplied output to this node's input and configures the parser from the model's RFDETRParser head and the + * model input's declared shape and layout. + * @param nnInput: Output to link + * @param model: Neural network model + */ + std::shared_ptr build(Node::Output& nnInput, const Model& model); + + /** + * @brief Build RFDETRParser node with the specific head from an NNArchive. Useful when the model has multiple heads. + * @param nnInput: Output to link + * @param head: Specific head from NNArchive to use for this parser + * @note A head carries no model input metadata, so the input size keeps its current value; segmentation mode requires it to be configured with + * setInputSize() when it is not set yet. + */ + std::shared_ptr build(Node::Output& nnInput, const dai::nn_archive::v1::Head& head); + + /** + * @brief Set NNArchive for this Node. The archive must contain exactly one RFDETRParser head; use setNNArchiveHead() to select a specific head from a + * multi-head archive. The input size is derived from the first model input's declared shape and layout (NHWC or NCHW). + * + * @param nnArchive: NNArchive to set + */ + void setNNArchive(const NNArchive& nnArchive); + + /** + * @brief Set NNArchive head for this Node. The head must be an RFDETRParser head with 2 output layers (boxes, class logits) for detection or 3 (boxes, + * class logits, mask logits) for segmentation. + * + * @param head: NNArchive head to set + * @note A head carries no model input metadata, so the input size keeps its current value; segmentation mode requires it to be configured with + * setInputSize() when it is not set yet. + */ + void setNNArchiveHead(const dai::nn_archive::v1::Head& head); + + /** + * Sets the confidence score threshold for detected objects. Detections with a score strictly greater than the threshold are kept. Defaults to 0.5. + * + * @param threshold Confidence score threshold, must be between 0 and 1 + * @note Configures startup behavior. Send RFDETRParserConfig to inputConfig after the pipeline starts. + */ + void setConfidenceThreshold(float threshold); + + /** + * Returns the confidence score threshold for detected objects. + */ + float getConfidenceThreshold() const; + + /** + * Sets the maximum number of detections to keep, applied to the detections ordered by descending score. In segmentation mode the applied limit is + * additionally capped at 255, the maximum number of instances the segmentation mask can encode. Defaults to 300. + * + * @param maxDetections Maximum number of detections to keep, must be greater than 0 + * @note Configures startup behavior. Send RFDETRParserConfig to inputConfig after the pipeline starts. + */ + void setMaxDetections(int maxDetections); + + /** + * Returns the maximum number of detections to keep. + */ + int getMaxDetections() const; + + /** + * Sets the label names for the detected objects, indexed by the class label. A detection whose label is out of range receives the name + * "class_