读取JPG图片的Exif属性(三) - Exif属性读取GPS信息代码(C/C++实现)

原创 2017年01月06日 09:12:17

Exif中GPS格式

本文是最后一篇关于Exif文章,终于要挑战最大的boss了,这个GPS信息的读取,我找了国内外很多资料都没有找 真正的实例。所以自己前两篇的基础上推断而来的。

读取JPG图片的Exif属性 - Exif信息简介

读取JPG图片的Exif属性 - C代码实现

 其实这个只要能够理解前面两篇的内容也就很容易获得GPS信息,但是还是要理解GPS的格式和各种tag参数。可以重新学习一下其格式如下:
   GPS Info IFD Pointer 0x02AE  从上面数据2A0行的后面看数据
   00 05       count of TAGs
   
   00 00       GPSVersionID
   00 01       BYTE
   00 00 00 04 count
   02 02 00 00 value 2.2.0.0
   
   00 01       GPSLatitudeRef
   00 02       ASCII
   00 00 00 02 count
   4E 00 00 00 N North  
   
   00 02       GPSLatitude
   00 05       RATIONAL               前4字节为分子,后4字节为分母,十六进制先转为十进制
   00 00 00 03 count
   00 00 05 46 offset+0c=0x552 16 00 00 00 01 00 00 00 22 00 00 00 01 00 00 00 51 00 00 00 04 00 00 00
                                                       22/1=22                             34/1=34                              81/4= 20.25
                                                      通过windows的右键属性看详细信息:22;34;20.249999999999915
   00 03       GPSLongitudeRef
   00 02
   00 00 00 02 count
   45 00 00 00 East
   
   00 04       GPSLongitude
   00 05       RATIONAL
   00 00 00 03 count
   00 00 05 5E offset+0c=0x56A 71 00 00 00 01 00 00 00 37 00 00 00 01 00 00 00 6C 00 00 00 04 00
                                                       113/1=113                          55/1=55                             108/4=27
                                                    通过windows的右键属性看详细信息:113;55;27.000000000000171
主要的函数还是要进入到如下得函数中:
 DecodeExif 函数主要是获取到Eixf属性在图片数据中入口地址:
process_EXIF 主要是对Exif属性做一个前期的处理,为了进一步详细的分析做处理
ProcessExifDir主要是对Exif属性详细的分析和处理。

GPS入口的定位

