C#:照片寫入經緯度坐標資訊(編輯照片的EXIF屬性)
C#:照片寫入經緯度坐標資訊(編輯照片的EXIF屬性)
1.引用的class
using System.Windows.Forms;
using System.IO; //MemoryStream
using System.Drawing.Imaging;//PropertyItem
2.程式碼
using (MemoryStream ms = new MemoryStream(File.ReadAllBytes("D:\\app\\photoEdit\\example\\DSC06160.JPG")))
{
using (Image Pic = Image.FromStream(ms))
{
const short ExifTypeByte = 1;
const short ExifTypeAscii = 2;
const short ExifTypeRational = 5;
const int ExifTagGPSLatitudeRef = 0x0001;
const int ExifTagGPSLatitude = 0x0002;
const int ExifTagGPSLongitudeRef = 0x0003;
const int ExifTagGPSLongitude = 0x0004;
const int ExifTagGPSAltitudeRef = 0x0005;
const int ExifTagGPSAltitude = 0x0006;
char latHemisphere = 'N';
char lngHemisphere = 'E';
const short altHemisphere = 0;
double photoX = 121.345688;
double photoY = 22.654339;
double photoZ = 356.876;
byte[] LonBytes = ConvertToRationalTriplet(photoX);
byte[] LatBytes = ConvertToRationalTriplet(photoY);
byte[] AltBytes = ConvertToRationalTriplet(photoZ);
AddProperty(Pic, ExifTagGPSLatitudeRef, ExifTypeAscii, new byte[] { (byte)latHemisphere, 0 });
AddProperty(Pic, ExifTagGPSLatitude, ExifTypeRational, ConvertToRationalTriplet(photoY));
AddProperty(Pic, ExifTagGPSLongitudeRef, ExifTypeAscii, new byte[] { (byte)lngHemisphere, 0 });
AddProperty(Pic, ExifTagGPSLongitude, ExifTypeRational, ConvertToRationalTriplet(photoX));
AddProperty(Pic, ExifTagGPSAltitudeRef, ExifTypeByte, new byte[] { (byte)altHemisphere, 0 });
AddProperty(Pic, ExifTagGPSAltitude, ExifTypeRational, ConvertToRationalAltitude(photoZ));
Pic.Save("D:\\app\\photoEdit\\example\\geotag_DSC06160.JPG");
}
}
3.functions
static byte[] ConvertToRationalTriplet(double value)
{
int degrees = (int)Math.Floor(value);
value = (value - degrees) * 60;
int minutes = (int)Math.Floor(value);
value = (value - minutes) * 60 * 1000000;
int seconds = (int)Math.Round(value);
byte[] bytes = new byte[3 * 2 * 4]; // Degrees, minutes, and seconds, each with a numerator and a denominator, each composed of 4 bytes
int i = 0;
Array.Copy(BitConverter.GetBytes(degrees), 0, bytes, i, 4); i += 4;
Array.Copy(BitConverter.GetBytes(1), 0, bytes, i, 4); i += 4;
Array.Copy(BitConverter.GetBytes(minutes), 0, bytes, i, 4); i += 4;
Array.Copy(BitConverter.GetBytes(1), 0, bytes, i, 4); i += 4;
Array.Copy(BitConverter.GetBytes(seconds), 0, bytes, i, 4); i += 4;
Array.Copy(BitConverter.GetBytes(1000000), 0, bytes, i, 4);
return bytes;
}
static byte[] ConvertToRationalAltitude(double value)
{
value = value * 1000;
int meters = (int)Math.Floor(value);
byte[] bytes = new byte[8];
int i = 0;
Array.Copy(BitConverter.GetBytes(meters), 0, bytes, i, 4); i += 4;
Array.Copy(BitConverter.GetBytes(1000), 0, bytes, i, 4); i += 4;
return bytes;
}
static void AddProperty(Image img, int id, short type, byte[] value)
{
PropertyItem pi = img.PropertyItems[0];
pi.Id = id;
pi.Type = type;
pi.Len = value.Length;
pi.Value = value;
img.SetPropertyItem(pi);
}
4.重點解說
(1)照片的GPS(經度、緯度)屬性須以度、分、秒方式儲存,因此如果坐標來源是度小數,須先進行轉換。詳細說明請務必參考https://www.exiv2.org/tags.html
(2)轉換好的經緯度資訊,需要再轉換成byte[]格式,byte[]的長度為24
[0],[1],[2],[3]===>這裡儲存的是"度"的分子
[4],[5],[6],[7]===>這裡儲存的是"度"的分母
[8],[9],[10],[11]=>這裡儲存的是"分"的分子
[12],[13],[14],[15]這裡儲存的是"分"的分母
[16],[17],[18],[19]這裡儲存的是"秒"的分子
[20],[21],[22],[23]這裡儲存的是"秒"的分母
要表現此浮點數,須將此數拆成整數及小數點
整數121也要拆成分子與分母
分子部分:
[0]=121,[1]=0,[2]=0,[3]=0
分母部分
[4]=1,[5]=0,[6]=0,[7]=0
所以"度"=121/1
因為byte[]陣列裡面的值都必須是整數,所以需要靠分子/分母這種方式表現浮點數
以高程356.876為例,我們不能直接在[0]填入356.876,其正確做法如下:
將356.876換算成356876/1000
宣告byte[] bytes = new byte[8];
//[0],[1],[2],[3]放分子356876
//[4],[5],[6],[7]放分母1000
[0]=12,[1]=144,[2]=5,[3]=0 (12+114*256+5*256^2=356876) //256進位,請自行驗算
[4]=232,[5]=3,[6]=0,[7]=0(232+3*256=1000)
如此一來,就可以藉由byte[]的資料格式儲存356.876,並且將此資訊寫入照片EXIF的GPSInfo中。
留言
張貼留言