#include "image_io.hpp" #include "utilities.hpp" #include "av/sws.hpp" extern "C" { #include #include #include #include #include } #include #include #include #include #include raw_rgb_image::raw_rgb_image() : data() , frame(nullptr, [](auto x){ av_frame_free(&x); }) {} raw_rgb_image::raw_rgb_image(int W, int H) : data(make_u(avpicture_get_size(AV_PIX_FMT_RGB24, W, H))) , frame(av::frame_alloc()) { avpicture_fill(reinterpret_cast(frame.get()), data.data(), AV_PIX_FMT_RGB24, W, H); frame->width = W; frame->height = H; frame->format = AV_PIX_FMT_RGB24; } int raw_rgb_image::width() const { return frame->width; } int raw_rgb_image::height() const { return frame->height; } AVPixelFormat raw_rgb_image::format() const { return av::get_format(frame); } av::frame open_image(std::string const & filename){ // Open the file auto format_context = av::format_open_input(filename, nullptr, nullptr); // Get the codec and let us own the buffers auto codec_context = av::context_from_stream(format_context, 0); const auto codec = avcodec_find_decoder(codec_context->codec_id); codec_context->refcounted_frames = 1; // Open the codec const auto opened_codec = av::codec_open(codec_context, codec, nullptr); // Allocate frame av::frame frame = av::frame_alloc(); // things to read and decode it av::packet_buffer empty_packet; bool need_extra_pass = true; while(auto packet = av::read_frame(format_context, empty_packet)) { if(packet->stream_index != 0) continue; // we only need the first frame if(av::codec_decode_video(codec_context, frame, packet)) { need_extra_pass = false; break; } } // some decoders need an extra pass // See the flag CODEC_CAP_DELAY for more info if(need_extra_pass) { av::codec_decode_video_remaining(codec_context, frame); } return frame; } void crop_to_square(av::frame& frame){ int diff = frame->width - frame->height; if(diff > 0) { av::crop(frame, diff/2, 0, frame->height, frame->height); } else if(diff < 0) { av::crop(frame, 0, -diff/2, frame->width, frame->width); } } av::frame crop_to_square(const av::frame& frame){ return crop_to_square(av::frame_clone(frame)); } av::frame crop_to_square(av::frame && frame){ crop_to_square(frame); return std::move(frame); } raw_rgb_image to_raw_rgb_image(av::frame const & frame, int new_width, int new_height){ raw_rgb_image image(new_width, new_height); auto context = sws::create_context(frame, image.frame); sws::scale(context, frame, image.frame); return image; } void apply_to_tiles(std::string const & filename, int h_tiles, int v_tiles, std::function fun) { const auto org_frame = open_image(filename); // create raw buffer for the callback // TODO: do not scale the cropped region raw_rgb_image image(512, 512); // create the tiles const int width = org_frame->width / h_tiles; const int height = org_frame->height / v_tiles; for(int r = 0; r < v_tiles; ++r){ for(int c = 0; c < h_tiles; ++c){ const int x_crop = c * width; const int y_crop = r * height; const auto cropped_frame = av::crop(org_frame, x_crop, y_crop, width, height); auto context = sws::create_context(cropped_frame, image.frame); sws::scale(context, cropped_frame, image.frame); 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; std::vector> data(make_u(avpicture_get_size(pix_fmt, frame->width, frame->height)), 0); av::frame converted_frame = av::frame_alloc(); avpicture_fill(reinterpret_cast(converted_frame.get()), data.data(), pix_fmt, frame->width, frame->height); converted_frame->width = frame->width; converted_frame->height = frame->height; converted_frame->format = pix_fmt; { auto sws_context = sws::create_context(frame, converted_frame); sws::scale(sws_context, frame, converted_frame); } encode_as_jpg(converted_frame, filename); } void encode_as_jpg(const av::frame& frame, const std::string& filename){ auto const codec_id= AV_CODEC_ID_MJPEG; const auto pix_fmt = av::get_format(frame); const auto codec = av::find_encoder(codec_id); auto codec_ctx = av::context_alloc(codec); 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); const auto opened_codec = av::codec_open(codec_ctx, 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); // const auto output_size = avcodec_encode_video(codec_ctx.get(), buffer.data(), buffer_size, frame.get()); // assert(output_size <= buffer_size); if(avcodec_send_frame(codec_ctx.get(), frame.get()) < 0) { std::cerr << "Error when encoding frame" << std::endl; } AVPacket * p = av_packet_alloc(); std::ofstream file(filename); int ret = 0; while (ret >= 0) { ret = avcodec_receive_packet(codec_ctx.get(), p); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) return; else if (ret < 0) { std::cerr << "error" << std::endl; return; } std::clog << "encoded frame " << p->pts << " of size " << p->size << std::endl; file.write(reinterpret_cast(p->data), p->size); av_packet_unref(p); } }