libheif icon indicating copy to clipboard operation
libheif copied to clipboard

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

Open li195111 opened this issue 3 years ago • 1 comments

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 avatar Jul 19 '21 01:07 li195111

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

li195111 avatar Jul 19 '21 01:07 li195111