diff --git a/CMakeLists.txt b/CMakeLists.txt index 8728443..dc99705 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ find_path(TENSORRT_INCLUDE_DIR NvInfer.h PATH_SUFFIXES aarch64-linux-gnu x86_64- find_library(TENSORRT_LIBRARY nvinfer REQUIRED) add_library(camera_risk_core src/trt_engine.cpp src/pose_engine.cu src/gpu_preprocess.cu src/uyvy_converter.cu - src/can_bus.cpp src/detector.cpp src/door_risk.cpp src/camera.cpp src/camera_pipeline.cpp src/inference_session.cpp src/serialization.cpp src/output_writer.cpp src/result_renderer.cpp src/config.cpp) + src/can_bus.cpp src/detector.cpp src/door_risk.cpp src/brightness_gate.cpp src/camera.cpp src/camera_pipeline.cpp src/inference_session.cpp src/serialization.cpp src/output_writer.cpp src/result_renderer.cpp src/config.cpp) target_include_directories(camera_risk_core PUBLIC include third_party ${TENSORRT_INCLUDE_DIR} ${OpenCV_INCLUDE_DIRS}) target_link_libraries(camera_risk_core PUBLIC ${TENSORRT_LIBRARY} ${OpenCV_LIBS} Threads::Threads CUDA::cudart CUDA::nppc CUDA::nppig CUDA::nppidei CUDA::nppicc) target_include_directories(camera_risk_core SYSTEM PRIVATE third_party/zlg) diff --git a/README.md b/README.md index 6ff5f67..ea92eca 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ C++ / TensorRT 实时摄像头风险推理,算法来自最新 run_risk.py。 相机每次取帧默认等待 **2000 ms**,连续超时 **6 次**才停止流水线(纯超时约 **12 秒**);成功取得有效帧后清零超时计数。单次等待时间由 `config/camera.json` 的 `camera.timeout_ms` 配置。运行中的坏帧、取流错误共用该重试期限;设备失效时尝试重新打开相机,恢复有效帧后继续推理。GPU 错误仍按原有异常处理退出。 +原始画面在 YOLO 前经过低照度门控。启动前 5 帧不输出有效风险;明显极暗连续 15 帧后暂停人体、姿态和风险推理,亮度满足恢复阈值连续 15 帧后自动恢复。低照度结果标记 `risk_valid=false`、`risk_level=unavailable`,不会转换成 `clear`。阈值和 ROI 可在 `brightness_gate` 配置段调整,`enabled=false` 可关闭门控。阈值依据研发原始视频验证说明设置,适用边界仍包括黄昏、局部强光和镜头污染等未覆盖场景。 + ## Orin 直接运行 适用环境:Jetson AGX Orin、JetPack 5.1.2 / L4T R35.4.1、CUDA 11.4、TensorRT 8.5.2、OpenCV 4.5.4。 diff --git a/config/camera.json b/config/camera.json index d2c6a98..f681a86 100644 --- a/config/camera.json +++ b/config/camera.json @@ -19,6 +19,20 @@ "release_relative": 0.45, "calibration_verified": false }, + "brightness_gate": { + "enabled": true, + "roi": [0.02, 0.05, 0.88, 0.98], + "dark_pixel_luma": 40.0, + "enter_median_luma": 18.0, + "enter_dark_ratio": 0.85, + "enter_dark_tile_ratio": 0.75, + "exit_median_luma": 24.0, + "exit_dark_ratio": 0.80, + "exit_dark_tile_ratio": 0.70, + "startup_frames": 5, + "enter_frames": 15, + "exit_frames": 15 + }, "can": { "enabled": true, "device_index": 0, diff --git a/include/brightness_gate.hpp b/include/brightness_gate.hpp new file mode 100644 index 0000000..1614811 --- /dev/null +++ b/include/brightness_gate.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +namespace cms { +struct BrightnessGateConfig { + bool enabled{true}; + double roi_x1{0.02}, roi_y1{0.05}, roi_x2{0.88}, roi_y2{0.98}; + double dark_pixel_luma{40.0}; + double enter_median_luma{18.0}, enter_dark_ratio{0.85}, enter_dark_tile_ratio{0.75}; + double exit_median_luma{24.0}, exit_dark_ratio{0.80}, exit_dark_tile_ratio{0.70}; + int startup_frames{5}, enter_frames{15}, exit_frames{15}; +}; + +struct BrightnessDecision { + std::string state{"INITIALIZING"}, reason{"initializing"}, transition; + bool allow_downstream{}; + double median_luma{}, p75_luma{}, mean_luma{}, dark_pixel_luma{40.0}, dark_ratio{}, dark_tile_ratio{}; + int dark_streak{}, bright_streak{}; + int enter_frames{}, exit_frames{}; +}; + +class BrightnessGate { +public: + explicit BrightnessGate(BrightnessGateConfig config = {}); + BrightnessDecision update(cv::Mat const& bgr); +private: + BrightnessGateConfig config_; + std::string state_{"INITIALIZING"}; + int startup_count_{}, startup_dark_votes_{}, dark_streak_{}, bright_streak_{}; +}; +} diff --git a/include/camera_pipeline.hpp b/include/camera_pipeline.hpp index 2ba89af..7544c3d 100644 --- a/include/camera_pipeline.hpp +++ b/include/camera_pipeline.hpp @@ -1,5 +1,6 @@ #pragma once #include "camera.hpp" +#include "brightness_gate.hpp" #include "frame_result.hpp" #include "output_writer.hpp" #include @@ -16,6 +17,7 @@ struct PipelineConfig { double detector_confidence{0.30}; int max_people{3}; double warning_relative{0.30},release_relative{0.45}; + BrightnessGateConfig brightness_gate; std::size_t queue_capacity{2}; bool door_mask_calibration_verified{false}; }; diff --git a/include/frame_result.hpp b/include/frame_result.hpp index 3bc16dd..341a3ff 100644 --- a/include/frame_result.hpp +++ b/include/frame_result.hpp @@ -2,6 +2,7 @@ // Public downstream contract. Owns its data; no GPU, JSON or network handles. #include "image.hpp" #include "can_types.hpp" +#include "brightness_gate.hpp" #include #include #include @@ -77,6 +78,7 @@ struct FrameResult { std::shared_ptr image; std::shared_ptr door_mask_pixels; bool door_mask_calibration_verified{}; + BrightnessDecision brightness; }; using FrameResultPtr = std::shared_ptr; } diff --git a/include/visualizer.hpp b/include/visualizer.hpp index 8a6474e..9f20c8d 100644 --- a/include/visualizer.hpp +++ b/include/visualizer.hpp @@ -5,8 +5,9 @@ namespace cms { class CameraPipeline; // Pure drawing from the immutable result. Never re-runs inference. -cv::Mat renderResult(FrameResult const& result,CanSnapshot const* liveCan=nullptr); +cv::Mat renderResult(FrameResult const& result,CanSnapshot const* liveCan=nullptr,bool drawText=true); +cv::Mat renderVisualization(FrameResult const& result,CanSnapshot const& liveCan); void renderCanOverlay(cv::Mat& image,CanSnapshot const& can); // Optional observer: closing the window returns without stopping the pipeline. -void runLocalVisualization(CameraPipeline const& pipeline); +void runLocalVisualization(CameraPipeline const& pipeline, bool keepFinalFrame = false); } diff --git a/orin/SHA256SUMS b/orin/SHA256SUMS index b324ede..26fc248 100644 --- a/orin/SHA256SUMS +++ b/orin/SHA256SUMS @@ -2,9 +2,9 @@ 6d5e874fd27fcfd75d303d7e35d9672028af71041f52dcc82a1e83676faefa65 ./bin/drivers/max9295.ko 8a78d04ef8be44e9b62a5e4ee315d3d6d679e34aedff02a234bbf4d40e222933 ./bin/drivers/max9296.ko 3da628587477cc35d5e3f8aee86e718e2ddf16808e893e00ba6e164b2307e843 ./bin/drivers/sgx-yuv-gmsl2.ko -1744ec8a2c584aa5306cdf975fb7f01ec9ee6cec524a447dfc85e179b270ffc3 ./build/camera_risk +cc34c1e0281c27b380a9a90bd58cfa65fefa322612be0de42a7faa86e15ef39e ./build/camera_risk 12ebf1d21a967cc4d7620636352fc5779a697a0fb9feaa8bdb339927292a3517 ./build/door_mask_tool -d44dcf10df43c7bcd73b7f2b64e4117d207435a5218c2146bfb659566cb320ba ./config/camera.json +e1ac783278a1dc4ebeb15f48072a865a51dc44bc31dcb42b4a8aa96b8bdc067e ./config/camera.json 7808f4ae27de49a02fe51f8e6b60ae8454ff775ea3e98fa0a7c633ee85027fb4 ./config/door_mask.json ebcd09e8c2e35008adbb0c1d222e580b17e0df1c8093df3481ba3374cd9a8174 ./engines/rtmw_l_384.engine f6a65b926ac1144484f4aa93cd26d7997d32d62310ab93fe867f2bd4412222e9 ./engines/yolo11s.engine diff --git a/orin/build/camera_risk b/orin/build/camera_risk index ad41ec8..4b4bc2c 100755 Binary files a/orin/build/camera_risk and b/orin/build/camera_risk differ diff --git a/orin/config/camera.json b/orin/config/camera.json index f036a4e..0827038 100644 --- a/orin/config/camera.json +++ b/orin/config/camera.json @@ -19,6 +19,20 @@ "release_relative": 0.45, "calibration_verified": false }, + "brightness_gate": { + "enabled": true, + "roi": [0.02, 0.05, 0.88, 0.98], + "dark_pixel_luma": 40.0, + "enter_median_luma": 18.0, + "enter_dark_ratio": 0.85, + "enter_dark_tile_ratio": 0.75, + "exit_median_luma": 24.0, + "exit_dark_ratio": 0.80, + "exit_dark_tile_ratio": 0.70, + "startup_frames": 5, + "enter_frames": 15, + "exit_frames": 15 + }, "can": { "enabled": true, "device_index": 0, diff --git a/src/brightness_gate.cpp b/src/brightness_gate.cpp new file mode 100644 index 0000000..c039325 --- /dev/null +++ b/src/brightness_gate.cpp @@ -0,0 +1,79 @@ +#include "brightness_gate.hpp" +#include +#include +#include +#include +#include + +namespace cms { +namespace { +double percentile(std::vector const& values, double fraction) { + double position=fraction*static_cast(values.size()-1); + auto lower=static_cast(std::floor(position)); + auto upper=static_cast(std::ceil(position)); + std::vector ordered=values; + std::nth_element(ordered.begin(),ordered.begin()+upper,ordered.end()); + double high=ordered[upper]; + if(lower==upper)return high; + auto low=*std::max_element(ordered.begin(),ordered.begin()+upper); + return low+(high-low)*(position-lower); +} +} +BrightnessGate::BrightnessGate(BrightnessGateConfig config):config_(config) { + if(config_.startup_frames<1||config_.enter_frames<1||config_.exit_frames<1|| + config_.roi_x1<0||config_.roi_y1<0||config_.roi_x2>1||config_.roi_y2>1|| + config_.roi_x1>=config_.roi_x2||config_.roi_y1>=config_.roi_y2) + throw std::invalid_argument("Invalid brightness gate configuration"); +} +BrightnessDecision BrightnessGate::update(cv::Mat const& bgr) { + if(bgr.empty()||bgr.type()!=CV_8UC3)throw std::invalid_argument("Brightness gate requires a non-empty BGR frame"); + int x1=static_cast(std::nearbyint(config_.roi_x1*bgr.cols)); + int y1=static_cast(std::nearbyint(config_.roi_y1*bgr.rows)); + int x2=static_cast(std::nearbyint(config_.roi_x2*bgr.cols)); + int y2=static_cast(std::nearbyint(config_.roi_y2*bgr.rows)); + cv::Mat resized,luma; + cv::resize(bgr(cv::Rect(x1,y1,x2-x1,y2-y1)),resized,{160,120},0,0,cv::INTER_AREA); + cv::cvtColor(resized,luma,cv::COLOR_BGR2YCrCb); + std::vector values;values.reserve(luma.total()); + std::size_t dark=0;double sum=0; + for(int y=0;y(y,x)[0];values.push_back(value);sum+=value; + if(value(dark)/values.size(); + int dark_tiles=0; + for(int ty=0;ty<4;++ty)for(int tx=0;tx<4;++tx) { + std::vector tile;tile.reserve(30*40); + for(int y=ty*30;y<(ty+1)*30;++y)for(int x=tx*40;x<(tx+1)*40;++x)tile.push_back(luma.at(y,x)[0]); + if(percentile(tile,0.50)(dark_tiles)/16; + bool is_dark=decision.median_luma<=config_.enter_median_luma&&decision.dark_ratio>=config_.enter_dark_ratio&&decision.dark_tile_ratio>=config_.enter_dark_tile_ratio; + bool is_bright=decision.median_luma>=config_.exit_median_luma&&decision.dark_ratio<=config_.exit_dark_ratio&&decision.dark_tile_ratio<=config_.exit_dark_tile_ratio; + auto previous=state_; + if(!config_.enabled)state_="NORMAL"; + else if(state_=="INITIALIZING") { + ++startup_count_;startup_dark_votes_+=is_dark; + if(startup_count_>=config_.startup_frames) + state_=startup_dark_votes_>=((config_.startup_frames+1)/2)?"LOW_LIGHT":"NORMAL"; + } else if(state_=="NORMAL") { + dark_streak_=is_dark?dark_streak_+1:0;bright_streak_=0; + if(dark_streak_>=config_.enter_frames){state_="LOW_LIGHT";dark_streak_=0;} + } else { + bright_streak_=is_bright?bright_streak_+1:0;dark_streak_=0; + if(bright_streak_>=config_.exit_frames){state_="NORMAL";bright_streak_=0;} + } + decision.state=state_;decision.allow_downstream=state_=="NORMAL"; + decision.dark_streak=dark_streak_;decision.bright_streak=bright_streak_; + if(previous!=state_)decision.transition=previous+"->"+state_; + if(state_=="INITIALIZING")decision.reason="initializing"; + else if(state_=="LOW_LIGHT")decision.reason=is_bright&&bright_streak_?"recovery_pending":"low_light"; + else decision.reason=is_dark&&dark_streak_?"low_light_pending":"sufficient_light"; + return decision; +} +} diff --git a/src/camera_pipeline.cpp b/src/camera_pipeline.cpp index e491467..871abd0 100644 --- a/src/camera_pipeline.cpp +++ b/src/camera_pipeline.cpp @@ -48,7 +48,7 @@ void attachPoses(FrameResult& result,fangjia::PoseFrameResult const& fused) { } } struct CameraPipeline::Impl { - struct Detected {CapturedFrame frame;std::vector boxes;double elapsed_ms{};}; + struct Detected {CapturedFrame frame;std::vector boxes;BrightnessDecision brightness;double elapsed_ms{};}; PipelineConfig config; BoundedQueue captures; BoundedQueue detections; @@ -138,11 +138,13 @@ struct CameraPipeline::Impl { captures.close(); } void detectorLoop() { - Stream stream; + Stream stream;BrightnessGate gate(config.brightness_gate); while(!stopped) { auto frame=captures.pop();if(!frame)break; - auto startedAt=monotonicNs();auto boxes=detector->infer(imageView(*frame->image),stream.value); - if(!detections.push({std::move(*frame),std::move(boxes),elapsedMs(startedAt)}))break; + auto startedAt=monotonicNs();auto brightness=gate.update(imageView(*frame->image)); + std::vector boxes; + if(brightness.allow_downstream)boxes=detector->infer(imageView(*frame->image),stream.value); + if(!detections.push({std::move(*frame),std::move(boxes),std::move(brightness),elapsedMs(startedAt)}))break; } detections.close(); } @@ -151,6 +153,34 @@ struct CameraPipeline::Impl { while(!stopped) { auto detected=detections.pop();if(!detected)break; if(detected->frame.metadata.frame_index>static_cast(std::numeric_limits::max()))throw std::runtime_error("Frame index limit reached; restart pipeline"); + if(!detected->brightness.allow_downstream) { + boxesTracker=fangjia::BoxTracker{}; + fusion=fangjia::PoseFusionTracker{}; + int index=static_cast(detected->frame.metadata.frame_index); + auto emptyFusion=fusion.update({}, {}, index); + auto record=makeRecord(index,{}, {},emptyFusion,!config.record_session.empty(),false); + auto qualityStatus=detected->brightness.state; + Json invalidRisk={{"status",qualityStatus},{"risk_score",nullptr},{"risk_level","unavailable"}, + {"decision","BRIGHTNESS_GATE"},{"source","brightness_gate"},{"track_id",nullptr},{"side",nullptr}, + {"door_mask",{{"status","not_evaluated"},{"annotation_index",index},{"path",nullptr},{"format","none"},{"area_px",0}}}, + {"warning_distance_relative",config.warning_relative},{"release_distance_relative",config.release_relative},{"regions",Json::array()}}; + auto result=std::make_shared(makeFrameResult(record,invalidRisk,detected->frame.metadata)); + result->brightness=detected->brightness; + result->image=std::move(detected->frame.image);result->door_mask_pixels=doorPixels; + result->door_mask_calibration_verified=config.door_mask_calibration_verified; + if(detected->frame.replay_record) { + auto const& recorded=detected->frame.replay_record->at("result"); + if(recorded.contains("can"))result->can=deserializeCan(recorded.at("can")); + else result->can.status="NOT_RECORDED"; + result->can.source="replay"; + } else result->can=can->snapshot(); + result->timing={detected->elapsed_ms,0,0,elapsedMs(result->capture.capture_monotonic_ns)}; + FrameResultPtr immutable=result;results.publish(std::move(immutable)); + {std::lock_guard lock(stateMutex);totalLatency+=result->timing.capture_to_result_ms;++published;} + if(!config.record_session.empty())sessionWriter->write(*result,record,detected->frame.replay_record.get()); + outputWriter->write(*result); + continue; + } auto index=static_cast(detected->frame.metadata.frame_index); auto& boxes=detected->boxes; auto frame=imageView(*detected->frame.image); @@ -168,13 +198,14 @@ struct CameraPipeline::Impl { auto record=makeRecord(index,raw,isolated,fused,!config.record_session.empty(),false); auto riskData=risk->evaluate(index,record); auto result=std::make_shared(makeFrameResult(record,riskData,detected->frame.metadata)); + result->brightness=detected->brightness; if(detected->frame.replay_record) { auto const& recorded=detected->frame.replay_record->at("result"); if(recorded.contains("can"))result->can=deserializeCan(recorded.at("can")); else result->can.status="NOT_RECORDED"; result->can.source="replay"; }else { - if(!sessionReader&&config.can.enabled)can->queueRisk(result->risk.risk_level); + if(!sessionReader&&config.can.enabled&&result->brightness.allow_downstream)can->queueRisk(result->risk.risk_level); result->can=can->snapshot(); } attachPoses(*result,fused); diff --git a/src/config.cpp b/src/config.cpp index 3b983ab..351b19f 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -19,6 +19,21 @@ PipelineConfig loadConfig(std::filesystem::path const& path) { auto const& door=j.at("door");config.door_mask=resolve(door.at("mask")); config.warning_relative=door.at("warning_relative");config.release_relative=door.at("release_relative"); config.door_mask_calibration_verified=door.at("calibration_verified"); + if(j.contains("brightness_gate")) { + auto const& gate=j.at("brightness_gate");auto& c=config.brightness_gate; + c.enabled=gate.value("enabled",c.enabled); + if(gate.contains("roi")){auto const& roi=gate.at("roi");if(!roi.is_array()||roi.size()!=4)throw std::runtime_error("brightness_gate.roi must contain four normalized coordinates");c.roi_x1=roi[0];c.roi_y1=roi[1];c.roi_x2=roi[2];c.roi_y2=roi[3];} + c.dark_pixel_luma=gate.value("dark_pixel_luma",c.dark_pixel_luma); + c.enter_median_luma=gate.value("enter_median_luma",c.enter_median_luma); + c.enter_dark_ratio=gate.value("enter_dark_ratio",c.enter_dark_ratio); + c.enter_dark_tile_ratio=gate.value("enter_dark_tile_ratio",c.enter_dark_tile_ratio); + c.exit_median_luma=gate.value("exit_median_luma",c.exit_median_luma); + c.exit_dark_ratio=gate.value("exit_dark_ratio",c.exit_dark_ratio); + c.exit_dark_tile_ratio=gate.value("exit_dark_tile_ratio",c.exit_dark_tile_ratio); + c.startup_frames=gate.value("startup_frames",c.startup_frames); + c.enter_frames=gate.value("enter_frames",c.enter_frames); + c.exit_frames=gate.value("exit_frames",c.exit_frames); + } if(j.contains("can")) { auto const& can=j.at("can");config.can.enabled=can.value("enabled",true); config.can.device_index=can.value("device_index",0);config.can.timeout_ms=can.value("timeout_ms",1000); diff --git a/src/inference_session.cpp b/src/inference_session.cpp index 1668e7f..3108f74 100644 --- a/src/inference_session.cpp +++ b/src/inference_session.cpp @@ -48,6 +48,12 @@ SessionWriter::SessionWriter(PipelineConfig const& c):directory_(c.record_sessio Json config={{"width",c.camera.width},{"height",c.camera.height},{"fps",c.camera.fps}, {"detector_confidence",c.detector_confidence},{"max_people",c.max_people}, {"warning_relative",c.warning_relative},{"release_relative",c.release_relative}, + {"brightness_gate",{{"enabled",c.brightness_gate.enabled},{"roi",{c.brightness_gate.roi_x1,c.brightness_gate.roi_y1,c.brightness_gate.roi_x2,c.brightness_gate.roi_y2}}, + {"dark_pixel_luma",c.brightness_gate.dark_pixel_luma},{"enter_median_luma",c.brightness_gate.enter_median_luma}, + {"enter_dark_ratio",c.brightness_gate.enter_dark_ratio},{"enter_dark_tile_ratio",c.brightness_gate.enter_dark_tile_ratio}, + {"exit_median_luma",c.brightness_gate.exit_median_luma},{"exit_dark_ratio",c.brightness_gate.exit_dark_ratio}, + {"exit_dark_tile_ratio",c.brightness_gate.exit_dark_tile_ratio},{"startup_frames",c.brightness_gate.startup_frames}, + {"enter_frames",c.brightness_gate.enter_frames},{"exit_frames",c.brightness_gate.exit_frames}}}, {"calibration_verified",c.door_mask_calibration_verified}, {"can",{{"enabled",c.can.enabled},{"device_index",c.can.device_index},{"timeout_ms",c.can.timeout_ms},{"termination",c.can.termination}}}}; Json models=Json::object(); @@ -109,6 +115,21 @@ SessionReader::SessionReader(PipelineConfig& c):directory_(c.replay_session) { c.camera.width=width_;c.camera.height=height_;c.camera.fps=j.at("fps"); c.detector_confidence=j.at("detector_confidence");c.max_people=j.at("max_people"); c.warning_relative=j.at("warning_relative");c.release_relative=j.at("release_relative"); + if(j.contains("brightness_gate")) { + auto const& gate=j.at("brightness_gate");auto& cgate=c.brightness_gate; + cgate.enabled=gate.value("enabled",cgate.enabled); + if(gate.contains("roi")){auto const& roi=gate.at("roi");cgate.roi_x1=roi[0];cgate.roi_y1=roi[1];cgate.roi_x2=roi[2];cgate.roi_y2=roi[3];} + cgate.dark_pixel_luma=gate.value("dark_pixel_luma",cgate.dark_pixel_luma); + cgate.enter_median_luma=gate.value("enter_median_luma",cgate.enter_median_luma); + cgate.enter_dark_ratio=gate.value("enter_dark_ratio",cgate.enter_dark_ratio); + cgate.enter_dark_tile_ratio=gate.value("enter_dark_tile_ratio",cgate.enter_dark_tile_ratio); + cgate.exit_median_luma=gate.value("exit_median_luma",cgate.exit_median_luma); + cgate.exit_dark_ratio=gate.value("exit_dark_ratio",cgate.exit_dark_ratio); + cgate.exit_dark_tile_ratio=gate.value("exit_dark_tile_ratio",cgate.exit_dark_tile_ratio); + cgate.startup_frames=gate.value("startup_frames",cgate.startup_frames); + cgate.enter_frames=gate.value("enter_frames",cgate.enter_frames); + cgate.exit_frames=gate.value("exit_frames",cgate.exit_frames); + } c.door_mask_calibration_verified=j.at("calibration_verified"); c.door_mask=directory_/"door_mask.json"; index_.open(directory_/"frames.txt"); diff --git a/src/main.cpp b/src/main.cpp index 9c5279c..9a140d3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -49,7 +49,7 @@ int main(int argc,char** argv)try { if(visualize) { auto display=std::getenv("DISPLAY");auto wayland=std::getenv("WAYLAND_DISPLAY"); if((!display||!*display)&&(!wayland||!*wayland))std::cerr<<"No display session; inference continues without visualization.\n"; - else try {cms::runLocalVisualization(pipeline);} + else try {cms::runLocalVisualization(pipeline,!replaySession.empty());} catch(std::exception const& e){std::cerr<<"Visualization unavailable; inference continues: "<risk_level); cv::polylines(canvas,std::vector>{hull},true,c,2,cv::LINE_AA); } - for(auto const& region:r.risk.regions)if(region.source=="YOLO_BODY_BOX_FALLBACK") { + if(drawText)for(auto const& region:r.risk.regions)if(region.source=="YOLO_BODY_BOX_FALLBACK") { auto it=std::find(r.track_ids.begin(),r.track_ids.end(),region.track_id); if(it!=r.track_ids.end()){auto const& b=r.boxes.at(it-r.track_ids.begin());cv::putText(canvas,"BODY FALLBACK",{int(b[0])+4,int(b[1])+50},cv::FONT_HERSHEY_SIMPLEX,.65,{0,160,255},2,cv::LINE_AA);} } - cv::rectangle(canvas,{0,0},{canvas.cols,148},{20,24,30},-1); + if(drawText) { + cv::rectangle(canvas,{0,0},{canvas.cols,94},{20,24,30},-1); std::ostringstream headline;headline<<"RISK ";if(r.risk.risk_score)headline< namespace cms { namespace { constexpr char windowName[]="CMS Camera Risk"; } -void runLocalVisualization(CameraPipeline const& pipeline) { +void runLocalVisualization(CameraPipeline const& pipeline,bool keepFinalFrame) { struct Window { Window(){cv::namedWindow(windowName,cv::WINDOW_NORMAL);cv::resizeWindow(windowName,1000,800);} ~Window(){try{if(cvGetWindowHandle(windowName))cv::destroyWindow(windowName);}catch(...) {}} } window; FrameResultPtr shown; auto nextRefresh=std::chrono::steady_clock::now(); - while(pipeline.running()) { + for(;;) { + bool running=pipeline.running(); + if(!running&&!keepFinalFrame)break; auto result=pipeline.latest(); + if(!running&&!result)break; auto now=std::chrono::steady_clock::now(); if(result!=shown||now>=nextRefresh) { auto can=pipeline.canStatus();cv::Mat image; - if(result)image=renderResult(*result,&can); + if(result)image=renderVisualization(*result,can); else { image=cv::Mat(800,1000,CV_8UC3,cv::Scalar(20,24,30)); cv::putText(image,"Waiting for inference...",{24,64},cv::FONT_HERSHEY_SIMPLEX,.8,{235,235,235},2); - renderCanOverlay(image,can); + cv::putText(image,can.status,{24,120},cv::FONT_HERSHEY_SIMPLEX,.6,{235,235,235},1); } cv::imshow(windowName,image);shown=std::move(result); nextRefresh=now+std::chrono::milliseconds(100);