From 2ee09e36cc70908ab67a32fb08524de65656d5a2 Mon Sep 17 00:00:00 2001 From: grabbit Date: Sat, 5 Apr 2025 13:40:46 +0800 Subject: [PATCH] fix opencv version aware --- Cargo.toml | 1 + build.rs | 189 +++++++++++++++++++++++++++ src/camera/v4l2.rs | 3 +- src/detection/brightness_detector.rs | 8 +- src/detection/feature_images.rs | 12 +- src/detection/frame_stacker.rs | 3 +- src/main.rs | 1 + src/utils/mod.rs | 2 + src/utils/opencv_compat.rs | 55 ++++++++ 9 files changed, 264 insertions(+), 10 deletions(-) create mode 100644 build.rs create mode 100644 src/utils/mod.rs create mode 100644 src/utils/opencv_compat.rs diff --git a/Cargo.toml b/Cargo.toml index a85cca1..b9bb642 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ description = "A Raspberry Pi based meteor detection system" [features] default = [] gpio = ["rppal", "embedded-hal"] # Feature to enable GPIO functionality +opencv-4-11-plus = [] # For OpenCV 4.11 and newer versions [dependencies] # Hardware interfaces diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..907f80e --- /dev/null +++ b/build.rs @@ -0,0 +1,189 @@ +use std::process::Command; +use std::env; + +fn main() { + // 通过包含的 OpenCV 库检测版本 + let opencv_version = detect_opencv_version(); + + println!("cargo:rustc-env=OPENCV_VERSION={}", opencv_version); + + // 解析版本 + let version_parts: Vec<&str> = opencv_version.split('.').collect(); + let major = version_parts.get(0).and_then(|s| s.parse::().ok()).unwrap_or(0); + let minor = version_parts.get(1).and_then(|s| s.parse::().ok()).unwrap_or(0); + + // 如果版本 >= 4.11.0,启用特性 + if major > 4 || (major == 4 && minor >= 11) { + println!("cargo:rustc-cfg=feature=\"opencv-4-11-plus\""); + println!("cargo:warning=Detected OpenCV {} >= 4.11.0, enabling opencv-4-11-plus feature", opencv_version); + } else { + println!("cargo:warning=Detected OpenCV {}, opencv-4-11-plus feature NOT enabled", opencv_version); + } +} + +fn detect_opencv_version() -> String { + // 方法 1: 尝试使用 pkg-config + if let Some(version) = try_pkg_config() { + return version; + } + + // 方法 2: 尝试使用 opencv_version 命令 + if let Some(version) = try_opencv_version_command() { + return version; + } + + // 方法 3: 尝试编译并运行一个小程序来检测版本 + if let Some(version) = try_compile_check() { + return version; + } + + // 方法 4: 检查 OpenCV 头文件 + if let Some(version) = try_header_check() { + return version; + } + + // 回退到默认版本 + let default_version = "4.0.0".to_string(); + println!("cargo:warning=Could not detect OpenCV version, assuming {}", default_version); + default_version +} + +fn try_pkg_config() -> Option { + // 尝试使用 pkg-config 来获取 OpenCV 版本 + let output = Command::new("pkg-config") + .args(["--modversion", "opencv4"]) + .output() + .ok()?; + + if output.status.success() { + let version = String::from_utf8(output.stdout).ok()?; + let version = version.trim().to_string(); + if !version.is_empty() { + return Some(version); + } + } + + // 尝试 opencv 包(旧版本) + let output = Command::new("pkg-config") + .args(["--modversion", "opencv"]) + .output() + .ok()?; + + if output.status.success() { + let version = String::from_utf8(output.stdout).ok()?; + let version = version.trim().to_string(); + if !version.is_empty() { + return Some(version); + } + } + + None +} + +fn try_opencv_version_command() -> Option { + // 尝试运行 opencv_version 命令 + let output = Command::new("opencv_version") + .output() + .ok()?; + + if output.status.success() { + let version = String::from_utf8(output.stdout).ok()?; + let version = version.trim().to_string(); + if !version.is_empty() { + return Some(version); + } + } + + None +} + +fn try_compile_check() -> Option { + // 创建一个临时目录 + let out_dir = env::var("OUT_DIR").ok()?; + let temp_file = format!("{}/version_check.cpp", out_dir); + let temp_exe = format!("{}/version_check", out_dir); + + // 写入一个简单的 C++ 程序来打印 OpenCV 版本 + std::fs::write(&temp_file, r#" + #include + #include + + int main() { + std::cout << CV_VERSION << std::endl; + return 0; + } + "#).ok()?; + + // 尝试编译 + let status = Command::new("c++") + .args([&temp_file, "-o", &temp_exe, "-lopencv_core"]) + .status() + .ok()?; + + if !status.success() { + return None; + } + + // 运行程序获取版本 + let output = Command::new(&temp_exe) + .output() + .ok()?; + + if output.status.success() { + let version = String::from_utf8(output.stdout).ok()?; + let version = version.trim().to_string(); + if !version.is_empty() { + return Some(version); + } + } + + None +} + +fn try_header_check() -> Option { + // 尝试在常见位置查找 OpenCV 头文件 + let paths = [ + "/usr/include/opencv4/opencv2/core/version.hpp", + "/usr/local/include/opencv4/opencv2/core/version.hpp", + "/usr/include/opencv2/core/version.hpp", + "/usr/local/include/opencv2/core/version.hpp", + "/opt/homebrew/include/opencv4/opencv2/core/version.hpp", + "/opt/homebrew/include/opencv2/core/version.hpp", + ]; + + for path in paths.iter() { + if let Ok(content) = std::fs::read_to_string(path) { + // 查找版本定义 #define CV_VERSION_MAJOR 4 + let mut major = None; + let mut minor = None; + let mut patch = None; + + for line in content.lines() { + if let Some(value) = extract_define(line, "CV_VERSION_MAJOR") { + major = Some(value); + } else if let Some(value) = extract_define(line, "CV_VERSION_MINOR") { + minor = Some(value); + } else if let Some(value) = extract_define(line, "CV_VERSION_REVISION") { + patch = Some(value); + } + + if major.is_some() && minor.is_some() && patch.is_some() { + return Some(format!("{}.{}.{}", major.unwrap(), minor.unwrap(), patch.unwrap())); + } + } + } + } + + None +} + +fn extract_define(line: &str, define_name: &str) -> Option { + let line = line.trim(); + if line.starts_with("#define") && line.contains(define_name) { + let parts: Vec<&str> = line.split_whitespace().collect(); + if parts.len() >= 3 && parts[1] == define_name { + return Some(parts[2].to_string()); + } + } + None +} diff --git a/src/camera/v4l2.rs b/src/camera/v4l2.rs index 865ac4a..b15f88f 100644 --- a/src/camera/v4l2.rs +++ b/src/camera/v4l2.rs @@ -8,6 +8,7 @@ // use v4l::{Format, FourCC}; // use opencv::{core, imgproc, prelude::*}; +// use crate::utils::opencv_compat::convert_color; // use crate::camera::{ExposureMode, Resolution}; @@ -263,7 +264,7 @@ // // Convert YUYV to BGR // let mut bgr = core::Mat::default()?; -// imgproc::cvt_color(&yuyv, &mut bgr, imgproc::COLOR_YUV2BGR_YUYV, 0)?; +// convert_color(&yuyv, &mut bgr, imgproc::COLOR_YUV2BGR_YUYV, 0)?; // bgr // }, diff --git a/src/detection/brightness_detector.rs b/src/detection/brightness_detector.rs index b56b7bc..beb549e 100644 --- a/src/detection/brightness_detector.rs +++ b/src/detection/brightness_detector.rs @@ -4,6 +4,8 @@ use opencv::{core, imgproc, prelude::*}; use serde::{Deserialize, Serialize}; use uuid::Uuid; +use crate::utils::opencv_compat::convert_color; + use crate::config::Config; use crate::detection::{DetectionResult, DetectorConfig, MeteorDetector}; @@ -87,7 +89,7 @@ impl BrightnessDetector { fn update_background(&mut self, frame: &core::Mat) -> Result<()> { // Convert frame to grayscale let mut gray = core::Mat::default(); - imgproc::cvt_color(frame, &mut gray, imgproc::COLOR_BGR2GRAY, 0)?; + convert_color(frame, &mut gray, imgproc::COLOR_BGR2GRAY, 0)?; match &mut self.background { Some(bg) => { // Gradually update background model (running average) @@ -111,7 +113,7 @@ impl BrightnessDetector { fn compute_difference(&mut self, frame: &core::Mat) -> Result { // Convert frame to grayscale let mut gray = core::Mat::default(); - imgproc::cvt_color(frame, &mut gray, imgproc::COLOR_BGR2GRAY, 0)?; + convert_color(frame, &mut gray, imgproc::COLOR_BGR2GRAY, 0)?; // Calculate absolute difference from background let mut diff = core::Mat::default(); @@ -160,7 +162,7 @@ impl BrightnessDetector { fn calculate_brightness(&self, frame: &core::Mat) -> Result { // Convert to grayscale let mut gray = core::Mat::default(); - imgproc::cvt_color(frame, &mut gray, imgproc::COLOR_BGR2GRAY, 0)?; + convert_color(frame, &mut gray, imgproc::COLOR_BGR2GRAY, 0)?; // Calculate mean brightness let mean = core::mean(&gray, &core::no_array())?; diff --git a/src/detection/feature_images.rs b/src/detection/feature_images.rs index 078485a..8be4315 100644 --- a/src/detection/feature_images.rs +++ b/src/detection/feature_images.rs @@ -1,9 +1,11 @@ use anyhow::{Context, Result}; use chrono::{DateTime, Utc}; use log::{debug, info}; -use opencv::{core, imgcodecs, prelude::*}; +use opencv::{core, imgcodecs, imgproc, prelude::*}; use std::path::Path; +use crate::utils::opencv_compat::convert_color; + /// CAMS FTP format feature images /// Used for meteor detection analysis #[derive(Debug, Clone)] @@ -147,10 +149,10 @@ impl FeatureImages { let mut stdpixel_color = core::Mat::default(); let mut maxframe_color = core::Mat::default(); - opencv::imgproc::cvt_color(&self.maxpixel, &mut maxpixel_color, opencv::imgproc::COLOR_GRAY2BGR, 0)?; - opencv::imgproc::cvt_color(&self.avepixel, &mut avepixel_color, opencv::imgproc::COLOR_GRAY2BGR, 0)?; - opencv::imgproc::cvt_color(&self.stdpixel, &mut stdpixel_color, opencv::imgproc::COLOR_GRAY2BGR, 0)?; - opencv::imgproc::cvt_color(&self.maxframe, &mut maxframe_color, opencv::imgproc::COLOR_GRAY2BGR, 0)?; + convert_color(&self.maxpixel, &mut maxpixel_color, imgproc::COLOR_GRAY2BGR, 0)?; + convert_color(&self.avepixel, &mut avepixel_color, imgproc::COLOR_GRAY2BGR, 0)?; + convert_color(&self.stdpixel, &mut stdpixel_color, imgproc::COLOR_GRAY2BGR, 0)?; + convert_color(&self.maxframe, &mut maxframe_color, imgproc::COLOR_GRAY2BGR, 0)?; // Create a region of interest for each quadrant let roi_top_left = core::Rect::new(0, 0, width, height); diff --git a/src/detection/frame_stacker.rs b/src/detection/frame_stacker.rs index cd341e5..7c23815 100644 --- a/src/detection/frame_stacker.rs +++ b/src/detection/frame_stacker.rs @@ -7,6 +7,7 @@ use opencv::{ imgproc, types, }; +use crate::utils::opencv_compat::convert_color; use serde::{Deserialize, Serialize}; use std::collections::VecDeque; use std::path::PathBuf; @@ -287,7 +288,7 @@ impl FrameStacker { // Convert to grayscale if needed let gray_frame = if frame.mat.channels() != 1 { let mut gray = core::Mat::default(); - imgproc::cvt_color(&frame.mat, &mut gray, imgproc::COLOR_BGR2GRAY, 0)?; + convert_color(&frame.mat, &mut gray, imgproc::COLOR_BGR2GRAY, 0)?; gray } else { frame.mat.clone() diff --git a/src/main.rs b/src/main.rs index 805a8df..b9492c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,6 +16,7 @@ mod storage; mod streaming; mod communication; mod monitoring; +mod utils; use anyhow::{Context, Result}; use log::{info, error, warn}; diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 0000000..a5feef9 --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1,2 @@ +// Utilities for working with OpenCV across different versions +pub mod opencv_compat; diff --git a/src/utils/opencv_compat.rs b/src/utils/opencv_compat.rs new file mode 100644 index 0000000..b914bdf --- /dev/null +++ b/src/utils/opencv_compat.rs @@ -0,0 +1,55 @@ +use anyhow::Result; +use opencv::{core, imgproc}; +use opencv::core::AlgorithmHint::ALGO_HINT_APPROX; +use opencv::prelude::*; + +/// Convert an image from one color space to another, with compatibility across OpenCV versions +/// +/// This function wraps imgproc::cvt_color with version-specific adaptations +/// +/// # Arguments +/// * `src` - Source image +/// * `dst` - Destination image +/// * `code` - Color conversion code (e.g., COLOR_BGR2GRAY) +/// * `dstCn` - Number of channels in the destination image (0 means auto) +/// +/// # Returns +/// * `Result<()>` - Success or error +#[cfg(feature = "opencv-4-11-plus")] +pub fn convert_color( + src: &core::Mat, + dst: &mut core::Mat, + code: i32, + dst_cn: i32, +) -> Result<(), opencv::Error> { + // OpenCV 4.11+ version with ALGO_HINT_APPROX parameter + imgproc::cvt_color(src, dst, code, dst_cn, ALGO_HINT_APPROX) +} + +/// Convert an image from one color space to another, with compatibility across OpenCV versions +/// +/// This function wraps imgproc::cvt_color with version-specific adaptations +/// +/// # Arguments +/// * `src` - Source image +/// * `dst` - Destination image +/// * `code` - Color conversion code (e.g., COLOR_BGR2GRAY) +/// * `dstCn` - Number of channels in the destination image (0 means auto) +/// +/// # Returns +/// * `Result<()>` - Success or error +#[cfg(not(feature = "opencv-4-11-plus"))] +pub fn convert_color( + src: &core::Mat, + dst: &mut core::Mat, + code: i32, + dst_cn: i32, +) -> Result<(), opencv::Error> { + // OpenCV 4.10 and earlier version without ALGO_HINT_APPROX parameter + imgproc::cvt_color(src, dst, code, dst_cn) +} + +/// Get the OpenCV version string from environment +pub fn get_opencv_version_string() -> String { + option_env!("OPENCV_VERSION").unwrap_or("unknown").to_string() +}