diff --git a/main.cpp b/main.cpp index 4fe6bc8..d67d4d5 100644 --- a/main.cpp +++ b/main.cpp @@ -216,7 +216,10 @@ void ProcessIR() waitKey(0); // 输出谷歌png - stitcher->ExportGeoPng("D:/", "IR_pan"); + stitcher->ExportGeoPng("D:/google_tiles", "IR_pan"); + + // 输出谷歌tile + stitcher->ExportGoogleTile("D:/google_tiles", "IR_tiles"); } @@ -333,10 +336,10 @@ void ProcessVL() waitKey(0); // 输出谷歌png - stitcher->ExportGeoPng("D:/", "VL_pan"); + stitcher->ExportGeoPng("D:/google_tiles", "VL_pan"); // 输出谷歌tile - stitcher->ExportGoogleTile("D:/", "VL_pan"); + stitcher->ExportGoogleTile("D:/google_tiles", "VL_tiles"); } diff --git a/stitch/CMakeLists.txt b/stitch/CMakeLists.txt index 592ab50..c83582e 100644 --- a/stitch/CMakeLists.txt +++ b/stitch/CMakeLists.txt @@ -12,14 +12,14 @@ endif() IF(WIN32) set(OpenCV_DIR "D:/opencv410_vc17") - + set(CMAKE_TOOLCHAIN_FILE "D:/wangchongwu_gitea_2023/vcpkg-2025.01.13/scripts/buildsystems/vcpkg.cmake") # set(CMAKE_TOOLCHAIN_FILE "D:/wangchongwu_gitea_2023/vcpkg-2025.01.13/scripts/buildsystems/vcpkg.cmake") # set(Ceres_DIR "D:/wangchongwu_gitea_2023/vcpkg-2025.01.13/packages/ceres_x64-windows/share/ceres") # set(Eigen3_DIR "D:/wangchongwu_gitea_2023/vcpkg-2025.01.13/packages/eigen3_x64-windows/share/eigen3") # set(glog_DIR "D:/wangchongwu_gitea_2023/vcpkg-2025.01.13/packages/glog_x64-windows/share/glog") ELSE(WIN32) + ENDIF(WIN32) - find_package(Ceres REQUIRED) include_directories(${CERES_INCLUDE_DIRS}) diff --git a/stitch/src/Arith_VideoStitch.cpp b/stitch/src/Arith_VideoStitch.cpp index d855e98..96388e8 100644 --- a/stitch/src/Arith_VideoStitch.cpp +++ b/stitch/src/Arith_VideoStitch.cpp @@ -153,8 +153,6 @@ BYTE8 VideoStitch::GeoStitch(GD_VIDEO_FRAME_S img, FrameInfo para) // uv2Geo H cv::Mat H1 = _GeoSolver->findHomography(para); - cv::Mat H2 = _GeoSolver->findHomography2(para); - cv::Mat H = _H_pan * H1; // 利用H投影当前帧到全景 @@ -330,13 +328,15 @@ bool VideoStitch::ExportGeoPng(std::string dir, std::string name) TileInfo info = { 0 }; - info.north = maxB; - info.south = minB; - info.east = minL; - info.west = maxL; + info.box.north = maxB; + info.box.south = minB; + info.box.west = minL; + info.box.east = maxL; + info.tileName = name; + info.href = name + ".png"; // 输出谷歌地图产品 - _googleProduct.ExportGeoPng(_panImage, info, dir, name); + _googleProduct.ExportGeoPng(_panImage, info, dir); return 0; } @@ -360,12 +360,13 @@ bool VideoStitch::ExportGoogleTile(std::string dir, std::string name) TileInfo info = { 0 }; - info.north = maxB; - info.south = minB; - info.east = minL; - info.west = maxL; + info.box.north = maxB; + info.box.south = minB; + info.box.west = minL; + info.box.east = maxL; + info.ind.z = 17; - _googleProduct.ExportTile(_panImage, info, dir, name); + _googleProduct.ExportTile(_panImage, info, dir); return 0; } diff --git a/stitch/src/GoogleTile.cpp b/stitch/src/GoogleTile.cpp index 2478653..0a51354 100644 --- a/stitch/src/GoogleTile.cpp +++ b/stitch/src/GoogleTile.cpp @@ -7,7 +7,7 @@ #include using std::string; - +namespace fs = std::filesystem; #define M_PI 3.1415926 googleTile::googleTile() @@ -19,50 +19,111 @@ googleTile::~googleTile() { } -void googleTile::ExportGeoPng(cv::Mat _pan, TileInfo info, std::string dir, std::string name) +void googleTile::ExportGeoPng(cv::Mat _pan, TileInfo info, std::string dir) { + // 创建目录 + if (!fs::exists(dir)) + { + fs::create_directories(dir); + } + // 保存全景图 - string png_path = dir + "/" + name + ".png"; + string png_path = dir + "/" + info.tileName + ".png"; cv::imwrite(png_path, _pan); - // 写入kml - string kml_path = dir + "/" + name + ".kml"; - std::ofstream kml_file(kml_path); + string kml_path = dir + "/" + info.tileName + ".kml"; - // 写入 KML 文件的头部 - kml_file << "" << std::endl; - kml_file << "" << std::endl; - kml_file << "" << std::endl; - - // 写入 GroundOverlay 元素 - kml_file << "" << std::endl; - kml_file << " My Panoramic Image" << std::endl; - kml_file << " " << std::endl; - kml_file << " " << name + ".png" << "" << std::endl; - kml_file << " " << std::endl; - - // 使用四个角点计算 LatLonBox - kml_file << " " << std::endl; - kml_file << " " << info.north << "" << std::endl; // 经度范围 - kml_file << " " << info.south << "" << std::endl; - kml_file << " " << info.east << "" << std::endl; // 纬度范围 - kml_file << " " << info.west << "" << std::endl; - kml_file << " " << std::endl; - - // 写入 KML 文件的尾部 - kml_file << "" << std::endl; - kml_file << "" << std::endl; - kml_file << "" << std::endl; + vector taskTiles; + taskTiles.push_back(info); + WriteKml(taskTiles, kml_path); - kml_file.close(); } -void googleTile::ExportTile(cv::Mat _pan, TileInfo info, std::string dir, std::string name) +void googleTile::ExportTile(cv::Mat _pan, TileInfo panInfo, std::string dir) { + int zoom = panInfo.ind.z; + TileBox panBox = panInfo.box; + // 计算四至的瓦片编号 - TileIndex westNorth = LatLonToTile(info.west, info.north, 10); - int a = 0; + TileIndex WN = LatLonToTile(panBox.north, panBox.west, zoom); + TileIndex WS = LatLonToTile(panBox.south, panBox.west, zoom); + TileIndex EN = LatLonToTile(panBox.north, panBox.east, zoom); + TileIndex ES = LatLonToTile(panBox.south, panBox.east, zoom); + + int xStart = WN.x; + int xEnd = EN.x; + + int yStart = EN.y; + int yEnd = ES.y; + + int panWidth = _pan.cols; + int panHeight = _pan.rows; + + vector taskTiles; + + for (size_t i = yStart; i < yEnd; i++) + { + for (size_t j = xStart; j < xEnd; j++) + { + TileIndex id = {j,i,17}; + TileBox tilebox = GetTileBox(id); + + // 填充 + TileInfo info = { 0 }; + info.box = tilebox; + info.ind = id; + + // 计算瓦片在全景图中的像素位置 + int x1 = (tilebox.west - panBox.west) / (panBox.east - panBox.west) * panWidth; + int y1 = (panBox.north - tilebox.north) / (panBox.north - panBox.south) * panHeight; + int x2 = (tilebox.east - panBox.west) / (panBox.east - panBox.west) * panWidth; + int y2 = (panBox.north - tilebox.south) / (panBox.north - panBox.south) * panHeight; + + // 有不完整瓦片,不保存 + if (x1 < 0 || y1 < 0 || x2 > panWidth - 1 || y2 > panHeight - 1) + { + continue; + } + + // 确保坐标在图像范围内 + x1 = std::max(0, std::min(x1, panWidth - 1)); + y1 = std::max(0, std::min(y1, panHeight - 1)); + x2 = std::max(0, std::min(x2, panWidth - 1)); + y2 = std::max(0, std::min(y2, panHeight - 1)); + + // 提取瓦片 + cv::Rect tileRect(x1, y1, x2 - x1, y2 - y1); + cv::Mat tile = _pan(tileRect); + + // 生成文件名 + std::string tileDir = dir + "/" + std::to_string(zoom); + std::string fileName = tileDir + "/" + std::to_string(j) + "_" + std::to_string(i) + ".png"; + // 创建目录 + if (!fs::exists(tileDir)) + { + fs::create_directories(tileDir); + } + + // 保存瓦片为图像文件 + cv::imwrite(fileName, tile); + + + // 补充瓦片信息 + info.tileName = std::to_string(j) + "_" + std::to_string(i) + "_" + std::to_string(zoom); + + info.href = std::to_string(zoom) + "/" + std::to_string(j) + "_" + std::to_string(i) + ".png"; + + taskTiles.push_back(info); + + } + + } + + + // 输出KML + WriteKml(taskTiles,dir + "/" + "tile" + std::to_string(zoom) + ".kml"); + } TileIndex googleTile::LatLonToTile(double latitude, double longitude, int zoomLevel) @@ -81,6 +142,68 @@ TileIndex googleTile::LatLonToTile(double latitude, double longitude, int zoomLe } +// 将瓦片编号转换为经纬度 +void TileToLatLon(int x, int y, int z, double& lat, double& lon) +{ + double n = std::pow(2.0, z); + lon = x / n * 360.0 - 180.0; + double lat_rad = std::atan(std::sinh(M_PI * (1 - 2 * y / n))); + lat = lat_rad * 180.0 / M_PI; +} + +TileBox googleTile::GetTileBox(TileIndex ind) +{ + TileBox box = {0}; + + // 计算瓦片的西北角经纬度 + TileToLatLon(ind.x, ind.y, ind.z, box.north, box.west); + + // 计算瓦片的东南角经纬度 + TileToLatLon(ind.x + 1, ind.y + 1, ind.z, box.south, box.east); + + return box; +} + +void googleTile::WriteKml(vector tiles, string filePath) +{ + // Open the KML file for writing + std::ofstream kml_file(filePath); + if (!kml_file.is_open()) { + std::cerr << "Failed to open KML file for writing: " << filePath << std::endl; + return; + } + + // Write the KML header + kml_file << "" << std::endl; + kml_file << "" << std::endl; + kml_file << "" << std::endl; + + // Loop through each tile and write a GroundOverlay element + for (const auto& tile : tiles) + { + kml_file << "" << std::endl; + kml_file << " " << tile.tileName << "" << std::endl; + kml_file << " " << std::endl; + kml_file << " " << tile.href << "" << std::endl; + kml_file << " " << std::endl; + kml_file << " " << std::endl; + kml_file << " " << tile.box.north << "" << std::endl; + kml_file << " " << tile.box.south << "" << std::endl; + kml_file << " " << tile.box.east << "" << std::endl; + kml_file << " " << tile.box.west << "" << std::endl; + kml_file << " " << std::endl; + kml_file << "" << std::endl; + } + + // Write the KML footer + kml_file << "" << std::endl; + kml_file << "" << std::endl; + + // Close the file + kml_file.close(); +} + + diff --git a/stitch/src/GoogleTile.h b/stitch/src/GoogleTile.h index a325c6f..4216741 100644 --- a/stitch/src/GoogleTile.h +++ b/stitch/src/GoogleTile.h @@ -5,6 +5,9 @@ #include #include +using std::vector; +using std::string; + struct TileIndex { int x; @@ -12,15 +15,22 @@ struct TileIndex int z; }; -struct TileInfo +struct TileBox { - TileIndex ind; double north; double south; double east; double west; }; +struct TileInfo +{ + TileIndex ind; + std::string tileName; + std::string href;//相对路径 + TileBox box; +}; + class googleTile { @@ -30,15 +40,20 @@ public: public: // 输出kml png - void ExportGeoPng(cv::Mat _pan, TileInfo info, std::string dir, std::string name); + void ExportGeoPng(cv::Mat _pan, TileInfo info, std::string dir); // 输出tile - void ExportTile(cv::Mat _pan, TileInfo info, std::string dir, std::string name); + void ExportTile(cv::Mat _pan, TileInfo info, std::string dir); public: TileIndex LatLonToTile(double latitude, double longitude, int zoomLevel); private: + // 瓦片编号对应的信息 + TileBox GetTileBox(TileIndex ind); + + + void WriteKml(vector tiles,string filePath); int zoom_;