实时动态切瓦片,流式输出

main
wangchongwu 4 months ago
parent 12bd9ad91d
commit cfc2925cf1

@ -110,8 +110,9 @@ unsigned char pImageVL[IMAGE_WIDTH_VL * (IMAGE_HEIGHT_VL) * 2] = { 0 };
void ProcessIR(string filePath, string outname) void ProcessIR(string filePath, string outname)
{ {
auto stitcher = API_UnderStitch::Create(IMAGE_WIDTH_IR, IMAGE_HEIGHT_IR); auto stitcher = API_UnderStitch::Create();
stitcher->SetOutput(outname, "D:/google_tiles");
GD_VIDEO_FRAME_S frame = { 0 };//输入帧 GD_VIDEO_FRAME_S frame = { 0 };//输入帧
GD_VIDEO_FRAME_S pan = { 0 };//输出全景 GD_VIDEO_FRAME_S pan = { 0 };//输出全景
@ -218,7 +219,7 @@ void ProcessIR(string filePath, string outname)
stitcher->ProcessFrame(); stitcher->ProcessFrame();
// 输出谷歌png // 输出谷歌png
stitcher->ExportGeoPng("D:/google_tiles", outname); stitcher->ExportGeoPng();
imshow("pan_opt", mat_pan); imshow("pan_opt", mat_pan);
waitKey(0); waitKey(0);
@ -227,7 +228,9 @@ void ProcessIR(string filePath, string outname)
void ProcessVL(string filePath,string outname) void ProcessVL(string filePath,string outname)
{ {
auto stitcher = API_UnderStitch::Create(IMAGE_WIDTH_VL, IMAGE_HEIGHT_VL); auto stitcher = API_UnderStitch::Create();
stitcher->SetOutput(outname, "D:/google_tiles");
GD_VIDEO_FRAME_S frame = { 0 };//输入帧 GD_VIDEO_FRAME_S frame = { 0 };//输入帧
GD_VIDEO_FRAME_S pan = { 0 };//输出全景 GD_VIDEO_FRAME_S pan = { 0 };//输出全景
@ -286,7 +289,7 @@ void ProcessVL(string filePath,string outname)
frame.u32Height = IMAGE_HEIGHT_VL; frame.u32Height = IMAGE_HEIGHT_VL;
frame.u64VirAddr[0] = pFrameVL; frame.u64VirAddr[0] = pFrameVL;
if (i == 0) if (i == 0 /*|| i == 200*/)
{ {
stitcher->Init(info); stitcher->Init(info);
@ -303,7 +306,7 @@ void ProcessVL(string filePath,string outname)
} }
else else
{ {
if (i % 2 != 0) if (i % 20 != 0)
{ {
i = i + 1; i = i + 1;
continue; continue;
@ -355,10 +358,10 @@ void ProcessVL(string filePath,string outname)
// 输出谷歌png // 输出谷歌png
stitcher->ExportGeoPng("D:/google_tiles", outname); //stitcher->ExportGeoPng();
// 输出谷歌tile // 输出谷歌tile
//stitcher->ExportGoogleTile("D:/google_tiles", outname); //stitcher->ExportGoogleTile();
} }
@ -485,12 +488,12 @@ void ProcessFrontVL(string filePath)
int main(int, char**) int main(int, char**)
{ {
//ProcessIR("H:/ir_1280_1024_para40_y16/20241219155238_20.xraw", "20241219155238_20"); //ProcessIR("H:/ir_1280_1024_para40_y16/20241219155238_20.xraw", "20241219155238_20");
//ProcessVL("H:/vl_1920_1080_para40_y8/22.video","22"); ProcessVL("H:/vl_1920_1080_para40_y8/22.video","22");
//ProcessVL("H:/vl_1920_1080_para40_y8/20241219153557_11.video", "20241219152643_1"); //ProcessVL("H:/vl_1920_1080_para40_y8/20241219153557_11.video", "20241219152643_1");
//ProcessVL("H:/vl_1920_1080_para40_y8/20241219152917_4.video", "20241219152917_4"); //ProcessVL("H:/vl_1920_1080_para40_y8/20241219152917_4.video", "20241219152917_4");
//ProcessVL("H:/vl_1920_1080_para40_y8/20241219153515_10.video", "20241219153515_10"); //ProcessVL("H:/vl_1920_1080_para40_y8/20241219153515_10.video", "20241219153515_10");
// //
//ProcessVL("H:/vl_1920_1080_para40_y8/1.video", "1"); //ProcessVL("H:/vl_1920_1080_para40_y8/1.video", "1");
ProcessFrontVL("H:/vl_1920_1080_para40_y8/1.video"); //ProcessFrontVL("H:/vl_1920_1080_para40_y8/1.video");
} }

@ -24,10 +24,16 @@ public:
virtual ~API_UnderStitch() = default; virtual ~API_UnderStitch() = default;
// 初始化拼接 // 初始化拼接
virtual UPanInfo Init(FrameInfo info) = 0; virtual UPanInfo Init(FrameInfo info) = 0;
// 设置输出标识和路径
virtual void SetOutput(std::string name, std::string outdir) = 0;
// 几何校正快拼 // 几何校正快拼
virtual BYTE8 GeoStitch(GD_VIDEO_FRAME_S img, FrameInfo para) = 0; virtual BYTE8 GeoStitch(GD_VIDEO_FRAME_S img, FrameInfo para) = 0;
// 实时裁切瓦片
virtual void CutTileRealTime() = 0;
// 缓存接收帧 // 缓存接收帧
virtual SINT32 ReceiveFrame(GD_VIDEO_FRAME_S img, FrameInfo para) = 0; virtual SINT32 ReceiveFrame(GD_VIDEO_FRAME_S img, FrameInfo para) = 0;
@ -39,12 +45,12 @@ public:
virtual GD_VIDEO_FRAME_S ExportPanAddr() = 0; virtual GD_VIDEO_FRAME_S ExportPanAddr() = 0;
// 输出地理产品:kml png全景 // 输出地理产品:kml png全景
virtual bool ExportGeoPng(std::string dir, std::string name) = 0; virtual bool ExportGeoPng() = 0;
// 输出地理产品:谷歌标准瓦片 // 输出地理产品:谷歌标准瓦片
virtual bool ExportGoogleTile(std::string dir, std::string name) = 0; virtual bool ExportGoogleTile() = 0;
public: public:
static API_UnderStitch* Create(SINT32 nWidth, SINT32 nHeight); static API_UnderStitch* Create(std::string cachedir = "./cache");
static void Destroy(API_UnderStitch* obj); static void Destroy(API_UnderStitch* obj);
}; };

@ -39,7 +39,7 @@ void MapBlend::DirectMap(vector<KeyType> frameInd, cv::Mat mapH, cv::Mat pan, cv
cv::warpPerspective(src, imagetmp, H, imagetmp.size(), cv::INTER_LINEAR, cv::BORDER_TRANSPARENT); cv::warpPerspective(src, imagetmp, H, imagetmp.size(), cv::INTER_LINEAR, cv::BORDER_TRANSPARENT);
cv::Mat mask = cv::Mat::ones(cv::Size(src.cols - 200, src.rows - 200), CV_8UC1) * 255; cv::Mat mask = cv::Mat::ones(cv::Size(src.cols - 200, src.rows - 200), CV_8UC1);
cv::Mat warped_mask; cv::Mat warped_mask;
cv::warpPerspective(mask, warped_mask, H, imagetmp.size(), cv::INTER_LINEAR); cv::warpPerspective(mask, warped_mask, H, imagetmp.size(), cv::INTER_LINEAR);
imagetmp.copyTo(pan, warped_mask); imagetmp.copyTo(pan, warped_mask);

@ -9,9 +9,9 @@
using namespace std; using namespace std;
using namespace cv; using namespace cv;
API_UnderStitch* API_UnderStitch::Create(SINT32 nWidth, SINT32 nHeight) API_UnderStitch* API_UnderStitch::Create(std::string cachedir)
{ {
return new UnderStitch(nWidth,nHeight); return new UnderStitch(cachedir);
} }
@ -21,14 +21,15 @@ void API_UnderStitch::Destroy(API_UnderStitch* obj)
} }
UnderStitch::UnderStitch(SINT32 nWidth, SINT32 nHeight) UnderStitch::UnderStitch(std::string cachedir)
{ {
// 创建缓存对象
_cache = new FileCache<FrameCache>(50, cachedir);
_GeoSolver = new GeoSolver(); _GeoSolver = new GeoSolver();
_FeaMatcher = new FeatureMatcher(DetectorType::SIFT, MatcherType::FLANN); _FeaMatcher = new FeatureMatcher(DetectorType::SIFT, MatcherType::FLANN);
_cache = new FileCache<FrameCache>(50,"./cache");
_BATask = new BA_Task(_cache); _BATask = new BA_Task(_cache);
_BlendTask = new MapBlend(_cache); _BlendTask = new MapBlend(_cache);
@ -63,9 +64,8 @@ UPanInfo UnderStitch::InitMap(FrameInfo info)
UPanInfo panPara = { 0 }; UPanInfo panPara = { 0 };
panPara.m_pan_width = MIN(info.nWidth * 5,2000);//全景宽 panPara.m_pan_width = MIN(info.nWidth * 5,2000);//全景宽
panPara.m_pan_height = MIN(info.nWidth * 5,2000);//全景高 panPara.m_pan_height = MIN(info.nWidth * 5,2000);//全景高
panPara.scale = gsd /5;//比例尺,1m = ?pix panPara.scale = gsd / 2;//比例尺,1m = ?pix
int zoom = _googleProduct.getZoomLevel(panPara.scale);
// 直接无平移解算 // 直接无平移解算
cv::Mat H_0 = getAffineFromGeo2Pan(panPara); cv::Mat H_0 = getAffineFromGeo2Pan(panPara);
@ -82,6 +82,30 @@ UPanInfo UnderStitch::InitMap(FrameInfo info)
// 获取全景图的投影矩阵,全景图配置更新前保持不变 // 获取全景图的投影矩阵,全景图配置更新前保持不变
_H_pan = getAffineFromGeo2Pan(panPara); _H_pan = getAffineFromGeo2Pan(panPara);
// 计算全景图瓦片相关
auto P1 = getBLHFromPan(cv::Point2f(0, 0), _H_pan);
auto P2 = getBLHFromPan(cv::Point2f(panPara.m_pan_width, 0), _H_pan);
auto P3 = getBLHFromPan(cv::Point2f(panPara.m_pan_width, panPara.m_pan_height), _H_pan);
auto P4 = getBLHFromPan(cv::Point2f(0, panPara.m_pan_height), _H_pan);
auto minL = min(min(min(P1.L, P2.L), P3.L), P4.L);
auto maxL = max(max(max(P1.L, P2.L), P3.L), P4.L);
auto minB = min(min(min(P1.B, P2.B), P3.B), P4.B);
auto maxB = max(max(max(P1.B, P2.B), P3.B), P4.B);
_panTile.boxLatLon.north = maxB;
_panTile.boxLatLon.south = minB;
_panTile.boxLatLon.west = minL;
_panTile.boxLatLon.east = maxL;
_panTile.ind.z = _googleProduct.ZoomLevel(panPara.scale);
// 生成瓦片划分
_taskTilesVec = _googleProduct.CalcTileOverlayVec(_panTile, panPara.m_pan_width, panPara.m_pan_height);
return panPara; return panPara;
} }
@ -90,6 +114,7 @@ UPanInfo UnderStitch::InitMap(FrameInfo info)
UPanInfo UnderStitch::Init(FrameInfo info) UPanInfo UnderStitch::Init(FrameInfo info)
{ {
_panPara = InitMap(info); _panPara = InitMap(info);
_panImage = cv::Mat::zeros(_panPara.m_pan_height, _panPara.m_pan_width, CV_8UC4); _panImage = cv::Mat::zeros(_panPara.m_pan_height, _panPara.m_pan_width, CV_8UC4);
@ -100,6 +125,15 @@ UPanInfo UnderStitch::Init(FrameInfo info)
return _panPara; return _panPara;
} }
void UnderStitch::SetOutput(std::string filename, std::string outdir)
{
_filename = filename;
_outDir = outdir;
_kmlPath = _outDir + "/" + _filename + ".kml";
}
//BYTE8 UnderStitch::GeoStitch(GD_VIDEO_FRAME_S img, FrameInfo para) //BYTE8 UnderStitch::GeoStitch(GD_VIDEO_FRAME_S img, FrameInfo para)
//{ //{
// Proj t_Proj = _GeoStitcher->AnlayseTform(para); // Proj t_Proj = _GeoStitcher->AnlayseTform(para);
@ -168,19 +202,76 @@ BYTE8 UnderStitch::GeoStitch(GD_VIDEO_FRAME_S img, FrameInfo para)
cv::warpPerspective(src, imagetmp, H, imagetmp.size(), cv::INTER_LINEAR, cv::BORDER_TRANSPARENT); cv::warpPerspective(src, imagetmp, H, imagetmp.size(), cv::INTER_LINEAR, cv::BORDER_TRANSPARENT);
cv::Mat mask = cv::Mat::ones(src.size(), CV_8UC1) * 255; cv::Mat mask = cv::Mat::ones(src.size(), CV_8UC1);
mask.colRange(0, 200).setTo(0); mask.colRange(0, 200).setTo(0);
cv::Mat warped_mask; cv::Mat warped_mask;
cv::warpPerspective(mask, warped_mask, H, imagetmp.size(), cv::INTER_LINEAR); cv::warpPerspective(mask, warped_mask, H, imagetmp.size(), cv::INTER_LINEAR);
imagetmp.copyTo(_panImage, warped_mask); imagetmp.copyTo(_panImage, warped_mask);
// 覆盖区域掩码更新
_panMask = _panMask | warped_mask;
// 生产快拼瓦片
CutTileRealTime();
return 0; return 0;
} }
void UnderStitch::CutTileRealTime()
{
// 在4倍降采样图上计算瓦片覆盖情况
int downSampleR = 4;
cv::Mat panMasksmall;
cv::resize(_panMask, panMasksmall, cv::Size(_panMask.cols / downSampleR, _panMask.rows / downSampleR));
// 计算覆盖图的积分图
cv::Mat integralMask;
cv::integral(panMasksmall, integralMask, CV_32S);
// 遍历已经在初始化阶段得到的瓦片划分
auto tiles = _taskTilesVec;
int cnt = 0;
for (size_t i = 0; i < tiles.size(); i++)
{
TileInfo* ptile = &_taskTilesVec[i];
RECT32S box = ptile->boxInPan;
// 跳过已输出块
if (ptile->nStatus > 0)
{
//cnt++;
//continue;
}
SINT32 x1 = box.x/ downSampleR;
SINT32 y1 = box.y/ downSampleR;
SINT32 x2 = box.x/ downSampleR + box.w/ downSampleR;
SINT32 y2 = box.y/ downSampleR + box.h/ downSampleR;
// 使用积分图快速计算区域和
float maskSum = integralMask.at<int>(y2, x2)
- integralMask.at<int>(y1, x2)
- integralMask.at<int>(y2, x1)
+ integralMask.at<int>(y1, x1);
// 计算瓦片影像覆盖率
float Coverage = maskSum * downSampleR * downSampleR / (box.w * box.h);
if (Coverage > 0.95)
{
// 输出并标记
ptile->nStatus = 1;
_googleProduct.ExportOneTile(_panImage, *ptile, _outDir, _filename);
}
}
}
SINT32 UnderStitch::ReceiveFrame(GD_VIDEO_FRAME_S img, FrameInfo para) SINT32 UnderStitch::ReceiveFrame(GD_VIDEO_FRAME_S img, FrameInfo para)
@ -191,8 +282,6 @@ SINT32 UnderStitch::ReceiveFrame(GD_VIDEO_FRAME_S img, FrameInfo para)
_t_frame_cache->_para = para; _t_frame_cache->_para = para;
_t_frame_cache->_frame_info = img; _t_frame_cache->_frame_info = img;
int imgSize = 0; int imgSize = 0;
if(img.enPixelFormat == GD_PIXEL_FORMAT_GRAY_Y8) if(img.enPixelFormat == GD_PIXEL_FORMAT_GRAY_Y8)
@ -261,7 +350,7 @@ SINT32 UnderStitch::ProcessFrame()
bool UnderStitch::ExportGeoPng(std::string dir, std::string name) bool UnderStitch::ExportGeoPng()
{ {
// 计算全景图的坐标范围 // 计算全景图的坐标范围
auto P1 = getBLHFromPan(cv::Point2f(0, 0), _H_pan); auto P1 = getBLHFromPan(cv::Point2f(0, 0), _H_pan);
@ -278,50 +367,27 @@ bool UnderStitch::ExportGeoPng(std::string dir, std::string name)
TileInfo info = { 0 }; TileInfo info = { 0 };
info.box.north = maxB; info.boxLatLon.north = maxB;
info.box.south = minB; info.boxLatLon.south = minB;
info.box.west = minL; info.boxLatLon.west = minL;
info.box.east = maxL; info.boxLatLon.east = maxL;
info.tileName = name; info.tileName = _filename;
info.href = name + ".png"; info.href = _filename + ".png";
// 输出谷歌地图产品 // 输出谷歌地图产品
_googleProduct.ExportGeoPng(_panImage, info, dir); _googleProduct.ExportGeoPng(_panImage, info, _outDir);
return 0; return 0;
} }
bool UnderStitch::ExportGoogleTile(std::string dir, std::string name) bool UnderStitch::ExportGoogleTile()
{ {
// 计算全景图的坐标范围 _googleProduct.ExportTileSet(_panImage, _taskTilesVec, _outDir, _filename);
auto P1 = getBLHFromPan(cv::Point2f(0, 0), _H_pan);
auto P2 = getBLHFromPan(cv::Point2f(_panImage.cols, 0), _H_pan);
auto P3 = getBLHFromPan(cv::Point2f(_panImage.cols, _panImage.rows), _H_pan);
auto P4 = getBLHFromPan(cv::Point2f(0, _panImage.rows), _H_pan);
auto minL = min(min(min(P1.L, P2.L), P3.L), P4.L);
auto maxL = max(max(max(P1.L, P2.L), P3.L), P4.L);
auto minB = min(min(min(P1.B, P2.B), P3.B), P4.B);
auto maxB = max(max(max(P1.B, P2.B), P3.B), P4.B);
TileInfo info = { 0 };
info.box.north = maxB;
info.box.south = minB;
info.box.west = minL;
info.box.east = maxL;
info.ind.z = _googleProduct.getZoomLevel(_panPara.scale);
_googleProduct.ExportTile(_panImage, info, dir, name);
return 0; return 0;
} }
GD_VIDEO_FRAME_S UnderStitch::ExportPanAddr() GD_VIDEO_FRAME_S UnderStitch::ExportPanAddr()
{ {
GD_VIDEO_FRAME_S pan_out; GD_VIDEO_FRAME_S pan_out;

@ -11,13 +11,19 @@
class UnderStitch :public API_UnderStitch class UnderStitch :public API_UnderStitch
{ {
public: public:
UnderStitch(SINT32 nWidth, SINT32 nHeight); UnderStitch(std::string cachedir = "./cache");
~UnderStitch(); ~UnderStitch();
UPanInfo Init(FrameInfo info); UPanInfo Init(FrameInfo info);
void SetOutput(std::string filename, std::string outdir);
BYTE8 GeoStitch(GD_VIDEO_FRAME_S img, FrameInfo para); BYTE8 GeoStitch(GD_VIDEO_FRAME_S img, FrameInfo para);
// 实时裁切瓦片
void CutTileRealTime();
// 接收帧 // 接收帧
SINT32 ReceiveFrame(GD_VIDEO_FRAME_S img, FrameInfo para); SINT32 ReceiveFrame(GD_VIDEO_FRAME_S img, FrameInfo para);
@ -25,10 +31,10 @@ public:
SINT32 ProcessFrame(); SINT32 ProcessFrame();
// 输出地理产品:kml png全景 // 输出地理产品:kml png全景
bool ExportGeoPng(std::string dir, std::string name); bool ExportGeoPng();
// //
bool ExportGoogleTile(std::string dir, std::string name); bool ExportGoogleTile();
public: public:
@ -62,12 +68,14 @@ private:
MapBlend* _BlendTask;// 融合模块 MapBlend* _BlendTask;// 融合模块
private:
UPanInfo _panPara;//全景图配置 UPanInfo _panPara;//全景图配置
cv::Mat _H_pan;//全景图投影矩阵:从地理系到全景地图 TileInfo _panTile;//全景图瓦片配置
cv::Mat _H_pan;//全景图投影矩阵:从地理系到全景地图
vector<TileInfo> _taskTilesVec;//划分的小瓦片方案
cv::Mat _panImage; cv::Mat _panImage;
@ -75,4 +83,11 @@ private:
int _totalFrameCnt;//处理帧计数 int _totalFrameCnt;//处理帧计数
private:
string _kmlPath;
string _filename;//输出文件标识
string _outDir;//输出拼接成果路径
string _cacheDir;//缓存路径优先使用SSD
}; };

@ -12,7 +12,6 @@ namespace fs = std::filesystem;
googleTile::googleTile() googleTile::googleTile()
{ {
} }
googleTile::~googleTile() googleTile::~googleTile()
@ -40,10 +39,135 @@ void googleTile::ExportGeoPng(cv::Mat _pan, TileInfo info, std::string dir)
} }
void googleTile::ExportTile(cv::Mat _pan, TileInfo panInfo, std::string dir,std::string fileString) void googleTile::ExportTileSet(cv::Mat _pan, vector<TileInfo> taskTilesVec, std::string dir,std::string fileString)
{
int panWidth = _pan.cols;
int panHeight = _pan.rows;
int zoom = taskTilesVec[0].ind.z;
for (size_t i = 0; i < taskTilesVec.size(); i++)
{
TileInfo tile = taskTilesVec[i];
TileBox tilebox = tile.boxLatLon;
RECT32S tileBoxInPan = tile.boxInPan;
int x1 = tileBoxInPan.x;
int x2 = tileBoxInPan.x + tileBoxInPan.w;
int y1 = tileBoxInPan.y;
int y2 = tileBoxInPan.y + tileBoxInPan.h;
// 有不完整瓦片,不保存
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 tileImg = _pan(tileRect);
// 标准web瓦片尺寸
if (tileImg.cols != 256 || tileImg.rows != 256)
{
cv::resize(tileImg, tileImg, cv::Size(256, 256));
}
// 生成文件名
std::string tileDir = dir + "/" + std::to_string(zoom);
std::string fileName = tileDir + "/" + std::to_string(tile.ind.x) + "_" + std::to_string(tile.ind.y) + ".png";
// 创建目录
if (!fs::exists(tileDir))
{
fs::create_directories(tileDir);
}
// 保存瓦片为图像文件
cv::imwrite(fileName, tileImg);
}
// 输出KML
WriteKml(taskTilesVec,dir + "/" + fileString + "_z" + std::to_string(zoom) + ".kml");
}
void googleTile::ExportOneTile(cv::Mat _pan, TileInfo tile, std::string dir, std::string fileString)
{ {
int zoom = panInfo.ind.z;
TileBox panBox = panInfo.box; int panWidth = _pan.cols;
int panHeight = _pan.rows;
int zoom = tile.ind.z;
TileBox tilebox = tile.boxLatLon;
RECT32S tileBoxInPan = tile.boxInPan;
int x1 = tileBoxInPan.x;
int x2 = tileBoxInPan.x + tileBoxInPan.w;
int y1 = tileBoxInPan.y;
int y2 = tileBoxInPan.y + tileBoxInPan.h;
// 有不完整瓦片,不保存
if (x1 < 0 || y1 < 0 || x2 > panWidth - 1 || y2 > panHeight - 1)
{
return;
}
// 确保坐标在图像范围内
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 tileImg = _pan(tileRect);
// 标准web瓦片尺寸
if (tileImg.cols != 256 || tileImg.rows != 256)
{
cv::resize(tileImg, tileImg, cv::Size(256, 256));
}
// 生成文件名
std::string tileDir = dir + "/" + std::to_string(zoom);
std::string fileName = tileDir + "/" + std::to_string(tile.ind.x) + "_" + std::to_string(tile.ind.y) + ".png";
std::string kmlPath = tileDir + "/" + std::to_string(tile.ind.x) + "_" + std::to_string(tile.ind.y) + ".kml";
tile.href = std::to_string(tile.ind.x) + "_" + std::to_string(tile.ind.y) + ".png";
// 创建目录
if (!fs::exists(tileDir))
{
fs::create_directories(tileDir);
}
// 保存瓦片为图像文件
cv::imwrite(fileName, tileImg);
vector<TileInfo> info;
info.push_back(tile);
WriteKml(info, kmlPath);
}
vector<TileInfo> googleTile::CalcTileOverlayVec(TileInfo areaInfo,int panWidth,int panHeight)
{
int zoom = areaInfo.ind.z;
TileBox panBox = areaInfo.boxLatLon;
// 计算四至的瓦片编号 // 计算四至的瓦片编号
TileIndex WN = LatLonToTile(panBox.north, panBox.west, zoom); TileIndex WN = LatLonToTile(panBox.north, panBox.west, zoom);
@ -57,8 +181,6 @@ void googleTile::ExportTile(cv::Mat _pan, TileInfo panInfo, std::string dir,std:
int yStart = EN.y; int yStart = EN.y;
int yEnd = ES.y; int yEnd = ES.y;
int panWidth = _pan.cols;
int panHeight = _pan.rows;
vector<TileInfo> taskTiles; vector<TileInfo> taskTiles;
@ -66,54 +188,32 @@ void googleTile::ExportTile(cv::Mat _pan, TileInfo panInfo, std::string dir,std:
{ {
for (size_t j = xStart; j < xEnd; j++) for (size_t j = xStart; j < xEnd; j++)
{ {
TileIndex id = {j,i,zoom}; TileIndex id = { j,i,zoom };
TileBox tilebox = GetTileBox(id); TileBox tilebox = GetTileBox(id);
// 填充 // 瓦片不完整覆盖则不输出
TileInfo info = { 0 }; if (tilebox.west < panBox.west|| panBox.north < tilebox.north
info.box = tilebox; || tilebox.east > panBox.east || panBox.south > tilebox.south)
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; 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);
// 标准web瓦片尺寸
// if (tile.cols != 256 || tile.rows != 256)
// {
// cv::resize(tile, tile, cv::Size(256, 256));
// }
// 生成文件名
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); RECT32S rect = { 0 };
rect.x = (tilebox.west - panBox.west) / (panBox.east - panBox.west) * panWidth;
rect.y = (panBox.north - tilebox.north) / (panBox.north - panBox.south) * panHeight;
rect.w = (tilebox.east - panBox.west) / (panBox.east - panBox.west) * panWidth - rect.x;
rect.h = (panBox.north - tilebox.south) / (panBox.north - panBox.south) * panHeight - rect.y;
// 填充
TileInfo info = { 0 };
info.boxLatLon = tilebox;
info.ind = id;
info.boxInPan = rect;
// 补充瓦片信息 // 补充瓦片信息
info.tileName = std::to_string(j) + "_" + std::to_string(i) + "_" + std::to_string(zoom); info.tileName = std::to_string(j) + "_" + std::to_string(i) + "_" + std::to_string(zoom);
@ -123,16 +223,14 @@ void googleTile::ExportTile(cv::Mat _pan, TileInfo panInfo, std::string dir,std:
taskTiles.push_back(info); taskTiles.push_back(info);
} }
} }
// 输出KML
WriteKml(taskTiles,dir + "/" + fileString + "_z" + std::to_string(zoom) + ".kml");
return taskTiles;
} }
int googleTile::getZoomLevel(float mp) int googleTile::ZoomLevel(float mp)
{ {
int nLev = 0; int nLev = 0;
// 计算单位瓦片的实际覆盖 // 计算单位瓦片的实际覆盖
@ -140,11 +238,12 @@ int googleTile::getZoomLevel(float mp)
int earthLen = 40075017; int earthLen = 40075017;
nLev = std::floor(std::log2(earthLen / meters_cover)); nLev = std::floor(std::log2(earthLen / meters_cover)) + 1;
return nLev; return nLev;
} }
TileIndex googleTile::LatLonToTile(double latitude, double longitude, int zoomLevel) TileIndex googleTile::LatLonToTile(double latitude, double longitude, int zoomLevel)
{ {
// 将纬度转换为墨卡托投影 // 将纬度转换为墨卡托投影
@ -206,10 +305,10 @@ void googleTile::WriteKml(vector<TileInfo> tiles, string filePath)
kml_file << " <href>" << tile.href << "</href>" << std::endl; kml_file << " <href>" << tile.href << "</href>" << std::endl;
kml_file << " </Icon>" << std::endl; kml_file << " </Icon>" << std::endl;
kml_file << " <LatLonBox>" << std::endl; kml_file << " <LatLonBox>" << std::endl;
kml_file << " <north>" << tile.box.north << "</north>" << std::endl; kml_file << " <north>" << std::fixed << std::setprecision(6) << tile.boxLatLon.north << "</north>" << std::endl;
kml_file << " <south>" << tile.box.south << "</south>" << std::endl; kml_file << " <south>" << std::fixed << std::setprecision(6) << tile.boxLatLon.south << "</south>" << std::endl;
kml_file << " <east>" << tile.box.east << "</east>" << std::endl; kml_file << " <east>" << std::fixed << std::setprecision(6) << tile.boxLatLon.east << "</east>" << std::endl;
kml_file << " <west>" << tile.box.west << "</west>" << std::endl; kml_file << " <west>" << std::fixed << std::setprecision(6) << tile.boxLatLon.west << "</west>" << std::endl;
kml_file << " </LatLonBox>" << std::endl; kml_file << " </LatLonBox>" << std::endl;
kml_file << "</GroundOverlay>" << std::endl; kml_file << "</GroundOverlay>" << std::endl;
} }
@ -223,6 +322,3 @@ void googleTile::WriteKml(vector<TileInfo> tiles, string filePath)
} }

