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.345688為例:
要表現此浮點數,須將此數拆成整數及小數點
整數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中。


留言