fix opencv version aware

This commit is contained in:
grabbit 2025-04-05 13:40:46 +08:00
parent 1bfe180153
commit 2ee09e36cc
9 changed files with 264 additions and 10 deletions

View File

@ -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

189
build.rs Normal file
View File

@ -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::<u32>().ok()).unwrap_or(0);
let minor = version_parts.get(1).and_then(|s| s.parse::<u32>().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<String> {
// 尝试使用 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<String> {
// 尝试运行 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<String> {
// 创建一个临时目录
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 <opencv2/core.hpp>
#include <iostream>
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<String> {
// 尝试在常见位置查找 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<String> {
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
}

View File

@ -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
// },

View File

@ -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<core::Mat> {
// 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<f32> {
// 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())?;

View File

@ -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);

View File

@ -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()

View File

@ -16,6 +16,7 @@ mod storage;
mod streaming;
mod communication;
mod monitoring;
mod utils;
use anyhow::{Context, Result};
use log::{info, error, warn};

2
src/utils/mod.rs Normal file
View File

@ -0,0 +1,2 @@
// Utilities for working with OpenCV across different versions
pub mod opencv_compat;

View File

@ -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()
}