1// This file is part of OpenCV project.
2// It is subject to the license terms in the LICENSE file found in the top-level directory
3// of this distribution and at http://opencv.org/license.html.
4
5#include "../precomp.hpp"
6
7#include "opencv_data_config.hpp"
8
9#include <vector>
10#include <fstream>
11
12#include <opencv2/core/utils/logger.defines.hpp>
13#undef CV_LOG_STRIP_LEVEL
14#define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_VERBOSE + 1
15#include "opencv2/core/utils/logger.hpp"
16#include "opencv2/core/utils/filesystem.hpp"
17
18#include <opencv2/core/utils/configuration.private.hpp>
19#include "opencv2/core/utils/filesystem.private.hpp"
20
21#ifdef _WIN32
22#define WIN32_LEAN_AND_MEAN
23#include <windows.h>
24#undef small
25#undef min
26#undef max
27#undef abs
28#elif defined(__linux__)
29#include <dlfcn.h> // requires -ldl
30#elif defined(__APPLE__)
31#include <TargetConditionals.h>
32#if TARGET_OS_MAC
33#include <dlfcn.h>
34#endif
35#endif
36
37namespace cv { namespace utils {
38
39static cv::Ptr< std::vector<cv::String> > g_data_search_path;
40static cv::Ptr< std::vector<cv::String> > g_data_search_subdir;
41
42static std::vector<cv::String>& _getDataSearchPath()
43{
44 if (g_data_search_path.empty())
45 g_data_search_path.reset(ptr: new std::vector<cv::String>());
46 return *(g_data_search_path.get());
47}
48
49static std::vector<cv::String>& _getDataSearchSubDirectory()
50{
51 if (g_data_search_subdir.empty())
52 {
53 g_data_search_subdir.reset(ptr: new std::vector<cv::String>());
54 g_data_search_subdir->push_back(x: "data");
55 g_data_search_subdir->push_back(x: "");
56 }
57 return *(g_data_search_subdir.get());
58}
59
60
61CV_EXPORTS void addDataSearchPath(const cv::String& path)
62{
63 if (utils::fs::isDirectory(path))
64 _getDataSearchPath().push_back(x: path);
65}
66CV_EXPORTS void addDataSearchSubDirectory(const cv::String& subdir)
67{
68 _getDataSearchSubDirectory().push_back(x: subdir);
69}
70
71#if OPENCV_HAVE_FILESYSTEM_SUPPORT
72static bool isPathSep(char c)
73{
74 return c == '/' || c == '\\';
75}
76static bool isSubDirectory_(const cv::String& base_path, const cv::String& path)
77{
78 size_t N = base_path.size();
79 if (N == 0)
80 return false;
81 if (isPathSep(c: base_path[N - 1]))
82 N--;
83 if (path.size() < N)
84 return false;
85 for (size_t i = 0; i < N; i++)
86 {
87 if (path[i] == base_path[i])
88 continue;
89 if (isPathSep(c: path[i]) && isPathSep(c: base_path[i]))
90 continue;
91 return false;
92 }
93 size_t M = path.size();
94 if (M > N)
95 {
96 if (!isPathSep(c: path[N]))
97 return false;
98 }
99 return true;
100}
101
102static bool isSubDirectory(const cv::String& base_path, const cv::String& path)
103{
104 bool res = isSubDirectory_(base_path, path);
105 CV_LOG_VERBOSE(NULL, 0, "isSubDirectory(): base: " << base_path << " path: " << path << " => result: " << (res ? "TRUE" : "FALSE"));
106 return res;
107}
108#endif //OPENCV_HAVE_FILESYSTEM_SUPPORT
109
110static cv::String getModuleLocation(const void* addr)
111{
112 CV_UNUSED(addr);
113#ifdef _WIN32
114 HMODULE m = 0;
115#if _WIN32_WINNT >= 0x0501 && (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
116 ::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
117 reinterpret_cast<LPCTSTR>(addr),
118 &m);
119#endif
120 if (m)
121 {
122 TCHAR path[MAX_PATH];
123 const size_t path_size = sizeof(path) / sizeof(*path);
124 size_t sz = GetModuleFileName(m, path, path_size);
125 if (sz > 0 && sz < path_size)
126 {
127 path[sz] = TCHAR('\0');
128#ifdef _UNICODE
129 char char_path[MAX_PATH];
130 size_t copied = wcstombs(char_path, path, MAX_PATH);
131 CV_Assert((copied != MAX_PATH) && (copied != (size_t)-1));
132 return cv::String(char_path);
133#else
134 return cv::String(path);
135#endif
136 }
137 }
138#elif defined(__linux__)
139 Dl_info info;
140 if (0 != dladdr(address: addr, info: &info))
141 {
142 return cv::String(info.dli_fname);
143 }
144#elif defined(__APPLE__)
145# if TARGET_OS_MAC
146 Dl_info info;
147 if (0 != dladdr(addr, &info))
148 {
149 return cv::String(info.dli_fname);
150 }
151# endif
152#else
153 // not supported, skip
154#endif
155 return cv::String();
156}
157
158bool getBinLocation(std::string& dst)
159{
160 dst = getModuleLocation(addr: (void*)getModuleLocation); // using code address, doesn't work with static linkage!
161 return !dst.empty();
162}
163
164#ifdef _WIN32
165bool getBinLocation(std::wstring& dst)
166{
167 void* addr = (void*)getModuleLocation; // using code address, doesn't work with static linkage!
168 HMODULE m = 0;
169#if _WIN32_WINNT >= 0x0501 && (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
170 ::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
171 reinterpret_cast<LPCTSTR>(addr),
172 &m);
173#endif
174 if (m)
175 {
176 wchar_t path[4096];
177 const size_t path_size = sizeof(path)/sizeof(*path);
178 size_t sz = GetModuleFileNameW(m, path, path_size);
179 if (sz > 0 && sz < path_size)
180 {
181 path[sz] = '\0';
182 dst.assign(path, sz);
183 return true;
184 }
185 }
186 return false;
187}
188#endif
189
190cv::String findDataFile(const cv::String& relative_path,
191 const char* configuration_parameter,
192 const std::vector<String>* search_paths,
193 const std::vector<String>* subdir_paths)
194{
195#if OPENCV_HAVE_FILESYSTEM_SUPPORT
196 configuration_parameter = configuration_parameter ? configuration_parameter : "OPENCV_DATA_PATH";
197 CV_LOG_DEBUG(NULL, cv::format("utils::findDataFile('%s', %s)", relative_path.c_str(), configuration_parameter));
198
199#define TRY_FILE_WITH_PREFIX(prefix) \
200{ \
201 cv::String path = utils::fs::join(prefix, relative_path); \
202 CV_LOG_DEBUG(NULL, cv::format("... Line %d: trying open '%s'", __LINE__, path.c_str())); \
203 FILE* f = fopen(path.c_str(), "rb"); \
204 if(f) { \
205 fclose(f); \
206 return path; \
207 } \
208}
209
210
211 // Step 0: check current directory or absolute path at first
212 TRY_FILE_WITH_PREFIX("");
213
214
215 // Step 1
216 const std::vector<cv::String>& search_path = search_paths ? *search_paths : _getDataSearchPath();
217 for(size_t i = search_path.size(); i > 0; i--)
218 {
219 const cv::String& prefix = search_path[i - 1];
220 TRY_FILE_WITH_PREFIX(prefix);
221 }
222
223 const std::vector<cv::String>& search_subdir = subdir_paths ? *subdir_paths : _getDataSearchSubDirectory();
224
225
226 // Step 2
227 const cv::String configuration_parameter_s(configuration_parameter ? configuration_parameter : "");
228 const cv::utils::Paths& search_hint = configuration_parameter_s.empty() ? cv::utils::Paths()
229 : getConfigurationParameterPaths(name: (configuration_parameter_s + "_HINT").c_str());
230 for (size_t k = 0; k < search_hint.size(); k++)
231 {
232 cv::String datapath = search_hint[k];
233 if (datapath.empty())
234 continue;
235 if (utils::fs::isDirectory(path: datapath))
236 {
237 CV_LOG_DEBUG(NULL, "utils::findDataFile(): trying " << configuration_parameter << "_HINT=" << datapath);
238 for(size_t i = search_subdir.size(); i > 0; i--)
239 {
240 const cv::String& subdir = search_subdir[i - 1];
241 cv::String prefix = utils::fs::join(base: datapath, path: subdir);
242 TRY_FILE_WITH_PREFIX(prefix);
243 }
244 }
245 else
246 {
247 CV_LOG_WARNING(NULL, configuration_parameter << "_HINT is specified but it is not a directory: " << datapath);
248 }
249 }
250
251
252 // Step 3
253 const cv::utils::Paths& override_paths = configuration_parameter_s.empty() ? cv::utils::Paths()
254 : getConfigurationParameterPaths(name: configuration_parameter);
255 for (size_t k = 0; k < override_paths.size(); k++)
256 {
257 cv::String datapath = override_paths[k];
258 if (datapath.empty())
259 continue;
260 if (utils::fs::isDirectory(path: datapath))
261 {
262 CV_LOG_DEBUG(NULL, "utils::findDataFile(): trying " << configuration_parameter << "=" << datapath);
263 for(size_t i = search_subdir.size(); i > 0; i--)
264 {
265 const cv::String& subdir = search_subdir[i - 1];
266 cv::String prefix = utils::fs::join(base: datapath, path: subdir);
267 TRY_FILE_WITH_PREFIX(prefix);
268 }
269 }
270 else
271 {
272 CV_LOG_WARNING(NULL, configuration_parameter << " is specified but it is not a directory: " << datapath);
273 }
274 }
275 if (!override_paths.empty())
276 {
277 CV_LOG_INFO(NULL, "utils::findDataFile(): can't find data file via " << configuration_parameter << " configuration override: " << relative_path);
278 return cv::String();
279 }
280
281
282 // Steps: 4, 5, 6
283 cv::String cwd = utils::fs::getcwd();
284 cv::String build_dir(OPENCV_BUILD_DIR);
285 bool has_tested_build_directory = false;
286 if (isSubDirectory(base_path: build_dir, path: cwd) || isSubDirectory(base_path: utils::fs::canonical(path: build_dir), path: utils::fs::canonical(path: cwd)))
287 {
288 CV_LOG_DEBUG(NULL, "utils::findDataFile(): the current directory is build sub-directory: " << cwd);
289 const char* build_subdirs[] = { OPENCV_DATA_BUILD_DIR_SEARCH_PATHS };
290 for (size_t k = 0; k < sizeof(build_subdirs)/sizeof(build_subdirs[0]); k++)
291 {
292 CV_LOG_DEBUG(NULL, "utils::findDataFile(): <build>/" << build_subdirs[k]);
293 cv::String datapath = utils::fs::join(base: build_dir, path: build_subdirs[k]);
294 if (utils::fs::isDirectory(path: datapath))
295 {
296 for(size_t i = search_subdir.size(); i > 0; i--)
297 {
298 const cv::String& subdir = search_subdir[i - 1];
299 cv::String prefix = utils::fs::join(base: datapath, path: subdir);
300 TRY_FILE_WITH_PREFIX(prefix);
301 }
302 }
303 }
304 has_tested_build_directory = true;
305 }
306
307 cv::String source_dir;
308 cv::String try_source_dir = cwd;
309 for (int levels = 0; levels < 3; ++levels)
310 {
311 if (utils::fs::exists(path: utils::fs::join(base: try_source_dir, path: "modules/core/include/opencv2/core/version.hpp")))
312 {
313 source_dir = try_source_dir;
314 break;
315 }
316 try_source_dir = utils::fs::join(base: try_source_dir, path: "/..");
317 }
318 if (!source_dir.empty())
319 {
320 CV_LOG_DEBUG(NULL, "utils::findDataFile(): the current directory is source sub-directory: " << source_dir);
321 CV_LOG_DEBUG(NULL, "utils::findDataFile(): <source>" << source_dir);
322 cv::String datapath = source_dir;
323 if (utils::fs::isDirectory(path: datapath))
324 {
325 for(size_t i = search_subdir.size(); i > 0; i--)
326 {
327 const cv::String& subdir = search_subdir[i - 1];
328 cv::String prefix = utils::fs::join(base: datapath, path: subdir);
329 TRY_FILE_WITH_PREFIX(prefix);
330 }
331 }
332 }
333
334 cv::String module_path;
335 if (getBinLocation(dst&: module_path))
336 {
337 CV_LOG_DEBUG(NULL, "Detected module path: '" << module_path << '\'');
338 }
339 else
340 {
341 CV_LOG_INFO(NULL, "Can't detect module binaries location");
342 }
343
344 if (!has_tested_build_directory &&
345 (isSubDirectory(base_path: build_dir, path: module_path) || isSubDirectory(base_path: utils::fs::canonical(path: build_dir), path: utils::fs::canonical(path: module_path)))
346 )
347 {
348 CV_LOG_DEBUG(NULL, "utils::findDataFile(): the binary module directory is build sub-directory: " << module_path);
349 const char* build_subdirs[] = { OPENCV_DATA_BUILD_DIR_SEARCH_PATHS };
350 for (size_t k = 0; k < sizeof(build_subdirs)/sizeof(build_subdirs[0]); k++)
351 {
352 CV_LOG_DEBUG(NULL, "utils::findDataFile(): <build>/" << build_subdirs[k]);
353 cv::String datapath = utils::fs::join(base: build_dir, path: build_subdirs[k]);
354 if (utils::fs::isDirectory(path: datapath))
355 {
356 for(size_t i = search_subdir.size(); i > 0; i--)
357 {
358 const cv::String& subdir = search_subdir[i - 1];
359 cv::String prefix = utils::fs::join(base: datapath, path: subdir);
360 TRY_FILE_WITH_PREFIX(prefix);
361 }
362 }
363 }
364 }
365
366#if defined OPENCV_INSTALL_DATA_DIR_RELATIVE
367 if (!module_path.empty()) // require module path
368 {
369 size_t pos = module_path.rfind(c: '/');
370 if (pos == cv::String::npos)
371 pos = module_path.rfind(c: '\\');
372 cv::String module_dir = (pos == cv::String::npos) ? module_path : module_path.substr(pos: 0, n: pos);
373 const char* install_subdirs[] = { OPENCV_INSTALL_DATA_DIR_RELATIVE };
374 for (size_t k = 0; k < sizeof(install_subdirs)/sizeof(install_subdirs[0]); k++)
375 {
376 cv::String datapath = utils::fs::join(base: module_dir, path: install_subdirs[k]);
377 CV_LOG_DEBUG(NULL, "utils::findDataFile(): trying install path (from binary path): " << datapath);
378 if (utils::fs::isDirectory(path: datapath))
379 {
380 for(size_t i = search_subdir.size(); i > 0; i--)
381 {
382 const cv::String& subdir = search_subdir[i - 1];
383 cv::String prefix = utils::fs::join(base: datapath, path: subdir);
384 TRY_FILE_WITH_PREFIX(prefix);
385 }
386 }
387 else
388 {
389 CV_LOG_DEBUG(NULL, "utils::findDataFile(): ... skip, not a valid directory: " << datapath);
390 }
391 }
392 }
393#endif
394
395#if defined OPENCV_INSTALL_PREFIX && defined OPENCV_DATA_INSTALL_PATH
396 cv::String install_dir(OPENCV_INSTALL_PREFIX);
397 // use core/world module path and verify that library is running from installation directory
398 // It is necessary to avoid touching of unrelated common /usr/local path
399 if (module_path.empty()) // can't determine
400 module_path = install_dir;
401 if (isSubDirectory(base_path: install_dir, path: module_path) || isSubDirectory(base_path: utils::fs::canonical(path: install_dir), path: utils::fs::canonical(path: module_path)))
402 {
403 cv::String datapath = utils::fs::join(base: install_dir, OPENCV_DATA_INSTALL_PATH);
404 if (utils::fs::isDirectory(path: datapath))
405 {
406 CV_LOG_DEBUG(NULL, "utils::findDataFile(): trying install path: " << datapath);
407 for(size_t i = search_subdir.size(); i > 0; i--)
408 {
409 const cv::String& subdir = search_subdir[i - 1];
410 cv::String prefix = utils::fs::join(base: datapath, path: subdir);
411 TRY_FILE_WITH_PREFIX(prefix);
412 }
413 }
414 }
415#endif
416
417 return cv::String(); // not found
418#else // OPENCV_HAVE_FILESYSTEM_SUPPORT
419 CV_UNUSED(relative_path);
420 CV_UNUSED(configuration_parameter);
421 CV_UNUSED(search_paths);
422 CV_UNUSED(subdir_paths);
423 CV_Error(Error::StsNotImplemented, "File system support is disabled in this OpenCV build!");
424#endif // OPENCV_HAVE_FILESYSTEM_SUPPORT
425}
426
427cv::String findDataFile(const cv::String& relative_path, bool required, const char* configuration_parameter)
428{
429#if OPENCV_HAVE_FILESYSTEM_SUPPORT
430 CV_LOG_DEBUG(NULL, cv::format("cv::utils::findDataFile('%s', %s, %s)",
431 relative_path.c_str(), required ? "true" : "false",
432 configuration_parameter ? configuration_parameter : "NULL"));
433 cv::String result = cv::utils::findDataFile(relative_path,
434 configuration_parameter,
435 NULL,
436 NULL);
437 if (result.empty() && required)
438 CV_Error(cv::Error::StsError, cv::format("OpenCV: Can't find required data file: %s", relative_path.c_str()));
439 return result;
440#else // OPENCV_HAVE_FILESYSTEM_SUPPORT
441 CV_UNUSED(relative_path);
442 CV_UNUSED(required);
443 CV_UNUSED(configuration_parameter);
444 CV_Error(Error::StsNotImplemented, "File system support is disabled in this OpenCV build!");
445#endif // OPENCV_HAVE_FILESYSTEM_SUPPORT
446}
447
448}} // namespace
449

source code of opencv/modules/core/src/utils/datafile.cpp