使用.net将GPS标签插入jpeg EXIF元数据 [英] Inserting GPS tags into jpeg EXIF metadata using .net

查看:81
本文介绍了使用.net将GPS标签插入jpeg EXIF元数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

用于修改EXIF元数据的旧方法似乎使用System.Drawing.Image命名空间 - 以及此类的propertyItems属性。我已经看到一些迹象表明这种方法只允许修改现有的EXIF数据而不添加之前不存在的新元数据。就我而言,我想将GPS标签添加到之前没有的jpg图像中。



任何人都可以验证新的propertyItems无法添加到现有图像 - 从而扩展元数据标题的长度吗?



更新的方法似乎是使用System.Windows.Media.Imaging命名空间及其InPlaceBitmapMetadataWriter方法。



有没有人有一个例子这样做?该示例将以带有GPS标签的图像开始,最后得到带有GPS标签的图像。



--- Jim

The older methods for modifying EXIF metadata appear to use the System.Drawing.Image namespace -- and the propertyItems property of this class. I have seen some indication that this approach only allows modifying existing EXIF data and not adding new metadata where none existed before. In my case, I'd like to add GPS tags to a jpg image that previously had none.

Can anyone verify that new propertyItems cannot be added to an existing image -- thus extending the length of the metadata header?

The newer approach seems to be to use the System.Windows.Media.Imaging namespace and its InPlaceBitmapMetadataWriter method.

Does anyone have an example of doing this? The example would begin with an image w/o the GPS tags and end up with an image that has the GPS tags.

--- Jim

推荐答案

http://blogs.msdn.com/b/kamalds/archive/2012/04/08/adding-updating-exif-data.aspx [ ^ ]有一个如何创建EXIF字段的示例如果它们还不存在于图像上。



基本上,您需要知道该字段的EXIF字段代码,并按照WriteEXIFField()中的代码进行操作示例代码中创建新字段的方法。



更新:您可以在此处找到EXIF标记列表:http://www.exiv2.org/tags.html [ ^ ]



您可以在Util类中找到有关如何创建和解析纬度和经度存储的Rational类型的信息,如在该项目的源代码中:ExifTagCollection - EXIF元数据提取库 [ ^ ]
http://blogs.msdn.com/b/kamalds/archive/2012/04/08/adding-updating-exif-data.aspx[^] has an example of how to create EXIF fields if they don't exist on the image yet.

Basically, you need to know the EXIF field code for the field, and follow the code in the WriteEXIFField() method in the sample code to create the new field.

UPDATE: You can find a list of EXIF tags here: http://www.exiv2.org/tags.html[^]

And you can find information on how to create and parse the Rational type that the Latitude and Longitude are stored as in the source code of this project in the Util class: ExifTagCollection - An EXIF metadata extraction library[^]


I修改了我在网络上找到的几组代码。

下面的代码采用了一个jpg图像文件,该图像文件没有用于GPS定位的exif标签,并添加了GPS信息。作为测试,读回修改的代码并测试GPS位置标签的存在。





I modified several sets of code that I found around the web.
The code below takes a jpg image file that does not have exif tags for GPS location and adds the GPS information. As a test, the modified code is read back in and test for the presence of the GPS location tags.


using System;
using System.Collections.Generic;
using System.IO;