GPS入口的定位就同上篇中关于Exif属性的定位入口是一个道理,因为GPS本身就是一个大section,在这个section中又有很多tag,而且tag的格式也是Exif属性中tag的格式是一样的。所以看到这里就比较简单了,其实就是利用上篇中读取Exif属性的代码来事项GPS。具体是现在Exif中定位到GPS入口下代码:
可以看到在最后的Tag中是可以找到GPS信息的入口,这是就可以定位GPS属性了,所以找到入口地址,进入到GPS所在位置进行分析。
  1. case TAG_GPS_INFO:
  2. unsigned char * SubdirStart;
  3. SubdirStart = OffsetBase + Get32u(ValuePtr);
  4. CalGPS(SubdirStart, OffsetBase);
  5. break;
  1. /*--------------------------------------------------------------------------
  2. Process one of the nested EXIF directories.
  3. --------------------------------------------------------------------------*/
  4. bool Cexif::ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength,
  5. EXIFINFO * const m_exifinfo, unsigned char ** const LastExifRefdP )
  6. {
  7. int de;
  8. int a;
  9. int NumDirEntries;
  10. unsigned ThumbnailOffset = 0;
  11. unsigned ThumbnailSize = 0;
  12. unsigned long gps = 0;
  13. NumDirEntries = Get16u(DirStart);
  14. if ((DirStart+2+NumDirEntries*12) > (OffsetBase+ExifLength)){
  15. strcpy(m_szLastError,"Illegally sized directory");
  16. return 0;
  17. }
  18. for (de=0;de<NumDirEntries;de++)
  19. {
  20. int Tag, Format, Components;
  21. unsigned char * ValuePtr;
  22. /* This actually can point to a variety of things; it must be
  23. cast to other types when used. But we use it as a unsigned char-by-unsigned char
  24. cursor, so we declare it as a pointer to a generic unsigned char here.
  25. */
  26. int BytesCount;
  27. unsigned char * DirEntry;
  28. DirEntry = DirStart+2+12*de;
  29. Tag = Get16u(DirEntry);
  30. Format = Get16u(DirEntry+2);
  31. Components = Get32u(DirEntry+4);
  32. if ((Format-1) >= NUM_FORMATS) {
  33. /* (-1) catches illegal zero case as unsigned underflows to positive large */
  34. strcpy(m_szLastError,"Illegal format code in EXIF dir");
  35. return 0;
  36. }
  37. BytesCount = Components * BytesPerFormat[Format];
  38. if (BytesCount > 4){
  39. unsigned OffsetVal;
  40. OffsetVal = Get32u(DirEntry+8);
  41. /* If its bigger than 4 unsigned chars, the dir entry contains an offset.*/
  42. if (OffsetVal+BytesCount > ExifLength){
  43. /* Bogus pointer offset and / or unsigned charcount value */
  44. strcpy(m_szLastError,"Illegal pointer offset value in EXIF.");
  45. return 0;
  46. }
  47. ValuePtr = OffsetBase+OffsetVal;
  48. }else{
  49. /* 4 unsigned chars or less and value is in the dir entry itself */
  50. ValuePtr = DirEntry+8;
  51. }
  52. if (*LastExifRefdP < ValuePtr+BytesCount){
  53. /* Keep track of last unsigned char in the exif header that was
  54. actually referenced. That way, we know where the
  55. discardable thumbnail data begins.
  56. */
  57. *LastExifRefdP = ValuePtr+BytesCount;
  58. }
  59. /* Extract useful components of tag */
  60. switch(Tag){
  61. case TAG_MAKE:
  62. strncpy(m_exifinfo->CameraMake, (char*)ValuePtr, 31);
  63. break;
  64. case TAG_MODEL:
  65. strncpy(m_exifinfo->CameraModel, (char*)ValuePtr, 39);
  66. break;
  67. case TAG_EXIF_VERSION:
  68. strncpy(m_exifinfo->Version,(char*)ValuePtr, 4);
  69. break;
  70. case TAG_DATETIME_ORIGINAL:
  71. strncpy(m_exifinfo->DateTime, (char*)ValuePtr, 19);
  72. break;
  73. case TAG_USERCOMMENT:
  74. // Olympus has this padded with trailing spaces. Remove these first.
  75. for (a=BytesCount;;){
  76. a--;
  77. if (((char*)ValuePtr)[a] == ' '){
  78. ((char*)ValuePtr)[a] = '\0';
  79. }else{
  80. break;
  81. }
  82. if (a == 0) break;
  83. }
  84. /* Copy the comment */
  85. if (memcmp(ValuePtr, "ASCII",5) == 0){
  86. for (a=5;a<10;a++){
  87. char c;
  88. c = ((char*)ValuePtr)[a];
  89. if (c != '\0' && c != ' '){
  90. strncpy(m_exifinfo->Comments, (char*)ValuePtr+a, 199);
  91. break;
  92. }
  93. }
  94. }else{
  95. strncpy(m_exifinfo->Comments, (char*)ValuePtr, 199);
  96. }
  97. break;
  98. case TAG_FNUMBER:
  99. /* Simplest way of expressing aperture, so I trust it the most.
  100. (overwrite previously computd value if there is one)
  101. */
  102. m_exifinfo->ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format);
  103. break;
  104. case TAG_APERTURE:
  105. case TAG_MAXAPERTURE:
  106. /* More relevant info always comes earlier, so only
  107. use this field if we don't have appropriate aperture
  108. information yet.
  109. */
  110. if (m_exifinfo->ApertureFNumber == 0){
  111. m_exifinfo->ApertureFNumber = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0)*0.5);
  112. }
  113. break;
  114. case TAG_BRIGHTNESS:
  115. m_exifinfo->Brightness = (float)ConvertAnyFormat(ValuePtr, Format);
  116. break;
  117. case TAG_FOCALLENGTH:
  118. /* Nice digital cameras actually save the focal length
  119. as a function of how farthey are zoomed in.
  120. */
  121. m_exifinfo->FocalLength = (float)ConvertAnyFormat(ValuePtr, Format);
  122. break;
  123. case TAG_SUBJECT_DISTANCE:
  124. /* Inidcates the distacne the autofocus camera is focused to.
  125. Tends to be less accurate as distance increases.
  126. */
  127. m_exifinfo->Distance = (float)ConvertAnyFormat(ValuePtr, Format);
  128. break;
  129. case TAG_EXPOSURETIME:
  130. /* Simplest way of expressing exposure time, so I
  131. trust it most. (overwrite previously computd value
  132. if there is one)
  133. */
  134. m_exifinfo->ExposureTime =
  135. (float)ConvertAnyFormat(ValuePtr, Format);
  136. break;
  137. case TAG_SHUTTERSPEED:
  138. /* More complicated way of expressing exposure time,
  139. so only use this value if we don't already have it
  140. from somewhere else.
  141. */
  142. if (m_exifinfo->ExposureTime == 0){
  143. m_exifinfo->ExposureTime = (float)
  144. (1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0)));
  145. }
  146. break;
  147. case TAG_FLASH:
  148. if ((int)ConvertAnyFormat(ValuePtr, Format) & 7){
  149. m_exifinfo->FlashUsed = 1;
  150. }else{
  151. m_exifinfo->FlashUsed = 0;
  152. }
  153. break;
  154. case TAG_ORIENTATION:
  155. m_exifinfo->Orientation = (int)ConvertAnyFormat(ValuePtr, Format);
  156. if (m_exifinfo->Orientation < 1 || m_exifinfo->Orientation > 8){
  157. strcpy(m_szLastError,"Undefined rotation value");
  158. m_exifinfo->Orientation = 0;
  159. }
  160. break;
  161. case TAG_EXIF_IMAGELENGTH:
  162. case TAG_EXIF_IMAGEWIDTH:
  163. /* Use largest of height and width to deal with images
  164. that have been rotated to portrait format.
  165. */
  166. a = (int)ConvertAnyFormat(ValuePtr, Format);
  167. if (ExifImageWidth < a) ExifImageWidth = a;
  168. break;
  169. case TAG_FOCALPLANEXRES:
  170. m_exifinfo->FocalplaneXRes = (float)ConvertAnyFormat(ValuePtr, Format);
  171. break;
  172. case TAG_FOCALPLANEYRES:
  173. m_exifinfo->FocalplaneYRes = (float)ConvertAnyFormat(ValuePtr, Format);
  174. break;
  175. case TAG_RESOLUTIONUNIT:
  176. switch((int)ConvertAnyFormat(ValuePtr, Format)){
  177. case 1: m_exifinfo->ResolutionUnit = 1.0f; break; /* 1 inch */
  178. case 2: m_exifinfo->ResolutionUnit = 1.0f; break;
  179. case 3: m_exifinfo->ResolutionUnit = 0.3937007874f; break; /* 1 centimeter*/
  180. case 4: m_exifinfo->ResolutionUnit = 0.03937007874f; break; /* 1 millimeter*/
  181. case 5: m_exifinfo->ResolutionUnit = 0.00003937007874f; /* 1 micrometer*/
  182. }
  183. break;
  184. case TAG_FOCALPLANEUNITS:
  185. switch((int)ConvertAnyFormat(ValuePtr, Format)){
  186. case 1: m_exifinfo->FocalplaneUnits = 1.0f; break; /* 1 inch */
  187. case 2: m_exifinfo->FocalplaneUnits = 1.0f; break;
  188. case 3: m_exifinfo->FocalplaneUnits = 0.3937007874f; break; /* 1 centimeter*/
  189. case 4: m_exifinfo->FocalplaneUnits = 0.03937007874f; break; /* 1 millimeter*/
  190. case 5: m_exifinfo->FocalplaneUnits = 0.00003937007874f; /* 1 micrometer*/
  191. }
  192. break;
  193. // Remaining cases contributed by: Volker C. Schoech <schoech(at)gmx(dot)de>
  194. case TAG_EXPOSURE_BIAS:
  195. m_exifinfo->ExposureBias = (float) ConvertAnyFormat(ValuePtr, Format);
  196. break;
  197. case TAG_WHITEBALANCE:
  198. m_exifinfo->Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format);
  199. break;
  200. case TAG_METERING_MODE:
  201. m_exifinfo->MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format);
  202. break;
  203. case TAG_EXPOSURE_PROGRAM:
  204. m_exifinfo->ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format);
  205. break;
  206. case TAG_ISO_EQUIVALENT:
  207. m_exifinfo->ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
  208. if ( m_exifinfo->ISOequivalent < 50 ) m_exifinfo->ISOequivalent *= 200;
  209. break;
  210. case TAG_COMPRESSION_LEVEL:
  211. m_exifinfo->CompressionLevel = (int)ConvertAnyFormat(ValuePtr, Format);
  212. break;
  213. case TAG_XRESOLUTION:
  214. m_exifinfo->Xresolution = (float)ConvertAnyFormat(ValuePtr, Format);
  215. break;
  216. case TAG_YRESOLUTION:
  217. m_exifinfo->Yresolution = (float)ConvertAnyFormat(ValuePtr, Format);
  218. break;
  219. case TAG_THUMBNAIL_OFFSET:
  220. ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format);
  221. break;
  222. case TAG_THUMBNAIL_LENGTH:
  223. ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format);
  224. break;
  225. case TAG_GPS_INFO:
  226. unsigned char * SubdirStart;
  227. SubdirStart = OffsetBase + Get32u(ValuePtr);
  228. CalGPS(SubdirStart, OffsetBase);
  229. break;
  230. }
  231. if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET || Tag == TAG_GPS_INFO)
  232. {
  233. unsigned char * SubdirStart;
  234. SubdirStart = OffsetBase + Get32u(ValuePtr);
  235. if (SubdirStart < OffsetBase ||
  236. SubdirStart > OffsetBase+ExifLength){
  237. strcpy(m_szLastError,"Illegal subdirectory link");
  238. return 0;
  239. }
  240. ProcessExifDir(SubdirStart, OffsetBase, ExifLength, m_exifinfo, LastExifRefdP);
  241. continue;
  242. }
  243. }
  244. {
  245. /* In addition to linking to subdirectories via exif tags,
  246. there's also a potential link to another directory at the end
  247. of each directory. This has got to be the result of a
  248. committee!
  249. */
  250. unsigned char * SubdirStart;
  251. unsigned Offset;
  252. Offset = Get16u(DirStart+2+12*NumDirEntries);
  253. if (Offset){
  254. SubdirStart = OffsetBase + Offset;
  255. if (SubdirStart < OffsetBase
  256. || SubdirStart > OffsetBase+ExifLength){
  257. strcpy(m_szLastError,"Illegal subdirectory link");
  258. return 0;
  259. }
  260. ProcessExifDir(SubdirStart, OffsetBase, ExifLength, m_exifinfo, LastExifRefdP);
  261. }
  262. }
  263. if (ThumbnailSize && ThumbnailOffset){
  264. if (ThumbnailSize + ThumbnailOffset <= ExifLength){
  265. /* The thumbnail pointer appears to be valid. Store it. */
  266. m_exifinfo->ThumbnailPointer = OffsetBase + ThumbnailOffset;
  267. m_exifinfo->ThumbnailSize = ThumbnailSize;
  268. }
  269. }
  270. return 1;
  271. }

