#include #include #include #include #include #include #include #include extern "C" { #include #include #include #include } #include #include #include static const int tile_width = 500; static const int tile_height = 500; using namespace std; using Metric = downscale; using Database = image_database; 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())); Mozaic 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 static void save_mozaic(Mozaiq const & mozaic, string filename){ auto const pix_fmt = AV_PIX_FMT_YUVJ444P; auto const codec_id= AV_CODEC_ID_MJPEG; auto const total_width = mozaic.h_tiles * tile_width; auto const total_height = mozaic.v_tiles * tile_height; // Create output frame std::vector> data(make_u(avpicture_get_size(pix_fmt, total_width, total_height)), 0); const av::frame frame = av::frame_alloc(); avpicture_fill(reinterpret_cast(frame.get()), data.data(), pix_fmt, total_width, total_height); frame->width = total_width; frame->height = total_height; frame->format = pix_fmt; // For each tile: get the part, copy input to it for(int r = 0; r < mozaic.v_tiles; ++r) { for(int c = 0; c < mozaic.h_tiles; ++c){ auto frame_part = av::crop(frame, c * tile_width, r * tile_height, tile_width, tile_height); auto input = crop_to_square(open_image(mozaic[r][c])); auto context = sws::create_context(input, frame_part); sws::scale(context, input, frame_part); } } // Encode auto codec = avcodec_find_encoder(codec_id); if(!codec) throw av::error("Could not find codec"); auto codec_ctx = std::unique_ptr>(avcodec_alloc_context3(codec), [](auto x){ avcodec_free_context(&x); }); if(!codec_ctx) throw av::error("Could not allocate codec context"); codec_ctx->pix_fmt = pix_fmt; codec_ctx->width = frame->width; codec_ctx->height = frame->height; codec_ctx->time_base = av_make_q(1, 1); auto opened_codec = av::codec_open(codec_ctx.get(), codec, nullptr); auto const buffer_size = avpicture_get_size(pix_fmt, codec_ctx->width, codec_ctx->height); std::vector buffer(make_u(buffer_size), 0); auto const output_size = avcodec_encode_video(codec_ctx.get(), buffer.data(), buffer_size, frame.get()); assert(output_size <= buffer_size); auto file = fopen(filename.c_str(), "wb"); fwrite(buffer.data(), 1, make_u(output_size), file); fclose(file); } int main(){ av_register_all(); av_log_set_level(AV_LOG_QUIET); // TODO: use boost::program_options string const database_directory = "vakantie"; string const filename = "vakantie.jpg"; string const output = "output/vakantie.jpg"; int const h_tiles = 4*4; int const v_tiles = 3*4; auto const db = read_database(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; }