jpg读取exif属性值

来源:互联网 发布:秒赞网哪一个源码好 编辑:程序博客网 时间:2024/06/04 04:01

项目在开发过程中,需要读取JPG影像中的exif的GPS,相机参数,影像大小等参数,根据exif属性的格式定义,解析所需要信息的字段,成功提取内容。
Exif 编辑
Exif是一种图像文件格式,它的数据存储与JPEG格式是完全相同的。实际上Exif格式就是在JPEG格式头部插入了数码照片的信息,包括拍摄时的光圈、快门、白平衡、ISO、焦距、日期时间等各种和拍摄条件以及相机品牌、型号、色彩编码、拍摄时录制的声音以及GPS全球定位系统数据、缩略图等。你可以利用任何可以查看JPEG文件的看图软件浏览Exif格式的照片,但并不是所有的图形程序都能处理Exif信息。图像右击属性一栏可以查看当前的exif属性值。
这里写图片描述
Exif 属性字段
读取exif属性,首先需要了解属性字段的标识符。我们根据标识符提取对应的exif属性值。下图中的标签号位属性标识符字段,其他属性的标识符根据标准文档查找,下文不一一列出
这里写图片描述

Exif 解析代码
1.首先从读取图像中字节流中查找到对应的exif信息的起始指针。

