JPEG添加EXIF
编译环境:Ubuntu16.04 64位交叉编译工具:arm-hisiv500-linux-gcc最近需要在hi3519抓拍到的JPEG图片增加GPS等EXIF信息,在网上也找了一些资料,介绍EXIF格式的很多,写到读写EXIF信息的很少,可以使用开源库libexif©或者exiv2(C++)实现。 libexif代码量相对较少(70+,exiv2有300+),也方便移植。果断下载libexi..
编译环境:Ubuntu16.04 64位
交叉编译工具:arm-hisiv500-linux-gcc
最近需要在hi3519抓拍到的JPEG图片增加GPS等EXIF信息,在网上也找了一些资料,介绍EXIF格式的很多,写到读写EXIF信息的很少,可以使用开源库libexif(C语言)或者exiv2(C++)实现。 libexif代码量相对较少(70+,exiv2有300+),也方便移植。果断下载libexif源码(版本0.6.21_release),结果发现里面没有linux平台的编译脚本,算了,自己写。
文章目录
1. sample代码
在contrib/examples目录下,有份写exif的sample代码write-exif.c,后面可以根据这份代码添加我们需要的EXIF信息(GPS等),代码如下:
/*
* write-exif.c
*
* Placed into the public domain by Daniel Fandrich
*
* Create a new EXIF data block and write it into a JPEG image file.
*
* The JPEG image data used in this example is fixed and is guaranteed not
* to contain an EXIF tag block already, so it is easy to precompute where
* in the file the EXIF data should be. In real life, a library like
* libjpeg (included with the exif command-line tool source code) would
* be used to write to an existing JPEG file.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <libexif/exif-data.h>
/* this file will be unilaterally overwritten */
#define FILE_NAME "write-exif.jpg"
/* raw JPEG image data */
static const unsigned char image_jpg[] = {
0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01,
0x01, 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xff, 0xdb, 0x00, 0x43,
0x00, 0x14, 0x0e, 0x0f, 0x12, 0x0f, 0x0d, 0x14, 0x12, 0x10, 0x12, 0x17,
0x15, 0x14, 0x18, 0x1e, 0x32, 0x21, 0x1e, 0x1c, 0x1c, 0x1e, 0x3d, 0x2c,
0x2e, 0x24, 0x32, 0x49, 0x40, 0x4c, 0x4b, 0x47, 0x40, 0x46, 0x45, 0x50,
0x5a, 0x73, 0x62, 0x50, 0x55, 0x6d, 0x56, 0x45, 0x46, 0x64, 0x88, 0x65,
0x6d, 0x77, 0x7b, 0x81, 0x82, 0x81, 0x4e, 0x60, 0x8d, 0x97, 0x8c, 0x7d,
0x96, 0x73, 0x7e, 0x81, 0x7c, 0xff, 0xc0, 0x00, 0x0b, 0x08, 0x00, 0x40,
0x00, 0x40, 0x01, 0x01, 0x11, 0x00, 0xff, 0xc4, 0x00, 0x1b, 0x00, 0x00,
0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x06, 0x04, 0x03, 0x07, 0x02, 0x01, 0xff,
0xc4, 0x00, 0x2f, 0x10, 0x00, 0x01, 0x03, 0x03, 0x02, 0x05, 0x03, 0x03,
0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x11,
0x00, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x81, 0x61, 0x71,
0x91, 0x13, 0x32, 0xa1, 0x14, 0x22, 0xc1, 0x15, 0x23, 0x52, 0xd1, 0xf0,
0xff, 0xda, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xb3, 0xa2,
0x8a, 0x28, 0xa2, 0x8a, 0x28, 0xa2, 0x97, 0x64, 0x72, 0xd6, 0x58, 0xd4,
0x8f, 0xd5, 0x3d, 0xca, 0xa5, 0x7d, 0xa8, 0x4e, 0xaa, 0x3e, 0xb0, 0x3a,
0x7a, 0x9d, 0x2b, 0x2d, 0x87, 0x13, 0xe3, 0xaf, 0x9f, 0x0c, 0xb6, 0xb5,
0xb6, 0xe2, 0xb4, 0x48, 0x71, 0x30, 0x14, 0x7b, 0x02, 0x09, 0x14, 0xee,
0x94, 0x64, 0x38, 0x87, 0x1f, 0x8d, 0x5f, 0xd3, 0x7d, 0xe2, 0xa7, 0x46,
0xe8, 0x6c, 0x49, 0x1e, 0xfd, 0x07, 0xb1, 0x33, 0x5f, 0xb8, 0xdc, 0xf5,
0x96, 0x51, 0x7c, 0x96, 0xee, 0x14, 0xba, 0x04, 0xf2, 0x2c, 0x42, 0xa3,
0xd3, 0x70, 0x7c, 0x1a, 0x6d, 0x45, 0x2c, 0xcd, 0xe5, 0x11, 0x89, 0xb1,
0x5b, 0xc4, 0x02, 0xe1, 0xd1, 0xb4, 0xcf, 0xdc, 0xa3, 0xfc, 0x0d, 0xcd,
0x41, 0xde, 0xe3, 0xb2, 0x2e, 0xda, 0x1c, 0xbd, 0xe2, 0x4a, 0x90, 0xe9,
0x04, 0x95, 0x2b, 0xf7, 0x41, 0xd0, 0x18, 0xe8, 0x36, 0x03, 0xc6, 0x91,
0x4a, 0x81, 0x20, 0x82, 0x24, 0x10, 0x66, 0x45, 0x5a, 0xdd, 0xf1, 0x32,
0xff, 0x00, 0xa2, 0x5a, 0x8b, 0x62, 0x57, 0x7d, 0x70, 0x80, 0x93, 0x1a,
0x94, 0x10, 0x79, 0x4a, 0xa3, 0xb9, 0x20, 0xc0, 0xf3, 0xd3, 0x55, 0x59,
0x4e, 0x1c, 0x7a, 0xc7, 0x12, 0x9b, 0xc7, 0x94, 0xa7, 0x2e, 0x14, 0xa9,
0x74, 0x0d, 0x42, 0x01, 0x04, 0xc9, 0x3b, 0x93, 0x30, 0x09, 0xdb, 0x5f,
0x35, 0x9b, 0x85, 0xad, 0xde, 0x7f, 0x39, 0x6e, 0xa6, 0x82, 0xa1, 0xa5,
0x73, 0x2d, 0x40, 0x68, 0x13, 0x06, 0x67, 0xdf, 0x6f, 0x35, 0xe9, 0xb4,
0x52, 0x4c, 0xbe, 0x0c, 0x65, 0x72, 0x16, 0xcf, 0x3a, 0xff, 0x00, 0xf6,
0x5a, 0xd1, 0x4d, 0x11, 0xa1, 0x13, 0x26, 0x0f, 0x73, 0x00, 0x1f, 0x41,
0x5b, 0x72, 0xd6, 0xa2, 0xeb, 0x15, 0x73, 0x6e, 0x13, 0xf7, 0x36, 0x42,
0x40, 0xee, 0x04, 0x8f, 0xc8, 0x15, 0xe4, 0xf5, 0x59, 0xc1, 0x16, 0x0d,
0x3d, 0x70, 0xed, 0xdb, 0x84, 0x29, 0x6c, 0x40, 0x42, 0x3b, 0x13, 0x3f,
0xb8, 0xfc, 0x10, 0x3c, 0xd5, 0xc1, 0x00, 0x88, 0x22, 0x41, 0xef, 0x5f,
0x0d, 0xb4, 0x86, 0x81, 0x0d, 0xa1, 0x28, 0x07, 0x52, 0x12, 0x00, 0x06,
0xba, 0xd1, 0x45, 0x71, 0xb8, 0xb8, 0x6e, 0xd9, 0x85, 0xbc, 0xf2, 0xb9,
0x5b, 0x40, 0x25, 0x47, 0xb0, 0xaf, 0x24, 0x79, 0x48, 0x53, 0xce, 0x29,
0xb4, 0x94, 0xa0, 0xa8, 0x94, 0xa7, 0xb0, 0x27, 0x41, 0xf1, 0x54, 0x9c,
0x09, 0x71, 0xc9, 0x92, 0x7d, 0x82, 0x74, 0x75, 0xa9, 0x1e, 0xa5, 0x27,
0xfd, 0x13, 0x55, 0xd9, 0x3c, 0x83, 0x38, 0xbb, 0x45, 0x5c, 0xbf, 0x3c,
0xa1, 0x40, 0x40, 0x02, 0x54, 0x4f, 0x41, 0x3e, 0x4f, 0x83, 0x5a, 0x2d,
0xae, 0x1b, 0xba, 0xb7, 0x6d, 0xf6, 0x89, 0x28, 0x71, 0x21, 0x49, 0x24,
0x41, 0x83, 0xe9, 0x5d, 0xe9, 0x3f, 0x12, 0x64, 0x97, 0x8b, 0xc6, 0x17,
0x59, 0x03, 0xea, 0xa9, 0x61, 0x08, 0x24, 0x48, 0x04, 0xc9, 0x9f, 0x80,
0x6a, 0x4b, 0x17, 0xc4, 0xd7, 0xac, 0xdf, 0x36, 0xab, 0xbb, 0x85, 0x39,
0x6e, 0xb5, 0x00, 0xe0, 0x50, 0x06, 0x01, 0x3a, 0x91, 0x02, 0x44, 0x6f,
0x02, 0xb7, 0x65, 0xb2, 0x0e, 0x71, 0x25, 0xf3, 0x58, 0xdc, 0x72, 0x8f,
0xe9, 0xc1, 0xe6, 0x52, 0xe0, 0xc2, 0xa3, 0x72, 0x46, 0xe0, 0x0e, 0x83,
0xa9, 0xf1, 0x4b, 0x38, 0x9f, 0x16, 0xde, 0x2e, 0xfd, 0xa4, 0x32, 0x92,
0x1a, 0x5b, 0x40, 0x83, 0xdc, 0x8d, 0x0f, 0x9d, 0x01, 0x3e, 0xf5, 0x83,
0x17, 0x7a, 0xac, 0x76, 0x41, 0x9b, 0xa4, 0xa7, 0x9b, 0xe9, 0xab, 0x51,
0x31, 0x20, 0x82, 0x08, 0xf8, 0x34, 0xf9, 0xdb, 0x97, 0xb8, 0xb7, 0x2c,
0xcd, 0xba, 0x12, 0xa6, 0xec, 0xda, 0x3c, 0xca, 0x13, 0x24, 0x0e, 0xa4,
0x91, 0xa4, 0x9d, 0x87, 0x69, 0xf7, 0xab, 0x74, 0x21, 0x2d, 0xa1, 0x28,
0x4a, 0x42, 0x52, 0x90, 0x02, 0x40, 0xd8, 0x01, 0xb0, 0xae, 0x95, 0x87,
0x29, 0x8f, 0x6f, 0x27, 0x64, 0xe5, 0xb3, 0xc4, 0x80, 0xa8, 0x29, 0x50,
0xdd, 0x24, 0x6c, 0x47, 0xfd, 0xd4, 0xd4, 0x92, 0x78, 0x1e, 0xef, 0xeb,
0x42, 0xae, 0x98, 0x0d, 0x7f, 0x90, 0x0a, 0x27, 0xe2, 0x00, 0xfc, 0xd5,
0x4e, 0x27, 0x11, 0x6d, 0x89, 0x60, 0xb7, 0x6e, 0x92, 0x54, 0xa8, 0xe6,
0x71, 0x5b, 0xa8, 0xff, 0x00, 0x03, 0xd2, 0xbe, 0x73, 0x38, 0x86, 0x72,
0xf6, 0xc1, 0xb7, 0x4f, 0x2a, 0xd1, 0x25, 0x0e, 0x01, 0x25, 0x27, 0xae,
0x9d, 0x41, 0xed, 0xed, 0x52, 0xe8, 0xe0, 0x7b, 0xcf, 0xab, 0x0e, 0x5d,
0x30, 0x96, 0xe7, 0xee, 0x4f, 0x31, 0x57, 0xc1, 0x00, 0x7e, 0x6a, 0xaf,
0x15, 0x8b, 0x63, 0x17, 0x6e, 0x1a, 0x61, 0x24, 0x92, 0x65, 0x6b, 0x3b,
0xa8, 0xf7, 0x3e, 0x9d, 0x87, 0x4a, 0x63, 0x45, 0x14, 0x51, 0x45, 0x14,
0x51, 0x45, 0x7f, 0xff, 0xd9
};
/* length of data in image_jpg */
static const unsigned int image_jpg_len = sizeof(image_jpg);
/* dimensions of image */
static const unsigned int image_jpg_x = 64;
static const unsigned int image_jpg_y = 64;
/* start of JPEG image data section */
static const unsigned int image_data_offset = 20;
#define image_data_len (image_jpg_len - image_data_offset)
/* raw EXIF header data */
static const unsigned char exif_header[] = {
0xff, 0xd8, 0xff, 0xe1
};
/* length of data in exif_header */
static const unsigned int exif_header_len = sizeof(exif_header);
/* byte order to use in the EXIF block */
#define FILE_BYTE_ORDER EXIF_BYTE_ORDER_INTEL
/* comment to write into the EXIF block */
#define FILE_COMMENT "libexif demonstration image"
/* special header required for EXIF_TAG_USER_COMMENT */
#define ASCII_COMMENT "ASCII\0\0\0"
/* 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)
{
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 = EXIF_FORMAT_UNDEFINED;
/* 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;
}
int main(int argc, char **argv)
{
int rc = 1;
FILE *f;
unsigned char *exif_data;
unsigned int exif_data_len;
ExifEntry *entry;
ExifData *exif = exif_data_new();
if (!exif) {
fprintf(stderr, "Out of memory\n");
return 2;
}
/* 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);
/* All these tags are created with default values by exif_data_fix() */
/* Change the data to the correct values for this image. */
entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_PIXEL_X_DIMENSION);
exif_set_long(entry->data, FILE_BYTE_ORDER, image_jpg_x);
entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_PIXEL_Y_DIMENSION);
exif_set_long(entry->data, FILE_BYTE_ORDER, image_jpg_y);
entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_COLOR_SPACE);
exif_set_short(entry->data, FILE_BYTE_ORDER, 1);
/* Create a EXIF_TAG_USER_COMMENT tag. This one must be handled
* differently because that tag isn't automatically created and
* allocated by exif_data_fix(), nor can it be created using
* exif_entry_initialize() so it must be explicitly allocated here.
*/
entry = create_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_USER_COMMENT,
sizeof(ASCII_COMMENT) + sizeof(FILE_COMMENT) - 2);
/* Write the special header needed for a comment tag */
memcpy(entry->data, ASCII_COMMENT, sizeof(ASCII_COMMENT)-1);
/* Write the actual comment text, without the trailing NUL character */
memcpy(entry->data+8, FILE_COMMENT, sizeof(FILE_COMMENT)-1);
/* create_tag() happens to set the format and components correctly for
* EXIF_TAG_USER_COMMENT, so there is nothing more to do. */
/* Create a EXIF_TAG_SUBJECT_AREA tag */
entry = create_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_SUBJECT_AREA,
4 * exif_format_get_size(EXIF_FORMAT_SHORT));
entry->format = EXIF_FORMAT_SHORT;
entry->components = 4;
exif_set_short(entry->data, FILE_BYTE_ORDER, image_jpg_x / 2);
exif_set_short(entry->data+2, FILE_BYTE_ORDER, image_jpg_y / 2);
exif_set_short(entry->data+4, FILE_BYTE_ORDER, image_jpg_x);
exif_set_short(entry->data+6, FILE_BYTE_ORDER, image_jpg_y);
/* 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);
f = fopen(FILE_NAME, "wb");
if (!f) {
fprintf(stderr, "Error creating file %s\n", FILE_NAME);
exif_data_unref(exif);
return rc;
}
/* Write EXIF header */
if (fwrite(exif_header, exif_header_len, 1, f) != 1) {
fprintf(stderr, "Error writing to file %s\n", FILE_NAME);
goto errout;
}
/* Write EXIF block length in big-endian order */
if (fputc((exif_data_len+2) >> 8, f) < 0) {
fprintf(stderr, "Error writing to file %s\n", FILE_NAME);
goto errout;
}
if (fputc((exif_data_len+2) & 0xff, f) < 0) {
fprintf(stderr, "Error writing to file %s\n", FILE_NAME);
goto errout;
}
/* Write EXIF data block */
if (fwrite(exif_data, exif_data_len, 1, f) != 1) {
fprintf(stderr, "Error writing to file %s\n", FILE_NAME);
goto errout;
}
/* Write JPEG image data, skipping the non-EXIF header */
if (fwrite(image_jpg+image_data_offset, image_data_len, 1, f) != 1) {
fprintf(stderr, "Error writing to file %s\n", FILE_NAME);
goto errout;
}
printf("Wrote file %s\n", FILE_NAME);
rc = 0;
errout:
if (fclose(f)) {
fprintf(stderr, "Error writing to file %s\n", FILE_NAME);
rc = 1;
}
/* 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;
}
2. 建立新的exif目录结构
从write-exif.c中可以看到,sample只需要#include <libexif/exif-data.h>即可。这样我就基于exif-data.h和exif-data.c开始,找到所需要的所有的源码,过程略。最终另外建立的目录结果如下:
jerry@ubuntu:~/work/exif/exif_my$ tree
.
├── exif-byte-order.c
├── exif-content.c
├── exif-data.c
├── exif-entry.c
├── exif-format.c
├── exif-ifd.c
├── exif-loader.c
├── exif-log.c
├── exif-mem.c
├── exif-mnote-data.c
├── exif-mnote-data-canon.c
├── exif-mnote-data-fuji.c
├── exif-mnote-data-olympus.c
├── exif-mnote-data-pentax.c
├── exif-tag.c
├── exif-utils.c
├── include
│ ├── config.h
│ └── libexif
│ ├── canon
│ │ ├── exif-mnote-data-canon.h
│ │ ├── mnote-canon-entry.h
│ │ └── mnote-canon-tag.h
│ ├── exif-byte-order.h
│ ├── exif-content.h
│ ├── exif-data.h
│ ├── exif-data-type.h
│ ├── exif-entry.h
│ ├── exif-format.h
│ ├── exif.h
│ ├── exif-ifd.h
│ ├── exif-loader.h
│ ├── exif-log.h
│ ├── exif-mem.h
│ ├── exif-mnote-data.h
│ ├── exif-mnote-data-priv.h
│ ├── exif-system.h
│ ├── exif-tag.h
│ ├── exif-utils.h
│ ├── fuji
│ │ ├── exif-mnote-data-fuji.h
│ │ ├── mnote-fuji-entry.h
│ │ └── mnote-fuji-tag.h
│ ├── i18n.h
│ ├── olympus
│ │ ├── exif-mnote-data-olympus.h
│ │ ├── mnote-olympus-entry.h
│ │ └── mnote-olympus-tag.h
│ ├── pentax
│ │ ├── exif-mnote-data-pentax.h
│ │ ├── mnote-pentax-entry.h
│ │ └── mnote-pentax-tag.h
│ └── _stdint.h
├── lib
├── Makefile
├── Makefile.param
├── mnote-canon-entry.c
├── mnote-canon-tag.c
├── mnote-fuji-entry.c
├── mnote-fuji-tag.c
├── mnote-olympus-entry.c
├── mnote-olympus-tag.c
├── mnote-pentax-entry.c
├── mnote-pentax-tag.c
└── sample
├── Makefile
├── sample.c
└── write-exif.jpg
8 directories, 60 files
3. 新增以及修改的文件
新增文件Makefile、Makefile.param、sample/Makefile,sample/sample.c是write-exif.c修改文件名得到,sample/write-exif.jpg是指定sample程序后得到的jpeg图片,lib目录用来放置生成的libexif.a。
3.1 config.h
其实没什么用,只是为了尽量减少修改点。
#ifndef __EXIF_CONFIG_H__
#define __EXIF_CONFIG_H__
#endif
3.2 i18n.h
#ifndef __I18N_H__
#define __I18N_H__
#include "config.h"
#define _(String) (String)
#define N_(String) (String)
#endif /* __I18N_H__ */
3.3 _stdint.h
#ifndef __STDINT_H
#define __STDINT_H
#include <sys/types.h>
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned long uint32_t;
#if 0
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed long int32_t;
#endif
#endif
3.4 Makefile
PWD := $(shell pwd)
PARAM_FILE:=$(PWD)/Makefile.param
include $(PARAM_FILE)
CFLAGS = -I$(PWD)/include/
TARGET = $(PWD)/lib/libexif.a
OBJS := $(patsubst %.c,%.o,$(wildcard *.c))
all : $(TARGET)
$(TARGET) : $(OBJS)
$(AR) -rc $@ $(OBJS)
.PHONY : clean
clean :
rm -f $(OBJS) $(TARGET)
3.5 Makefile.param
这里指定交叉编译工具,我用的是arm-hisiv500-linux-,需要做对应的修改。
export CROSS := arm-hisiv500-linux-
export CC := $(CROSS)gcc
export AR := $(CROSS)ar
3.6 sample/Makefile
PWD := $(shell pwd)
PARAM_FILE := $(PWD)/../Makefile.param
include $(PARAM_FILE)
TARGET = $(PWD)/sample
CFLAGS = -L$(PWD)/../lib -I$(PWD)/../include -lexif -lm
all :
$(CC) sample.c -o $(TARGET) $(CFLAGS)
clean :
rm -f $(TARGET)
3.7 其他文件
以上操作完成后,使用命令make执行Makefile,会出现一系列头文件找不到的问题和几个类型不匹配的警告,可以直接在代码中修改包含头文件的路径,也可以在Makefile中添加头文件的查找路径,直到编译顺利完成,生成静态库lib/libexif.a。
注意:libexif.a使用了log10和pow函数,依赖math库,可以看到在编译sample.c的时候添加了-lm选项。
4. libexif的使用
4.1 移植
移植不需要所有的头文件,如下:
jerry@ubuntu:~/work/exif/exif_explant$ tree
.
├── include
│ └── libexif
│ ├── exif-byte-order.h
│ ├── exif-content.h
│ ├── exif-data.h
│ ├── exif-data-type.h
│ ├── exif-entry.h
│ ├── exif-format.h
│ ├── exif-ifd.h
│ ├── exif-log.h
│ ├── exif-mem.h
│ ├── exif-mnote-data.h
│ ├── exif-tag.h
│ ├── exif-utils.h
│ └── _stdint.h
└── lib
└── libexif.a
3 directories, 14 files
4.2 使用(添加GPS信息)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <libexif/exif-data.h>
static const unsigned char g_Make[16] = "AF";
static const unsigned char g_Model[16] = "JerryJiu";
static const unsigned char g_Version[16] = "181203Release";
/* start of JPEG image data section */
static const unsigned int image_data_offset = 20;
/* raw EXIF header data */
static const unsigned char exif_header[] = {
0xff, 0xd8, 0xff, 0xe1
};
/* GPS Version */
static const unsigned char g_GPSVersion[] = {
0x02, 0x02, 0x00, 0x00
};
/* length of data in exif_header */
static const unsigned int exif_header_len = sizeof(exif_header);
/* byte order to use in the EXIF block */
#define FILE_BYTE_ORDER EXIF_BYTE_ORDER_INTEL
/* 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)
{
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 = EXIF_FORMAT_UNDEFINED;
/* 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;
}
static int AF_SaveJpeg(char *pFilePath, unsigned char *pFrame, unsigned int nFrameLen, unsigned int nWidth, unsigned int nHeight)
{
int nRet = 0;
FILE *pFile = NULL;
unsigned char *exif_data = NULL;
unsigned int exif_data_len = 0;
ExifEntry *entry = NULL;
ExifData *exif = exif_data_new();
if (!exif) {
nRet = -1;
goto ERR_EXIT;
}
/* 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);
/* All these tags are created with default values by exif_data_fix() */
/* Change the data to the correct values for this image. */
entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_PIXEL_X_DIMENSION);
exif_set_long(entry->data, FILE_BYTE_ORDER, nWidth);
entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_PIXEL_Y_DIMENSION);
exif_set_long(entry->data, FILE_BYTE_ORDER, nHeight);
entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_COLOR_SPACE);
exif_set_short(entry->data, FILE_BYTE_ORDER, 1);
entry = create_tag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_VERSION_ID, 4 * exif_format_get_size(EXIF_FORMAT_BYTE));
entry->format = EXIF_FORMAT_BYTE;
entry->components = 4;
int i = 0;
for (i = 0; i < 4; i++) {
exif_set_sshort(entry->data + i, FILE_BYTE_ORDER, g_GPSVersion[i]);
}
// 上海北纬 30度40分~31度53分 东经 120度51分~122度12分
entry = create_tag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_LATITUDE_REF, 2 * exif_format_get_size(EXIF_FORMAT_ASCII));
entry->format = EXIF_FORMAT_ASCII;
entry->components = 2;
memcpy(entry->data, "N", 2);// N北纬/S南纬
entry = create_tag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_LATITUDE, 3 * exif_format_get_size(EXIF_FORMAT_RATIONAL));
entry->format = EXIF_FORMAT_RATIONAL;
entry->components = 3;
ExifRational fir, mid, las;
fir.numerator = 31;
fir.denominator = 1;
mid.numerator = 00;
mid.denominator = 1;
las.numerator = 00;
las.denominator = 1;
exif_set_rational(entry->data, FILE_BYTE_ORDER, fir);
exif_set_rational(entry->data + 8, FILE_BYTE_ORDER, mid);
exif_set_rational(entry->data + 16 ,FILE_BYTE_ORDER, las);
entry = create_tag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_LONGITUDE_REF, 2 * exif_format_get_size(EXIF_FORMAT_ASCII));
entry->format = EXIF_FORMAT_ASCII;
entry->components = 2;
memcpy(entry->data, "E", 2);// E东经/W西经
entry = create_tag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_LONGITUDE, 3 * exif_format_get_size(EXIF_FORMAT_RATIONAL));
entry->format = EXIF_FORMAT_RATIONAL;
entry->components = 3;
fir.numerator = 121;
fir.denominator = 1;
mid.numerator = 0;
mid.denominator = 1;
las.numerator = 0;
las.denominator = 1;
exif_set_rational(entry->data, FILE_BYTE_ORDER, fir);
exif_set_rational(entry->data + 8, FILE_BYTE_ORDER, mid);
exif_set_rational(entry->data + 16, FILE_BYTE_ORDER, las);
// 绝对-海平面
entry = create_tag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_ALTITUDE_REF, 1 * exif_format_get_size(EXIF_FORMAT_BYTE));
entry->format = EXIF_FORMAT_BYTE;
entry->components = 1;
exif_set_short(entry->data, FILE_BYTE_ORDER, 0);// 0-海面上 1-海面下
// 高度
entry = create_tag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_ALTITUDE, 1 * exif_format_get_size(EXIF_FORMAT_RATIONAL));
entry->format = EXIF_FORMAT_RATIONAL;
entry->components = 1;
fir.numerator = 100;
fir.denominator = 11;
exif_set_rational(entry->data, FILE_BYTE_ORDER, fir);
//GPS速度单位K KM/H
entry = create_tag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_SPEED_REF, 2 * exif_format_get_size(EXIF_FORMAT_ASCII));
entry->format = EXIF_FORMAT_ASCII;
entry->components = 2;
memcpy(entry->data, "K", 2);
//GPS速度值
entry = create_tag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_SPEED, 1 * exif_format_get_size(EXIF_FORMAT_RATIONAL));
entry->format = EXIF_FORMAT_RATIONAL;
entry->components = 1;
fir.numerator = 50;
fir.denominator = 1;
exif_set_rational(entry->data, FILE_BYTE_ORDER, fir);
// 拍摄时间
// EXIF_TAG_SUB_SEC_TIME EXIF_TAG_SUB_SEC_TIME_ORIGINAL EXIF_TAG_SUB_SEC_TIME_DIGITIZED 毫秒时间不写入
entry = create_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_ORIGINAL, 20 * exif_format_get_size(EXIF_FORMAT_ASCII));
entry->format = EXIF_FORMAT_ASCII;
entry->components = 20;
time_t t = time(NULL);
struct tm stTime;
localtime_r(&t, &stTime);
char pDataTime[20] = { 0 };
snprintf(pDataTime, sizeof(pDataTime), "%04d-%02d-%02d %02d:%02d:%02d", stTime.tm_year + 1900, stTime.tm_mon + 1, stTime.tm_mday, stTime.tm_hour, stTime.tm_min, stTime.tm_sec);
memcpy(entry->data, pDataTime, sizeof(pDataTime));
// 数字化时间
entry = create_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_DIGITIZED, 20 * exif_format_get_size(EXIF_FORMAT_ASCII));
entry->format = EXIF_FORMAT_ASCII;
entry->components = 20;
memcpy(entry->data, pDataTime, sizeof(pDataTime));
//制造商
entry = create_tag(exif, EXIF_IFD_0, EXIF_TAG_MAKE, sizeof(g_Make) * exif_format_get_size(EXIF_FORMAT_ASCII));
entry->format = EXIF_FORMAT_ASCII;
entry->components = sizeof(g_Make);
memcpy(entry->data, g_Make, sizeof(g_Make));
// 型号
entry = create_tag(exif, EXIF_IFD_0, EXIF_TAG_MODEL, sizeof(g_Model) * exif_format_get_size(EXIF_FORMAT_ASCII));
entry->format = EXIF_FORMAT_ASCII;
entry->components = sizeof(g_Model);
memcpy(entry->data, g_Model, sizeof(g_Model));
// 修改时间
entry = create_tag(exif, EXIF_IFD_0, EXIF_TAG_DATE_TIME, 20 * exif_format_get_size(EXIF_FORMAT_ASCII));
entry->format = EXIF_FORMAT_ASCII;
entry->components = 20;
memcpy(entry->data, pDataTime, sizeof(pDataTime));
// 软件
entry = create_tag(exif, EXIF_IFD_0, EXIF_TAG_SOFTWARE, sizeof(g_Version) * exif_format_get_size(EXIF_FORMAT_ASCII));
entry->format = EXIF_FORMAT_ASCII;
entry->components = sizeof(g_Version);
memcpy(entry->data, g_Version, sizeof(g_Version));
exif_data_save_data(exif, &exif_data, &exif_data_len);
assert(exif_data != NULL);
pFile = fopen(pFilePath, "wb+");
if (!pFile) {
nRet = -1;
goto ERR_EXIT;
}
/* Write EXIF header */
if (fwrite(exif_header, exif_header_len, 1, pFile) != 1) {
nRet = -1;
goto ERR_EXIT;
}
/* Write EXIF block length in big-endian order */
if (fputc((exif_data_len+2) >> 8, pFile) < 0) {
nRet = -1;
goto ERR_EXIT;
}
if (fputc((exif_data_len+2) & 0xff, pFile) < 0) {
nRet = -1;
goto ERR_EXIT;
}
/* Write EXIF data block */
if (fwrite(exif_data, exif_data_len, 1, pFile) != 1) {
nRet = -1;
goto ERR_EXIT;
}
/* Write JPEG image data, skipping the non-EXIF header */
if (fwrite(pFrame + image_data_offset, nFrameLen - image_data_offset, 1, pFile) != 1) {
nRet = -1;
goto ERR_EXIT;
}
ERR_EXIT:
if (pFile) {
fclose(pFile);
}
if (exif_data) {
free(exif_data);
}
if (exif) {
exif_data_unref(exif);
}
return nRet;
}
修改后的libexif源码包点击 使用libexif为JPEG图片添加EXIF属性 下载。
转载请注明出处,如有错漏之处,敬请指正。
更多推荐
所有评论(0)