// ============================================================================ // CvLoader.cpp — OpenCV DLL discovery and pre-loading // ============================================================================ #include "CvLoader.h" #include "DynLibUtils.h" #include #include using namespace ANSCENTER::DynLib; // ───────────────────────────────────────────────────────────────────────────── // Static member definitions // ───────────────────────────────────────────────────────────────────────────── std::mutex CvLoader::s_mutex; bool CvLoader::s_initialized = false; CvInfo CvLoader::s_info; LibHandle CvLoader::s_hCvWorld = nullptr; LibHandle CvLoader::s_hCvImgHash = nullptr; // ───────────────────────────────────────────────────────────────────────────── // Helper — extract version string from version code (e.g. 4130 → "4.13.0") // ───────────────────────────────────────────────────────────────────────────── static std::string VersionFromCode(int code) { // OpenCV version encoding: XYZZ where X=major, Y=minor, ZZ=patch // 4130 → major=4, minor=13, patch=0 // 4100 → major=4, minor=10, patch=0 // 490 → major=4, minor=9, patch=0 int major = code / 1000; int minor = (code / 10) % 100; int patch = code % 10; return std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(patch); } // ───────────────────────────────────────────────────────────────────────────── // Helper — extract version code from DLL filename // "opencv_world4130.dll" → 4130 // "opencv_world4100.dll" → 4100 // "libopencv_world.so.413"→ 413 (Linux) // ───────────────────────────────────────────────────────────────────────────── static int ExtractVersionCode(const std::string& path) { #ifdef _WIN32 // Windows: opencv_world.dll — digits between "opencv_world" and ".dll" const std::string marker = "opencv_world"; size_t pos = path.find(marker); if (pos == std::string::npos) return 0; pos += marker.size(); size_t dot = path.find('.', pos); if (dot == std::string::npos) return 0; try { return std::stoi(path.substr(pos, dot - pos)); } catch (...) { return 0; } #else // Linux: libopencv_world.so. size_t dot = path.find_last_of('.'); if (dot == std::string::npos || dot + 1 >= path.size()) return 0; try { return std::stoi(path.substr(dot + 1)); } catch (...) { return 0; } #endif } // ───────────────────────────────────────────────────────────────────────────── // Candidate lists — search ANSCENTER shared dir first, then system PATH // ───────────────────────────────────────────────────────────────────────────── std::vector CvLoader::CvWorldCandidates(const std::string& shared_dir) { std::vector v; // Versioned candidates, newest first (covers OpenCV 4.5 through 4.20+) // Version codes: 4200, 4190, 4180, ..., 4130, ..., 4100, ..., 450 static const int versions[] = { 4200, 4190, 4180, 4170, 4160, 4150, 4140, 4130, 4120, 4110, 4100, 490, 480, 470, 460, 450 }; // ── Priority 1: ANSCENTER shared directory ──────────────────────────────── for (int ver : versions) { #ifdef _WIN32 v.push_back(JoinPath(shared_dir, "opencv_world" + std::to_string(ver) + ".dll")); #else v.push_back(JoinPath(shared_dir, "libopencv_world.so." + std::to_string(ver))); #endif } // ── Priority 2: System PATH / LD_LIBRARY_PATH ───────────────────────────── for (int ver : versions) { #ifdef _WIN32 v.push_back("opencv_world" + std::to_string(ver) + ".dll"); #else v.push_back("libopencv_world.so." + std::to_string(ver)); #endif } return v; } std::vector CvLoader::CvImgHashCandidates(const std::string& shared_dir) { std::vector v; static const int versions[] = { 4200, 4190, 4180, 4170, 4160, 4150, 4140, 4130, 4120, 4110, 4100, 490, 480, 470, 460, 450 }; // ── Priority 1: ANSCENTER shared directory ──────────────────────────────── for (int ver : versions) { #ifdef _WIN32 v.push_back(JoinPath(shared_dir, "opencv_img_hash" + std::to_string(ver) + ".dll")); #else v.push_back(JoinPath(shared_dir, "libopencv_img_hash.so." + std::to_string(ver))); #endif } // ── Priority 2: System PATH / LD_LIBRARY_PATH ───────────────────────────── for (int ver : versions) { #ifdef _WIN32 v.push_back("opencv_img_hash" + std::to_string(ver) + ".dll"); #else v.push_back("libopencv_img_hash.so." + std::to_string(ver)); #endif } return v; } // ───────────────────────────────────────────────────────────────────────────── // Initialize // ───────────────────────────────────────────────────────────────────────────── const CvInfo& CvLoader::Initialize(const std::string& shared_dir, bool verbose) { std::lock_guard lock(s_mutex); if (s_initialized) return s_info; // Clean up any partially-loaded handles from a previous failed attempt if (s_hCvWorld) { FreeLib(s_hCvWorld); s_hCvWorld = nullptr; } if (s_hCvImgHash) { FreeLib(s_hCvImgHash); s_hCvImgHash = nullptr; } if (verbose) { std::cout << "==================================================" << std::endl; std::cout << "[CvLoader] Discovering OpenCV libraries..." << std::endl; std::cout << " Shared dir : " << shared_dir << std::endl; std::cout << "==================================================" << std::endl; } // ── STEP 1: Inject shared dir into search path ─────────────────────────── // This ensures dependent DLLs (e.g. opencv_videoio_ffmpeg) can also be // found when they are co-located with opencv_world in the shared dir. InjectDllSearchPath(shared_dir); // ── STEP 2: Pre-load opencv_world ──────────────────────────────────────── std::string worldPath; s_hCvWorld = FindAndLoad(CvWorldCandidates(shared_dir), worldPath, verbose); if (s_hCvWorld) { s_info.dllPath = worldPath; s_info.versionCode = ExtractVersionCode(worldPath); s_info.version = VersionFromCode(s_info.versionCode); s_info.loaded = true; if (verbose) { std::cout << "[CvLoader] OpenCV version = " << s_info.version << " (code " << s_info.versionCode << ")" << std::endl; } } else { std::cout << "[CvLoader] WARNING: No opencv_world DLL found in " << shared_dir << " or system PATH." << std::endl; std::cout << "[CvLoader] OpenCV will rely on import-lib resolution at load time." << std::endl; } // ── STEP 3: Pre-load companion modules ─────────────────────────────────── // These are optional — missing is a warning, not a failure. std::string imgHashPath; s_hCvImgHash = FindAndLoad(CvImgHashCandidates(shared_dir), imgHashPath, verbose); if (!s_hCvImgHash && verbose) { std::cout << "[CvLoader] opencv_img_hash not found (optional)." << std::endl; } s_initialized = true; if (verbose) { std::cout << "[CvLoader] " << (s_info.loaded ? "initialised successfully." : "WARNING — opencv_world not pre-loaded.") << std::endl; std::cout << "==================================================" << std::endl; } return s_info; } // ───────────────────────────────────────────────────────────────────────────── // Shutdown // ───────────────────────────────────────────────────────────────────────────── void CvLoader::Shutdown() { std::lock_guard lock(s_mutex); // Release in reverse order if (s_hCvImgHash) { FreeLib(s_hCvImgHash); s_hCvImgHash = nullptr; } if (s_hCvWorld) { FreeLib(s_hCvWorld); s_hCvWorld = nullptr; } s_info = CvInfo{}; s_initialized = false; }