详细分析GPS属性


关于GPS属性的分析如下:

 00 00       GPSVersionID
  00 01       BYTE
  00 00 00 04 count
  02 02 00 00 value 2.2.0.0
通过代码实现如下:
  1. int Cexif::CalGPS(unsigned char * GPSStart, unsigned char * OffsetBase)
  2. {
  3. int ret = 0;
  4. int de;
  5. int a;
  6. int NumDirEntries;
  7. unsigned char * ValuePtr;
  8. NumDirEntries = Get16u(GPSStart);
  9. memset(&m_gpsInfo, 0, sizeof(tag_GPSInfo));
  10. for (de = 0; de<NumDirEntries; de++)
  11. {
  12. int Tag, Format, Components;
  13. unsigned char * ValuePtr;
  14. /* This actually can point to a variety of things; it must be
  15. cast to other types when used. But we use it as a unsigned char-by-unsigned char
  16. cursor, so we declare it as a pointer to a generic unsigned char here.
  17. */
  18. int BytesCount;
  19. unsigned char * DirEntry;
  20. DirEntry = GPSStart + 2 + 12 * de;
  21. Tag = Get16u(DirEntry);
  22. Format = Get16u(DirEntry + 2);
  23. Components = Get32u(DirEntry + 4);
  24. BytesCount = Components * BytesPerFormat[Format];
  25. if (BytesCount > 4)
  26. {
  27. unsigned OffsetVal;
  28. OffsetVal = Get32u(DirEntry + 8);
  29. ValuePtr = OffsetBase + OffsetVal;
  30. }
  31. else
  32. {
  33. /* 4 unsigned chars or less and value is in the dir entry itself */
  34. ValuePtr = DirEntry + 8;
  35. }
  36. /* Extract useful components of tag */
  37. switch (Tag)
  38. {
  39. case TAG_GPS_VERSION:
  40. strncpy(m_gpsInfo.GPSVersion, (char*)ValuePtr, 10);
  41. break;
  42. case TAG_GPS_LATITUDEREF:
  43. strncpy(m_gpsInfo.GPSLatRef, (char*)ValuePtr, 1);
  44. break;
  45. case TAG_GPS_LATITUDE:
  46. float lat;
  47. lat = (float)ConvertAnyFormat(ValuePtr, Format);
  48. lat += ((float)ConvertAnyFormat(ValuePtr + 8, Format))/60;
  49. lat += ((float)ConvertAnyFormat(ValuePtr + 16, Format))/3600;
  50. m_gpsInfo.GPSLat = lat;
  51. break;
  52. case TAG_GPS_LONGITUDEREF:
  53. strncpy(m_gpsInfo.GPSLogRef, (char*)ValuePtr, 1);
  54. break;
  55. case TAG_GPS_LONGITUDE:
  56. float longitude;
  57. longitude = (float)ConvertAnyFormat(ValuePtr, Format);
  58. longitude += ((float)ConvertAnyFormat(ValuePtr + 8, Format)) / 60;
  59. longitude += ((float)ConvertAnyFormat(ValuePtr + 16, Format)) / 3600;
  60. m_gpsInfo.GPSLog = longitude;
  61. break;
  62. case TAG_GPS_ALTITUDEREF:
  63. strncpy(m_gpsInfo.GPSAltRef, (char*)ValuePtr, 1);
  64. break;
  65. case TAG_GPS_ALTITUDE:
  66. m_gpsInfo.GPSAlt = (float)ConvertAnyFormat(ValuePtr, Format);
  67. break;
  68. case TAG_BRIGHTNESS:
  69. m_exifinfo->Brightness = (float)ConvertAnyFormat(ValuePtr, Format);
  70. break;
  71. case TAG_FOCALLENGTH:
  72. /* Nice digital cameras actually save the focal length
  73. as a function of how farthey are zoomed in.
  74. */
  75. m_exifinfo->FocalLength = (float)ConvertAnyFormat(ValuePtr, Format);
  76. break;
  77. case TAG_SUBJECT_DISTANCE:
  78. /* Inidcates the distacne the autofocus camera is focused to.
  79. Tends to be less accurate as distance increases.
  80. */
  81. m_exifinfo->Distance = (float)ConvertAnyFormat(ValuePtr, Format);
  82. break;
  83. case TAG_EXPOSURETIME:
  84. /* Simplest way of expressing exposure time, so I
  85. trust it most. (overwrite previously computd value
  86. if there is one)
  87. */
  88. m_exifinfo->ExposureTime =
  89. (float)ConvertAnyFormat(ValuePtr, Format);
  90. break;
  91. }
  92. }
  93. return ret;
  94. }

