diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c4cad1..9cb6a8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,30 +3,24 @@ project(stitch VERSION 0.1.0 LANGUAGES C CXX) SET(ArithStitchDir stitch) - IF(WIN32) set(OpenCV_DIR "D:/opencv410_vc17") set(CMAKE_TOOLCHAIN_FILE "D:/wangchongwu_gitea_2023/vcpkg-2025.01.13/scripts/buildsystems/vcpkg.cmake") ELSE(WIN32) set(OpenCV_DIR "/home/wcw/opencv-3.4.16/install/share/OpenCV") -ENDIF(WIN32) +ENDIF(WIN32) find_package(OpenCV REQUIRED) include_directories(${OpenCV_INCLUDE_DIRS} public_include ${ArithStitchDir}/src) - - - # 设置算法库目录 SET(LIB_STITCH GuideStitch) - add_subdirectory(stitch) add_executable(stitch main.cpp) - target_link_libraries(stitch ${OpenCV_LIBS} ${LIB_STITCH}) # 可执行文件输出路径 diff --git a/ReadMe.md b/ReadMe.md new file mode 100644 index 0000000..346dca6 --- /dev/null +++ b/ReadMe.md @@ -0,0 +1,9 @@ +# 航拍图像拼接系统 + +## 日志 +- 1 2025/1/26 +完成了基础的外参拼接+BA优化过程,目前不支持动态调整拼接大小位置比例尺等功能。当载体持续运动扫描时,无法支持连续拼接以及回溯。因此要求必须加入文件缓存机制,并使用瓦片对数据进行管理,这样可以极大扩展拼接范围,且输出内容更为标准化。 + +【初步设想】:建立二级文件缓存,其中第一级存储原始帧(图像+H矩阵等),第二级存储生成的标准瓦片。当系统工作时,根据当前视野范围和XYZ瓦片等级(X),开始从2级缓存搜索瓦片,如果存在瓦片则直接加载,如果不存在则从1级缓存读取原始帧生成瓦片,并将瓦片加入2级缓存。 +【关键技术】:文件缓存技术、标准瓦片转换技术 + diff --git a/stitch/src/Arith_BATask.cpp b/stitch/src/Arith_BATask.cpp index e8a1ecb..c2a9fd0 100644 --- a/stitch/src/Arith_BATask.cpp +++ b/stitch/src/Arith_BATask.cpp @@ -34,16 +34,17 @@ struct HomographyResidual P_j[row] += H_j[row * 3 + col] * p_j[col]; } } - + P_i[0] /= P_i[2]; P_i[1] /= P_i[2]; P_j[0] /= P_j[2]; P_j[1] /= P_j[2]; + // 1.添加重投影误差 residual[0] = P_i[0] - P_j[0]; residual[1] = P_i[1] - P_j[1]; - // 计算单应性矩阵的范数(正则化项) + // 计算单应性矩阵的F范数 T norm_H_i = sqrt(h_i[0] * h_i[0] + h_i[1] * h_i[1] + h_i[2] * h_i[2] + h_i[3] * h_i[3] + h_i[4] * h_i[4] + h_i[5] * h_i[5] + h_i[6] * h_i[6] + h_i[7] * h_i[7] + T(1.0)); @@ -52,7 +53,7 @@ struct HomographyResidual h_j[3] * h_j[3] + h_j[4] * h_j[4] + h_j[5] * h_j[5] + h_j[6] * h_j[6] + h_j[7] * h_j[7] + T(1.0)); - // 添加正则化项到残差 + // 2.添加尺度正则项 residual[2] = norm_H_i - T(cv::norm(H1_)); residual[3] = norm_H_j - T(cv::norm(H2_)); @@ -337,10 +338,7 @@ SINT32 BA_Task::CalMatchMat(float fiou_thre) -float F_Norm(cv::Mat H) -{ - return 0.0f; -} + // 函数:绘制多边形 void drawPolygons(cv::Mat& image, const std::vector>& polygons) diff --git a/stitch/src/Arith_BATask.h b/stitch/src/Arith_BATask.h index ce8267c..18ecce7 100644 --- a/stitch/src/Arith_BATask.h +++ b/stitch/src/Arith_BATask.h @@ -68,6 +68,6 @@ private: }; -float F_Norm(cv::Mat H); + void drawPolygons(cv::Mat& image, const std::vector>& polygons); \ No newline at end of file diff --git a/stitch/src/GoogleTile.h b/stitch/src/GoogleTile.h new file mode 100644 index 0000000..074675e --- /dev/null +++ b/stitch/src/GoogleTile.h @@ -0,0 +1,113 @@ +#pragma once + +#include +#include +#include + +class GoogleTile +{ +public: + // 构造函数 + GoogleTile(int zoom, int x, int y, cv::Mat content = cv::Mat()) + { + _zoom = zoom; + _x = x; + _y = y; + _content = content.clone(); + _isValid = !content.empty(); + } + + // 获取缩放级别 + int getZoom() const + { + return _zoom; + } + + // 获取X坐标 + int getX() const + { + return _x; + } + + // 获取Y坐标 + int getY() const + { + return _y; + } + + // 获取瓦片内容 + cv::Mat getContent() const + { + return _content; + } + + // 设置瓦片内容 + void setContent(cv::Mat content) + { + _content = content.clone(); + _isValid = !content.empty(); + } + + // 检查瓦片是否有效 + bool isValid() const + { + return _isValid; + } + + // 获取相邻瓦片坐标 + GoogleTile getLeftTile() const + { + int newX = (_x - 1 + (1 << _zoom)) % (1 << _zoom); + return GoogleTile(_zoom, newX, _y); + } + + GoogleTile getRightTile() const + { + int newX = (_x + 1) % (1 << _zoom); + return GoogleTile(_zoom, newX, _y); + } + + GoogleTile getTopTile() const + { + int newY = (_y - 1 + (1 << _zoom)) % (1 << _zoom); + return GoogleTile(_zoom, _x, newY); + } + + GoogleTile getBottomTile() const + { + int newY = (_y + 1) % (1 << _zoom); + return GoogleTile(_zoom, _x, newY); + } + + // 将瓦片坐标转换为经纬度 + void tileToLatLon(double& lat, double& lon) const + { + double n = M_PI - 2.0 * M_PI * _y / (1 << _zoom); + lat = 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n))); + lon = _x / (double)(1 << _zoom) * 360.0 - 180.0; + } + + // 将经纬度转换为瓦片坐标 + static void latLonToTile(double lat, double lon, int zoom, int& x, int& y) + { + double lat_rad = lat * M_PI / 180.0; + double n = 1 << zoom; + x = static_cast((lon + 180.0) / 360.0 * n); + y = static_cast((1.0 - log(tan(lat_rad) + 1.0 / cos(lat_rad)) / M_PI) / 2.0 * n); + } + + // 获取瓦片URL(根据谷歌地图规范) + std::string getUrl() const + { + std::stringstream ss; + ss << "https://mt.google.com/vt/lyrs=s&x=" << _x << "&y=" << _y << "&z=" << _zoom; + return ss.str(); + } + +private: + int _zoom; // 缩放级别 (0-21) + int _x; // 瓦片X坐标 + int _y; // 瓦片Y坐标 + cv::Mat _content; // 瓦片图像数据 + bool _isValid; // 瓦片是否有效 +}; \ No newline at end of file diff --git a/stitch/src/utils/FileCache.cpp b/stitch/src/utils/FileCache.cpp new file mode 100644 index 0000000..8ace939 --- /dev/null +++ b/stitch/src/utils/FileCache.cpp @@ -0,0 +1,179 @@ +#include "FileCache.h" +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#include +#endif + +namespace fs = std::filesystem; + +FileCache::FileCache(const std::string& cache_dir, size_t max_memory_items) + : cache_dir_(cache_dir), max_memory_items_(max_memory_items) +{ + fs::create_directories(cache_dir_); +} + +FileCache::~FileCache() +{ + // Clean up memory-mapped files + for (auto& [key, mapped_file] : mapped_files_) { + if (mapped_file.data) { + unmap_file(mapped_file.data, mapped_file.size); + } + } +} + +std::string FileCache::hash_key(const std::string& key) const +{ + // Simple hash function (replace with a better one if needed) + std::hash hasher; + size_t hash_value = hasher(key); + std::ostringstream hex_stream; + hex_stream << std::hex << hash_value; + return hex_stream.str(); +} + +std::string FileCache::get_file_path(const std::string& key) const +{ + std::string hashed = hash_key(key); + std::string sub_dir = cache_dir_ + "/" + hashed.substr(0, 2); + fs::create_directories(sub_dir); + return sub_dir + "/" + hashed.substr(2) + ".cache"; +} + +template +std::optional FileCache::get(const std::string& key) +{ + { + std::lock_guard lock(cache_mutex_); + if (memory_cache_.count(key)) { + const auto& data = memory_cache_[key]; + return *reinterpret_cast(data.data()); + } + + if (mapped_files_.count(key)) { + const auto& mapped_file = mapped_files_[key]; + return *reinterpret_cast(mapped_file.data); + } + } + + auto file_path = get_file_path(key); + return load_from_disk(file_path); +} + +template +void FileCache::set(const std::string& key, const T& data) +{ + auto serialized = std::vector(reinterpret_cast(&data), + reinterpret_cast(&data) + sizeof(T)); + + { + std::lock_guard lock(cache_mutex_); + memory_cache_[key] = serialized; + + // Evict if necessary + if (memory_cache_.size() > max_memory_items_) + { + memory_cache_.erase(memory_cache_.begin()); + } + } + + // Async save to disk + auto file_path = get_file_path(key); + async_tasks_.push_back(std::async(std::launch::async, [this, file_path, serialized]() { + save_to_disk(file_path, serialized); + })); +} + +template +bool FileCache::save_to_disk(const std::string& file_path, const T& data) +{ + std::ofstream out(file_path, std::ios::binary); + if (!out) return false; + + out.write(reinterpret_cast(&data), sizeof(data)); + return true; +} + +template +std::optional FileCache::load_from_disk(const std::string& file_path) const +{ + size_t file_size = fs::file_size(file_path); + void* mapped_data = map_file(file_path, file_size); + if (!mapped_data) + { + return std::nullopt; + } + + // Store the mapped file for later use + std::lock_guard lock(cache_mutex_); + mapped_files_[file_path] = {mapped_data, file_size}; + + return *reinterpret_cast(mapped_data); +} + +void FileCache::clear() +{ + std::lock_guard lock(cache_mutex_); + memory_cache_.clear(); + for (const auto& [key, mapped_file] : mapped_files_) { + if (mapped_file.data) + { + unmap_file(mapped_file.data, mapped_file.size); + } + } + mapped_files_.clear(); + for (const auto& entry : fs::directory_iterator(cache_dir_)) + { + fs::remove_all(entry.path()); + } +} + +// Platform-specific memory mapping +void* FileCache::map_file(const std::string& file_path, size_t size) +{ +#ifdef _WIN32 + HANDLE file = CreateFileA(file_path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + if (file == INVALID_HANDLE_VALUE) return nullptr; + + HANDLE mapping = CreateFileMapping(file, nullptr, PAGE_READONLY, 0, 0, nullptr); + if (!mapping) + { + CloseHandle(file); + return nullptr; + } + + void* data = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, size); + CloseHandle(mapping); + CloseHandle(file); + return data; +#else + int fd = open(file_path.c_str(), O_RDONLY); + if (fd == -1) return nullptr; + + void* data = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0); + close(fd); + + if (data == MAP_FAILED) { + return nullptr; + } + return data; +#endif +} + +void FileCache::unmap_file(void* data, size_t size) +{ +#ifdef _WIN32 + UnmapViewOfFile(data); +#else + munmap(data, size); +#endif +} diff --git a/stitch/src/utils/FileCache.h b/stitch/src/utils/FileCache.h new file mode 100644 index 0000000..383109e --- /dev/null +++ b/stitch/src/utils/FileCache.h @@ -0,0 +1,55 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +class FileCache +{ +public: + FileCache(const std::string& cache_dir = "./tile_cache", size_t max_memory_items = 1000); + ~FileCache(); + + template + std::optional get(const std::string& key); + + template + void set(const std::string& key, const T& data); + + void clear(); + +private: + std::string get_file_path(const std::string& key) const; + std::string hash_key(const std::string& key) const; + + template + bool save_to_disk(const std::string& file_path, const T& data); + + template + std::optional load_from_disk(const std::string& file_path) const; + + std::string cache_dir_; + size_t max_memory_items_; + + // Memory cache + mutable std::mutex cache_mutex_; + std::unordered_map> memory_cache_; + + // Memory-mapped files + struct MappedFile { + void* data; + size_t size; + }; + std::unordered_map mapped_files_; + + // Async tasks + std::vector> async_tasks_; + + // Platform-specific memory mapping + void* map_file(const std::string& file_path, size_t size); + void unmap_file(void* data, size_t size); +};