新增readme

main
wangchongwu 6 months ago
parent f7b49a5beb
commit d04e0234f2

@ -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})
#

@ -0,0 +1,9 @@
# 航拍图像拼接系统
## 日志
- 1 2025/1/26
完成了基础的外参拼接+BA优化过程目前不支持动态调整拼接大小位置比例尺等功能。当载体持续运动扫描时无法支持连续拼接以及回溯。因此要求必须加入文件缓存机制并使用瓦片对数据进行管理这样可以极大扩展拼接范围且输出内容更为标准化。
【初步设想】:建立二级文件缓存,其中第一级存储原始帧(图像+H矩阵等第二级存储生成的标准瓦片。当系统工作时根据当前视野范围和XYZ瓦片等级X开始从2级缓存搜索瓦片如果存在瓦片则直接加载如果不存在则从1级缓存读取原始帧生成瓦片并将瓦片加入2级缓存。
【关键技术】:文件缓存技术、标准瓦片转换技术

@ -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<std::vector<cv::Point2f>>& polygons)

@ -68,6 +68,6 @@ private:
};
float F_Norm(cv::Mat H);
void drawPolygons(cv::Mat& image, const std::vector<std::vector<cv::Point2f>>& polygons);

@ -0,0 +1,113 @@
#pragma once
#include <opencv2/opencv.hpp>
#include <cmath>
#include <sstream>
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<int>((lon + 180.0) / 360.0 * n);
y = static_cast<int>((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; // 瓦片是否有效
};

@ -0,0 +1,179 @@
#include "FileCache.h"
#include <fstream>
#include <sstream>
#include <iomanip>
#include <stdexcept>
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#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<std::string> 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<typename T>
std::optional<T> FileCache::get(const std::string& key)
{
{
std::lock_guard<std::mutex> lock(cache_mutex_);
if (memory_cache_.count(key)) {
const auto& data = memory_cache_[key];
return *reinterpret_cast<const T*>(data.data());
}
if (mapped_files_.count(key)) {
const auto& mapped_file = mapped_files_[key];
return *reinterpret_cast<const T*>(mapped_file.data);
}
}
auto file_path = get_file_path(key);
return load_from_disk<T>(file_path);
}
template<typename T>
void FileCache::set(const std::string& key, const T& data)
{
auto serialized = std::vector<uint8_t>(reinterpret_cast<const uint8_t*>(&data),
reinterpret_cast<const uint8_t*>(&data) + sizeof(T));
{
std::lock_guard<std::mutex> 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<typename T>
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<const char*>(&data), sizeof(data));
return true;
}
template<typename T>
std::optional<T> 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<std::mutex> lock(cache_mutex_);
mapped_files_[file_path] = {mapped_data, file_size};
return *reinterpret_cast<const T*>(mapped_data);
}
void FileCache::clear()
{
std::lock_guard<std::mutex> 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
}

@ -0,0 +1,55 @@
#pragma once
#include <string>
#include <unordered_map>
#include <mutex>
#include <future>
#include <filesystem>
#include <optional>
#include <vector>
#include <cstdint>
class FileCache
{
public:
FileCache(const std::string& cache_dir = "./tile_cache", size_t max_memory_items = 1000);
~FileCache();
template<typename T>
std::optional<T> get(const std::string& key);
template<typename T>
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<typename T>
bool save_to_disk(const std::string& file_path, const T& data);
template<typename T>
std::optional<T> 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<std::string, std::vector<uint8_t>> memory_cache_;
// Memory-mapped files
struct MappedFile {
void* data;
size_t size;
};
std::unordered_map<std::string, MappedFile> mapped_files_;
// Async tasks
std::vector<std::future<void>> 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);
};
Loading…
Cancel
Save