Demo最终实现的结果

 

参考文档


尾巴

关于源码的话,本来是直接上传的,但是想想还是留给真正有需要的人吧。想要源码的可以私信交流啊!

版权声明:本文为博主原创文章,欢迎大家转载,请大家在转载时标明出处,谢谢!在成长的道路上,与你同行!

android:如何从照片中获取拍摄地址信息

在开发中遇到一个需求,需要解析拿到照片拍摄时的地址信息,在网上有很多网站提供照片上传后解析出照片的具体信息,很详细。android也很给力,提供ExifInterface ,可以获取到拍摄照片时的很多...
  • qq_17326933
  • qq_17326933
  • 2015年08月29日 20:50
  • 4983

从EXIF JPEG图片中提取GPS位置信息

图片中附带GPS信息
  • whucyl
  • whucyl
  • 2014年06月21日 01:54
  • 15083

jpg图片的Exif及gps信息和示例分析

转自:http://www.verydemo.com/demo_c173_i10439.html jpg图片的Exif及gps信息和示例分析 分类: 操作系...
  • yapingmcu
  • yapingmcu
  • 2016年02月05日 17:24
  • 7846

利用图片中的exif元数据批量查找图片中所包含的GPS信息

在图片的exif(交换图像文件格式)中标准定义了如何存储图像和音频文件的标准,而在这些标签中往往存在了一些容易被人们忽视却又重要的东西。 有一款工具名为exiftool,可以快速的解析所有标签,并将...
  • AcSuccess
  • AcSuccess
  • 2017年06月14日 20:37
  • 673

