太久没来这里了,这两天又在之前批量调整照片日期的那段代码的基础上整了两个程序,拿来分享一下。
上周我买了个牛排,对就是the new iPad。哈哈,从此我可以在这上面得瑟我的照片啦~~ 啦呀啦,想一想这是一件多么美妙的事情啊!
题外话,iTunes很无耐,iTools很好用。当我满心欢喜地导入几千张以往的照片后,我崩溃了。
iOS的“照片”软件可以说做的很不错,除了不能建立子目录(可能是iOS限制的)以往,其他功能都还不错,尤其是“地图”功能,在Google Map中看到那一堆堆的“图钉”,那可是我曾走过的足迹啊!
【发现问题】
啊!不对,我没去过俄罗斯啊,也没到过钓鱼岛海域!那明明是昆明的石林啊,怎么回事?照片怎么出现在哪里?
我的照片中的GPS数据,都是我用软件(顺便广告一个GPicSync,类似的还有PhotoMapper(好大啊)和cPicture(2个文件,还是绿色的))把手机记录的GPS轨迹文件(.gpx文件)导入到JPG里的。(事先要给相机对表呦)还不明白?那你自己百度、Google吧。
是不是手机的GPS又因为没信号而漂移了? 不对啊,飘的也太多了,就没有一个准的!
是不是数据错了?在AcdSee里看看,没问题啊!
【分析问题】
我研究了一下Exif里的经纬度坐标,是个24长的byte数组,8个byte“度”,8个byte “分”,8个byte “秒”
8个byte当中,前4个代表分子(高位在后),后4个代表分母(高位在后)
如图,这是 纬度 29 33’ 43.3872”
我试过修改PGX文件,弄了一个整数的经纬度,然后导入JPG,在弄到pad上,它的位置就正常了。
我以为是我的写入软件不好,但是又找了2个同样功能的软件(就是上面说的那一大一小),问题依旧。
是我这种DIY式的带GPS信息的JPG的问题吗? 我的Android手机打开GPS,拍一张,导入iPad,哇咔咔,俄罗斯去啦!
于是,我又找了2张iPhone拍的照片,GPS数据和iPad拍照的有同样的规律:
1.没有“秒”信息(固定分子0,分母1),“秒”是靠“分”位的小数来表示的
2.“分”位的分母一律是100。两位小数精度
而通常的照片,那8个Byte来看,4个分子,4个分母,所以无限不循环是家常便饭了。
让我想不通的是,同样都是Exif 2.21格式,Apple怎么只认满足上面2点的特例的呢!?
唯一能让自己听着过的去的解释,就是为了降低精度,避免一些法律问题(怕你去炸大楼)
而Apple用的方法不像Google加入人为偏移量,而是最简单也最彻底的办法:缩小数据精度。这样一来,最小精度只有0.01分,就是0.6秒。在赤道上就是18米!
所以,坐标上记录的位置和真实位置,差个8、9米就很常见啦。我一路走,一路拍的照片,也变成三五成堆的啦。
【解决问题】
那么,打开一个已有GPS信息的JPG,从Exif中读取出经纬度,并按照苹果的格式重新写入,再做保存。这不就解决问题了吗,我的那上千张带GPS信息的照片,终于在iPad上出现在Google Map的正确位置了。(由于强制采用GCJ-02等加密算法而导致的几米、几十米的偏差,可在App Store中找“地图相册”来解决,这个App可以做修正)
在网上搜索来的“C#读取Exif源码”基础上,追加如下代码:
首先是“坐标”类
public struct Coordinates
{
private double degrees;
public double Degrees
{
get { return degrees; }
}
private double minutes;
public double Minutes
{
get { return minutes; }
}
private double seconds;
public double Seconds
{
get { return seconds; }
}
public Coordinates(double degrees) : this(degrees, 0, 0) { }
public Coordinates(double degrees, double minutes)
: this(degrees, minutes, 0)
{
}
public Coordinates(double degrees, double minutes, double seconds)
{
this.degrees = Math.Floor(degrees);
minutes += (degrees - this.degrees) * 60;
this.minutes = Math.Floor(minutes);
this.seconds = seconds + (minutes - this.minutes) * 60;
}
public new string ToString()
{
string str = "";
try
{
str = this.degrees.ToString() + "," + this.minutes.ToString() + "' " + Math.Round(this.seconds, 2).ToString() + "\" ";
}
catch { }
return str;
}
public double Value
{
get
{
return this.degrees + this.minutes / 60 + this.seconds / 60 / 60;
}
}
}
然后,给ExifManager类增加GPS相关的属性
//北纬 or 南纬?
public string GpsLatitudeRef
{
get
{
return this.GetPropertyString((int)TagNames.GpsLatitudeRef);
}
set
{
this.SetPropertyString((int)TagNames.GpsLatitudeRef, value);
}
}
//纬度
public Coordinates GpsLatitude
{
get
{
double degrees = this.GetPropertyRational((int)TagNames.GpsLatitude).ToDouble();
double minutes = this.GetPropertyRational((int)TagNames.GpsLatitude, 8).ToDouble();
double seconds = this.GetPropertyRational((int)TagNames.GpsLatitude, 16).ToDouble();
minutes += 60 * (degrees - Math.Floor(degrees));
degrees = Math.Floor(degrees);
seconds += 60 * (minutes - Math.Floor(minutes));
minutes = Math.Floor(minutes);
return new Coordinates(degrees, minutes, seconds);
}
set
{
try
{
byte[] bytes = new byte[24];
for (int i = 0; i < 24; i++)
{
bytes[i] = 0;
}
bytes[0] = (byte)value.Degrees;
bytes[4] = 1;
int min = (int)(Math.Round(value.Minutes + value.Seconds / 60,2) * 100);
if (min > 256)
{
bytes[9] = (byte)(int)Math.Floor(min / 256D);
bytes[8] = (byte)(min - bytes[9] * 256);
}
else
{
bytes[8] = (byte)min;
}
bytes[12] = 100;
bytes[20] = 1;
this.SetProperty((int)TagNames.GpsLatitude, bytes, ExifDataTypes.UnsignedRational);
}
catch(Exception e)
{
string a = e.ToString();
}
}
}
//经度就略了。。。
//海拔
public double GpsAltitude
{
get
{
return this.GetPropertyRational((int)TagNames.GpsAltitude).ToDouble();
}
}
主程序只需递归遍历所有JPG文件,然后
ExifManager exif = new ExifManager(fi.FullName);
string latRef = exif.GpsLatitudeRef;
if (latRef.Length > 0)
{
txtFileName.Text = fi.FullName;
txtExif.Text = exif.ToString();
this.Refresh();
if (this.backupToolStripMenuItem.Checked)
{
string backupPath = fi.DirectoryName + "\\backup";
if (!Directory.Exists(backupPath))
{
Directory.CreateDirectory(backupPath);
}
fi.CopyTo(backupPath + "\\" + fi.Name);
}
ExifManager.Coordinates x = exif.GpsLatitude;
exif.GpsLatitude = new ExifManager.Coordinates(x.Value);
ExifManager.Coordinates y = exif.GpsLongitude;
exif.GpsLongitude = new ExifManager.Coordinates(y.Value);
try
{
if (File.Exists(fi.DirectoryName + TEMP))
{
File.Delete(fi.DirectoryName + TEMP);
}
exif.Save(fi.DirectoryName + TEMP);
exif.Dispose();
fi.Delete();
File.Move(fi.DirectoryName + TEMP, fi.FullName);
count++;
//Thread.Sleep(10);
}
catch
{
MessageBox.Show("文件操作失败,请确保没有其他程序正在打开文件 " + fi.Name, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
exif.Dispose();
progressBar.Value++;
this.Refresh();
}
注意,在读取经纬度时,我对原来的代码GetPropertyRational进行了重载。
因为这个函数原本的作用是读取8个byte的内容,而经纬度都有3*8个byte,用原来的函数只能读到“度”,要想读出“分”和“秒”就要向后便宜8个和16个byte
public Rational GetPropertyRational(Int32 PID)
{
return GetPropertyRational(PID, 0);
}
public Rational GetPropertyRational(Int32 PID, Int16 disp)
{
if (IsPropertyDefined(PID))
{
byte[] arr = new byte[8];
Array.Copy(this._Image.GetPropertyItem(PID).Value, disp, arr, 0, 8);
return GetRational(arr);
}
else
{
Rational R;
R.Numerator = 0;
R.Denominator = 1;
return R;
}
}
【结束语】
终于大功告成了。
我在这个程序的About中写了如下文字:
“本软件用于将照片中的GPS坐标信息批量修改为苹果iOS系统所能识别的格式。
作为摄影爱好者,我喜欢在照片中保留拍摄地点的经纬度坐标,无论是使用内置GPS模块的相机,还是外置GPS附件,甚至是通过手机记录GPS轨迹,回家后再使用软件批量写入JPG文件(这需要相机的时钟要准确)。
然而我却发现这些照片在iPhone和iPad中所显示的位置与真实坐标相去甚远,并不是Google地图的混淆性偏移,那个也就几米、几十米,而这个偏差有几百几千甚至上万公里!
分析得知,虽然都是Exif2.21标准下的GPS信息,但苹果的格式存在某些特殊要求,这也许是苹果出于混淆精确度的考虑,苹果的最小精度为18米(赤道上)。
如果你准备把一组照片同步到iOS设备上,那么之前你可以使用本软件将它们的GPS格式改为iOS可以正确识别的。但这也带来了精度降低的问题,因此我建议你最好在PC上保留照片的转换前版本。
欢迎交流,新浪微博 @长江游泳鱼 http://weibo.com/10391867
”
最后提供下载(需要.Net Framework2.0及以上环境)
耐心一点,并没死。根据文件大小,大概1-3秒一张。虽然用了委托,但是进度条和Exif信息,到后面还是有些卡。谁能给点提示?
======================================================================
最后还要提一点,就是当我有多个相簿的时候,只有最后一个导入的相簿中的照片的GPS位置才能显示出来。也就是说一旦导入了新照片(不管是否含有GPS信息),那么以前的坐标位置就荡然无存了。
如果之前的问题可以用“非军事用途”解释,那么这一点应该是iOS的Bug了吧?
有线索的话,请跟帖告诉我好吗?
- 大小: 546.7 KB
- 大小: 163 KB
- 大小: 142.3 KB
分享到:
相关推荐
这是个免费的iPhone/iPad的Pages软件,是真的免费哦,不管你是否已越狱,一样可以安装, Pages 是移动设备上您所看到的极其漂亮的文字处理程序。该功能强大的应用程序是专门为 iPad、iPhone 和 iPod touch 设计的。...
iPhoneFolder ipad iphone连接电脑查看文件
A Learner’s Guide to Creating Objective-C Applications for the iPhone and iPad Book Description : Let’s say you have a killer app idea for iPhone and iPad. Where do you begin? Head First iPhone and...
实现日历效果。可以点击日历上的日期,可以切换不同月份与年份。类似于Apple的MobileCal效果。 注意:请在Mac下解压使用
To use LonelyScreen, what app should I install on my iPhone/iPad? Nothing. You don't need to install anything on your iPhone/iPad. You just need install LonelyScreen on your Windows or Mac. Does ...
它可以帮助您将几乎所有流行的视频格式,如:RM、RMVB、VOB、DAT、VCD、DVD、SVCD、ASF、MOV、QT、MPEG、WMV、 MP4、3GP、DivX、XviD、AVI、FLV等视频文件转换为Apple iPad/iPhone/iPod上所支持的视频格式。...
详细介绍iphone、ipad的发展历史,介绍他们的使用方法和破解方法,以及高级应用技巧
隆重推荐一款新的苹果设备管理工具iTools,它只有700K、完全绿色、像iTunes一般安全,并且使用非常方便。 每个人都很难理解为什么...也正因此让危险的91手机助手依然活跃在玩家中,即使白苹果也不去忍受iTunes的变态。
It is assumed that the reader will have some knowledge of object-oriented programming and the Apple and iPhone/iPad developer environment. amazon link:...
【关注微信公众号:refs-cn,可免费下载】 Refs.cn 原型设计元件库, 基于 Apple iOS/macOS Human Interface Guidelines 创建,适用于 iPhone、iPad 和 macOS 桌面应用的原型设计。
它的功能就是在电脑上开启Airplay服务,让iOS设备(iPhone/iPad)通过WiFi直接把视频、音频、图片、桌面等用电脑来播放。如果你的电脑接个大屏幕电视机,那就几乎成了Apple TV。而且它支持多个iOS设备同时推送,同时...
iOS 10 SDK Development: Creating iPhone and iPad Apps with Swift by Chris Adamson English | 24 Mar. 2017 | ASIN: B071RRCK9R | 264 Pages | AZW3 | 5.24 MB All in on Swift! iOS 10 and Xcode 8 make it ...
apple设备发现无线接入点,包括隐藏的没有广播的接入点。快来试试吧。
You'll not only learn how to design for Apple's devices, you'll also master the iPhone SDK tools -- including Interface Builder, Xcode, and Objective-C programming principles -- to make your app ...
交易猫
越狱团队evad3rs终于在官网上正式发布了iOS 6.1完美越狱工具,该工具支持的设备有iPhone 3GS、iPhone 4、iPhone 4S、iPhone 5、iPad mini、iPad 2、iPad 3、iPad 4、iPod touch 4/5以及Apple TV 2。 此外该工具...