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
- jpg读取exif属性值
- 读取JPG图片的Exif属性(二) - C代码实现
- 读取JPG图片的Exif属性(一) - Exif信息简介
- 读取JPG图片的Exif属性(三) - Exif属性读取GPS信息代码(C/C++实现)
- 图像处理2_读取JPG图片的Exif属性(一)
- 读取jpg文件的exif信息
- Jpeg图片属性读取EXIF
- Jpg, Jpeg, Exif
- Exif 属性
- JPG图片EXIF信息提取工具exif
- 数码照片的JPG文件高级信息(用C#读取图片的EXIF信息)
- JPG图片叠加exif信息
- 读取数码照片Exif信息
- JMagick读取照片EXIF
- Malware Hidden Inside JPG EXIF Headers
- C#读取JPEG Exif 信息
- Java读取图片EXIF信息
- C#读取图片Exif信息
- LeetCode.62
- PAT 1012 排序
- java中System.exit()方法
- [LeetCode] 3. Longest Substring Without Repeating Characters
- Android Studio AIDL实现
- jpg读取exif属性值
- more effective C++条款六解析
- git log的简化及美化
- web启动spring过程
- vimの可视模式
- 【CS 3287】货车运输-2013年NOIP全国联赛提高组(最大生成树)
- 操作系统找不到已输入的环境选项解决方案
- hdu 6156 回文数 数位dp
- 【数字图像处理】灰度变换函数(对数变换、反对数变换、幂次变换)