读取JPG图片的Exif属性(三) - Exif属性读取GPS信息代码(C/C++实现)
Exif中GPS格式
读取JPG图片的Exif属性 - Exif信息简介
读取JPG图片的Exif属性 - C代码实现
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
GPS入口的定位
case TAG_GPS_INFO:
unsigned char * SubdirStart;
SubdirStart = OffsetBase + Get32u(ValuePtr);
CalGPS(SubdirStart, OffsetBase);
break;
/*--------------------------------------------------------------------------
Process one of the nested EXIF directories.
--------------------------------------------------------------------------*/
bool Cexif::ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength,
EXIFINFO * const m_exifinfo, unsigned char ** const LastExifRefdP )
{
int de;
int a;
int NumDirEntries;
unsigned ThumbnailOffset = 0;
unsigned ThumbnailSize = 0;
unsigned long gps = 0;
NumDirEntries = Get16u(DirStart);
if ((DirStart+2+NumDirEntries*12) > (OffsetBase+ExifLength)){
strcpy(m_szLastError,"Illegally sized directory");
return 0;
}
for (de=0;de<NumDirEntries;de++)
{
int Tag, Format, Components;
unsigned char * ValuePtr;
/* This actually can point to a variety of things; it must be
cast to other types when used. But we use it as a unsigned char-by-unsigned char
cursor, so we declare it as a pointer to a generic unsigned char here.
*/
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) {
/* (-1) catches illegal zero case as unsigned underflows to positive large */
strcpy(m_szLastError,"Illegal format code in EXIF dir");
return 0;
}
BytesCount = Components * BytesPerFormat[Format];
if (BytesCount > 4){
unsigned OffsetVal;
OffsetVal = Get32u(DirEntry+8);
/* If its bigger than 4 unsigned chars, the dir entry contains an offset.*/
if (OffsetVal+BytesCount > ExifLength){
/* Bogus pointer offset and / or unsigned charcount value */
strcpy(m_szLastError,"Illegal pointer offset value in EXIF.");
return 0;
}
ValuePtr = OffsetBase+OffsetVal;
}else{
/* 4 unsigned chars or less and value is in the dir entry itself */
ValuePtr = DirEntry+8;
}
if (*LastExifRefdP < ValuePtr+BytesCount){
/* Keep track of last unsigned char in the exif header that was
actually referenced. That way, we know where the
discardable thumbnail data begins.
*/
*LastExifRefdP = ValuePtr+BytesCount;
}
/* Extract useful components of tag */
switch(Tag){
case TAG_MAKE:
strncpy(m_exifinfo->CameraMake, (char*)ValuePtr, 31);
break;
case TAG_MODEL:
strncpy(m_exifinfo->CameraModel, (char*)ValuePtr, 39);
break;
case TAG_EXIF_VERSION:
strncpy(m_exifinfo->Version,(char*)ValuePtr, 4);
break;
case TAG_DATETIME_ORIGINAL:
strncpy(m_exifinfo->DateTime, (char*)ValuePtr, 19);
break;
case TAG_USERCOMMENT:
// Olympus has this padded with trailing spaces. Remove these first.
for (a=BytesCount;;){
a--;
if (((char*)ValuePtr)[a] == ' '){
((char*)ValuePtr)[a] = '\0';
}else{
break;
}
if (a == 0) break;
}
/* Copy the comment */
if (memcmp(ValuePtr, "ASCII",5) == 0){
for (a=5;a<10;a++){
char c;
c = ((char*)ValuePtr)[a];
if (c != '\0' && c != ' '){
strncpy(m_exifinfo->Comments, (char*)ValuePtr+a, 199);
break;
}
}
}else{
strncpy(m_exifinfo->Comments, (char*)ValuePtr, 199);
}
break;
case TAG_FNUMBER:
/* Simplest way of expressing aperture, so I trust it the most.
(overwrite previously computd value if there is one)
*/
m_exifinfo->ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_APERTURE:
case TAG_MAXAPERTURE:
/* More relevant info always comes earlier, so only
use this field if we don't have appropriate aperture
information yet.
*/
if (m_exifinfo->ApertureFNumber == 0){
m_exifinfo->ApertureFNumber = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0)*0.5);
}
break;
case TAG_BRIGHTNESS:
m_exifinfo->Brightness = (float)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_FOCALLENGTH:
/* Nice digital cameras actually save the focal length
as a function of how farthey are zoomed in.
*/
m_exifinfo->FocalLength = (float)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_SUBJECT_DISTANCE:
/* Inidcates the distacne the autofocus camera is focused to.
Tends to be less accurate as distance increases.
*/
m_exifinfo->Distance = (float)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_EXPOSURETIME:
/* Simplest way of expressing exposure time, so I
trust it most. (overwrite previously computd value
if there is one)
*/
m_exifinfo->ExposureTime =
(float)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_SHUTTERSPEED:
/* More complicated way of expressing exposure time,
so only use this value if we don't already have it
from somewhere else.
*/
if (m_exifinfo->ExposureTime == 0){
m_exifinfo->ExposureTime = (float)
(1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0)));
}
break;
case TAG_FLASH:
if ((int)ConvertAnyFormat(ValuePtr, Format) & 7){
m_exifinfo->FlashUsed = 1;
}else{
m_exifinfo->FlashUsed = 0;
}
break;
case TAG_ORIENTATION:
m_exifinfo->Orientation = (int)ConvertAnyFormat(ValuePtr, Format);
if (m_exifinfo->Orientation < 1 || m_exifinfo->Orientation > 8){
strcpy(m_szLastError,"Undefined rotation value");
m_exifinfo->Orientation = 0;
}
break;
case TAG_EXIF_IMAGELENGTH:
case TAG_EXIF_IMAGEWIDTH:
/* Use largest of height and width to deal with images
that have been rotated to portrait format.
*/
a = (int)ConvertAnyFormat(ValuePtr, Format);
if (ExifImageWidth < a) ExifImageWidth = a;
break;
case TAG_FOCALPLANEXRES:
m_exifinfo->FocalplaneXRes = (float)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_FOCALPLANEYRES:
m_exifinfo->FocalplaneYRes = (float)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_RESOLUTIONUNIT:
switch((int)ConvertAnyFormat(ValuePtr, Format)){
case 1: m_exifinfo->ResolutionUnit = 1.0f; break; /* 1 inch */
case 2: m_exifinfo->ResolutionUnit = 1.0f; break;
case 3: m_exifinfo->ResolutionUnit = 0.3937007874f; break; /* 1 centimeter*/
case 4: m_exifinfo->ResolutionUnit = 0.03937007874f; break; /* 1 millimeter*/
case 5: m_exifinfo->ResolutionUnit = 0.00003937007874f; /* 1 micrometer*/
}
break;
case TAG_FOCALPLANEUNITS:
switch((int)ConvertAnyFormat(ValuePtr, Format)){
case 1: m_exifinfo->FocalplaneUnits = 1.0f; break; /* 1 inch */
case 2: m_exifinfo->FocalplaneUnits = 1.0f; break;
case 3: m_exifinfo->FocalplaneUnits = 0.3937007874f; break; /* 1 centimeter*/
case 4: m_exifinfo->FocalplaneUnits = 0.03937007874f; break; /* 1 millimeter*/
case 5: m_exifinfo->FocalplaneUnits = 0.00003937007874f; /* 1 micrometer*/
}
break;
// Remaining cases contributed by: Volker C. Schoech <schoech(at)gmx(dot)de>
case TAG_EXPOSURE_BIAS:
m_exifinfo->ExposureBias = (float) ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_WHITEBALANCE:
m_exifinfo->Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_METERING_MODE:
m_exifinfo->MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_EXPOSURE_PROGRAM:
m_exifinfo->ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_ISO_EQUIVALENT:
m_exifinfo->ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
if ( m_exifinfo->ISOequivalent < 50 ) m_exifinfo->ISOequivalent *= 200;
break;
case TAG_COMPRESSION_LEVEL:
m_exifinfo->CompressionLevel = (int)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_XRESOLUTION:
m_exifinfo->Xresolution = (float)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_YRESOLUTION:
m_exifinfo->Yresolution = (float)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_THUMBNAIL_OFFSET:
ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_THUMBNAIL_LENGTH:
ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_GPS_INFO:
unsigned char * SubdirStart;
SubdirStart = OffsetBase + Get32u(ValuePtr);
CalGPS(SubdirStart, OffsetBase);
break;
}
if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET || Tag == TAG_GPS_INFO)
{
unsigned char * SubdirStart;
SubdirStart = OffsetBase + Get32u(ValuePtr);
if (SubdirStart < OffsetBase ||
SubdirStart > OffsetBase+ExifLength){
strcpy(m_szLastError,"Illegal subdirectory link");
return 0;
}
ProcessExifDir(SubdirStart, OffsetBase, ExifLength, m_exifinfo, LastExifRefdP);
continue;
}
}
{
/* In addition to linking to subdirectories via exif tags,
there's also a potential link to another directory at the end
of each directory. This has got to be the result of a
committee!
*/
unsigned char * SubdirStart;
unsigned Offset;
Offset = Get16u(DirStart+2+12*NumDirEntries);
if (Offset){
SubdirStart = OffsetBase + Offset;
if (SubdirStart < OffsetBase
|| SubdirStart > OffsetBase+ExifLength){
strcpy(m_szLastError,"Illegal subdirectory link");
return 0;
}
ProcessExifDir(SubdirStart, OffsetBase, ExifLength, m_exifinfo, LastExifRefdP);
}
}
if (ThumbnailSize && ThumbnailOffset){
if (ThumbnailSize + ThumbnailOffset <= ExifLength){
/* The thumbnail pointer appears to be valid. Store it. */
m_exifinfo->ThumbnailPointer = OffsetBase + ThumbnailOffset;
m_exifinfo->ThumbnailSize = ThumbnailSize;
}
}
return 1;
}
详细分析GPS属性
关于GPS属性的分析如下:
00 00 GPSVersionID
00 01 BYTE
00 00 00 04 count
02 02 00 00 value 2.2.0.0
int Cexif::CalGPS(unsigned char * GPSStart, unsigned char * OffsetBase)
{
int ret = 0;
int de;
int a;
int NumDirEntries;
unsigned char * ValuePtr;
NumDirEntries = Get16u(GPSStart);
memset(&m_gpsInfo, 0, sizeof(tag_GPSInfo));
for (de = 0; de<NumDirEntries; de++)
{
int Tag, Format, Components;
unsigned char * ValuePtr;
/* This actually can point to a variety of things; it must be
cast to other types when used. But we use it as a unsigned char-by-unsigned char
cursor, so we declare it as a pointer to a generic unsigned char here.
*/
int BytesCount;
unsigned char * DirEntry;
DirEntry = GPSStart + 2 + 12 * de;
Tag = Get16u(DirEntry);
Format = Get16u(DirEntry + 2);
Components = Get32u(DirEntry + 4);
BytesCount = Components * BytesPerFormat[Format];
if (BytesCount > 4)
{
unsigned OffsetVal;
OffsetVal = Get32u(DirEntry + 8);
ValuePtr = OffsetBase + OffsetVal;
}
else
{
/* 4 unsigned chars or less and value is in the dir entry itself */
ValuePtr = DirEntry + 8;
}
/* Extract useful components of tag */
switch (Tag)
{
case TAG_GPS_VERSION:
strncpy(m_gpsInfo.GPSVersion, (char*)ValuePtr, 10);
break;
case TAG_GPS_LATITUDEREF:
strncpy(m_gpsInfo.GPSLatRef, (char*)ValuePtr, 1);
break;
case TAG_GPS_LATITUDE:
float lat;
lat = (float)ConvertAnyFormat(ValuePtr, Format);
lat += ((float)ConvertAnyFormat(ValuePtr + 8, Format))/60;
lat += ((float)ConvertAnyFormat(ValuePtr + 16, Format))/3600;
m_gpsInfo.GPSLat = lat;
break;
case TAG_GPS_LONGITUDEREF:
strncpy(m_gpsInfo.GPSLogRef, (char*)ValuePtr, 1);
break;
case TAG_GPS_LONGITUDE:
float longitude;
longitude = (float)ConvertAnyFormat(ValuePtr, Format);
longitude += ((float)ConvertAnyFormat(ValuePtr + 8, Format)) / 60;
longitude += ((float)ConvertAnyFormat(ValuePtr + 16, Format)) / 3600;
m_gpsInfo.GPSLog = longitude;
break;
case TAG_GPS_ALTITUDEREF:
strncpy(m_gpsInfo.GPSAltRef, (char*)ValuePtr, 1);
break;
case TAG_GPS_ALTITUDE:
m_gpsInfo.GPSAlt = (float)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_BRIGHTNESS:
m_exifinfo->Brightness = (float)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_FOCALLENGTH:
/* Nice digital cameras actually save the focal length
as a function of how farthey are zoomed in.
*/
m_exifinfo->FocalLength = (float)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_SUBJECT_DISTANCE:
/* Inidcates the distacne the autofocus camera is focused to.
Tends to be less accurate as distance increases.
*/
m_exifinfo->Distance = (float)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_EXPOSURETIME:
/* Simplest way of expressing exposure time, so I
trust it most. (overwrite previously computd value
if there is one)
*/
m_exifinfo->ExposureTime =
(float)ConvertAnyFormat(ValuePtr, Format);
break;
}
}
return ret;
}
Demo最终实现的结果
参考文档
尾巴
读取JPG图片的Exif属性(三) - Exif属性读取GPS信息代码(C/C++实现)相关推荐
- python的image读取的图片是什么类型的-opencv python 读取图像/显示图像/保存图像...
以前也用过opencv, 不过都是按需使用, 掌握的知识很零散, 这次希望能够系统学习opencv-python 本文直接从Gui Features开始. 1 读取图片 使用cv2.imread()函 ...
- jpg图片的Exif及gps信息和示例分析
转自:http://www.verydemo.com/demo_c173_i10439.html jpg图片的Exif及gps信息和示例分析 分类: 操作系统 / Windows / 文章 jp ...
- python批量修改图片的exif信息,增加GPS信息。
有时候需要给图片增加地理信息,比如在用无人机影像做3维建模或者正射影像时,可以将无人机的gps写入图片,然后用ODM快速完成三维模型与正射影像生成. ODM的使用方法可以参看下面这篇博客. h ...
- python读取raw图片文件_python读取raw binary图片并提取统计信息的实例
python读取raw binary图片并提取统计信息的实例 发布时间:2020-09-08 23:22:52 来源:脚本之家 阅读:66 用python语言读取二进制图片文件,并提取非零数据统计信息 ...
- JAVA 拍照 exif GPS_读取图片EXIF块中GPS信息,转换为高德地图API坐标
最近两天做了一个需求,从APP端上传的照片信息里面读取出GPS位置信息,然后翻译成可读的地点信息. 总结一下,分为三步: (1)提取图片中的GPS信息,使用到了metadata-extractor; ...
- 轻松获取图片和视频文件的Exif信息-Java篇
前言 随着现在实景地图的如火如荼建设,无人机等航拍测绘手段的不断升级,我们在获取全景照片或者正射影像,全景视频等数据上更加快速.便捷.由于无人机本身不进行相关数据的处理,比如全景地图的生成.视频的信息 ...
- java根据exif旋转,关于图片文件旋转JPEG与EXIF信息
关于图片文件旋转JPEG与EXIF信息 2019/10/31 0:36:39 YuLimin 程序员俱乐部 我要评论(1) 摘要:关于图片文件旋转JPEG与EXIF信息比如某相机拍摄出来的相片, ...
- 关于图片文件旋转JPEG与EXIF信息
关于图片文件旋转JPEG与EXIF信息 比如某相机拍摄出来的相片,文件分辨率比如宽度7360像素,高度4912像素 十进制表示为 DEC : 7360 * 4912 十六进制则为 HEX : 1CC0 ...
- JPG图片EXIF信息提取工具exif
JPG图片EXIF信息提取工具exif 有相机生成的JPG图片(如照片)都包含有EXIF信息.这些信息包括拍照所使用的设备.地理位置.拍摄时间等敏感信息.Kali Linux内置了EXIF信息提取工具 ...
- opencv java 显示图片_【opencv三】利用opencv读取显示图片
在opencv中读取显示图片的头文件是highgui.hpp. 整体代码如下,如要测试自己的图片,需要将代码段中的图片地址更改为自己图片的绝对路径. #include "opencv2/hi ...
最新文章
- C++:随笔4--对象
- Linux基础常用命令
- gitlab新建项目_基础架构之GitLab
- Auto-Publishing and Monitoring APIs With Spring Boot--转
- 手把手教您如何识别翻新机子
- Hive的基本操作-创建表的格式
- mysql操作xml字段_SQL XML 字段操作
- linux svn可视化工具,CentOS6.5安装SVN 可视化管理工具iF.SVNAdmin
- 史上最贵域名诞生!360斥资1700万美元买360.com
- Intelij IDEA解决Dependency无法更新问题
- python入门经典代码-python经典入门学习锦集就这篇够了,强烈建议收藏!
- MS08067 第一期 “恶意代码分析”实战班 12.17号开班~
- 个人计算机之前,很久之前的个人pc机 文曲星,是什么档次,jrs还有印象么?
- GA/GP中的适应度函数
- sql删除元组_SQL笔记
- 【POJ】1003_Hangover宿醉
- python识别屏幕内容_python之屏幕抓取
- wireshark蓝牙数据包分析_Wireshark数据包分析
- 一个浪漫又悲情的爱情故事——笛卡尔心形线
- LeetCode881:救生艇 (C、C++实现)
热门文章
- 【营销学堂】从饥饿营销到口碑营销
- 如何将高程数据转成南方CASS的DAT格式
- python输出dat格式_输出dat文件
- android绘画时钟,Android画个时钟玩玩
- Matlab与Excel文件的数据交换
- 【耀杨闯荡华儿街】当曹阿门问起hascode()和equals()方法“上篇”
- word文档打不开怎么办,word安全模式怎么解除,word打不开的解决办法
- 野火 FireConfig 配置连接Wifi
- java反射--Field用法实践
- java 反射 field.set,java 反射之Field