#include #include #include #include #include #include #include "API_UnderStitch.h" #include "API_FrontStitch.h" #include "PlatformDefine.h" #include #include "opencv2/opencv.hpp" #include struct FrameData { int frameNumber; std::string timeRange; int frameCnt; std::string timestamp; float focal_len = 0; float dzoom_ratio = 0; double latitude = 0; double longitude = 0; float rel_alt = 0; float abs_alt = 0; float gb_yaw = 0; float gb_pitch = 0; float gb_roll = 0; // 衍生参数 float real_focal_mm = 0; float pixel_size_um = 0; }; float extractValueAfter(const std::string& line, const std::string& key) { size_t pos = line.find(key); if (pos == std::string::npos) return 0; pos += key.length(); size_t end = line.find(' ', pos); if (end == std::string::npos) end = line.length(); return std::stof(line.substr(pos, end - pos)); } double extractDoubleAfter(const std::string& line, const std::string& key) { size_t pos = line.find(key); if (pos == std::string::npos) return 0; pos += key.length(); size_t end = line.find(' ', pos); if (end == std::string::npos) end = line.length(); return std::stod(line.substr(pos, end - pos)); } // 获取真实焦距和像元尺寸 void inferCameraParams_H20(FrameData& frame, const std::string& filename) { const float full_frame_diag = 43.3f; float sensor_diag = 0.0f; if (filename.find("_W") != std::string::npos) { sensor_diag = 9.4f; frame.pixel_size_um = 1.98f; } else if (filename.find("_Z") != std::string::npos) { sensor_diag = 7.7f; frame.pixel_size_um = 2.4f; } else if (filename.find("_T") != std::string::npos) { sensor_diag = 10.9f; frame.pixel_size_um = 12.0f; } else { sensor_diag = 9.4f; frame.pixel_size_um = 1.98f; } frame.real_focal_mm = frame.focal_len * (sensor_diag / full_frame_diag); } // 获取真实焦距和像元尺寸 void inferCameraParams_H30(FrameData& frame, const std::string& filename) { const float full_frame_diag = 43.3f; float sensor_diag = 0.0f; if (filename.find("_W") != std::string::npos) { frame.real_focal_mm = 6.72; frame.pixel_size_um = 2.4; } else if (filename.find("_Z") != std::string::npos) { } else if (filename.find("_T") != std::string::npos) { frame.real_focal_mm = 24; frame.pixel_size_um = 12; } else { } //frame.real_focal_mm = frame.focal_len * (sensor_diag / full_frame_diag); } std::vector parseDJISRT(const std::string& filename) { std::vector frames; std::ifstream file(filename); if (!file.is_open()) { std::cerr << "Error: Could not open file " << filename << std::endl; return frames; } std::string lines[5]; while (std::getline(file, lines[0])) { // Frame number line // Read next 4 lines for (int i = 1; i < 5; ++i) { if (!std::getline(file, lines[i])) break; } FrameData frame; // Line 1: Frame number frame.frameNumber = std::stoi(lines[0]); // Line 2: Time range (00:00:00,000 --> 00:00:00,033) frame.timeRange = lines[1]; // Line 3: FrameCnt and timestamp std::istringstream iss(lines[2]); std::string temp; iss >> temp >> frame.frameCnt >> frame.timestamp; // Line 4: Metadata (direct field extraction) const std::string& meta = lines[3]; frame.focal_len = extractValueAfter(meta, "focal_len: "); frame.dzoom_ratio = extractValueAfter(meta, "dzoom_ratio: "); frame.latitude = extractDoubleAfter(meta, "latitude: "); frame.longitude = extractDoubleAfter(meta, "longitude: "); frame.rel_alt = extractValueAfter(meta, "rel_alt: "); frame.abs_alt = extractValueAfter(meta, "abs_alt: "); frame.gb_yaw = extractValueAfter(meta, "gb_yaw: "); frame.gb_pitch = extractValueAfter(meta, "gb_pitch: "); frame.gb_roll = extractValueAfter(meta, "gb_roll: "); inferCameraParams_H30(frame, filename); frames.push_back(frame); // Skip the empty line (line 5) } file.close(); return frames; } void ProcDJVideo(vector videoPathList, vector srtPathList) { auto stitcher = API_UnderStitch::Create(); stitcher->SetOutput("DJI", "D:/google_tiles"); GD_VIDEO_FRAME_S frame = { 0 };//输入帧 GD_VIDEO_FRAME_S pan = { 0 };//输出全景 cv::Mat mat_pan;//全景显示 cv::VideoCapture cap(videoPathList[0]); // Get video properties double fps = cap.get(cv::CAP_PROP_FPS); int width = static_cast(cap.get(cv::CAP_PROP_FRAME_WIDTH)); int height = static_cast(cap.get(cv::CAP_PROP_FRAME_HEIGHT)); int frame_count = static_cast(cap.get(cv::CAP_PROP_FRAME_COUNT)); // dsample int nDownSample = 1; if (width > 3000) { nDownSample = 2; } std::vector srt_init = parseDJISRT(srtPathList[0]); FrameInfo info = { 0 }; info.nFrmID = 0; info.camInfo.nFocus = srt_init[0].real_focal_mm; info.camInfo.fPixelSize = srt_init[0].pixel_size_um * nDownSample; info.craft.stAtt.fYaw = srt_init[0].gb_yaw; info.craft.stAtt.fPitch = srt_init[0].gb_pitch + 90; info.craft.stAtt.fRoll = srt_init[0].gb_roll; info.craft.stPos.B = srt_init[0].latitude; info.craft.stPos.L = srt_init[0].longitude; info.craft.stPos.H = srt_init[0].abs_alt; info.nEvHeight = srt_init[0].abs_alt; info.servoInfo.fServoAz = 0; info.servoInfo.fServoPt = 0; info.nWidth = width / nDownSample; info.nHeight = height / nDownSample; // 初始化 stitcher->Init(info); mat_pan = stitcher->ExportPanMat(); cap.release(); cv::VideoWriter output; output.open("D:/DJ_stitchVL.mp4", cv::VideoWriter::fourcc('H', '2', '6', '4'), 5, cv::Size(mat_pan.cols/8, mat_pan.rows/8), true); for (size_t vid = 0; vid < videoPathList.size(); vid++) { printf("Proc %s\n", videoPathList[vid].c_str()); cv::VideoCapture cap(videoPathList[vid]); std::vector srt = parseDJISRT(srtPathList[vid]); int frmID = 0; while (true) { cv::Mat mat; // Read a new frame cap >> mat; // Check if frame is empty (end of video) if (mat.empty()) { std::cout << "End of video\n"; cap.release(); break; } frmID++; Mat mat_ds2; cv::resize(mat, mat_ds2, cv::Size(width / nDownSample, height / nDownSample)); FrameInfo info = { 0 }; info.nFrmID = frmID; info.camInfo.nFocus = srt[frmID].real_focal_mm; info.camInfo.fPixelSize = srt[frmID].pixel_size_um * nDownSample; info.craft.stAtt.fYaw = srt[frmID].gb_yaw; info.craft.stAtt.fPitch = srt[frmID].gb_pitch + 90; info.craft.stAtt.fRoll = srt[frmID].gb_roll; info.craft.stPos.B = srt[frmID].latitude; info.craft.stPos.L = srt[frmID].longitude; info.craft.stPos.H = srt[frmID].abs_alt; info.nEvHeight = srt[frmID].abs_alt; info.servoInfo.fServoAz = 0; info.servoInfo.fServoPt = 0; info.nWidth = mat_ds2.cols; info.nHeight = mat_ds2.rows; frame.enPixelFormat = GD_PIXEL_FORMAT_RGB_PACKED; frame.u32Width = mat_ds2.cols; frame.u32Height = mat_ds2.rows; frame.u64VirAddr[0] = mat_ds2.data; if (frmID % 10 != 0) { continue; } int progress = float(frmID) / frame_count * 100; std::cout << progress << "% " << info.craft.stPos.B << " " << info.craft.stPos.L << " " << info.craft.stPos.H << " " << info.craft.stAtt.fYaw << " " << info.craft.stAtt.fPitch << " " << info.craft.stAtt.fRoll << " " << info.servoInfo.fServoAz << " " << info.servoInfo.fServoPt << std::endl; cv::TickMeter tm; tm.start(); // 基于外参的快拼 stitcher->Run(frame, info); tm.stop(); Mat pan_rgb, pan_rgb_ds; cv::cvtColor(mat_pan, pan_rgb, cv::COLOR_BGRA2BGR); cv::resize(pan_rgb, pan_rgb_ds, cv::Size(pan_rgb.cols / 8, pan_rgb.rows / 8)); output.write(pan_rgb_ds); imshow("pan_rgb", pan_rgb_ds); if (cv::waitKey(1) == 27) { break; } } cv::TickMeter tm; tm.start(); // 处理帧 stitcher->OptAndOutCurrPan(); stitcher->Stop(); tm.stop(); std::cout << "time opt:" << tm.getTimeMilli() << std::endl; output.release(); } } using std::string; int main() { vector videoPathList; vector srtPathList; string folder = "F:/DJI_202504181507_016/"; // //videoPathList.push_back(folder + "DJI_20250418152649_0005_W.MP4"); //srtPathList.push_back(folder + "DJI_20250418152649_0005_W.srt"); //videoPathList.push_back(folder + "DJI_20250418153043_0006_T.MP4"); //srtPathList.push_back(folder + "DJI_20250418153043_0006_T.srt"); videoPathList.push_back(folder + "DJI_20250418152649_0005_T.MP4"); srtPathList.push_back(folder + "DJI_20250418152649_0005_T.srt"); ProcDJVideo(videoPathList, srtPathList); return 0; }