using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace AddGPSTagsToEXIF
{
    class MetadataExample
    {
        // North or South Latitude 
        private const string GPSLatitudeRefQuery = "/app1/ifd/gps/subifd:{ulong=1}"; // ASCII 2
        // Latitude        
        private const string GPSLatitudeQuery = "/app1/ifd/gps/subifd:{ulong=2}"; // RATIONAL 3
        // East or West Longitude 
        private const string GPSLongitudeRefQuery = "/app1/ifd/gps/subifd:{ulong=3}"; // ASCII 2
        // Longitude 
        private const string GPSLongitudeQuery = "/app1/ifd/gps/subifd:{ulong=4}"; // RATIONAL 3
        // Altitude reference 
        private const string GPSAltitudeRefQuery = "/app1/ifd/gps/subifd:{ulong=5}"; // BYTE 1
        // Altitude 
        private const string GPSAltitudeQuery = "/app1/ifd/gps/subifd:{ulong=6}"; // RATIONAL 1

        static void Main( string[] args )
        {
            //original image file
            string originalPath = @"D:\Temp\test.jpg";
            //image file after adding the GPS tags
            string outputPath = @"D:\Temp\testModified.jpg";

            BitmapCreateOptions createOptions = BitmapCreateOptions.PreservePixelFormat | BitmapCreateOptions.IgnoreColorProfile;
            uint paddingAmount = 2048; 

            //open the image file
            using ( Stream originalFile = File.Open ( originalPath, FileMode.Open, FileAccess.Read ) )
            {
                BitmapDecoder original = BitmapDecoder.Create ( originalFile, createOptions, BitmapCacheOption.None );

                //this becomes the new image that contains new metadata
                JpegBitmapEncoder output = new JpegBitmapEncoder ( );

                if ( original.Frames[0] != null && original.Frames[0].Metadata != null )
                {
                    //clone the metadata from the original input image so that it can be modified
                    BitmapMetadata metadata = original.Frames[0].Metadata.Clone ( ) as BitmapMetadata;

                    //pad the metadata so that it can be expanded with new tags
                    metadata.SetQuery ( "/app1/ifd/PaddingSchema:Padding", paddingAmount );
                    metadata.SetQuery ( "/app1/ifd/exif/PaddingSchema:Padding", paddingAmount );
                    metadata.SetQuery ( "/xmp/PaddingSchema:Padding", paddingAmount );

                    //form the new metadata that is to be added
                    double latitude  =   30.0 + 15.0 / 60.0 + 22.0 / 3600.0;
                    double longitude = -(86.0 + 16.0 / 60.0 + 23.0 / 3600.0);
                    double altitude = 44;

                    GPSRational latitudeRational = new GPSRational(latitude);
                    GPSRational longitudeRational = new GPSRational(longitude);
                    metadata.SetQuery(GPSLatitudeQuery, latitudeRational.bytes);
                    metadata.SetQuery(GPSLongitudeQuery, longitudeRational.bytes);
                    if (latitude > 0) metadata.SetQuery(GPSLatitudeRefQuery, "N");
                    else metadata.SetQuery(GPSLatitudeRefQuery, "S");
                    if (longitude > 0) metadata.SetQuery(GPSLongitudeRefQuery, "E");
                    else metadata.SetQuery(GPSLongitudeRefQuery, "W");

                    Rational altitudeRational = new Rational((int)altitude, 1);  //denoninator = 1 for Rational
                    metadata.SetQuery(GPSAltitudeQuery, altitudeRational.bytes);

                  
                    //create the output image using the image data, thumbnail, and metadata from the original image as modified above
                    output.Frames.Add ( 
                        BitmapFrame.Create ( original.Frames[0], original.Frames[0].Thumbnail, metadata, original.Frames[0].ColorContexts ) );
                }

                //save the output image
                using ( Stream outputFile = File.Open ( outputPath, FileMode.Create, FileAccess.ReadWrite ) )
                {
                    output.Save ( outputFile );
                }
            }


            //Check to see if the modified and saved file has the correct added metadata
            using ( Stream savedFile = File.Open ( outputPath, FileMode.Open, FileAccess.Read ) )
            {
                BitmapDecoder output = BitmapDecoder.Create ( savedFile, BitmapCreateOptions.None, BitmapCacheOption.Default );
                //InPlaceBitmapMetadataWriter metadata = output.Frames[0].CreateInPlaceBitmapMetadataWriter ( );
                BitmapMetadata metadata = output.Frames[0].Metadata.Clone() as BitmapMetadata;

                byte[] lat4 = (byte[])metadata.GetQuery(GPSLatitudeQuery);
                byte[] lon4 = (byte[])metadata.GetQuery(GPSLongitudeQuery);
                String latitudeRef  = (String)metadata.GetQuery(GPSLatitudeRefQuery);
                String longitudeRef = (String)metadata.GetQuery(GPSLongitudeRefQuery);
                byte[] altitude = (byte[])metadata.GetQuery(GPSAltitudeQuery);
            }
        }

     }


    //EXIF Rational Type (pack 4-byte numerator and 4-byte denominator into 8 bytes
    public class Rational
    {
        public Int32 _num;     //numerator of exif rational
        public Int32 _denom;   //denominator of exif rational
        public byte[] bytes;   //8 bytes that form the exif rational value

        //form rational from a given 4-byte numerator and denominator
        public Rational(Int32 _Num, Int32 _Denom)
        {
            _num = _Num;
            _denom = _Denom;

            bytes = new byte[8];  //create a byte array with 8 bytes
            BitConverter.GetBytes(_num).CopyTo(bytes, 0);  //copy 4 bytes of num to location 0 in the byte array
            BitConverter.GetBytes(_denom).CopyTo(bytes, 4);  //copy 4 bytes of denom to location 4 in the byte array
        }

        //form rational from an array of 8 bytes
        public Rational(byte[] _bytes)
        {
            byte[] n = new byte[4];
            byte[] d = new byte[4];
            //copy 4 bytes from bytes-array into n-array starting at index 0 from bytes and 0 from n 
            Array.Copy(_bytes, 0, n, 0, 4);
            //copy 4 bytes from bytes-array into d-array starting at index 4 from bytes and 0 from d 
            Array.Copy(_bytes, 4, d, 0, 4);
            //convert the 4 bytes from n into a 4-byte int (becomes the numerator of the rational)
            _num = BitConverter.ToInt32(n, 0);
            //convert the 4 bytes from d into a 4-byte int (becomes the denonimator of the rational)
            _denom = BitConverter.ToInt32(d, 0);

        }

        //convert the exif rational into a double value
        public double ToDouble()
        {
            //round the double value to 5 digits
            return Math.Round(Convert.ToDouble(_num) / Convert.ToDouble(_denom), 5);
        }
    }

    //special rational class to handle the GPS three rational values  (degrees, minutes, seconds)
    public class GPSRational
    {
        public Rational _degrees;
        public Rational _minutes;
        public Rational _seconds;
        public byte[] bytes;  //becomes an array of 24 bytes that represent hrs, minutes, seconds as 3 rationals
        double angleInDegrees;  //latitude or longitude as decimal degrees

        //form the 3-rational exif value from an angle in decimal degrees
        public GPSRational(double angleInDeg)
        {
            //convert angle in decimal degrees to three rationals (deg, min, sec) with denominator of 1
            //NOTE:  this formulation results in a descretization of about 100 ft in the lat/lon position
            double absAngleInDeg = Math.Abs(angleInDeg);
            int degreesInt = (int)(absAngleInDeg);
            absAngleInDeg -= degreesInt;
            int minutesInt = (int)(absAngleInDeg * 60.0);
            absAngleInDeg -= minutesInt / 60.0;
            int secondsInt = (int)(absAngleInDeg * 3600.0 + 0.50);

            //form a rational using "1" as the denominator
            int denominator = 1;
            _degrees = new Rational(degreesInt, denominator);
            _minutes = new Rational(minutesInt, denominator);
            _seconds = new Rational(secondsInt, denominator);

            angleInDegrees = _degrees.ToDouble() + _minutes.ToDouble() / 60.0 + _seconds.ToDouble() / 3600.0;

            // form the 24-byte array representing the 3 rationals
            bytes = new byte[24];
            BitConverter.GetBytes(degreesInt).CopyTo(bytes, 0);
            BitConverter.GetBytes(denominator).CopyTo(bytes, 4);
            BitConverter.GetBytes(minutesInt).CopyTo(bytes, 8);
            BitConverter.GetBytes(denominator).CopyTo(bytes, 12);
            BitConverter.GetBytes(secondsInt).CopyTo(bytes, 16);
            BitConverter.GetBytes(denominator).CopyTo(bytes, 20);
        }

        //Form the GPSRational object from an array of 24 bytes
        public GPSRational(byte[] _bytes)
        {
            byte[] degBytes = new byte[8]; byte[] minBytes = new byte[8]; byte[] secBytes = new byte[8];

            //form the hours, minutes, seconds rational values from the input 24 bytes
            // first 8 are hours, second 8 are the minutes, third 8 are the seconds
            Array.Copy(_bytes, 0, degBytes, 0, 8); Array.Copy(_bytes, 8, minBytes, 0, 8); Array.Copy(_bytes, 16, secBytes, 0, 8);

            _degrees = new Rational(degBytes);
            _minutes = new Rational(minBytes);
            _seconds = new Rational(secBytes);

            angleInDegrees = _degrees.ToDouble() + _minutes.ToDouble() / 60.0 + _seconds.ToDouble() / 3600.0;
            bytes = new byte[24];
            _bytes.CopyTo(bytes, 0);
        }
    }

}


这篇关于使用.net将GPS标签插入jpeg EXIF元数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