212 lines
10 KiB
C++
212 lines
10 KiB
C++
// ============================================================================
|
|
// CvLoader.cpp — OpenCV DLL discovery and pre-loading
|
|
// ============================================================================
|
|
|
|
#include "CvLoader.h"
|
|
#include "DynLibUtils.h"
|
|
|
|
#include <iostream>
|
|
#include <mutex>
|
|
|
|
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<XYZZ>.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.<digits>
|
|
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<std::string> CvLoader::CvWorldCandidates(const std::string& shared_dir)
|
|
{
|
|
std::vector<std::string> 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<std::string> CvLoader::CvImgHashCandidates(const std::string& shared_dir)
|
|
{
|
|
std::vector<std::string> 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<std::mutex> 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<std::mutex> 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;
|
|
}
|