#include #include #include #include #include #include #include extern "C" { #include #include #include #include } #include #include #include #include #include #include static const int tile_width = 128; static const int tile_height = 128; using namespace std; namespace fs = boost::filesystem; namespace ar = boost::archive; using Database = image_database; using Mozaic = map, string>; static Database read_database(string const & database_directory){ Database db; auto const database_file = database_directory + ".db"; if (!boost::filesystem::exists(database_file)){ fs::path const directory(database_directory); fs::directory_iterator eod; for(fs::directory_iterator it(directory); it != eod; ++it){ auto const path = it->path(); auto const ext = path.extension(); if(ext != ".png" && ext != ".jpg") continue; cout << colors::green("adding: ") << path.string() << endl; db.add(path.string()); } ofstream file(database_file); ar::binary_oarchive archive(file); archive << db; } else { ifstream file(database_file); ar::binary_iarchive archive(file); archive >> db; } cout << colors::green("read database: ") << db.size() << endl; return db; } static Mozaic create_mozaic(Database const & db, string const & filename, int h_tiles, int v_tiles){ Mozaic mozaic; apply_to_tiles(filename, h_tiles, v_tiles, [&](int c, int r, av::frame const & frame){ auto const index = db.nearest_image(frame); cout << colors::red("tile ") << c << ", " << r << ": " << db.filename(index) << endl; mozaic[make_pair(c, r)] = db.filename(index); }); return mozaic; } static void save_mozaic(Mozaic const & mozaic, string filename, int h_tiles, int v_tiles){ auto const pix_fmt = AV_PIX_FMT_YUVJ444P; auto const codec_id= AV_CODEC_ID_MJPEG; // Open all files we need map frames; for(auto&& x : mozaic){ // only open a file once if(frames.count(x.second) > 0) continue; frames.emplace(x.second, crop_to_square(open_image(x.second))); } auto const total_width = h_tiles * tile_width; auto const total_height = v_tiles * tile_height; // Create output frame std::vector> data(make_u(avpicture_get_size(pix_fmt, total_width, total_height)), 0); 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 av::frame frame_part = av::frame_clone(frame); for(int r = 0; r < v_tiles; ++r) { for(int c = 0; c < h_tiles; ++c){ av_picture_crop(reinterpret_cast(frame_part.get()), reinterpret_cast(frame.get()), av::get_format(frame), r * tile_height, c * tile_width); frame_part->width = tile_width; frame_part->height = tile_height; auto&& input = frames.at(mozaic.at(make_pair(c, r))); auto sws_context = sws_getContext(input->width, input->height, av::get_format(input), frame_part->width, frame_part->height, av::get_format(frame_part), 0, nullptr, nullptr, nullptr); if(!sws_context) throw std::runtime_error("boem sws context"); sws_scale (sws_context, {input->data}, {input->linesize}, 0, input->height, {frame_part->data}, {frame_part->linesize}); sws_freeContext(sws_context); } } // Done with the input frames.clear(); // 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); cout << "output size" << output_size << endl; auto file = fopen(filename.c_str(), "wb"); fwrite(buffer.data(), 1, make_u(output_size), file); fclose(file); } int main(){ av_register_all(); string const database_directory = "database"; string const filename = "image.jpg"; string const output = "output.jpg"; int const h_tiles = 4 * 7; int const v_tiles = 3 * 7; auto const db = read_database(database_directory); auto const mozaic = create_mozaic(db, filename, h_tiles, v_tiles); save_mozaic(mozaic, output, h_tiles, v_tiles); // debugging for(int r = 0; r < v_tiles; ++r){ for(int c = 0; c < h_tiles; ++c){ cout << mozaic.at(make_pair(c, r)) << "\t"; } cout << endl; } }