int W_ExifInfo::DecodeExif(const char * fPath){    m_pkFile = fopen(fPath, "rb");    int a = fgetc(m_pkFile);    if (a != 0xff || fgetc(m_pkFile) != M_SOI)    {        cout<<"底图打开失败"<<endl;        fclose(m_pkFile);        return 0;    }    for(;;) //    {        int itemlen;        int marker = 0;        int ll,lh, got;        unsigned char * Data;        for (int i = 0; i < 7; i++)        {            marker = fgetc(m_pkFile);            if (marker != 0xff) break;            if (i >= 6)            {               cout<< "底图打开失败,太多的填充字符"<<endl;                fclose(m_pkFile);                return 0;            }        }        if (marker == 0xff)        {           cout<< "底图打开失败,太多的填充字符"<<endl;            fclose(m_pkFile);            return 0;        }        lh = fgetc(m_pkFile);        ll = fgetc(m_pkFile);        itemlen = (lh << 8) | ll;        if (itemlen < 2)        {           cout<< "底图打开失败,无效的图片标记"<<endl;            fclose(m_pkFile);            return 0;        }        Data = (unsigned char *)malloc(itemlen);        if (Data == NULL)        {           cout<< "底图打开失败,内存分配失败"<<endl;            fclose(m_pkFile);            return 0;        }        Data[0] = (unsigned char)lh;        Data[1] = (unsigned char)ll;        got = fread(Data+2, 1, itemlen-2,m_pkFile);        if (got != itemlen-2)        {            fclose(m_pkFile);            return 0;        }        if (M_EXIF == marker)        {            if (memcmp(Data+2, "Exif", 4) == 0)            {                ProcessExif((unsigned char *)Data+2, itemlen);            }        }    }    fclose(m_pkFile);    return 1;}

2.判断是否符合exif标准,同时查找到字符流中关于属性信息起始地址。

int W_ExifInfo::ProcessExif(unsigned char * CharBuf, unsigned int length){    static const unsigned char ExifHeader[] = "Exif\0\0";    if (memcmp(CharBuf+0, ExifHeader,6))    {       cout<<"底图打开失败,错误的EXIF信息头"<<endl;        return 0;    }    if (memcmp(CharBuf+6,"II",2) == 0)    {        m_iMotorolaOrder = 0;    }    else    {        if (memcmp(CharBuf+6,"MM",2) == 0)        {            m_iMotorolaOrder = 1;        }        else        {           cout<<"底图打开失败,无效的EXIF对准标记"<<endl;            return 0;        }    }    if (Get16u(CharBuf+8) != 0x2a)    {       cout<<"底图打开失败,无效的EXIF头"<<endl;        return 0;    }    int FirstOffset = Get32u(CharBuf+10);    if (FirstOffset < 8 || FirstOffset > 16)    {       cout<<"底图打开失败,无效的偏移"<<endl;        return 0;    }    unsigned char * LastExifRefd = CharBuf;    if (!ProcessExifDir(CharBuf+14, CharBuf+6, length-6, &LastExifRefd))    {        return 0;    }    return 1;}

3.根据每个属性的定义格式如下:
Tag 标识符
Format: 格式,tag TYPE
count: 最多的字符个数为
offset: 偏移量,但是这里的偏移量要记得加上从(II 49 49)+1D
我们根据属性定义的格式,找到对应的数据字符流,根据格式进行数据解析,得到最后的所需要的值。

#define TAG_GPS2_OFFSET         0x8825  //GPS 偏移#define TAG_GPS_LATITUDE        0x0002  //纬度值#define TAG_GPS_LONGITUDE       0x0004  //经度值#define TAG_GPS_ALTITUDE        0x0006  //高度#define TAG_MAKE                0x010F  //相机制造商#define TAG_EXIF_IMAGEHEIGHT    0xA003  //图像高度#define TAG_EXIF_IMAGEWIDTH     0xA002  //图像宽度int W_ExifInfo::ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase,                           unsigned ExifLength, unsigned char ** const LastExifRefdP){    int NumDirEntries = Get16u(DirStart);    if ((DirStart + 2 + NumDirEntries*12) > (OffsetBase+ExifLength))    {       cout<<"底图打开失败,目录大小非法"<<endl;        return 0;    }    for (int de=0;de<NumDirEntries; de++)    {        int Tag, Format, Components;        unsigned char * ValuePtr;        int BytesCount;        unsigned char * DirEntry;        DirEntry = DirStart+2+12*de;        Tag = Get16u(DirEntry);        Format = Get16u(DirEntry+2);        Components = Get32u(DirEntry+4);        if ((Format-1) >= NUM_FORMATS)        {           cout<<"底图打开失败,EXIF DIR格式代码非法"<<endl;            return 0;        }        BytesCount = Components * BytesPerFormat[Format];        if (BytesCount > 4)        {            unsigned OffsetVal;            OffsetVal = Get32u(DirEntry+8);            if (OffsetVal+BytesCount > ExifLength)            {               cout<<"底图打开失败,EXIF指针偏移值非法"<<endl;                return 0;            }            ValuePtr = OffsetBase+OffsetVal;        }        else        {            ValuePtr = DirEntry+8;        }        if (*LastExifRefdP < ValuePtr+BytesCount)        {            *LastExifRefdP = ValuePtr+BytesCount;        }        switch(Tag)        {        case TAG_MAKE:        {            m_strCameraMake = ConvertToString((char*)ValuePtr, 31);            break;        }        case TAG_EXIF_IMAGEWIDTH:        {            m_lImageWidth = (long)ConvertAnyFormat(ValuePtr, Format);            break;        }        case TAG_EXIF_IMAGEHEIGHT:        {            m_lImageHeight = (long)ConvertAnyFormat(ValuePtr, Format);            break;        }       case TAG_GPS_LATITUDE:       {            m_fDegree = (float)ConvertAnyFormat(ValuePtr, Format);            m_fMinute = (float)ConvertAnyFormat(ValuePtr+8, Format);            m_fSecond = (float)ConvertAnyFormat(ValuePtr+16, Format);            if ((0 == m_fDegree)                    && (0 == m_fMinute)                    && (0 == m_fSecond))            {                break;            }            else            {            //qDebug()<<"m_fLatitude = ("<<m_fDegree<<", "<<m_fMinute<<", "<<m_fSecond<<")";            this->m_fLatitude = m_fDegree + m_fMinute/60.0 + m_fSecond/3600.0;            break;            }        }        case TAG_GPS_LONGITUDE:        {            m_fDegree = (float)ConvertAnyFormat(ValuePtr, Format);            m_fMinute = (float)ConvertAnyFormat(ValuePtr+8, Format);            m_fSecond = (float)ConvertAnyFormat(ValuePtr+16, Format);            //qDebug()<<"m_fLongitude = ("<<m_fDegree<<", "<<m_fMinute<<", "<<m_fSecond<<")";            this->m_fLongitude = m_fDegree + m_fMinute/60.0 + m_fSecond/3600.0;            break;        }        case TAG_GPS_ALTITUDE:        {            m_fAltitude = (float)ConvertAnyFormat(ValuePtr, Format);            break;        }        default:            break;        }        if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET  || Tag == TAG_GPS2_OFFSET){            unsigned char * SubdirStart;            SubdirStart = OffsetBase + Get32u(ValuePtr);            if (SubdirStart < OffsetBase ||                SubdirStart > OffsetBase+ExifLength){                cout<<"底图打开失败,非法目录链接"<<endl;                return 0;            }            ProcessExifDir(SubdirStart, OffsetBase, ExifLength, LastExifRefdP);            continue;        }    }    return 1;}

Exif 运行结果
下面结果仅截取影像列表中的GPS信息。
./Image/DJI_0001.JPG 120.228119 31.585005 138.457001
./Image/DJI_0002.JPG 120.228119 31.585005 138.356995
./Image/DJI_0003.JPG 120.228119 31.585005 138.257004
./Image/DJI_0004.JPG 120.228119 31.585005 138.457001

参考文档
http://blog.csdn.net/fioletfly/article/details/53605959
https://baike.baidu.com/item/Exif/422825?fr=aladdin