Update the video conversion: Ensure the correct file order
This commit is contained in:
@@ -2021,8 +2021,23 @@ namespace ANSCENTER
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort for consistent ordering
|
// Sort by filename (lexicographic, case-sensitive) so playback order
|
||||||
std::sort(imageFiles.begin(), imageFiles.end());
|
// follows the on-disk name order. Sorting by std::filesystem::path::
|
||||||
|
// filename() is explicit and robust even if cv::glob ever returns
|
||||||
|
// entries with different path prefixes or separators.
|
||||||
|
// Example input:
|
||||||
|
// 20260114_181439.703.jpg
|
||||||
|
// 20260114_181439.703_000.jpg
|
||||||
|
// 20260114_181439.703_001.jpg
|
||||||
|
// 20260114_181439.703_002.jpg
|
||||||
|
// 20260114_181439.803.jpg
|
||||||
|
// Byte-wise '.' (0x2E) < '_' (0x5F), so "...703.jpg" correctly
|
||||||
|
// precedes "...703_000.jpg".
|
||||||
|
std::sort(imageFiles.begin(), imageFiles.end(),
|
||||||
|
[](const cv::String& a, const cv::String& b) {
|
||||||
|
return std::filesystem::path(a).filename().string() <
|
||||||
|
std::filesystem::path(b).filename().string();
|
||||||
|
});
|
||||||
|
|
||||||
// Cap at 5 minutes max duration
|
// Cap at 5 minutes max duration
|
||||||
const int maxFrames = fps * 300;
|
const int maxFrames = fps * 300;
|
||||||
@@ -2307,7 +2322,13 @@ namespace ANSCENTER
|
|||||||
std::cerr << "Error: No images found in folder: " << imageFolder << std::endl;
|
std::cerr << "Error: No images found in folder: " << imageFolder << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::sort(imageFiles.begin(), imageFiles.end());
|
// Sort by filename (lexicographic, case-sensitive). See matching
|
||||||
|
// comment in ImagesToMP4() for rationale and ordering example.
|
||||||
|
std::sort(imageFiles.begin(), imageFiles.end(),
|
||||||
|
[](const cv::String& a, const cv::String& b) {
|
||||||
|
return std::filesystem::path(a).filename().string() <
|
||||||
|
std::filesystem::path(b).filename().string();
|
||||||
|
});
|
||||||
|
|
||||||
// Cap at 5 minutes max duration
|
// Cap at 5 minutes max duration
|
||||||
const int maxFrames = fps * 300;
|
const int maxFrames = fps * 300;
|
||||||
@@ -2712,7 +2733,13 @@ namespace ANSCENTER
|
|||||||
std::cerr << "Error: No images found in folder: " << imageFolder << std::endl;
|
std::cerr << "Error: No images found in folder: " << imageFolder << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::sort(imageFiles.begin(), imageFiles.end());
|
// Sort by filename (lexicographic, case-sensitive). See matching
|
||||||
|
// comment in ImagesToMP4() for rationale and ordering example.
|
||||||
|
std::sort(imageFiles.begin(), imageFiles.end(),
|
||||||
|
[](const cv::String& a, const cv::String& b) {
|
||||||
|
return std::filesystem::path(a).filename().string() <
|
||||||
|
std::filesystem::path(b).filename().string();
|
||||||
|
});
|
||||||
|
|
||||||
const int maxFrames = fps * 300;
|
const int maxFrames = fps * 300;
|
||||||
if (static_cast<int>(imageFiles.size()) > maxFrames) {
|
if (static_cast<int>(imageFiles.size()) > maxFrames) {
|
||||||
@@ -5551,7 +5578,7 @@ extern "C" __declspec(dllexport) int ANSCV_ImagesToMP4_S(
|
|||||||
const char* imageFolder,
|
const char* imageFolder,
|
||||||
const char* outputVideoPath,
|
const char* outputVideoPath,
|
||||||
int maxWidth, int fps) {
|
int maxWidth, int fps) {
|
||||||
|
return ANSCV_ImagesToMP4FF_S(imageFolder,outputVideoPath,maxWidth,fps);
|
||||||
try {
|
try {
|
||||||
if (!imageFolder || strlen(imageFolder) == 0) {
|
if (!imageFolder || strlen(imageFolder) == 0) {
|
||||||
std::cerr << "Error: Invalid image folder path!" << std::endl;
|
std::cerr << "Error: Invalid image folder path!" << std::endl;
|
||||||
|
|||||||
@@ -13,6 +13,9 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <chrono>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
using namespace ANSCENTER;
|
using namespace ANSCENTER;
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
@@ -1369,7 +1372,62 @@ int TestGetImage() {
|
|||||||
int GenerateVideo() {
|
int GenerateVideo() {
|
||||||
std::string imageFolder = "E:\\Programs\\DemoAssets\\ImageSeries\\20260415_142435.655";
|
std::string imageFolder = "E:\\Programs\\DemoAssets\\ImageSeries\\20260415_142435.655";
|
||||||
std::string outputVideoPath = "E:\\Programs\\DemoAssets\\ImageSeries\\output7.mp4";
|
std::string outputVideoPath = "E:\\Programs\\DemoAssets\\ImageSeries\\output7.mp4";
|
||||||
int conversionResult = ANSCV_ImagesToMP4_S(imageFolder.c_str(), outputVideoPath.c_str(), 0,20);
|
|
||||||
|
// Enumerate + sort by filename (same comparator the DLL uses) so we can
|
||||||
|
// preview the exact playback order.
|
||||||
|
std::vector<std::string> images;
|
||||||
|
try {
|
||||||
|
for (const auto& entry : std::filesystem::directory_iterator(imageFolder)) {
|
||||||
|
if (!entry.is_regular_file()) continue;
|
||||||
|
std::string ext = entry.path().extension().string();
|
||||||
|
std::transform(ext.begin(), ext.end(), ext.begin(),
|
||||||
|
[](unsigned char c) { return std::tolower(c); });
|
||||||
|
if (ext == ".jpg" || ext == ".jpeg" || ext == ".png" ||
|
||||||
|
ext == ".bmp" || ext == ".tif" || ext == ".tiff") {
|
||||||
|
images.push_back(entry.path().string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "Warning: could not enumerate image folder: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
std::sort(images.begin(), images.end(),
|
||||||
|
[](const std::string& a, const std::string& b) {
|
||||||
|
return std::filesystem::path(a).filename().string() <
|
||||||
|
std::filesystem::path(b).filename().string();
|
||||||
|
});
|
||||||
|
size_t imageCount = images.size();
|
||||||
|
|
||||||
|
std::cout << "[Benchmark] ImagesToMP4_S" << std::endl;
|
||||||
|
std::cout << " source folder : " << imageFolder << std::endl;
|
||||||
|
std::cout << " output file : " << outputVideoPath << std::endl;
|
||||||
|
std::cout << " image count : " << imageCount << std::endl;
|
||||||
|
std::cout << " fps argument : 20" << std::endl;
|
||||||
|
|
||||||
|
// Print the on-disk playback order (what the DLL will consume).
|
||||||
|
std::cout << " playback order:" << std::endl;
|
||||||
|
for (size_t i = 0; i < images.size(); ++i) {
|
||||||
|
std::cout << " [" << (i + 1) << "] "
|
||||||
|
<< std::filesystem::path(images[i]).filename().string() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto t0 = std::chrono::high_resolution_clock::now();
|
||||||
|
int conversionResult = ANSCV_ImagesToMP4_S(imageFolder.c_str(), outputVideoPath.c_str(), 0, 20);
|
||||||
|
auto t1 = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
auto elapsed_us = std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0).count();
|
||||||
|
double elapsed_ms = elapsed_us / 1000.0;
|
||||||
|
double elapsed_s = elapsed_us / 1000000.0;
|
||||||
|
|
||||||
|
std::cout << "[Benchmark] ImagesToMP4_S finished." << std::endl;
|
||||||
|
std::cout << " result : " << conversionResult << std::endl;
|
||||||
|
std::cout << " elapsed : " << elapsed_ms << " ms (" << elapsed_s << " s)" << std::endl;
|
||||||
|
if (imageCount > 0) {
|
||||||
|
double per_image_ms = elapsed_ms / static_cast<double>(imageCount);
|
||||||
|
double throughput = static_cast<double>(imageCount) / elapsed_s;
|
||||||
|
std::cout << " per image : " << per_image_ms << " ms" << std::endl;
|
||||||
|
std::cout << " throughput : " << throughput << " images/s" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
if (!conversionResult) {
|
if (!conversionResult) {
|
||||||
std::cerr << "Failed to convert images to MP4." << std::endl;
|
std::cerr << "Failed to convert images to MP4." << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
|
|||||||
Reference in New Issue
Block a user