@ -4,10 +4,11 @@
#include <opencv2/opencv.hpp> #include <opencv2/opencv.hpp>
#include <string> #include <string>
#include <vector> #include <vector>
#include "StitchStruct.h"
using std::vector; using std::vector;
using std::string; using std::string;
struct TileIndex struct TileIndex
{ {
int x; int x;
@ -23,14 +24,20 @@ struct TileBox
double west; double west;
}; };
// 瓦片信息
struct TileInfo struct TileInfo
{ {
TileIndex ind; TileIndex ind;
std::string tileName; std::string tileName;
std::string href;//相对路径 std::string href;//相对路径
TileBox box; TileBox boxLatLon;
RECT32S boxInPan;
SINT32 nStatus;//状态:0-未输出 1-已输出 2-已优化并输出
}; };
// 标准web瓦片大小
const int TILE_SIZE = 256; const int TILE_SIZE = 256;
class googleTile class googleTile
@ -43,23 +50,25 @@ public:
// 输出kml png // 输出kml png
void ExportGeoPng(cv::Mat _pan, TileInfo info, std::string dir); void ExportGeoPng(cv::Mat _pan, TileInfo info, std::string dir);
// 输出tile // 输出tile组
void ExportTile(cv::Mat _pan, TileInfo info, std::string dir, std::string fileString); void ExportTileSet(cv::Mat _pan, vector<TileInfo> taskTilesVec, std::string dir, std::string fileString);
// 输出单个tile
void ExportOneTile(cv::Mat _pan, TileInfo tile, std::string dir, std::string fileString);
// 计算给定分辨率最接近的瓦片等级,mp单位 米/像素
int getZoomLevel(float mp);
// 使用感兴趣区大瓦片计算小瓦片覆盖表
vector<TileInfo> CalcTileOverlayVec(TileInfo areaInfo, int panWidth, int panHeight);
// 计算给定分辨率最接近的瓦片等级,mp单位 米/像素
int ZoomLevel(float mp);
private: private:
TileIndex LatLonToTile(double latitude, double longitude, int zoomLevel); TileIndex LatLonToTile(double latitude, double longitude, int zoomLevel);
// 瓦片编号对应的信息 // 瓦片编号对应的信息
TileBox GetTileBox(TileIndex ind); TileBox GetTileBox(TileIndex ind);
void WriteKml(vector<TileInfo> tiles, string filePath);
void WriteKml(vector<TileInfo> tiles,string filePath);
int zoom_;
}; };

Loading…
Cancel
Save