Making mosaic images with ffmpeg
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

106 lines
3.3 KiB

#include <database.hpp>
#include <fingerprints.hpp>
#include <image_io.hpp>
#include <mozaic.hpp>
#include <read_database.hpp>
#include <utilities.hpp>
#include <av/av.hpp>
#include <av/sws.hpp>
#include <boost/filesystem.hpp>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}
#include <fstream>
#include <iostream>
#include <utility>
static const int tile_width = 500;
static const int tile_height = 500;
using namespace std;
using Metric = fingerprints::rgb;
using Database = image_database<Metric>;
static auto calculate_all_distances(Database const & db, string const & filename, int h_tiles, int v_tiles){
using return_type = decltype(db.distances_for_image(std::declval<av::frame const &>()));
Mozaic<return_type> mozaic(h_tiles, v_tiles);
apply_to_tiles(filename, h_tiles, v_tiles, [&](int c, int r, av::frame const & frame){
mozaic[r][c] = db.distances_for_image(frame);
});
return mozaic;
}
template <typename Mozaiq>
static void save_mozaic(Mozaiq const & mozaic, string filename){
auto const pix_fmt = AV_PIX_FMT_YUVJ444P;
auto const total_width = mozaic.h_tiles * tile_width;
auto const total_height = mozaic.v_tiles * tile_height;
// Create output frame
const auto frame_data = [=]{
std::vector<uint8_t, av::allocator<uint8_t>> data(make_u(avpicture_get_size(pix_fmt, total_width, total_height)), 0);
auto frame = av::frame_alloc();
avpicture_fill(reinterpret_cast<AVPicture*>(frame.get()), data.data(), pix_fmt, total_width, total_height);
frame->width = total_width;
frame->height = total_height;
frame->format = pix_fmt;
return std::make_pair(std::move(frame), std::move(data));
}();
const auto & frame = frame_data.first;
// For each tile: get the part, copy input to it
av::frame frame_part = av::frame_clone(frame);
frame_part->width = tile_width;
frame_part->height = tile_height;
for(int r = 0; r < mozaic.v_tiles; ++r) {
for(int c = 0; c < mozaic.h_tiles; ++c){
av_picture_crop(reinterpret_cast<AVPicture*>(frame_part.get()), reinterpret_cast<AVPicture const*>(frame.get()), av::get_format(frame), r * tile_height, c * tile_width);
auto const input = crop_to_square(open_image(mozaic[r][c]));
auto context = sws::create_context(input, frame_part);
sws::scale(context, input, frame_part);
}
}
encode_as_jpg(frame, filename);
}
int main(int argc, char *argv[]){
if(argc != 6){
cerr << "usage: main <dir> <photo> <output> <h tiles> <v tiles>\n";
return 1;
}
av_register_all();
av_log_set_level(AV_LOG_QUIET);
// TODO: use boost::program_options
string const database_directory = argv[1];
string const filename = argv[2];
string const output = argv[3];
int const h_tiles = stoi(argv[4]);
int const v_tiles = stoi(argv[5]);
auto const db = read_database<Metric>(database_directory);
cout << colors::green("Read database: ") << db.size() << endl;
auto const distances = calculate_all_distances(db, filename, h_tiles, v_tiles);
cout << colors::green("Calculated distances for ") << filename << endl;
auto const best_indices = optimize_unique(distances);
cout << colors::green("Picked best distances") << endl;
auto const mozaic = mozaic_fmap(best_indices, [&](auto i){ return db.filename(i); });
cout << colors::green("Picked filenames") << endl;
save_mozaic(mozaic, output);
cout << colors::green("Saved Mozaic to ") << output << endl;
}