strukturag / libheif

libheif is an HEIF and AVIF file format decoder and encoder.
Other
1.76k stars 304 forks source link

Read .HEIC from iPhone and rewrite to .heic got 0 width and height and Image has Black boarder #530

Open li195111 opened 3 years ago

li195111 commented 3 years ago

Hi, thanks for sharing this awesome lib. I have some problems when I test to read from iPhone's .HEIC file and want to rewrite to .heic.

First I try to use your simple encoding API but not work, after that I use the code from example/heif_enc.cc but still have this problem.

Here is my code

int main(int argc, char** argv) {
    const char* file_path = "Path/to/IMG_6894.HEIC";
    const char* output_path = "Path/to/vs_output.heic";
    vector<string> raw_params = { 
        "x265:crf=20.5", 
        "x265:colorprim=smpte170m", 
        "x265:rdoq-level=1", 
        "x265:aq-strength=1.2", 
        "x265:deblock=-2:-2" };
    int master_alpha = 1;
    int two_colr_boxes = 0;
    int thumb_alpha = 1;
    int list_encoders = 0;
    const char* encoderId = nullptr;

    int nclx_matrix_coefficients = 6;
    int nclx_colour_primaries = 2;
    int nclx_transfer_characteristic = 2;
    int nclx_full_range = true;
    bool lossless = false;
    int logging_level = 0;
    int thumbnail_bbox_size = 0;
    bool enc_av1f = false;
    bool crop_to_even_size = false;

    heif_context* ctx = heif_context_alloc();
    if (!ctx) {
        std::cerr << "Could not create context object\n";
        return 1;
    }

    // get the default encoder
    heif_encoder* encoder = nullptr;
    heif_context_get_encoder_for_format(ctx, heif_compression_HEVC, &encoder);
#define MAX_ENCODERS 5
    const heif_encoder_descriptor* encoder_descriptors[MAX_ENCODERS];
    int count = heif_context_get_encoder_descriptors(ctx,
        enc_av1f ? heif_compression_AV1 : heif_compression_HEVC,
        nullptr,
        encoder_descriptors, MAX_ENCODERS);

    if (list_encoders) {
        show_list_of_encoders(encoder_descriptors, count);
        return 0;
    }

    if (count > 0) {
        int idx = 0;
        if (encoderId != nullptr) {
            for (int i = 0; i <= count; i++) {
                if (i == count) {
                    std::cerr << "Unknown encoder ID. Choose one from the list below.\n";
                    show_list_of_encoders(encoder_descriptors, count);
                    return 5;
                }

                if (strcmp(encoderId, heif_encoder_descriptor_get_id_name(encoder_descriptors[i])) == 0) {
                    idx = i;
                    break;
                }
            }
        }

        heif_error error = heif_context_get_encoder(ctx, encoder_descriptors[idx], &encoder);
        if (error.code) {
            std::cerr << error.message << "\n";
            return 5;
        }
    }
    else {
        std::cerr << "No " << (enc_av1f ? "AV1" : "HEVC") << " encoder available.\n";
        return 5;
    }

    tuple<uint8_t*, int, int, int, int, heif_image*> datas = read_heif_data(file_path);
    uint8_t* data = get<0>(datas);
    int stride = get<1>(datas);
    int width = get<2>(datas);
    int height = get<3>(datas);
    int has_alpha = get<4>(datas);
    int channels = has_alpha ? 4 : 3;
    struct heif_image* image = get<5>(datas);
    struct heif_error err;
    err = heif_image_create((int)width, (int)height,
        heif_colorspace_RGB,
        has_alpha ? heif_chroma_interleaved_RGBA : heif_chroma_interleaved_RGB,
        &image);
    (void)err;
    heif_image_add_plane(image, heif_channel_interleaved, (int)width, (int)height,
        has_alpha ? 32 : 24);

    int img_stride;
    uint8_t* p = (uint8_t*)heif_image_get_plane(image, heif_channel_interleaved, &img_stride);

    cout << "Read Width: " << width << endl
        << "Read Height: " << height << endl
        << "Read Stride: " << stride << endl
        << "Write Width: " << width << endl
        << "Write Height: " << height << endl
        << "Write Stride: " << img_stride << endl
        ;
    for (uint32_t y = 0; y < height; y++) {
        uint32_t prow = y * img_stride;
        uint32_t irow = y * stride;
        uint32_t row_len = width * channels;
        memcpy(p + prow, data + irow, row_len);
    }
    struct heif_error error;

    // encode the image
    heif_color_profile_nclx nclx;
    nclx.matrix_coefficients = (heif_matrix_coefficients)nclx_matrix_coefficients;
    nclx.transfer_characteristics = (heif_transfer_characteristics)nclx_transfer_characteristic;
    nclx.color_primaries = (heif_color_primaries)nclx_colour_primaries;
    nclx.full_range_flag = (uint8_t)nclx_full_range;
    heif_image_set_nclx_color_profile(image, &nclx);

    //// set the encoder parameters
    heif_encoder_set_lossy_quality(encoder, 50);
    heif_encoder_set_lossless(encoder, lossless);
    heif_encoder_set_logging_level(encoder, logging_level);

    set_params(encoder, raw_params);

    heif_encoding_options* opts = heif_encoding_options_alloc();
    opts->save_alpha_channel = (uint8_t)master_alpha;
    opts->save_two_colr_boxes_when_ICC_and_nclx_available = (uint8_t)two_colr_boxes;

    if (crop_to_even_size) {
        if (heif_image_get_primary_width(image) == 1 ||
            heif_image_get_primary_height(image) == 1) {
            std::cerr << "Image only has a size of 1 pixel width or height. Cannot crop to even size.\n";
            return 1;
        }

        std::cerr << "Warning: option --even-size/-E is deprecated as it is not needed anymore.\n";

        int right = heif_image_get_primary_width(image) % 2;
        int bottom = heif_image_get_primary_height(image) % 2;

        error = heif_image_crop(image, 0, right, 0, bottom);
        if (error.code != 0) {
            heif_encoding_options_free(opts);
            std::cerr << "Could not crop image: " << error.message << "\n";
            return 1;
        }
    }

    heif_image_handle* handle;
    error = heif_context_encode_image(ctx, image, encoder, opts, &handle);

    if (error.code != 0) {
        heif_encoding_options_free(opts);
        std::cerr << "Could not encode HEIF/AVIF file: " << error.message << "\n";
        return 1;
    }
    if (thumbnail_bbox_size > 0) {
        // encode thumbnail

        struct heif_image_handle* thumbnail_handle;

        opts->save_alpha_channel = master_alpha && thumb_alpha;

        error = heif_context_encode_thumbnail(ctx,
            image,
            handle,
            encoder,
            opts,
            thumbnail_bbox_size,
            &thumbnail_handle);
        if (error.code) {
            heif_encoding_options_free(opts);
            std::cerr << "Could not generate thumbnail: " << error.message << "\n";
            return 5;
        }

        if (thumbnail_handle) {
            heif_image_handle_release(thumbnail_handle);
        }
    }
    heif_image_handle_release(handle);
    heif_encoding_options_free(opts);

    heif_encoder_release(encoder);
    error = heif_context_write_to_file(ctx, output_path);
    if (error.code) {
        std::cerr << error.message << "\n";
        return NULL;
    }

    //write_heif_data(image, 50, output_path);
    datas = read_heif_data(output_path);
    data = get<0>(datas);
    stride = get<1>(datas);
    width = get<2>(datas);
    height = get<3>(datas);

    cout << "Read Width: " << width << endl
        << "Read Height: " << height << endl
        << "Read Stride: " << stride << endl
        ;
    return 0;
}

