配套文件
将这些文件复制到使用 AI Hub 和原生 QNN 在 NPU 上实现实时 YOLO 深度估计中所示的路径。
这些文件是原型教程代码。在生产环境中使用之前,请先审查错误处理、内存所有权、张量元数据发现以及部署策略。
qnn_dlc_runner.cpp
#include <QNN/QnnInterface.h>
#include <QNN/System/QnnSystemContext.h>
#include <QNN/System/QnnSystemInterface.h>
#include <QNN/QnnTypes.h>
#include <dlfcn.h>
#include <algorithm>
#include <chrono>
#include <cstdarg>
#include <cstring>
#include <fstream>
#include <iostream>
#include <numeric>
#include <stdexcept>
#include <string>
#include <vector>
using QnnInterfaceGetProvidersFn = Qnn_ErrorHandle_t (*)(const QnnInterface_t***, uint32_t*);
using QnnSystemInterfaceGetProvidersFn = Qnn_ErrorHandle_t (*)(const QnnSystemInterface_t***, uint32_t*);
#define CHECK_QNN(expr) do { \
Qnn_ErrorHandle_t _e = (expr); \
if (_e != QNN_SUCCESS) { \
std::cerr << "QNN call failed: " #expr " -> 0x" << std::hex << (uint64_t)_e << std::dec << std::endl; \
throw std::runtime_error("QNN error"); \
} \
} while(0)
static std::vector<uint8_t> readFile(const std::string& path) {
std::ifstream f(path, std::ios::binary);
if (!f) throw std::runtime_error("failed to open " + path);
f.seekg(0, std::ios::end);
size_t n = (size_t)f.tellg();
f.seekg(0);
std::vector<uint8_t> b(n);
f.read((char*)b.data(), n);
if (!f) throw std::runtime_error("failed to read " + path);
return b;
}
static void writeFile(const std::string& path, const void* data, size_t n) {
std::ofstream f(path, std::ios::binary);
if (!f) throw std::runtime_error("failed to open output " + path);
f.write((const char*)data, n);
}
static size_t dtypeSize(Qnn_DataType_t dt) {
switch (dt) {
case QNN_DATATYPE_FLOAT_32: return 4;
case QNN_DATATYPE_FLOAT_16: return 2;
case QNN_DATATYPE_UINT_8: return 1;
case QNN_DATATYPE_INT_8: return 1;
case QNN_DATATYPE_UINT_16: return 2;
case QNN_DATATYPE_INT_16: return 2;
case QNN_DATATYPE_UINT_32: return 4;
case QNN_DATATYPE_INT_32: return 4;
default: throw std::runtime_error("unsupported dtype " + std::to_string((int)dt));
}
}
static const Qnn_TensorV2_t& tv2(const Qnn_Tensor_t& t) {
if (t.version != QNN_TENSOR_VERSION_2) throw std::runtime_error("expected tensor v2 metadata");
return t.v2;
}
static uint64_t elemCount(const Qnn_TensorV2_t& t) {
uint64_t n = 1;
for (uint32_t i = 0; i < t.rank; ++i) n *= t.dimensions[i];
return n;
}
static Qnn_Tensor_t makeAppTensor(const Qnn_Tensor_t& meta, void* data, size_t bytes, Qnn_TensorType_t type) {
Qnn_Tensor_t t = meta;
if (t.version == QNN_TENSOR_VERSION_1) {
t.v1.type = type;
t.v1.memType = QNN_TENSORMEMTYPE_RAW;
t.v1.clientBuf.data = data;
t.v1.clientBuf.dataSize = bytes;
} else if (t.version == QNN_TENSOR_VERSION_2) {
t.v2.type = type;
t.v2.memType = QNN_TENSORMEMTYPE_RAW;
t.v2.clientBuf.data = data;
t.v2.clientBuf.dataSize = bytes;
} else {
throw std::runtime_error("unsupported tensor version");
}
return t;
}
static void logCb(const char* fmt, QnnLog_Level_t level, uint64_t timestamp, va_list args) {
(void)fmt; (void)level; (void)timestamp; (void)args;
}
int main(int argc, char** argv) {
std::string dlc = "../aihub_compiled/yolo26n-depth-aihub-qcs8275-qnn-dlc.dlc";
std::string input = "../aihub_dlc_test/input/images.raw";
std::string output = "qnn_app_output.raw";
int loops = 200;
int warmup = 10;
bool serverMode = false;
for (int i = 1; i < argc; ++i) {
std::string a = argv[i];
if (a == "--dlc" && i + 1 < argc) dlc = argv[++i];
else if (a == "--input" && i + 1 < argc) input = argv[++i];
else if (a == "--output" && i + 1 < argc) output = argv[++i];
else if (a == "--loops" && i + 1 < argc) loops = std::stoi(argv[++i]);
else if (a == "--warmup" && i + 1 < argc) warmup = std::stoi(argv[++i]);
else if (a == "--server") serverMode = true;
else { std::cerr << "usage: " << argv[0] << " [--dlc file.dlc] [--input images.raw] [--output out.raw] [--loops N] [--warmup N] [--server]\n"; return 2; }
}
void* htpLib = dlopen("libQnnHtp.so", RTLD_NOW | RTLD_LOCAL);
if (!htpLib) { std::cerr << "dlopen libQnnHtp.so failed: " << dlerror() << "\n"; return 1; }
void* systemLib = dlopen("libQnnSystem.so", RTLD_NOW | RTLD_LOCAL);
if (!systemLib) { std::cerr << "dlopen libQnnSystem.so failed: " << dlerror() << "\n"; return 1; }
auto getProviders = (QnnInterfaceGetProvidersFn)dlsym(htpLib, "QnnInterface_getProviders");
if (!getProviders) { std::cerr << "dlsym QnnInterface_getProviders failed\n"; return 1; }
const QnnInterface_t** providers = nullptr;
uint32_t numProviders = 0;
CHECK_QNN(getProviders(&providers, &numProviders));
if (numProviders == 0) throw std::runtime_error("no QNN providers");
const auto& qnn = providers[0]->QNN_INTERFACE_VER_NAME;
auto getSystemProviders = (QnnSystemInterfaceGetProvidersFn)dlsym(systemLib, "QnnSystemInterface_getProviders");
if (!getSystemProviders) { std::cerr << "dlsym QnnSystemInterface_getProviders failed\n"; return 1; }
const QnnSystemInterface_t** systemProviders = nullptr;
uint32_t numSystemProviders = 0;
CHECK_QNN(getSystemProviders(&systemProviders, &numSystemProviders));
if (numSystemProviders == 0) throw std::runtime_error("no QNN System providers");
const auto& sys = systemProviders[0]->QNN_SYSTEM_INTERFACE_VER_NAME;
Qnn_LogHandle_t logger = nullptr;
if (qnn.logCreate) qnn.logCreate(logCb, QNN_LOG_LEVEL_WARN, &logger);
Qnn_BackendHandle_t backend = nullptr;
CHECK_QNN(qnn.backendCreate(logger, nullptr, &backend));
Qnn_DeviceHandle_t device = nullptr;
if (qnn.deviceCreate) CHECK_QNN(qnn.deviceCreate(logger, nullptr, &device));
auto bin = readFile(dlc);
// Extract embedded HTP context binary from the DLC. qnn-net-run does this internally for --dlc_path.
// QnnContext_createFromBinary() expects the context record, not necessarily the whole DLC file.
QnnSystemDlc_Handle_t dlcHandle = nullptr;
QnnSystemDlc_RecordHandle_t* recordHandles = nullptr;
uint32_t numRecordHandles = 0;
QnnSystemDlc_RecordHandle_t contextRecord = nullptr;
const uint8_t* contextData = bin.data();
uint64_t contextSize = bin.size();
if (sys.systemDlcCreateFromFile && sys.systemDlcGetRecordsByType && sys.systemDlcReadRecordDataMemoryMapped) {
Qnn_ErrorHandle_t de = sys.systemDlcCreateFromFile(logger, dlc.c_str(), &dlcHandle);
if (de == QNN_SUCCESS && dlcHandle) {
de = sys.systemDlcGetRecordsByType(dlcHandle,
QNN_SYSTEM_DLC_RECORD_PREFIX_HTP_CACHE_RECORD,
1,
&recordHandles,
&numRecordHandles);
if (de == QNN_SUCCESS && numRecordHandles > 0 && recordHandles && recordHandles[0]) {
contextRecord = recordHandles[0];
de = sys.systemDlcReadRecordDataMemoryMapped(contextRecord, &contextData, &contextSize);
if (de == QNN_SUCCESS) {
std::cerr << "extracted HTP context record from DLC: " << contextSize << " bytes\n";
} else {
std::cerr << "failed reading HTP context record; falling back to whole DLC\n";
contextData = bin.data();
contextSize = bin.size();
}
} else {
std::cerr << "no HTP context record found in DLC; falling back to whole DLC\n";
}
} else {
std::cerr << "systemDlcCreateFromFile failed; falling back to whole DLC\n";
}
}
QnnSystemContext_Handle_t sysCtx = nullptr;
const QnnSystemContext_BinaryInfo_t* info = nullptr;
const char* graphName = nullptr;
uint32_t numInputs = 0, numOutputs = 0;
Qnn_Tensor_t* metaInputs = nullptr;
Qnn_Tensor_t* metaOutputs = nullptr;
Qnn_Tensor_t fallbackInput = QNN_TENSOR_INIT;
Qnn_Tensor_t fallbackOutput = QNN_TENSOR_INIT;
uint32_t inDims[4] = {1, 320, 320, 3};
uint32_t outDims[4] = {1, 1, 320, 320};
bool haveMetadata = false;
if (sys.systemContextCreate && sys.systemContextGetMetaData) {
if (sys.systemContextCreate(&sysCtx) == QNN_SUCCESS &&
sys.systemContextGetMetaData(sysCtx, contextData, (Qnn_ContextBinarySize_t)contextSize, &info) == QNN_SUCCESS) {
uint32_t numGraphs = 0;
QnnSystemContext_GraphInfo_t* graphs = nullptr;
if (info->version == QNN_SYSTEM_CONTEXT_BINARY_INFO_VERSION_1) {
numGraphs = info->contextBinaryInfoV1.numGraphs;
graphs = info->contextBinaryInfoV1.graphs;
} else if (info->version == QNN_SYSTEM_CONTEXT_BINARY_INFO_VERSION_2) {
numGraphs = info->contextBinaryInfoV2.numGraphs;
graphs = info->contextBinaryInfoV2.graphs;
} else if (info->version == QNN_SYSTEM_CONTEXT_BINARY_INFO_VERSION_3) {
numGraphs = info->contextBinaryInfoV3.numGraphs;
graphs = info->contextBinaryInfoV3.graphs;
}
if (numGraphs >= 1) {
auto& gmeta = graphs[0];
if (gmeta.version == QNN_SYSTEM_CONTEXT_GRAPH_INFO_VERSION_1) {
graphName = gmeta.graphInfoV1.graphName; numInputs = gmeta.graphInfoV1.numGraphInputs; metaInputs = gmeta.graphInfoV1.graphInputs; numOutputs = gmeta.graphInfoV1.numGraphOutputs; metaOutputs = gmeta.graphInfoV1.graphOutputs;
} else if (gmeta.version == QNN_SYSTEM_CONTEXT_GRAPH_INFO_VERSION_2) {
graphName = gmeta.graphInfoV2.graphName; numInputs = gmeta.graphInfoV2.numGraphInputs; metaInputs = gmeta.graphInfoV2.graphInputs; numOutputs = gmeta.graphInfoV2.numGraphOutputs; metaOutputs = gmeta.graphInfoV2.graphOutputs;
} else if (gmeta.version == QNN_SYSTEM_CONTEXT_GRAPH_INFO_VERSION_3) {
graphName = gmeta.graphInfoV3.graphName; numInputs = gmeta.graphInfoV3.numGraphInputs; metaInputs = gmeta.graphInfoV3.graphInputs; numOutputs = gmeta.graphInfoV3.numGraphOutputs; metaOutputs = gmeta.graphInfoV3.graphOutputs;
}
haveMetadata = (graphName && numInputs == 1 && numOutputs == 1 && metaInputs && metaOutputs);
}
}
}
if (!haveMetadata) {
std::cerr << "QNN system metadata unavailable for DLC; using known graph/tensor metadata fallback\n";
graphName = "graph_ymndtmzg";
numInputs = 1;
numOutputs = 1;
fallbackInput.version = QNN_TENSOR_VERSION_2;
fallbackInput.v2.id = 1;
fallbackInput.v2.name = "images";
fallbackInput.v2.type = QNN_TENSOR_TYPE_APP_WRITE;
fallbackInput.v2.dataFormat = QNN_TENSOR_DATA_FORMAT_FLAT_BUFFER;
fallbackInput.v2.dataType = QNN_DATATYPE_FLOAT_32;
fallbackInput.v2.rank = 4;
fallbackInput.v2.dimensions = inDims;
fallbackInput.v2.memType = QNN_TENSORMEMTYPE_RAW;
fallbackOutput.version = QNN_TENSOR_VERSION_2;
fallbackOutput.v2.id = 842;
fallbackOutput.v2.name = "output_0";
fallbackOutput.v2.type = QNN_TENSOR_TYPE_APP_READ;
fallbackOutput.v2.dataFormat = QNN_TENSOR_DATA_FORMAT_FLAT_BUFFER;
fallbackOutput.v2.dataType = QNN_DATATYPE_FLOAT_32;
fallbackOutput.v2.rank = 4;
fallbackOutput.v2.dimensions = outDims;
fallbackOutput.v2.memType = QNN_TENSORMEMTYPE_RAW;
metaInputs = &fallbackInput;
metaOutputs = &fallbackOutput;
}
std::cerr << "graph: " << graphName << " inputs=" << numInputs << " outputs=" << numOutputs << "\n";
if (numInputs != 1 || numOutputs != 1) throw std::runtime_error("runner currently expects 1 input/1 output");
const auto& inMeta = tv2(metaInputs[0]);
const auto& outMeta = tv2(metaOutputs[0]);
size_t inBytes = elemCount(inMeta) * dtypeSize(inMeta.dataType);
size_t outBytes = elemCount(outMeta) * dtypeSize(outMeta.dataType);
std::cerr << "input: " << inMeta.name << " bytes=" << inBytes << " dtype=0x" << std::hex << inMeta.dataType << std::dec << "\n";
std::cerr << "output: " << outMeta.name << " bytes=" << outBytes << " dtype=0x" << std::hex << outMeta.dataType << std::dec << "\n";
auto inputBuf = readFile(input);
if (inputBuf.size() != inBytes) throw std::runtime_error("input size mismatch: got " + std::to_string(inputBuf.size()) + " expected " + std::to_string(inBytes));
std::vector<uint8_t> outputBuf(outBytes);
Qnn_ContextHandle_t context = nullptr;
CHECK_QNN(qnn.contextCreateFromBinary(backend, device, nullptr, contextData, (Qnn_ContextBinarySize_t)contextSize, &context, nullptr));
Qnn_GraphHandle_t graph = nullptr;
CHECK_QNN(qnn.graphRetrieve(context, graphName, &graph));
Qnn_Tensor_t inTensor = makeAppTensor(metaInputs[0], inputBuf.data(), inputBuf.size(), QNN_TENSOR_TYPE_APP_WRITE);
Qnn_Tensor_t outTensor = makeAppTensor(metaOutputs[0], outputBuf.data(), outputBuf.size(), QNN_TENSOR_TYPE_APP_READ);
for (int i = 0; i < warmup; ++i) {
CHECK_QNN(qnn.graphExecute(graph, &inTensor, 1, &outTensor, 1, nullptr, nullptr));
}
if (serverMode) {
// Persistent mode. Read commands from stdin:
// RUN <input.raw> <output.raw>
// Reply on stdout:
// OK <ms> <output_bytes>
// or:
// ERR <message>
std::cout << "READY " << inBytes << " " << outBytes << std::endl;
std::string cmd, inPath, outPath;
while (std::cin >> cmd) {
if (cmd == "QUIT" || cmd == "EXIT") break;
if (cmd != "RUN") {
std::cout << "ERR expected RUN" << std::endl;
continue;
}
if (!(std::cin >> inPath >> outPath)) {
std::cout << "ERR missing paths" << std::endl;
break;
}
try {
auto frameBuf = readFile(inPath);
if (frameBuf.size() != inBytes) {
std::cout << "ERR input_size got=" << frameBuf.size() << " expected=" << inBytes << std::endl;
continue;
}
std::memcpy(inputBuf.data(), frameBuf.data(), inBytes);
auto t0 = std::chrono::steady_clock::now();
CHECK_QNN(qnn.graphExecute(graph, &inTensor, 1, &outTensor, 1, nullptr, nullptr));
auto t1 = std::chrono::steady_clock::now();
double oneMs = std::chrono::duration<double, std::milli>(t1 - t0).count();
writeFile(outPath, outputBuf.data(), outputBuf.size());
std::cout << "OK " << oneMs << " " << outputBuf.size() << std::endl;
} catch (const std::exception& e) {
std::cout << "ERR " << e.what() << std::endl;
}
}
} else {
std::vector<double> ms;
ms.reserve(loops);
for (int i = 0; i < loops; ++i) {
auto t0 = std::chrono::steady_clock::now();
CHECK_QNN(qnn.graphExecute(graph, &inTensor, 1, &outTensor, 1, nullptr, nullptr));
auto t1 = std::chrono::steady_clock::now();
ms.push_back(std::chrono::duration<double, std::milli>(t1 - t0).count());
}
writeFile(output, outputBuf.data(), outputBuf.size());
double sum = std::accumulate(ms.begin(), ms.end(), 0.0);
double avg = sum / ms.size();
std::sort(ms.begin(), ms.end());
double p50 = ms[ms.size()/2];
double p90 = ms[(size_t)(ms.size()*0.90)];
double p99 = ms[std::min(ms.size()-1, (size_t)(ms.size()*0.99))];
std::cout << "loops=" << loops << " warmup=" << warmup << "\n";
std::cout << "avg_ms=" << avg << " p50_ms=" << p50 << " p90_ms=" << p90 << " p99_ms=" << p99 << " fps=" << (1000.0/avg) << "\n";
std::cout << "wrote=" << output << " bytes=" << outBytes << "\n";
}
if (qnn.contextFree) qnn.contextFree(context, nullptr);
if (qnn.deviceFree && device) qnn.deviceFree(device);
if (qnn.backendFree) qnn.backendFree(backend);
if (qnn.logFree && logger) qnn.logFree(logger);
if (sysCtx && sys.systemContextFree) sys.systemContextFree(sysCtx);
if (contextRecord && sys.systemDlcFreeRecord) sys.systemDlcFreeRecord(contextRecord);
if (dlcHandle && sys.systemDlcFree) sys.systemDlcFree(dlcHandle);
dlclose(systemLib);
dlclose(htpLib);
return 0;
}
Makefile
CXX ?= g++
CXXFLAGS := -std=c++17 -O3 -Wall -Wextra -I/usr/include -I/usr/include/QNN
LDFLAGS := -ldl
all: qnn_dlc_runner
qnn_dlc_runner: qnn_dlc_runner.cpp
$(CXX) $(CXXFLAGS) $< -o $@ $(LDFLAGS)
clean:
rm -f qnn_dlc_runner
live_aihub_qnn_native.py
#!/usr/bin/env python3
"""Live USB camera depth viewer using the persistent AI Hub QNN/NPU native app."""
import argparse
import subprocess
import sys
import tempfile
import time
from pathlib import Path
import cv2
import numpy as np
from ultralytics.utils.plotting import colorize_depth
ROOT = Path(__file__).resolve().parent
APP = ROOT / "qnn_app" / "qnn_dlc_runner"
CONTEXT = ROOT / "qnn_context" / "yolo26n-depth-aihub-qcs8275-context.bin.bin"
SIZE = 320
def cam_id(value):
try:
return int(value)
except ValueError:
return value
def letterbox_bgr(frame, size=SIZE):
h, w = frame.shape[:2]
scale = min(size / h, size / w)
nh, nw = int(round(h * scale)), int(round(w * scale))
resized = cv2.resize(frame, (nw, nh), interpolation=cv2.INTER_LINEAR)
canvas = np.full((size, size, 3), 114, dtype=np.uint8)
top = (size - nh) // 2
left = (size - nw) // 2
canvas[top : top + nh, left : left + nw] = resized
rgb = cv2.cvtColor(canvas, cv2.COLOR_BGR2RGB)
x = (rgb.astype(np.float32) / 255.0)[None, ...]
meta = (top, left, nh, nw, h, w)
return x, meta
def unletterbox_depth(depth_320, meta):
top, left, nh, nw, h, w = meta
crop = depth_320[top : top + nh, left : left + nw]
return cv2.resize(crop, (w, h), interpolation=cv2.INTER_LINEAR)
def read_ready(proc, timeout=20.0):
deadline = time.time() + timeout
lines = []
while time.time() < deadline:
line = proc.stdout.readline()
if not line:
if proc.poll() is not None:
raise RuntimeError("native QNN app exited early:\n" + "".join(lines))
time.sleep(0.01)
continue
lines.append(line)
if line.startswith("READY "):
print("Native QNN app ready:", line.strip())
return line.strip()
# Keep setup messages visible; useful if something fails.
print("[qnn]", line.rstrip())
raise TimeoutError("Timed out waiting for native QNN app READY. Last output:\n" + "".join(lines[-30:]))
def main():
ap = argparse.ArgumentParser(description="Live AI Hub QNN/NPU native depth camera")
ap.add_argument("--camera", default=0, type=cam_id, help="Camera index/path, e.g. 0 or /dev/video0")
ap.add_argument("--width", default=640, type=int)
ap.add_argument("--height", default=480, type=int)
ap.add_argument("--display-width", default=1280, type=int, help="Display width; 0 keeps native stacked width")
ap.add_argument("--display-height", default=0, type=int, help="Display height; 0 preserves aspect")
ap.add_argument("--font-scale", default=1.0, type=float)
ap.add_argument("--metric", action="store_true", help="Use metric color mode")
ap.add_argument("--vmax", default=20.0, type=float, help="Max meters for --metric mode")
ap.add_argument("--save-dir", default="aihub_qnn_native_captures")
ap.add_argument("--app", default=str(APP))
ap.add_argument("--context", default=str(CONTEXT))
args = ap.parse_args()
app = Path(args.app)
context = Path(args.context)
if not app.exists():
raise SystemExit(f"Missing native app: {app}\nBuild it with: cd {ROOT/qnn_app} && make")
if not context.exists():
raise SystemExit(f"Missing QNN context: {context}")
cv2.setNumThreads(1)
tmp = tempfile.TemporaryDirectory(prefix="qnn_live_")
tmpdir = Path(tmp.name)
in_raw = tmpdir / "input.raw"
out_raw = tmpdir / "output.raw"
# The native runner validates the initial --input path even in --server mode,
# so create a correctly-sized dummy NHWC float32 input before launching it.
np.zeros((1, SIZE, SIZE, 3), dtype=np.float32).tofile(in_raw)
cmd = [str(app), "--dlc", str(context), "--input", str(in_raw), "--output", str(out_raw), "--warmup", "10", "--loops", "1", "--server"]
print("Starting native QNN app:", " ".join(cmd))
# stderr is merged so we can show setup logs and still wait for READY.
proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1)
try:
read_ready(proc)
cap = cv2.VideoCapture(args.camera, cv2.CAP_V4L2)
if not cap.isOpened():
raise SystemExit(f"Could not open camera {args.camera!r}. Try --camera /dev/video0 or check: ls /dev/video*")
cap.set(cv2.CAP_PROP_FRAME_WIDTH, args.width)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, args.height)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
save_dir = Path(args.save_dir)
save_dir.mkdir(exist_ok=True)
print("Live AI Hub QNN native depth started")
print("Controls: q/Esc quit, s save current RGB/depth/overlay")
last_depth = None
last_color = None
last_overlay = None
last_frame = None
frame_idx = 0
fps_smooth = None
while True:
ok, frame = cap.read()
if not ok:
print("Camera read failed")
break
last_frame = frame.copy()
x, meta = letterbox_bgr(frame, SIZE)
x.tofile(in_raw)
t0 = time.perf_counter()
proc.stdin.write(f"RUN {in_raw} {out_raw}\n")
proc.stdin.flush()
line = proc.stdout.readline().strip()
while line and not (line.startswith("OK ") or line.startswith("ERR ")):
# Forward any unexpected QNN log lines.
print("[qnn]", line)
line = proc.stdout.readline().strip()
if line.startswith("ERR "):
raise RuntimeError(line)
if not line.startswith("OK "):
raise RuntimeError("native QNN app produced no response")
qnn_ms = float(line.split()[1])
total_ms = (time.perf_counter() - t0) * 1000.0
inst_fps = 1000.0 / total_ms if total_ms > 0 else 0.0
fps_smooth = inst_fps if fps_smooth is None else (0.9 * fps_smooth + 0.1 * inst_fps)
d320 = np.fromfile(out_raw, dtype=np.float32).reshape(1, 1, SIZE, SIZE)[0, 0]
depth = unletterbox_depth(d320, meta).astype(np.float32)
last_depth = depth
if args.metric:
color = colorize_depth(depth, vmin=0.0, vmax=args.vmax, cmap="inferno", mode="metric")
else:
color = colorize_depth(depth, cmap="spectral", mode="disparity")
overlay = cv2.addWeighted(frame, 0.45, color, 0.55, 0)
last_color = color
last_overlay = overlay
view = np.hstack([frame, color, overlay])
valid = depth[np.isfinite(depth) & (depth > 0)]
if valid.size:
txt = f"QNN {qnn_ms:.1f} ms | total {total_ms:.1f} ms | {fps_smooth:.1f} FPS | depth {valid.min():.1f}/{valid.mean():.1f}/{valid.max():.1f} m | q quit | s save"
else:
txt = f"QNN {qnn_ms:.1f} ms | total {total_ms:.1f} ms | {fps_smooth:.1f} FPS | q quit | s save"
fs = args.font_scale
thick_bg = max(3, int(round(4 * fs)))
thick_fg = max(1, int(round(1.5 * fs)))
y_top = int(38 * fs)
y_bottom = view.shape[0] - int(18 * fs)
for text, pos in [(txt, (15, y_top)), ("RGB | DEPTH | OVERLAY", (15, y_bottom))]:
cv2.putText(view, text, pos, cv2.FONT_HERSHEY_SIMPLEX, fs, (0, 0, 0), thick_bg, cv2.LINE_AA)
cv2.putText(view, text, pos, cv2.FONT_HERSHEY_SIMPLEX, fs, (255, 255, 255), thick_fg, cv2.LINE_AA)
show = view
if args.display_width or args.display_height:
h, w = view.shape[:2]
if args.display_width and args.display_height:
dw, dh = args.display_width, args.display_height
elif args.display_width:
dw = args.display_width
dh = max(1, int(h * dw / w))
else:
dh = args.display_height
dw = max(1, int(w * dh / h))
show = cv2.resize(view, (dw, dh), interpolation=cv2.INTER_AREA if dw < w else cv2.INTER_LINEAR)
cv2.namedWindow("AI Hub QNN Native Live Depth", cv2.WINDOW_NORMAL)
cv2.imshow("AI Hub QNN Native Live Depth", show)
key = cv2.waitKey(1) & 0xFF
if key in (ord("q"), 27):
break
if key == ord("s") and last_depth is not None:
stamp = time.strftime("%Y%m%d-%H%M%S")
cv2.imwrite(str(save_dir / f"{stamp}_rgb.jpg"), last_frame)
cv2.imwrite(str(save_dir / f"{stamp}_depth_color.png"), last_color)
cv2.imwrite(str(save_dir / f"{stamp}_overlay.png"), last_overlay)
np.save(save_dir / f"{stamp}_depth.npy", last_depth)
print("Saved", save_dir.resolve(), stamp)
frame_idx += 1
cap.release()
cv2.destroyAllWindows()
finally:
try:
if proc.stdin:
proc.stdin.write("QUIT\n")
proc.stdin.flush()
except Exception:
pass
try:
proc.terminate()
proc.wait(timeout=2)
except Exception:
proc.kill()
tmp.cleanup()
if __name__ == "__main__":
main()
live_aihub_qnn_native.sh
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
source ~/yolo-depth-venv/bin/activate
python ./live_aihub_qnn_native.py "$@"

