libexif 是一个用来读取数码相机照片中包含的 EXIF 信息的 C 语言库,支持多种平台。可以使用libexif 库对jpeg图片进行exif信息的写入,读取,修改等操作
/* Get an existing tag, or create one if it doesn't exist */
static ExifEntry *init_tag(ExifData *exif, ExifIfd ifd, ExifTag tag)
{
ExifEntry *entry;
/* Return an existing tag if one exists */
if (!((entry = exif_content_get_entry (exif->ifd[ifd], tag)))) {
/* Allocate a new entry */
entry = exif_entry_new ();
assert(entry != NULL); /* catch an out of memory condition */
entry->tag = tag; /* tag must be set before calling
exif_content_add_entry */
/* Attach the ExifEntry to an IFD */
exif_content_add_entry (exif->ifd[ifd], entry);
/* Allocate memory for the entry and fill with default data */
exif_entry_initialize (entry, tag);
/* Ownership of the ExifEntry has now been passed to the IFD.
* One must be very careful in accessing a structure after
* unref'ing it; in this case, we know "entry" won't be freed
* because the reference count was bumped when it was added to
* the IFD.
*/
exif_entry_unref(entry);
}
return entry;
}
/* Create a brand-new tag with a data field of the given length, in the
* given IFD. This is needed when exif_entry_initialize() isn't able to create
* this type of tag itself, or the default data length it creates isn't the
* correct length.
*/
static ExifEntry *create_tag(ExifData *exif, ExifIfd ifd, ExifTag tag, size_t len, ExifFormat format)
{
void *buf;
ExifEntry *entry;
/* Create a memory allocator to manage this ExifEntry */
ExifMem *mem = exif_mem_new_default();
assert(mem != NULL); /* catch an out of memory condition */
/* Create a new ExifEntry using our allocator */
entry = exif_entry_new_mem (mem);
assert(entry != NULL);
/* Allocate memory to use for holding the tag data */
buf = exif_mem_alloc(mem, len);
assert(buf != NULL);
/* Fill in the entry */
entry->data = buf;
entry->size = len;
entry->tag = tag;
entry->components = len;
entry->format = format;
/* Attach the ExifEntry to an IFD */
exif_content_add_entry (exif->ifd[ifd], entry);
/* The ExifMem and ExifEntry are now owned elsewhere */
exif_mem_unref(mem);
exif_entry_unref(entry);
return entry;
}
long filesize( FILE *fp )
{
long int save_pos;
long size_of_file;
/* Save the current position. */
save_pos = ftell( fp );
/* Jump to the end of the file. */
fseek( fp, 0L, SEEK_END );
/* Get the end position. */
size_of_file = ftell( fp );
/* Jump back to the original position. */
fseek( fp, save_pos, SEEK_SET );
return( size_of_file );
}
/*
给jpeg图片添加 exif信息
input_file_fath 输入jpeg图片
output_file_path 加exif信息的jpeg图片保存路径
*/
int add_customer_exif_info_jpeg(char *input_file_fath, char *output_file_path)
{
int rc = 1;
unsigned char *exif_data = NULL;
unsigned int exif_data_len;
ExifEntry *entry = NULL;
ExifData *exif = NULL;
FILE *fp_src_file = NULL;
FILE *fp_dst_file = NULL;
unsigned long src_file_length;
unsigned char *ptr_src_file_buf = NULL;
char camera_maker[32]; // 相机制造商
char camera_model[32]; // 实际型号
unsigned short iso_val = 60; //iso
ExifRational exposure_time; //曝光时间
ExifRational f_number; //光圈值
ExifSRational exposure_bias_value;
ExifRational focal_length; //光圈值
ExifRational max_aperture_value; //光圈值
ExifShort metering_mode; //测光模式
ExifRational subject_distance; //光圈值
ExifShort flash_mode; //闪光灯模式
ExifRational flash_energy; //闪光灯能量
ExifRational gps_lat; //gps 经度
ExifRational gps_long; //gps 维度
char gps_lat_ref[2] = "W";
char gps_long_ref[2] = "N";
exif = exif_data_new();
if (!exif) {
fprintf(stderr, "Out of memory\n");
return rc;
}
/* Set the image options */
exif_data_set_option(exif, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION);
exif_data_set_data_type(exif, EXIF_DATA_TYPE_COMPRESSED);
exif_data_set_byte_order(exif, FILE_BYTE_ORDER);
/* Create the mandatory EXIF fields with default data */
//exif_data_fix(exif);
/************EXIF信息添加**************************/
//相机制造商
sprintf(camera_maker, "%s", "Sony");
entry = create_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_MAKE, strlen(camera_maker) + 1, EXIF_FORMAT_ASCII);
memcpy(entry->data, camera_maker, strlen(camera_maker) + 1);
//相机型号
sprintf(camera_model, "%s", "imx274");
entry = create_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_MODEL, strlen(camera_model) + 1, EXIF_FORMAT_ASCII);
memcpy(entry->data, camera_model, strlen(camera_model) + 1);
//曝光时间设置 uint:s
exposure_time.numerator = 1;
exposure_time.denominator = 18;
entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_TIME);
exif_set_rational(entry->data, FILE_BYTE_ORDER, exposure_time);
//ISO 大小
entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_ISO_SPEED_RATINGS);
exif_set_short(entry->data, FILE_BYTE_ORDER, iso_val);
//曝光补偿 unit EV
exposure_bias_value.numerator = 0;
exposure_bias_value.denominator = 1;
entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_BIAS_VALUE);
exif_set_srational(entry->data, FILE_BYTE_ORDER, exposure_bias_value);
//焦距 numerator/denominator, uint mm
focal_length.numerator = 35;
focal_length.denominator = 1;
entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_FOCAL_LENGTH);
exif_set_rational(entry->data, FILE_BYTE_ORDER, focal_length);
/*
https://baike.baidu.com/item/%E5%85%89%E5%9C%88%E5%80%BC/10310445?fr=aladdin
最大光圈 AV f/(max_aperture_value.numerator/max_aperture_value.denominator)
光圈值(AV) 0 1 2 3 4 5 6 7 8 9 10
光圈数值(F)1 1.4 2 2.8 4 5.6 8 11 16 2232
*/
//光圈值 最大光值
f_number.numerator = 4;
f_number.denominator = 1;
entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_FNUMBER);
exif_set_rational(entry->data, FILE_BYTE_ORDER, f_number);
max_aperture_value.numerator = 4;
max_aperture_value.denominator = 1;
entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_MAX_APERTURE_VALUE);
exif_set_rational(entry->data, FILE_BYTE_ORDER, max_aperture_value);
/*
测光模式 1~6
1 平均测光
2 中央重点测光
3 点测光
4 多点测光
5 矩阵测光
6 部分测光
*/
metering_mode = 5;
entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_METERING_MODE);
exif_set_short(entry->data, FILE_BYTE_ORDER, metering_mode);
//目标距离
subject_distance.numerator = 0;
subject_distance.denominator = 1;
entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_SUBJECT_DISTANCE);
exif_set_rational(entry->data, FILE_BYTE_ORDER, subject_distance);
/*
闪光灯模式
0 - 关闭
1 - 开启
*/
flash_mode = 1;
entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_FLASH);
exif_set_short(entry->data, FILE_BYTE_ORDER, flash_mode);
//闪光灯能量 这里根据flash mode简单设置下 Unit:bcps
flash_energy.numerator = flash_mode > 0 ? 0xffff : 0;
flash_energy.denominator = 1;
entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_FLASH_ENERGY);
exif_set_rational(entry->data, FILE_BYTE_ORDER, flash_energy);
//gps 经度参考
entry = create_tag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_LATITUDE_REF, 2, EXIF_FORMAT_ASCII);
memcpy(entry->data, gps_lat_ref, 2);
//gps 经度
gps_lat.numerator = 114;
gps_lat.denominator = 3;
entry = create_tag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_LATITUDE, sizeof(gps_lat), EXIF_FORMAT_RATIONAL);
exif_set_rational(entry->data, FILE_BYTE_ORDER, gps_lat);
//gps 纬度参考
entry = create_tag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_LONGITUDE_REF,2, EXIF_FORMAT_ASCII);
memcpy(entry->data, gps_long_ref, 2);
//gps 纬度
gps_long.numerator = 18;
gps_long.denominator = 2;
entry = create_tag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_LONGITUDE, sizeof(gps_lat), EXIF_FORMAT_RATIONAL);
exif_set_rational(entry->data, FILE_BYTE_ORDER, gps_long);
/* Get a pointer to the EXIF data block we just created */
exif_data_save_data(exif, &exif_data, &exif_data_len);
assert(exif_data != NULL);
fp_src_file = fopen(input_file_fath, "rb");
if (!fp_src_file) {
fprintf(stderr, "Error creating file %s\n", input_file_fath);
exif_data_unref(exif);
return rc;
}
/*read src jpeg file to buff*/
src_file_length = filesize(fp_src_file);
ptr_src_file_buf = (unsigned char *)malloc(src_file_length);
if (!ptr_src_file_buf) {
printf("Allocate buf for %s failed.\n", input_file_fath);
goto errout;
}
if(fread(ptr_src_file_buf, src_file_length, 1, fp_src_file) != 1) {
printf("Read %s error\n", input_file_fath);
goto errout;
}
fclose(fp_src_file);
if (!(ptr_src_file_buf[0] == 0xFF && ptr_src_file_buf[1] == 0xD8)) {
printf("%s is not jpeg file,won't add exif for it!\n", input_file_fath);
goto errout;
}
if ((ptr_src_file_buf[6] == 'E' && ptr_src_file_buf[7] == 'x' && ptr_src_file_buf[8] == 'i' && ptr_src_file_buf[9] == 'f')) {
printf("%s adready add exif,won't overwrite exif info!\n", input_file_fath);
goto errout;
}
fp_dst_file = fopen(output_file_path, "wb");
if (!fp_dst_file) {
fprintf(stderr, "Error creating file %s\n", output_file_path);
exif_data_unref(exif);
return rc;
}
/* Write EXIF header */
if (fwrite(exif_header, exif_header_len, 1, fp_dst_file) != 1) {
fprintf(stderr, "Error writing to file %s\n", output_file_path);
goto errout;
}
/* Write EXIF block length in big-endian order */
if (fputc((exif_data_len+2) >> 8, fp_dst_file) < 0) {
fprintf(stderr, "Error writing to file %s\n", output_file_path);
goto errout;
}
if (fputc((exif_data_len+2) & 0xff, fp_dst_file) < 0) {
fprintf(stderr, "Error writing to file %s\n", output_file_path);
goto errout;
}
/* Write EXIF data block */
if (fwrite(exif_data, exif_data_len, 1, fp_dst_file) != 1) {
fprintf(stderr, "Error writing to file %s\n", output_file_path);
goto errout;
}
/* Write JPEG image data, skipping the non-EXIF header */
if (fwrite(ptr_src_file_buf +image_data_offset, src_file_length-image_data_offset, 1, fp_dst_file) != 1) {
fprintf(stderr, "Error writing to file %s\n", output_file_path);
goto errout;
}
rc = 0;
errout:
if (ptr_src_file_buf) {
free(ptr_src_file_buf);
}
/* The allocator we're using for ExifData is the standard one, so use
* it directly to free this pointer.
*/
free(exif_data);
exif_data_unref(exif);
return rc;
}
int add_customer_exif_info_jpeg(char *input_file_fath, char *output_file_path)
这个函数就是对input_file_fath 图片添加自定义的exif信息,然后输出到output_file_path
从这个函数可以看到libexif添加exif信息的步骤也是相当简单,添加流程如下:
entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_METERING_MODE);
exif_set_short(entry->data, FILE_BYTE_ORDER, metering_mode);
或者
//gps 纬度
gps_long.numerator = 18;
gps_long.denominator = 2;
entry = create_tag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_LONGITUDE, sizeof(gps_lat), EXIF_FORMAT_RATIONAL);
exif_set_rational(entry->data, FILE_BYTE_ORDER, gps_long);
init_tag() 和 create_tag() 有点差异:
init_tag() 提供了常见的IFD及tag的初始化操作
create_tag() 如果init_tag() 没有默认支持你要操作IFD及tag,就用这个接口初始化一个entry
插入exif步骤:
void read_exif_entry(ExifEntry *ee, void* ifd)
{
char v[1024];
// strncpy(t, exif_tag_get_title_in_ifd(ee->tag, exif_entry_get_ifd(ee)), sizeof(t));
// strncpy(t, exif_tag_get_title_in_ifd(ee->tag, *((ExifIfd*)ifd)), sizeof(t));
//trim t
printf("%s: %s\n"
// , exif_tag_get_name_in_ifd(ee->tag, *((ExifIfd*)ifd))
, exif_tag_get_title_in_ifd(ee->tag, *((ExifIfd*)ifd))
// , exif_tag_get_description_in_ifd(ee->tag, *((ExifIfd*)ifd))
, exif_entry_get_value(ee, v, sizeof(v)));
}
void read_exif_content(ExifContent *ec, void *user_data)
{
ExifIfd ifd = exif_content_get_ifd(ec);
if (ifd == EXIF_IFD_COUNT)
fprintf(stderr, "exif_content_get_ifd error");
printf("======IFD: %d %s======\n", ifd, exif_ifd_get_name(ifd));
exif_content_foreach_entry(ec, read_exif_entry, &ifd);
}
int main(int argc, char** argv)
{
if (argc < 2) {
printf("Usage %s jpeg_list\n", argv[0]);
}
for (int i = 1; i<argc; i++) {
printf("-------------->ExifInfo:%s Start<--------------\n", argv[i]);
ExifData* ed = exif_data_new_from_file(argv[1]);
if (!ed) {
fprintf(stderr, "An error occur\n");
return 1;
}
//exif_data_set_option(ed,
exif_data_foreach_content(ed, read_exif_content, NULL);
exif_data_unref(ed);
printf("-------------->ExifInfo:%s End<--------------\n\n", argv[i]);
}
return 0;
}
···
读取exif信息过程也比较简单就是依次遍历各个entry,然后打印出entry相关的信息
exif_data_foreach_content
read_exif_content(ExifContent *ec, void *user_data)
exif_content_foreach_entry(ec, read_exif_entry, &ifd);
read_exif_entry(ExifEntry *ee, void* ifd) //这个函数最终会打印出各个Entery的内容
csdn资源地址
代码使用SourceInsight 或者notepad++ 打开,否则可能出现中文乱码
资源详细的使用见里面的MyReadme.txt,这里简单的介绍下资源的使用:
1.编译库
运行libexif-mater 文件夹的mybuild_ubuntu.sh
运行后当前目录build的文件夹会有编译文件
ls build/
include lib share
2. 编译示例demo
cd contrib/examples/
./compile_exampale.sh
gcc -Wall -I ../../build/include/ --static -o photographer photographer.c -L ../../build/lib/ -lexif -lm
gcc -Wall -I ../../build/include/ --static -o thumbnail thumbnail.c -L ../../build/lib/ -lexif -lm
gcc -Wall -I ../../build/include/ --static -o write-exif write-exif.c -L ../../build/lib/ -lexif -lm
gcc -Wall -I ../../build/include/ --static -o test_jpeg_exif_new test_jpeg_exif_new.c -L ../../build/lib/ -lexif -lm
gcc -Wall -I ../../build/include/ --static -o read_jpeg_exif read_jpeg_exif.c -L ../../build/lib/ -lexif -lm
All done!!!
test_jpeg_exif_new 写自定义exif 信息
read_jpeg_exif 读取写入的exif信息
写exif 信息使用:
用windows自带画图软件随便产生一个jpg文件或者使用自带的test.jpg文件
./test_jpeg_exif_new test.jpg test_exif.jps
Add customer exif info for test.jpg ok! #正常会有这个输出
读取写入的exif信息:
./read_jpeg_exif test_exif.jps
-------------->ExifInfo:test_exif.jps Start<--------------
======IFD: 0 0======
X-Resolution: 72
Y-Resolution: 72
Resolution Unit: Inch
======IFD: 1 1======
======IFD: 2 EXIF======
Exposure Time: 1/18 sec.
F-Number: f/4.0
ISO Speed Ratings: 60
Exposure Bias: 0.00 EV
Maximum Aperture Value: 4.00 EV (f/4.0)
Subject Distance: 0.0 m
Metering Mode: Pattern
Flash: Flash fired
Focal Length: 35.0 mm
Flash Energy: 65535
Exif Version: Exif Version 2.1
FlashPixVersion: FlashPix Version 1.0
Color Space: Uncalibrated
======IFD: 3 GPS======
North or South Latitude: W
Latitude: 38.0, 132897/0, 0/0, 0/0, 0/0, 0/0, 0/0, 0/0
East or West Longitude: N
Longitude: 9.0, 132833/0, 0/0, 0/0, 0/0, 0/0, 0/0, 0/0
======IFD: 4 Interoperability======
-------------->ExifInfo:test_exif.jps End<--------------