My read_heif_data function is following:

tuple<uint8_t*,int,int,int,int, heif_image*> read_heif_data(const char* input_filename) {
    heif_context* ctx = heif_context_alloc();
    heif_context_read_from_file(ctx, input_filename, nullptr);
    int num_images = heif_context_get_number_of_top_level_images(ctx);

    // get a handle to the primary image
    heif_image_handle* handle;
    heif_context_get_primary_image_handle(ctx, &handle);

    int img_width = heif_image_handle_get_width(handle);
    int img_height = heif_image_handle_get_height(handle);
    int has_alpha = heif_image_handle_has_alpha_channel(handle);
    struct heif_image* img;
    struct heif_error err;

    int stride; // bytes per line
    uint8_t* data;
    // decode the image and convert colorspace to RGB, saved as 24bit interleaved
    err = heif_decode_image(handle, &img, heif_colorspace_RGB, has_alpha ? heif_chroma_interleaved_RGBA : heif_chroma_interleaved_RGB, nullptr);
    if (err.code) {
        heif_image_handle_release(handle);
        std::cerr << "Could not decode image: " << ": "
            << err.message << "\n";
        return { data, stride, img_width, img_height, has_alpha, img };
    }
    data = heif_image_get_plane(img, heif_channel_interleaved, &stride);

    //err = heif_image_get_nclx_color_profile(img, &nclx);
    return { data, stride, img_width, img_height, has_alpha, img };
}

Here is the output I got.

Read Width: 2268
Read Height: 4032
Read Stride: 6816
Write Width: 2268
Write Height: 4032
Write Stride: 6816
Read Width: 0
Read Height: 0
Read Stride: 6816

My Original Input Image like this image

and my output .heic file got this image

You can see the black border there.

Is there something I need to know? I'm a newbie in C++.

thanks for the reply. 😄

li195111 commented 3 years ago

My OS env is Windows 10 21H2 Microsoft Visual Studio Community 2019 16.10.3 Install libheif by Vcpkg image