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

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最终实现的结果

 

参考文档


尾巴

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

  • 6
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 50
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 50
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值