JPEG图像获取GPS数据

背景周一来到实验室本来要做项目,这个月快到月底了,想赶快写,要不然导师就要催。导师突然交给任务,让完成一个基于c++的JPEG图像获取GPS数据的小程序,他说很小,很简单,让我赶快写。让我做的事情如下...
  • z_x_b5
  • z_x_b5
  • 2015年10月26日 21:09
  • 1160

读取/修改照片中EXIF记录的GPS信息

  • 2015年03月12日 14:24
  • 289KB
  • 下载

Android中通过Exifinterface读取图片地理位置信息

效果图: 一、了解Exif EXIF(Exchangeable Image File)是“可交换图像文件”的缩写,是一种图像文件格式,它的数据存储与JPEG格式是完全相同的,当中包含了...
  • diyangxia
  • diyangxia
  • 2016年03月30日 16:23
  • 11322

读取JPG图片的Exif属性(一) - Exif信息简介

Exif 是什么? Exif就是用来记录拍摄图像时的各种信息:图像信息(厂商,分辨率等),相机拍摄记录(ISO,白平衡,饱和度,锐度等),缩略图(缩略图宽度,高度等),gps(拍摄时的经度,纬度,高...
  • fioletfly
  • fioletfly
  • 2016年12月13日 08:47
  • 6424

照片EXIF信息库EXIV2(c++)

前一段时间要做基于flicker提供的带有exif信息的照片库给无地理信息的photos进行定位。 大部分exif的库都不是很好用,exiv2 (http://www.exiv2.org) 挺不错。...
  • whuyao
  • whuyao
  • 2016年04月23日 16:11
  • 1552

exif 的 C++ 读取类,可以返回数码照片中的各种参数信息cexif_src.zip

  • 2009年08月18日 16:27
  • 8KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:读取JPG图片的Exif属性(三) - Exif属性读取GPS信息代码(C/C++实现)
举报原因:
原因补充:

(最多只允许输入30个字)