From 8361c6a412953fce43df67db90c90de41785ca29 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Sat, 12 Oct 2024 22:30:32 +0200 Subject: [PATCH] heif-enc: option to switch tiling method --- examples/heif_enc.cc | 87 +++++++++++++++++++++++-- libheif/api/libheif/heif.cc | 3 +- libheif/api/libheif/heif_experimental.h | 3 +- libheif/context.cc | 7 +- libheif/context.h | 3 +- libheif/image-items/image_item.cc | 22 +++++++ libheif/image-items/image_item.h | 2 + libheif/image-items/tiled.cc | 18 +++-- libheif/image-items/tiled.h | 7 +- 9 files changed, 134 insertions(+), 18 deletions(-) diff --git a/examples/heif_enc.cc b/examples/heif_enc.cc index 1a678d505a..d8cdc1c936 100644 --- a/examples/heif_enc.cc +++ b/examples/heif_enc.cc @@ -49,6 +49,7 @@ #include "benchmark.h" #include "common.h" +#include "libheif/heif_experimental.h" int master_alpha = 1; int thumb_alpha = 1; @@ -62,6 +63,7 @@ const char* encoderId = nullptr; std::string chroma_downsampling; int tiled_image_width = 0; int tiled_image_height = 0; +std::string tiling_method; uint16_t nclx_colour_primaries = 1; uint16_t nclx_transfer_characteristic = 13; @@ -100,6 +102,7 @@ const int OPTION_USE_HTJ2K_COMPRESSION = 1009; const int OPTION_USE_VVC_COMPRESSION = 1010; const int OPTION_TILED_IMAGE_WIDTH = 1011; const int OPTION_TILED_IMAGE_HEIGHT = 1012; +const int OPTION_TILING_METHOD = 1013; static struct option long_options[] = { @@ -140,6 +143,7 @@ static struct option long_options[] = { {(char* const) "tiled-image-width", required_argument, nullptr, OPTION_TILED_IMAGE_WIDTH}, {(char* const) "tiled-image-height", required_argument, nullptr, OPTION_TILED_IMAGE_HEIGHT}, {(char* const) "tiled-input-x-y", no_argument, &tiled_input_x_y, 1}, + {(char* const) "tiling-method", required_argument, nullptr, OPTION_TILING_METHOD}, {0, 0, 0, 0}, }; @@ -202,6 +206,7 @@ void show_help(const char* argv0) << " --tiled-image-height # override image height of tiled image\n" << " --tiled-input-x-y usually, the first number in the input tile filename should be the y position.\n" << " With this option, this can be swapped so that the first number is x, the second number y.\n" + << " --tiling-method METHOD choose one of these methods: grid, tili, unci. The default is 'grid'.\n" ; } @@ -738,10 +743,10 @@ std::optional determine_input_images_tiling(const std::st } -heif_image_handle* encode_tiled(heif_context* ctx, heif_encoder* encoder, heif_encoding_options* options, - int output_bit_depth, - const input_tiles_generator& tile_generator, - const heif_image_tiling& tiling) +heif_image_handle* encode_tiled_grid(heif_context* ctx, heif_encoder* encoder, heif_encoding_options* options, + int output_bit_depth, + const input_tiles_generator& tile_generator, + const heif_image_tiling& tiling) { std::vector image_ids; @@ -787,6 +792,71 @@ heif_image_handle* encode_tiled(heif_context* ctx, heif_encoder* encoder, heif_e } +heif_image_handle* encode_tiled(heif_context* ctx, heif_encoder* encoder, heif_encoding_options* options, + int output_bit_depth, + const input_tiles_generator& tile_generator, + const heif_image_tiling& tiling) +{ + if (tiling_method == "grid") { + return encode_tiled_grid(ctx, encoder, options, output_bit_depth, tile_generator, tiling); + } + + + heif_image_handle* tiled_image; + heif_error error; + if (tiling_method == "tili") { + heif_tiled_image_parameters tiled_params{}; + tiled_params.version = 1; + tiled_params.image_width = tiling.image_width; + tiled_params.image_height = tiling.image_height; + tiled_params.tile_width = tiling.tile_width; + tiled_params.tile_height = tiling.tile_height; + // tiled_params.compression_format_fourcc = heif_fourcc('a', 'v', '0', '1'); // TODO HACK + tiled_params.offset_field_length = 32; + tiled_params.size_field_length = 24; + tiled_params.tiles_are_sequential = 1; + + error = heif_context_add_tiled_image(ctx, &tiled_params, options, encoder, &tiled_image); + if (error.code != 0) { + std::cerr << "Could not generate grid image: " << error.message << "\n"; + return nullptr; + } + } + else if (tiling_method == "unci") { + // TODO + } + else { + assert(false); + } + + std::cout << "encoding tiled image, tile size: " << tiling.tile_width << "x" << tiling.tile_height + << " image size: " << tiling.image_width << "x" << tiling.image_height << "\n"; + + for (uint32_t ty = 0; ty < tile_generator.nRows(); ty++) + for (uint32_t tx = 0; tx < tile_generator.nColumns(); tx++) { + std::string input_filename = tile_generator.filename(tx,ty); + + InputImage input_image = load_image(input_filename, output_bit_depth); + + std::cout << "encoding tile " << ty+1 << " " << tx+1 + << " (of " << tile_generator.nRows() << "x" << tile_generator.nColumns() << ") \r"; + std::cout.flush(); + + heif_error error = heif_context_add_image_tile(ctx, tiled_image, tx, ty, + input_image.image.get(), + encoder); + if (error.code != 0) { + std::cerr << "Could not encode HEIF/AVIF file: " << error.message << "\n"; + return nullptr; + } + } + + std::cout << "\n"; + + return tiled_image; +} + + class LibHeifInitializer { public: @@ -918,6 +988,15 @@ int main(int argc, char** argv) case OPTION_TILED_IMAGE_HEIGHT: tiled_image_height = (int) strtol(optarg, nullptr, 0); break; + case OPTION_TILING_METHOD: + tiling_method = optarg; + if (tiling_method != "grid" && + tiling_method != "tili" && + tiling_method != "unci") { + std::cerr << "Invalid tiling method '" << tiling_method << "'\n"; + exit(5); + } + break; case 'C': chroma_downsampling = optarg; if (chroma_downsampling != "nn" && diff --git a/libheif/api/libheif/heif.cc b/libheif/api/libheif/heif.cc index c056b31b63..09f55db0f0 100644 --- a/libheif/api/libheif/heif.cc +++ b/libheif/api/libheif/heif.cc @@ -3529,10 +3529,11 @@ struct heif_error heif_context_add_overlay_image(struct heif_context* ctx, struct heif_error heif_context_add_tiled_image(struct heif_context* ctx, const struct heif_tiled_image_parameters* parameters, const struct heif_encoding_options* options, // TODO: do we need this? + const struct heif_encoder* encoder, struct heif_image_handle** out_grid_image_handle) { Result> gridImageResult; - gridImageResult = ctx->context->add_tiled_item(parameters); + gridImageResult = ctx->context->add_tiled_item(parameters, encoder); if (gridImageResult.error != Error::Ok) { return gridImageResult.error.error_struct(ctx->context.get()); diff --git a/libheif/api/libheif/heif_experimental.h b/libheif/api/libheif/heif_experimental.h index b98675f12e..91ca2de7fc 100644 --- a/libheif/api/libheif/heif_experimental.h +++ b/libheif/api/libheif/heif_experimental.h @@ -123,7 +123,7 @@ struct heif_tiled_image_parameters { uint32_t tile_width; uint32_t tile_height; - uint32_t compression_type_fourcc; // TODO: can this be set automatically ? + uint32_t compression_format_fourcc; // will be set automatically when calling heif_context_add_tiled_image() uint8_t offset_field_length; // one of: 32, 40, 48, 64 uint8_t size_field_length; // one of: 0, 24, 32, 64 @@ -140,6 +140,7 @@ LIBHEIF_API struct heif_error heif_context_add_tiled_image(struct heif_context* ctx, const struct heif_tiled_image_parameters* parameters, const struct heif_encoding_options* options, // TODO: do we need this? + const struct heif_encoder* encoder, struct heif_image_handle** out_tiled_image_handle); diff --git a/libheif/context.cc b/libheif/context.cc index d4ead4cca6..7ccc6c0d97 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -1342,9 +1342,10 @@ Result> HeifContext::add_iovl_item(const Imag } -Result> HeifContext::add_tiled_item(const heif_tiled_image_parameters* parameters) +Result> HeifContext::add_tiled_item(const heif_tiled_image_parameters* parameters, + const struct heif_encoder* encoder) { - return ImageItem_Tiled::add_new_tiled_item(this, parameters); + return ImageItem_Tiled::add_new_tiled_item(this, parameters, encoder); } @@ -1354,7 +1355,7 @@ Error HeifContext::add_tiled_image_tile(heif_item_id tild_id, uint32_t tile_x, u { auto item = ImageItem::alloc_for_compression_format(this, encoder->plugin->compression_format); - heif_encoding_options* options = heif_encoding_options_alloc(); + heif_encoding_options* options = heif_encoding_options_alloc(); // TODO: should this be taken from heif_context_add_tiled_image() ? Result> colorConversionResult = item->convert_colorspace_for_encoding(image, encoder, *options); if (colorConversionResult.error) { diff --git a/libheif/context.h b/libheif/context.h index 025e4de8d7..0572dcfe4a 100644 --- a/libheif/context.h +++ b/libheif/context.h @@ -160,7 +160,8 @@ class HeifContext : public ErrorBuffer Result> add_iovl_item(const ImageOverlay& overlayspec); - Result> add_tiled_item(const heif_tiled_image_parameters* parameters); + Result> add_tiled_item(const heif_tiled_image_parameters* parameters, + const struct heif_encoder* encoder); Error add_tiled_image_tile(heif_item_id tili_id, uint32_t tile_x, uint32_t tile_y, const std::shared_ptr& image, diff --git a/libheif/image-items/image_item.cc b/libheif/image-items/image_item.cc index bda776d301..d15a6eaaac 100644 --- a/libheif/image-items/image_item.cc +++ b/libheif/image-items/image_item.cc @@ -108,6 +108,28 @@ heif_compression_format ImageItem::compression_format_from_fourcc_infe_type(uint } } +uint32_t ImageItem::compression_format_to_fourcc_infe_type(heif_compression_format format) +{ + switch (format) { + case heif_compression_JPEG: + return fourcc("jpeg"); + case heif_compression_HEVC: + return fourcc("hvc1"); + case heif_compression_AV1: + return fourcc("av01"); + case heif_compression_VVC: + return fourcc("vvc1"); + case heif_compression_JPEG2000: + return fourcc("j2k1"); + case heif_compression_uncompressed: + return fourcc("unci"); + case heif_compression_mask: + return fourcc("mski"); + default: + return 0; + } +} + std::shared_ptr ImageItem::alloc_for_infe_box(HeifContext* ctx, const std::shared_ptr& infe) { diff --git a/libheif/image-items/image_item.h b/libheif/image-items/image_item.h index 28f5bbbfb1..c55d323c82 100644 --- a/libheif/image-items/image_item.h +++ b/libheif/image-items/image_item.h @@ -62,6 +62,8 @@ class ImageItem : public ErrorBuffer static heif_compression_format compression_format_from_fourcc_infe_type(uint32_t type); + static uint32_t compression_format_to_fourcc_infe_type(heif_compression_format); + Result> convert_colorspace_for_encoding(const std::shared_ptr& image, struct heif_encoder* encoder, const struct heif_encoding_options& options); diff --git a/libheif/image-items/tiled.cc b/libheif/image-items/tiled.cc index 6df735f55e..3cb140db72 100644 --- a/libheif/image-items/tiled.cc +++ b/libheif/image-items/tiled.cc @@ -24,6 +24,7 @@ #include #include "security_limits.h" #include "codecs/hevc_dec.h" +#include "libheif/api_structs.h" static uint64_t readvec(const std::vector& data, size_t& ptr, int len) @@ -127,7 +128,7 @@ Error Box_tilC::write(StreamWriter& writer) const writer.write32(m_parameters.tile_width); writer.write32(m_parameters.tile_height); - writer.write32(m_parameters.compression_type_fourcc); + writer.write32(m_parameters.compression_format_fourcc); writer.write8(m_parameters.number_of_extra_dimensions); @@ -150,7 +151,7 @@ std::string Box_tilC::dump(Indent& indent) const sstr << indent << "version: " << ((int) get_version()) << "\n" //<< indent << "image size: " << m_parameters.image_width << "x" << m_parameters.image_height << "\n" << indent << "tile size: " << m_parameters.tile_width << "x" << m_parameters.tile_height << "\n" - << indent << "compression: " << fourcc_to_string(m_parameters.compression_type_fourcc) << "\n" + << indent << "compression: " << fourcc_to_string(m_parameters.compression_format_fourcc) << "\n" << indent << "tiles are sequential: " << (m_parameters.tiles_are_sequential ? "yes" : "no") << "\n" << indent << "offset field length: " << ((int) m_parameters.offset_field_length) << " bits\n" << indent << "size field length: " << ((int) m_parameters.size_field_length) << " bits\n" @@ -213,7 +214,7 @@ Error Box_tilC::parse(BitstreamRange& range, const heif_security_limits* limits) m_parameters.tile_width = range.read32(); m_parameters.tile_height = range.read32(); - m_parameters.compression_type_fourcc = range.read32(); + m_parameters.compression_format_fourcc = range.read32(); if (m_parameters.tile_width == 0 || m_parameters.tile_height == 0) { return {heif_error_Invalid_input, @@ -445,7 +446,7 @@ ImageItem_Tiled::ImageItem_Tiled(HeifContext* ctx, heif_item_id id) heif_compression_format ImageItem_Tiled::get_compression_format() const { - return compression_format_from_fourcc_infe_type(m_tild_header.get_parameters().compression_type_fourcc); + return compression_format_from_fourcc_infe_type(m_tild_header.get_parameters().compression_format_fourcc); } @@ -481,7 +482,7 @@ Error ImageItem_Tiled::on_load_file() return err; } - m_tile_decoder = Decoder::alloc_for_infe_type(get_context(), get_id(), parameters.compression_type_fourcc); + m_tile_decoder = Decoder::alloc_for_infe_type(get_context(), get_id(), parameters.compression_format_fourcc); if (!m_tile_decoder) { return {heif_error_Unsupported_feature, heif_suberror_Unsupported_codec, @@ -499,7 +500,8 @@ Error ImageItem_Tiled::on_load_file() Result> -ImageItem_Tiled::add_new_tiled_item(HeifContext* ctx, const heif_tiled_image_parameters* parameters) +ImageItem_Tiled::add_new_tiled_item(HeifContext* ctx, const heif_tiled_image_parameters* parameters, + const heif_encoder* encoder) { auto max_tild_tiles = ctx->get_security_limits()->max_number_of_tiles; if (max_tild_tiles && number_of_tiles(*parameters) > max_tild_tiles) { @@ -521,12 +523,14 @@ ImageItem_Tiled::add_new_tiled_item(HeifContext* ctx, const heif_tiled_image_par auto tilC_box = std::make_shared(); tilC_box->set_parameters(*parameters); + tilC_box->set_compression_format(encoder->plugin->compression_format); ctx->get_heif_file()->add_property(tild_id, tilC_box, true); // Create header + offset table TildHeader tild_header; tild_header.set_parameters(*parameters); + tild_header.set_compression_format(encoder->plugin->compression_format); std::vector header_data = tild_header.write_offset_table(); @@ -616,7 +620,7 @@ Result> ImageItem_Tiled::decode_grid_tile(const heif_decoding_options& options, uint32_t tx, uint32_t ty) const { heif_compression_format format = compression_format_from_fourcc_infe_type( - m_tild_header.get_parameters().compression_type_fourcc); + m_tild_header.get_parameters().compression_format_fourcc); // --- get compressed data diff --git a/libheif/image-items/tiled.h b/libheif/image-items/tiled.h index c5c8608c4a..de6782638e 100644 --- a/libheif/image-items/tiled.h +++ b/libheif/image-items/tiled.h @@ -59,6 +59,8 @@ class Box_tilC : public FullBox void set_parameters(const heif_tiled_image_parameters& params) { m_parameters = params; } + void set_compression_format(heif_compression_format format) { m_parameters.compression_format_fourcc = ImageItem::compression_format_to_fourcc_infe_type(format); } + const heif_tiled_image_parameters& get_parameters() const { return m_parameters; } Error write(StreamWriter& writer) const override; @@ -84,6 +86,8 @@ class TildHeader const heif_tiled_image_parameters& get_parameters() const { return m_parameters; } + void set_compression_format(heif_compression_format format) { m_parameters.compression_format_fourcc = ImageItem::compression_format_to_fourcc_infe_type(format); } + Error read_full_offset_table(const std::shared_ptr& file, heif_item_id tild_id, const heif_security_limits* limits); Error read_offset_table_range(const std::shared_ptr& file, heif_item_id tild_id, @@ -140,7 +144,8 @@ class ImageItem_Tiled : public ImageItem heif_compression_format get_compression_format() const override; - static Result> add_new_tiled_item(HeifContext* ctx, const heif_tiled_image_parameters* parameters); + static Result> add_new_tiled_item(HeifContext* ctx, const heif_tiled_image_parameters* parameters, + const heif_encoder* encoder); Error on_load_file() override;