Files
ANSCORE/ANSLibsLoader/CvLoader.cpp

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;
}