video compression and such
This commit is contained in:
@@ -10,6 +10,7 @@
|
|||||||
#include "../util/output/bmpwriter.hpp"
|
#include "../util/output/bmpwriter.hpp"
|
||||||
#include "../util/timing_decorator.cpp"
|
#include "../util/timing_decorator.cpp"
|
||||||
#include "../util/output/frame.hpp"
|
#include "../util/output/frame.hpp"
|
||||||
|
#include "../util/output/video.hpp"
|
||||||
|
|
||||||
struct AnimationConfig {
|
struct AnimationConfig {
|
||||||
int width = 1024;
|
int width = 1024;
|
||||||
@@ -71,7 +72,6 @@ void expandPixel(Grid2& grid, AnimationConfig config, std::vector<std::tuple<siz
|
|||||||
TIME_FUNCTION;
|
TIME_FUNCTION;
|
||||||
std::vector<std::tuple<size_t, Vec2, Vec4>> newseeds;
|
std::vector<std::tuple<size_t, Vec2, Vec4>> newseeds;
|
||||||
|
|
||||||
|
|
||||||
std::unordered_set<size_t> visitedThisFrame;
|
std::unordered_set<size_t> visitedThisFrame;
|
||||||
for (const auto& seed : seeds) {
|
for (const auto& seed : seeds) {
|
||||||
visitedThisFrame.insert(std::get<0>(seed));
|
visitedThisFrame.insert(std::get<0>(seed));
|
||||||
@@ -89,7 +89,6 @@ void expandPixel(Grid2& grid, AnimationConfig config, std::vector<std::tuple<siz
|
|||||||
}
|
}
|
||||||
visitedThisFrame.insert(neighbor);
|
visitedThisFrame.insert(neighbor);
|
||||||
|
|
||||||
|
|
||||||
Vec2 neipos = grid.getPositionID(neighbor);
|
Vec2 neipos = grid.getPositionID(neighbor);
|
||||||
Vec4 neighborColor = grid.getColor(neighbor);
|
Vec4 neighborColor = grid.getColor(neighbor);
|
||||||
float distance = seedPOS.distance(neipos);
|
float distance = seedPOS.distance(neipos);
|
||||||
@@ -116,63 +115,52 @@ void expandPixel(Grid2& grid, AnimationConfig config, std::vector<std::tuple<siz
|
|||||||
seeds = std::move(newseeds);
|
seeds = std::move(newseeds);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool exportavi(std::vector<std::vector<uint8_t>> frames, AnimationConfig config) {
|
|
||||||
TIME_FUNCTION;
|
|
||||||
std::string filename = "output/chromatic_transformation.avi";
|
|
||||||
|
|
||||||
std::cout << "Frame count: " << frames.size() << std::endl;
|
|
||||||
std::cout << "Frame size: " << (frames.empty() ? 0 : frames[0].size()) << std::endl;
|
|
||||||
std::cout << "Width: " << config.width << ", Height: " << config.height << std::endl;
|
|
||||||
|
|
||||||
std::filesystem::path dir = "output";
|
|
||||||
if (!std::filesystem::exists(dir)) {
|
|
||||||
if (!std::filesystem::create_directories(dir)) {
|
|
||||||
std::cout << "Failed to create output directory!" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool success = AVIWriter::saveAVI(filename, frames, config.width+1, config.height+1, config.fps);
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
// Check if file actually exists
|
|
||||||
if (std::filesystem::exists(filename)) {
|
|
||||||
auto file_size = std::filesystem::file_size(filename);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::cout << "Failed to save AVI file!" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
AnimationConfig config;
|
AnimationConfig config;
|
||||||
|
|
||||||
Grid2 grid = setup(config);
|
Grid2 grid = setup(config);
|
||||||
Preview(grid);
|
Preview(grid);
|
||||||
std::vector<std::tuple<size_t, Vec2, Vec4>> seeds = pickSeeds(grid,config);
|
std::vector<std::tuple<size_t, Vec2, Vec4>> seeds = pickSeeds(grid,config);
|
||||||
std::vector<frame> frames; // Change to vector of frame objects
|
|
||||||
|
// Create video object with BGR channels and configured FPS
|
||||||
|
video vid(config.width+1, config.height+1, {'B', 'G', 'R'}, config.fps, true);
|
||||||
|
|
||||||
for (int i = 0; i < config.totalFrames; ++i){
|
for (int i = 0; i < config.totalFrames; ++i){
|
||||||
std::cout << "Processing frame " << i + 1 << "/" << config.totalFrames << std::endl;
|
std::cout << "Processing frame " << i + 1 << "/" << config.totalFrames << std::endl;
|
||||||
expandPixel(grid,config,seeds);
|
expandPixel(grid,config,seeds);
|
||||||
|
|
||||||
frame outputFrame;
|
frame outputFrame;
|
||||||
grid.getGridAsFrame(outputFrame, {'B', 'G', 'R'}); // Directly get as BGR frame
|
grid.getGridAsFrame(outputFrame, {'B', 'G', 'R'});
|
||||||
|
|
||||||
// Alternative: Use the dedicated BGR method
|
// Add frame to video (this will compress it using RLE + differential encoding)
|
||||||
// int width, height;
|
vid.add_frame(outputFrame);
|
||||||
// grid.getGridAsBGRFrame(width, height, outputFrame);
|
|
||||||
|
|
||||||
frames.push_back(outputFrame);
|
// Optional: Print compression stats every 50 frames
|
||||||
|
// if ((i + 1) % 50 == 0) {
|
||||||
|
// auto stats = vid.get_compression_stats();
|
||||||
|
// std::cout << "Frame " << (i + 1) << " - Compression ratio: " << stats.overall_ratio
|
||||||
|
// << ", Total compressed: " << stats.total_compressed_bytes << " bytes" << std::endl;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the frame-based AVIWriter overload
|
// Use the video-based AVIWriter overload
|
||||||
bool success = AVIWriter::saveAVI("output/chromatic_transformation.avi", frames, config.fps);
|
bool success = AVIWriter::saveAVI("output/chromatic_transformation.avi", vid, config.fps);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
std::cout << "Successfully saved AVI with " << frames.size() << " frames" << std::endl;
|
// Print final compression statistics
|
||||||
|
auto final_stats = vid.get_compression_stats();
|
||||||
|
std::cout << "Successfully saved AVI with " << vid.frame_count() << " frames" << std::endl;
|
||||||
|
std::cout << "Final compression statistics:" << std::endl;
|
||||||
|
std::cout << " Total frames: " << final_stats.total_frames << std::endl;
|
||||||
|
std::cout << " Video duration: " << final_stats.video_duration << " seconds" << std::endl;
|
||||||
|
std::cout << " Uncompressed size: " << final_stats.total_uncompressed_bytes << " bytes" << std::endl;
|
||||||
|
std::cout << " Compressed size: " << final_stats.total_compressed_bytes << " bytes" << std::endl;
|
||||||
|
std::cout << " Overall compression ratio: " << final_stats.overall_ratio << std::endl;
|
||||||
|
std::cout << " Average frame compression ratio: " << final_stats.average_frame_ratio << std::endl;
|
||||||
|
|
||||||
|
// Optional: Save compressed video data for later use
|
||||||
|
auto serialized_data = vid.serialize();
|
||||||
|
std::cout << "Serialized video data size: " << serialized_data.size() << " bytes" << std::endl;
|
||||||
} else {
|
} else {
|
||||||
std::cout << "Failed to save AVI file!" << std::endl;
|
std::cout << "Failed to save AVI file!" << std::endl;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,6 +178,28 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// New method for video objects
|
||||||
|
static bool saveAVI(const std::string& filename,
|
||||||
|
const video& vid,
|
||||||
|
float fps = 0.0f) {
|
||||||
|
if (vid.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use video's FPS if not overridden, otherwise use provided FPS
|
||||||
|
float actualFps = (fps > 0.0f) ? fps : static_cast<float>(vid.fps());
|
||||||
|
|
||||||
|
if (actualFps <= 0.0f) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all frames from the video
|
||||||
|
std::vector<frame> frames = vid.get_all_frames();
|
||||||
|
|
||||||
|
// Use the existing frame-based implementation
|
||||||
|
return saveAVI(filename, frames, actualFps);
|
||||||
|
}
|
||||||
|
|
||||||
// Original method for vector of raw frame data
|
// Original method for vector of raw frame data
|
||||||
static bool saveAVI(const std::string& filename,
|
static bool saveAVI(const std::string& filename,
|
||||||
const std::vector<std::vector<uint8_t>>& frames,
|
const std::vector<std::vector<uint8_t>>& frames,
|
||||||
@@ -186,9 +208,6 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::cout << "1" << "width: " << width <<
|
|
||||||
// "height: " << height << "frame count: " << fps << std::endl;
|
|
||||||
|
|
||||||
// Validate frame sizes
|
// Validate frame sizes
|
||||||
size_t expectedFrameSize = width * height * 3;
|
size_t expectedFrameSize = width * height * 3;
|
||||||
for (const auto& frame : frames) {
|
for (const auto& frame : frames) {
|
||||||
@@ -197,13 +216,11 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::cout << "2" << std::endl;
|
|
||||||
// Create directory if needed
|
// Create directory if needed
|
||||||
if (!createDirectoryIfNeeded(filename)) {
|
if (!createDirectoryIfNeeded(filename)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::cout << "3" << std::endl;
|
|
||||||
std::ofstream file(filename, std::ios::binary);
|
std::ofstream file(filename, std::ios::binary);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return false;
|
return false;
|
||||||
@@ -217,7 +234,6 @@ public:
|
|||||||
uint32_t frameSize = rowSize * height;
|
uint32_t frameSize = rowSize * height;
|
||||||
uint32_t totalDataSize = frameCount * frameSize;
|
uint32_t totalDataSize = frameCount * frameSize;
|
||||||
|
|
||||||
// std::cout << "4" << std::endl;
|
|
||||||
// RIFF AVI header
|
// RIFF AVI header
|
||||||
RIFFChunk riffHeader;
|
RIFFChunk riffHeader;
|
||||||
riffHeader.chunkId = 0x46464952; // 'RIFF'
|
riffHeader.chunkId = 0x46464952; // 'RIFF'
|
||||||
@@ -231,7 +247,6 @@ public:
|
|||||||
uint32_t hdrlListStart = static_cast<uint32_t>(file.tellp());
|
uint32_t hdrlListStart = static_cast<uint32_t>(file.tellp());
|
||||||
writeList(file, 0x6C726468, nullptr, 0); // 'hdrl' - we'll fill size later
|
writeList(file, 0x6C726468, nullptr, 0); // 'hdrl' - we'll fill size later
|
||||||
|
|
||||||
// std::cout << "5" << std::endl;
|
|
||||||
// avih chunk
|
// avih chunk
|
||||||
AVIMainHeader mainHeader;
|
AVIMainHeader mainHeader;
|
||||||
mainHeader.microSecPerFrame = microSecPerFrame;
|
mainHeader.microSecPerFrame = microSecPerFrame;
|
||||||
@@ -251,7 +266,6 @@ public:
|
|||||||
|
|
||||||
writeChunk(file, 0x68697661, &mainHeader, sizeof(mainHeader)); // 'avih'
|
writeChunk(file, 0x68697661, &mainHeader, sizeof(mainHeader)); // 'avih'
|
||||||
|
|
||||||
// std::cout << "6" << std::endl;
|
|
||||||
// strl list
|
// strl list
|
||||||
uint32_t strlListStart = static_cast<uint32_t>(file.tellp());
|
uint32_t strlListStart = static_cast<uint32_t>(file.tellp());
|
||||||
writeList(file, 0x6C727473, nullptr, 0); // 'strl' - we'll fill size later
|
writeList(file, 0x6C727473, nullptr, 0); // 'strl' - we'll fill size later
|
||||||
@@ -294,7 +308,6 @@ public:
|
|||||||
|
|
||||||
writeChunk(file, 0x66727473, &bitmapInfo, sizeof(bitmapInfo)); // 'strf'
|
writeChunk(file, 0x66727473, &bitmapInfo, sizeof(bitmapInfo)); // 'strf'
|
||||||
|
|
||||||
// std::cout << "7" << std::endl;
|
|
||||||
// Update strl list size
|
// Update strl list size
|
||||||
uint32_t strlListEnd = static_cast<uint32_t>(file.tellp());
|
uint32_t strlListEnd = static_cast<uint32_t>(file.tellp());
|
||||||
file.seekp(strlListStart + 4);
|
file.seekp(strlListStart + 4);
|
||||||
@@ -302,7 +315,6 @@ public:
|
|||||||
file.write(reinterpret_cast<const char*>(&strlListSize), 4);
|
file.write(reinterpret_cast<const char*>(&strlListSize), 4);
|
||||||
file.seekp(strlListEnd);
|
file.seekp(strlListEnd);
|
||||||
|
|
||||||
// std::cout << "8" << std::endl;
|
|
||||||
// Update hdrl list size
|
// Update hdrl list size
|
||||||
uint32_t hdrlListEnd = static_cast<uint32_t>(file.tellp());
|
uint32_t hdrlListEnd = static_cast<uint32_t>(file.tellp());
|
||||||
file.seekp(hdrlListStart + 4);
|
file.seekp(hdrlListStart + 4);
|
||||||
@@ -310,7 +322,6 @@ public:
|
|||||||
file.write(reinterpret_cast<const char*>(&hdrlListSize), 4);
|
file.write(reinterpret_cast<const char*>(&hdrlListSize), 4);
|
||||||
file.seekp(hdrlListEnd);
|
file.seekp(hdrlListEnd);
|
||||||
|
|
||||||
// std::cout << "9" << std::endl;
|
|
||||||
// movi list
|
// movi list
|
||||||
uint32_t moviListStart = static_cast<uint32_t>(file.tellp());
|
uint32_t moviListStart = static_cast<uint32_t>(file.tellp());
|
||||||
writeList(file, 0x69766F6D, nullptr, 0); // 'movi' - we'll fill size later
|
writeList(file, 0x69766F6D, nullptr, 0); // 'movi' - we'll fill size later
|
||||||
@@ -322,7 +333,6 @@ public:
|
|||||||
for (uint32_t i = 0; i < frameCount; ++i) {
|
for (uint32_t i = 0; i < frameCount; ++i) {
|
||||||
uint32_t frameStart = static_cast<uint32_t>(file.tellp()) - moviListStart - 4;
|
uint32_t frameStart = static_cast<uint32_t>(file.tellp()) - moviListStart - 4;
|
||||||
|
|
||||||
// std::cout << "10-" << i << std::endl;
|
|
||||||
// Create padded frame data (BMP-style bottom-to-top with padding)
|
// Create padded frame data (BMP-style bottom-to-top with padding)
|
||||||
std::vector<uint8_t> paddedFrame(frameSize, 0);
|
std::vector<uint8_t> paddedFrame(frameSize, 0);
|
||||||
const auto& frame = frames[i];
|
const auto& frame = frames[i];
|
||||||
@@ -336,7 +346,6 @@ public:
|
|||||||
// Padding bytes remain zeros
|
// Padding bytes remain zeros
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::cout << "11-" << i << std::endl;
|
|
||||||
// Write frame as '00db' chunk
|
// Write frame as '00db' chunk
|
||||||
writeChunk(file, 0x62643030, paddedFrame.data(), frameSize); // '00db'
|
writeChunk(file, 0x62643030, paddedFrame.data(), frameSize); // '00db'
|
||||||
|
|
||||||
@@ -349,7 +358,6 @@ public:
|
|||||||
indexEntries.push_back(entry);
|
indexEntries.push_back(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::cout << "12" << std::endl;
|
|
||||||
// Update movi list size
|
// Update movi list size
|
||||||
uint32_t moviListEnd = static_cast<uint32_t>(file.tellp());
|
uint32_t moviListEnd = static_cast<uint32_t>(file.tellp());
|
||||||
file.seekp(moviListStart + 4);
|
file.seekp(moviListStart + 4);
|
||||||
@@ -357,7 +365,6 @@ public:
|
|||||||
file.write(reinterpret_cast<const char*>(&moviListSize), 4);
|
file.write(reinterpret_cast<const char*>(&moviListSize), 4);
|
||||||
file.seekp(moviListEnd);
|
file.seekp(moviListEnd);
|
||||||
|
|
||||||
// std::cout << "13" << std::endl;
|
|
||||||
// idx1 chunk - index
|
// idx1 chunk - index
|
||||||
uint32_t idx1Size = static_cast<uint32_t>(indexEntries.size() * sizeof(AVIIndexEntry));
|
uint32_t idx1Size = static_cast<uint32_t>(indexEntries.size() * sizeof(AVIIndexEntry));
|
||||||
writeChunk(file, 0x31786469, indexEntries.data(), idx1Size); // 'idx1'
|
writeChunk(file, 0x31786469, indexEntries.data(), idx1Size); // 'idx1'
|
||||||
@@ -368,7 +375,6 @@ public:
|
|||||||
uint32_t riffSize = fileEnd - riffStartPos - 8;
|
uint32_t riffSize = fileEnd - riffStartPos - 8;
|
||||||
file.write(reinterpret_cast<const char*>(&riffSize), 4);
|
file.write(reinterpret_cast<const char*>(&riffSize), 4);
|
||||||
|
|
||||||
// std::cout << "14" << std::endl;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user