From 224b7bfc0bfd628da49d5d1fc8b7aec4998aadea Mon Sep 17 00:00:00 2001 From: Joshua Moerman Date: Tue, 19 Aug 2014 08:21:29 +0200 Subject: [PATCH] Adds possibility to do asymmetric fingerprint comparison Adds another metric (doesn't work yet) Adds more constness --- lib/CMakeLists.txt | 4 +- lib/av.hpp | 2 +- lib/fingerprint_traits.hpp | 16 ++++ lib/fingerprints.hpp | 4 + lib/fingerprints/downscale.cpp | 35 +++++++++ lib/fingerprints/downscale.hpp | 27 +++++++ .../wavelet.cpp} | 77 ++++++++++--------- .../wavelet.hpp} | 22 ++---- lib/image_database.cpp | 2 - lib/image_database.hpp | 16 ++-- lib/image_io.cpp | 63 ++++++++++++++- lib/image_io.hpp | 6 +- src/compress.cpp | 3 +- src/fingerprint_test.cpp | 10 ++- src/main.cpp | 51 ++++++------ src/needle.cpp | 14 ++-- src/writer.cpp | 58 +------------- 17 files changed, 247 insertions(+), 163 deletions(-) create mode 100644 lib/fingerprint_traits.hpp create mode 100644 lib/fingerprints.hpp create mode 100644 lib/fingerprints/downscale.cpp create mode 100644 lib/fingerprints/downscale.hpp rename lib/{fingerprint.cpp => fingerprints/wavelet.cpp} (50%) rename lib/{fingerprint.hpp => fingerprints/wavelet.hpp} (68%) delete mode 100644 lib/image_database.cpp diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 2ad3b1d..dda9976 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,6 +1,6 @@ -file(GLOB sources "*.cpp") -file(GLOB headers "*.hpp") +file(GLOB_RECURSE sources "*.cpp") +file(GLOB_RECURSE headers "*.hpp") set(libs avformat avcodec avutil swscale) diff --git a/lib/av.hpp b/lib/av.hpp index 55ef0cc..c943e5c 100644 --- a/lib/av.hpp +++ b/lib/av.hpp @@ -44,7 +44,7 @@ namespace av { // AVFrame related using frame = std::unique_ptr>; frame frame_alloc(); - frame frame_clone(frame const & f); + frame frame_clone(frame const & f); // creates a clone with the *same* buffer AVPixelFormat get_format(frame const & f); // Allocator diff --git a/lib/fingerprint_traits.hpp b/lib/fingerprint_traits.hpp new file mode 100644 index 0000000..10e3076 --- /dev/null +++ b/lib/fingerprint_traits.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include"av.hpp" +#include + +// A fingerprint is used to speed up comparison. However, this is done assymetrically. Only the +// compressed version is used in the database, but for comparison the full image can be used. +template +struct fingerprint_traits { + using fingerprint = Fingerprint; + using pre_fingerprint = decltype(fingerprint::pre_calculate(std::declval())); + + static pre_fingerprint pre_calculate(av::frame const & frame) { return fingerprint::pre_calculate(frame); } + static fingerprint calculate(av::frame const & frame) { return fingerprint::calculate(frame); } + static auto distance(fingerprint const & x, pre_fingerprint const & y) { return x.distance_to(y); } +}; diff --git a/lib/fingerprints.hpp b/lib/fingerprints.hpp new file mode 100644 index 0000000..5c749dd --- /dev/null +++ b/lib/fingerprints.hpp @@ -0,0 +1,4 @@ +#pragma once + +#include "fingerprints/wavelet.hpp" +#include "fingerprints/downscale.hpp" diff --git a/lib/fingerprints/downscale.cpp b/lib/fingerprints/downscale.cpp new file mode 100644 index 0000000..360ac49 --- /dev/null +++ b/lib/fingerprints/downscale.cpp @@ -0,0 +1,35 @@ +#include "downscale.hpp" + +#include +#include + +downscale downscale::pre_calculate(const av::frame& frame){ + // ffmpeg doesnt let us downscale all the way to 5 at once :( + auto const image = to_raw_rgb_image(frame, 5, 5); + + downscale ret; + ret.data.assign(image.data.size(), 0); + + std::copy(image.data.begin(), image.data.end(), ret.data.begin()); + + return ret; +} + +downscale downscale::calculate(const av::frame& frame){ + return pre_calculate(frame); +} + +double square(double x){ + return x*x; +} + +double downscale::distance_to(const downscale& fingerprint) const { + assert(data.size() == fingerprint.data.size()); + + double distance = 0; + for(size_t i = 0; i < data.size(); ++i){ + distance += square(data[i] - fingerprint.data[i]); + } + + return distance; +} diff --git a/lib/fingerprints/downscale.hpp b/lib/fingerprints/downscale.hpp new file mode 100644 index 0000000..45d65be --- /dev/null +++ b/lib/fingerprints/downscale.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +#include +#include + +#include +#include + +struct downscale { + std::vector data; + + static downscale pre_calculate(av::frame const & frame); + static downscale calculate(av::frame const & frame); + double distance_to(downscale const & fingerprint) const; + +private: + friend class boost::serialization::access; + template + void serialize(Archive & ar, const unsigned int /*version*/){ + ar & data; + } +}; + +std::ostream& operator<<(std::ostream& out, downscale const & x); diff --git a/lib/fingerprint.cpp b/lib/fingerprints/wavelet.cpp similarity index 50% rename from lib/fingerprint.cpp rename to lib/fingerprints/wavelet.cpp index 0c33728..73e0493 100644 --- a/lib/fingerprint.cpp +++ b/lib/fingerprints/wavelet.cpp @@ -1,14 +1,37 @@ -#include "fingerprint.hpp" #include "wavelet.hpp" -#include "utilities.hpp" +#include +#include +#include #include #include -rgb_wavelet_coefficients rgb_wavelet_coefficients::calculate(const raw_rgb_image& image){ +static const int size = 512; + +rgb_wavelet_coefficients::pre_fingerprint rgb_wavelet_coefficients::pre_calculate(av::frame const & frame) { + auto const image = to_raw_rgb_image(crop_to_square(frame), size, size); + rgb_wavelet_coefficients::pre_fingerprint ret; + + // for every color + for(unsigned int color = 0; color < 3; ++color){ + auto & vector = ret[color]; + vector.assign(make_u(image.width() * image.height()), 0); + + for(unsigned int n = 0; n < make_u(image.width() * image.height()); ++n){ + vector[n] = 2.0 * image.data[3*n + color] / double(255) - 1.0; + } + + wvlt::wavelet_2D(vector.data(), make_u(image.width()), make_u(image.height())); + } + + return ret; +} + +rgb_wavelet_coefficients rgb_wavelet_coefficients::calculate(av::frame const & frame){ + auto const image = to_raw_rgb_image(crop_to_square(frame), size, size); rgb_wavelet_coefficients ret; - std::vector vector(make_u(image.width() * image.height())); + std::vector vector(make_u(image.width() * image.height()), 0); // for every color for(unsigned int color = 0; color < 3; ++color){ @@ -24,12 +47,12 @@ rgb_wavelet_coefficients rgb_wavelet_coefficients::calculate(const raw_rgb_image auto copy = vector; for(auto & x : copy) x = std::abs(x); - auto n_coefficients = coefficient_array.size(); + auto const n_coefficients = coefficient_array.size(); std::nth_element(copy.begin(), copy.begin() + n_coefficients, copy.end(), std::greater()); - double threshold = copy[n_coefficients-1]; + auto const threshold = copy[n_coefficients-1]; for(unsigned int n = 0; n < vector.size(); ++n){ - auto x = vector[n]; + auto const x = vector[n]; if(std::abs(x) >= threshold) { coefficient_array[array_index++] = std::make_pair(n, x); } @@ -46,38 +69,16 @@ static double square(double x){ return x*x; } -double rgb_wavelet_coefficients::distance_to(const rgb_wavelet_coefficients& y) const { +double rgb_wavelet_coefficients::distance_to(pre_fingerprint const & fingerprint) const { double distance = 0; for(unsigned int color = 0; color < 3; ++color){ - unsigned int i = 0, j = 0; - auto& x_array = color == 0 ? reds : (color == 1 ? greens : blues); - auto& y_array = color == 0 ? y.reds : (color == 1 ? y.greens : y.blues); - - // "merge" - while(i < x_array.size() && j < y_array.size()){ - auto x_pair = x_array[i]; - auto y_pair = y_array[j]; - - if(x_pair.first == y_pair.first) { - distance += square(y_pair.second - x_pair.second); - ++i; - ++j; - } else if(x_pair.first < y_pair.first) { - distance += square(x_pair.second); - ++i; - } else { - distance += square(y_pair.second); - ++j; - } - } + auto const & coefficients = color == 0 ? reds : (color == 1 ? greens : blues); - // remaining part, either x or y - for(; i < x_array.size(); ++i){ - distance += square(x_array[i].second); - } - for(; j < y_array.size(); ++j){ - distance += square(y_array[j].second); + for(auto&& p : coefficients){ + auto const x = p.second; + auto const y = fingerprint[color][p.first]; + distance += square(x - y) - square(y); } } @@ -95,10 +96,12 @@ std::ostream& operator<<(std::ostream& out, rgb_wavelet_coefficients const & x){ out << "rgb_wavelet_coefficients" << std::endl; for(int color = 0; color < 3; ++color){ - auto& coefficient_array = color == 0 ? x.reds : (color == 1 ? x.greens : x.blues); + auto const & coefficient_array = color == 0 ? x.reds : (color == 1 ? x.greens : x.blues); out << '[' << coefficient_array[0]; - for(unsigned int i = 1; i < coefficient_array.size(); ++i) out << ", " << coefficient_array[i]; + for(unsigned int i = 1; i < coefficient_array.size(); ++i) { + out << ", " << coefficient_array[i]; + } out << ']' << std::endl; } return out; diff --git a/lib/fingerprint.hpp b/lib/fingerprints/wavelet.hpp similarity index 68% rename from lib/fingerprint.hpp rename to lib/fingerprints/wavelet.hpp index 0c6c16e..6c4f82f 100644 --- a/lib/fingerprint.hpp +++ b/lib/fingerprints/wavelet.hpp @@ -1,6 +1,7 @@ #pragma once -#include "image_io.hpp" +#include +#include #include #include @@ -9,6 +10,7 @@ #include #include #include +#include namespace boost { namespace serialization { @@ -19,28 +21,18 @@ namespace boost { } // namespace serialization } // namespace boost -// Default implementation -template -struct fingerprint_traits { - static Fingerprint calculate(raw_rgb_image const & image) { - return Fingerprint::calculate(image); - } - - static auto distance(Fingerprint const & x, Fingerprint const & y) { - return x.distance_to(y); - } -}; - struct rgb_wavelet_coefficients { // a double for (x, y) location represented in a single int using coefficient = std::pair; + using pre_fingerprint = std::array, 3>; std::array reds; std::array greens; std::array blues; - static rgb_wavelet_coefficients calculate(raw_rgb_image const & image); - double distance_to(rgb_wavelet_coefficients const & y) const; + static pre_fingerprint pre_calculate(av::frame const & frame); + static rgb_wavelet_coefficients calculate(av::frame const & frame); + double distance_to(pre_fingerprint const & fingerprint) const; private: friend class boost::serialization::access; diff --git a/lib/image_database.cpp b/lib/image_database.cpp deleted file mode 100644 index ebefd50..0000000 --- a/lib/image_database.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "image_database.hpp" - diff --git a/lib/image_database.hpp b/lib/image_database.hpp index 61642fc..75c37a2 100644 --- a/lib/image_database.hpp +++ b/lib/image_database.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include @@ -13,9 +13,9 @@ template fun){ +void apply_to_tiles(std::string const & filename, int h_tiles, int v_tiles, std::function fun) { auto org_frame = open_image(filename); // create clone to crop av::frame cropped_frame = av::frame_clone(org_frame); // create raw buffer for the callback + // TODO: do not scale the cropped region raw_rgb_image image(512, 512); // create the tiles @@ -144,7 +149,61 @@ void apply_to_tiles(const std::string& filename, int h_tiles, int v_tiles, std:: sws_scale (context, {cropped_frame->data}, {cropped_frame->linesize}, 0, cropped_frame->height, {image.frame->data}, {image.frame->linesize}); sws_freeContext(context); - fun(c, r, image); + fun(c, r, image.frame); } } } + +void save_as_jpg(av::frame const & frame, std::string const & filename){ + const auto pix_fmt = AV_PIX_FMT_YUVJ444P; + const auto codec_id= AV_CODEC_ID_MJPEG; + + // Convert + int tile_width = 800; + int tile_height = 600; + + int h_tiles = 8; + int v_tiles = 6; + + std::vector> data(make_u(avpicture_get_size(pix_fmt, h_tiles * tile_width, v_tiles * tile_height)), 0); + av::frame converted_frame = av::frame_alloc(); + avpicture_fill(reinterpret_cast(converted_frame.get()), data.data(), pix_fmt, h_tiles * tile_width, v_tiles * tile_height); + converted_frame->width = h_tiles * tile_width; + converted_frame->height = v_tiles * tile_height; + converted_frame->format = pix_fmt; + + auto const sws_context = sws_getContext(frame->width, frame->height, av::get_format(frame), tile_width, tile_height, av::get_format(converted_frame), 0, nullptr, nullptr, nullptr); + if(!sws_context) throw std::runtime_error("boem sws context"); + + av::frame cropped_frame = av::frame_clone(converted_frame); + for(int r = 0; r < v_tiles; ++r) { + for(int c = 0; c < h_tiles; ++c){ + av_picture_crop(reinterpret_cast(cropped_frame.get()), reinterpret_cast(converted_frame.get()), av::get_format(converted_frame), r * tile_height, c * tile_width); + sws_scale (sws_context, {frame->data}, {frame->linesize}, 0, frame->height, {cropped_frame->data}, {cropped_frame->linesize}); + } + } + + sws_freeContext(sws_context); + + // Encode + auto const 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 = converted_frame->width; + codec_ctx->height = converted_frame->height; + codec_ctx->time_base = av_make_q(1, 1); + auto const 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, converted_frame.get()); + assert(output_size <= buffer_size); + + auto const file = fopen(filename.c_str(), "wb"); + fwrite(buffer.data(), 1, make_u(output_size), file); + fclose(file); +} diff --git a/lib/image_io.hpp b/lib/image_io.hpp index 7c67bf1..bf47609 100644 --- a/lib/image_io.hpp +++ b/lib/image_io.hpp @@ -28,6 +28,7 @@ av::frame open_image(std::string const & filename); // crops to the bottom right square (cheap operation) void crop_to_square(av::frame & frame); +av::frame crop_to_square(av::frame const & frame); av::frame crop_to_square(av::frame && frame); // converts and resizes @@ -39,4 +40,7 @@ inline raw_rgb_image open_as_rgb(const std::string &filename){ } // apply function to every tile, fun :: Column, Row, Image -> Void -void apply_to_tiles(std::string const & filename, int h_tiles, int v_tiles, std::function fun); +void apply_to_tiles(std::string const & filename, int h_tiles, int v_tiles, std::function fun); + +// does what you think it does +void save_as_jpg(av::frame const & frame, std::string const & filename); diff --git a/src/compress.cpp b/src/compress.cpp index 159dffd..55cbbe8 100644 --- a/src/compress.cpp +++ b/src/compress.cpp @@ -50,6 +50,7 @@ int main(){ } } - save_as_ppm(image, "output.ppm"); + // TODO: save as jpg + save_as_jpg(image.frame, "output.ppm"); } diff --git a/src/fingerprint_test.cpp b/src/fingerprint_test.cpp index 85626d0..d5fe8d9 100644 --- a/src/fingerprint_test.cpp +++ b/src/fingerprint_test.cpp @@ -1,4 +1,5 @@ -#include +#include +#include extern "C" { #include @@ -10,10 +11,11 @@ using namespace std; int main(){ av_register_all(); - auto image = open_as_rgb("test.jpg"); + auto const image = open_image("test.jpg"); - rgb_wavelet_coefficients x = rgb_wavelet_coefficients::calculate(image); + auto const x = rgb_wavelet_coefficients::calculate(image); + auto const y = rgb_wavelet_coefficients::pre_calculate(image); cout << x << endl; - cout << x.distance_to(x) << endl; + cout << x.distance_to(y) << endl; } diff --git a/src/main.cpp b/src/main.cpp index c161bd8..939d3f3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include @@ -21,6 +21,9 @@ extern "C" { #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; @@ -29,15 +32,15 @@ using Database = image_database; using Mozaic = map, string>; Database read_database(string const & database_directory){ - image_database db; - auto database_file = database_directory + ".db"; + Database db; + auto const database_file = database_directory + ".db"; if (!boost::filesystem::exists(database_file)){ - fs::path directory(database_directory); + fs::path const directory(database_directory); fs::directory_iterator eod; for(fs::directory_iterator it(directory); it != eod; ++it){ - auto && path = it->path(); - auto ext = path.extension(); + auto const path = it->path(); + auto const ext = path.extension(); if(ext != ".png" && ext != ".jpg") continue; cout << colors::green("adding: ") << path.string() << endl; @@ -50,7 +53,6 @@ Database read_database(string const & database_directory){ } else { ifstream file(database_file); ar::binary_iarchive archive(file); - // read class state from archive archive >> db; } @@ -59,10 +61,10 @@ Database read_database(string const & database_directory){ } Mozaic create_mozaic(Database const & db, string const & filename, int h_tiles, int v_tiles){ - map, string> mozaic; + Mozaic mozaic; - apply_to_tiles("image.jpg", h_tiles, v_tiles, [&](int c, int r, raw_rgb_image const & image){ - auto index = db.nearest_image(image); + apply_to_tiles("image.jpg", 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); }); @@ -71,11 +73,8 @@ Mozaic create_mozaic(Database const & db, string const & filename, int h_tiles, } void save_mozaic(Mozaic const & mozaic, string filename, int h_tiles, int v_tiles){ - const auto pix_fmt = AV_PIX_FMT_YUVJ444P; - const auto codec_id= AV_CODEC_ID_MJPEG; - - int tile_width = 128; - int tile_height = 128; + auto const pix_fmt = AV_PIX_FMT_YUVJ444P; + auto const codec_id= AV_CODEC_ID_MJPEG; // Open all files we need map frames; @@ -85,8 +84,8 @@ void save_mozaic(Mozaic const & mozaic, string filename, int h_tiles, int v_tile frames.emplace(x.second, crop_to_square(open_image(x.second))); } - auto total_width = h_tiles * tile_width; - auto total_height = v_tiles * tile_height; + 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); @@ -128,9 +127,9 @@ void save_mozaic(Mozaic const & mozaic, string filename, int h_tiles, int v_tile codec_ctx->time_base = av_make_q(1, 1); auto opened_codec = av::codec_open(codec_ctx.get(), codec, nullptr); - const auto buffer_size = avpicture_get_size(pix_fmt, codec_ctx->width, codec_ctx->height); + auto const buffer_size = avpicture_get_size(pix_fmt, codec_ctx->width, codec_ctx->height); std::vector buffer(make_u(buffer_size), 0); - auto output_size = avcodec_encode_video(codec_ctx.get(), buffer.data(), buffer_size, frame.get()); + 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; @@ -142,14 +141,14 @@ void save_mozaic(Mozaic const & mozaic, string filename, int h_tiles, int v_tile int main(){ av_register_all(); - string database_directory = "database"; - string filename = "image.jpg"; - string output = "output.jpg"; - int h_tiles = 4 * 13; - int v_tiles = 3 * 13; + 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; - const auto db = read_database(database_directory); - const auto mozaic = create_mozaic(db, filename, h_tiles, v_tiles); + 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 diff --git a/src/needle.cpp b/src/needle.cpp index e229328..0090391 100644 --- a/src/needle.cpp +++ b/src/needle.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include @@ -19,16 +19,16 @@ namespace fs = boost::filesystem; int main(){ av_register_all(); - string database_directory = "database"; - string filename = "needle.jpg"; + string const database_directory = "database"; + string const filename = "needle.jpg"; image_database db; - fs::path directory(database_directory); + fs::path const directory(database_directory); fs::directory_iterator eod; for(fs::directory_iterator it(directory); it != eod; ++it){ - auto && path = it->path(); - auto ext = path.extension(); + auto const path = it->path(); + auto const ext = path.extension(); if(ext != ".png" && ext != ".jpg") continue; cout << colors::green("adding: ") << path.string() << endl; @@ -40,7 +40,7 @@ int main(){ cout << colors::green("database: ") << db.size() << endl; cout << colors::green("press enter to search match for: ") << filename << endl; cin.ignore(); - auto index = db.nearest_image(open_as_rgb(filename)); + auto const index = db.nearest_image(open_image(filename)); cout << colors::green("match: ") << db.filename(index) << endl; } } diff --git a/src/writer.cpp b/src/writer.cpp index 79cf292..f58ca1e 100644 --- a/src/writer.cpp +++ b/src/writer.cpp @@ -9,67 +9,11 @@ extern "C" { using namespace std; - -static void save_as_jpg(av::frame const & frame, std::string const & filename){ - const auto pix_fmt = AV_PIX_FMT_YUVJ444P; - const auto codec_id= AV_CODEC_ID_MJPEG; - - // Convert - int tile_width = 800; - int tile_height = 600; - - int h_tiles = 8; - int v_tiles = 6; - - std::vector> data(make_u(avpicture_get_size(pix_fmt, h_tiles * tile_width, v_tiles * tile_height)), 0); - av::frame converted_frame = av::frame_alloc(); - avpicture_fill(reinterpret_cast(converted_frame.get()), data.data(), pix_fmt, h_tiles * tile_width, v_tiles * tile_height); - converted_frame->width = h_tiles * tile_width; - converted_frame->height = v_tiles * tile_height; - converted_frame->format = pix_fmt; - - auto sws_context = sws_getContext(frame->width, frame->height, av::get_format(frame), tile_width, tile_height, av::get_format(converted_frame), 0, nullptr, nullptr, nullptr); - if(!sws_context) throw std::runtime_error("boem sws context"); - - av::frame cropped_frame = av::frame_clone(converted_frame); - for(int r = 0; r < v_tiles; ++r) { - for(int c = 0; c < h_tiles; ++c){ - av_picture_crop(reinterpret_cast(cropped_frame.get()), reinterpret_cast(converted_frame.get()), av::get_format(converted_frame), r * tile_height, c * tile_width); - sws_scale (sws_context, {frame->data}, {frame->linesize}, 0, frame->height, {cropped_frame->data}, {cropped_frame->linesize}); - } - } - - sws_freeContext(sws_context); - - // 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 = converted_frame->width; - codec_ctx->height = converted_frame->height; - codec_ctx->time_base = av_make_q(1, 1); - auto opened_codec = av::codec_open(codec_ctx.get(), codec, nullptr); - - const auto buffer_size = avpicture_get_size(pix_fmt, codec_ctx->width, codec_ctx->height); - std::vector buffer(make_u(buffer_size), 0); - auto output_size = avcodec_encode_video(codec_ctx.get(), buffer.data(), buffer_size, converted_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(); while(true){ - auto image = open_image("needle.png"); + auto const image = open_image("needle.png"); save_as_jpg(image, "output.jpg"); } }