Kirjautuminen

Haku

Tehtävät

Keskustelu: Nettisivujen teko: Jpeg-kuvien Geotaggaus C#(asp.net)

Synomi [03.01.2012 13:48:40]

#

Olen tässä jo useamman tunnin koittanut saada selville miten tuo Jpeggien geotaggaus onnistuisi. Koittanu tuossa kirjastoja kuten 'ExifExtractor (http://www.codeproject.com/KB/graphics/exifextractor.aspx)' sekä 'ExifLibrary for .NET (http://www.codeproject.com/KB/graphics/ExifLibrary.aspx)', mutta kumminkaan siinä onnistumatta. ExifExtractorilla kyllä sain kuviin tallentumaan muita tietoja kuten Copyright ja artist, mutta Gps tietojen tallennus ei meinaa onnistua. Kuviin ei siis tule tietoihin sitä GPS kenttää, jossa on mm. leveysaste sekä pituusaste.

Tällä ExifExtractorilla exif tietojen tallennus tapahtuu seuraavasti:

System.Drawing.Bitmap bmp =
             new System.Drawing.Bitmap(Path.Combine(Server.MapPath("~/App_Data/uploads/images"), filename + ".jpg"));

                Goheer.EXIF.EXIFextractor er =
             new Goheer.EXIF.EXIFextractor(ref bmp, "\n");


                er.setTag(getExifTagId("Artist"), username);
                er.setTag(getExifTagId("Copyright"), "Yritys Oy");

bmp.Save(Path.Combine(Server.MapPath("~/App_Data/uploads/images"), filename + "_en.jpg"));

bmp.Dispose();

Tämä getExitTagId palauttaa exif tagin id:n, jonka uskosin olevan ihan toimiva, koska Copyright ja Artist tallentuu oikeisiin kohtiin.

private int getExifTagId(string tagstr)
            {
                Goheer.EXIF.translation tags = new Goheer.EXIF.translation();


                foreach (DictionaryEntry s in tags)
                    if ((string)s.Value == tagstr)
                        return (int)s.Key;

                return 0;
            }

Yllä oleva koodi siis toimii niinkuin pitää, ongelma tulee kun yrittää tallentaa Gps tietoja.

Seuraavalla tavalla niitä olen yrittänyt tallentaa onnistumatta:

   System.Drawing.Bitmap bmp =
             new System.Drawing.Bitmap(Path.Combine(Server.MapPath("~/App_Data/uploads/images"), filename + ".jpg"));

                Goheer.EXIF.EXIFextractor er =
             new Goheer.EXIF.EXIFextractor(ref bmp, "\n");

                er.setTag(0x0000, "2");//gps ver
                er.setTag(0x0001, "N");//lat. ref
                er.setTag(0x0003, "E");//longitude. ref

                er.setTag(0x0002, latitude);

                er.setTag(0x0004, longitude);

bmp.Save(Path.Combine(Server.MapPath("~/App_Data/uploads/images"), filename + "_en.jpg"));
bmp.Dispose();

Edit: Nämäkin tiedot näyttää tallentuvan oikeisiin paikkoihin kun yhdellä viewer ohjelmalla katsoin, eli joku homma puuttuu kait siis joka estää sen että Gps tiedot alkaisi näkymään itse resurssien hallinnan tiedoissa.

Sivulta: http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/GPS.html löytyy nuo hexa idt mitä tossa käytän. Olen myös kokeillu samalla tapaa kuin aikaisemmissa esimerkeissä suoraa 'Gps Altitude' jne. jotka tulee tuosta ExifExtractorista, niin neki pitäisi toimia samanlailla, kun niillä samat idt näyttäis olevan.

Myös yrittänyt ilman ylimääräistä kirjastoa suoraan:

Image Pic = Image.FromFile(Path.Combine(Server.MapPath("~/App_Data/uploads/images"), filename + ".jpg"));

PropertyItem propertyItem = Pic.PropertyItems[0];

propertyItem.Id = 0x0000; //index of the EXIF TAG
                propertyItem.Type = 0x3;//
                propertyItem.Len = 4;
                propertyItem.Value = new byte[4] {2,0,0,0};
                Pic.SetPropertyItem(propertyItem);



                propertyItem.Id = 0x0002; //index of the EXIF TAG
                propertyItem.Type = 5;//
                propertyItem.Len = 4;
                propertyItem.Value = new byte[4];
                Pic.SetPropertyItem(propertyItem);

Pic.Save(Path.Combine(Server.MapPath("~/App_Data/uploads/images"), filename + "_pr.jpg"));

Tuosta en oo varma että miten tuo edes tallentaa nuo propertyItemit, että meneekö ne jotenki päällekkäin, no joka tapauksessa mitään tietoja ei tunnu tollakaan tallentuvan.

Edit:
No tuo property itemeiden tallennukset näyttäis toimivan shukot ok tuossa kun katsoin yhellä exif viewerillä. Siltikkään kuvan tiedoissa ei näy mitään GPS tietoja kylläkään.


Oiskohan jollan kokemusta tai tietämystä miten nuita Exif tietoja käsiteltäis tarkemmin tai Gps tietoja? Kyseessä on siis aina uusi kuva joka on juuri luotu base64-muodossa olevasta kuvasta(johon ei saa tallentumaan gps tietoja valmiiksi ainkaan tässä tapauksessa). Siinä on vain 2 exif-tagia valmiina jotka ovat: Chrominance Table, Luminance Table.

Synomi [03.01.2012 15:56:37]

#

Lähti tuo hieman edistymään, kun muutti tallennuksen byte muotoon seuraavasti:

er.setTag(1, 2, 2, StrToByteArray("N")); //lat ref
er.setTag(2, 24, 7, StrToByteArray("33°24'51.8")); //latitude
er.setTag(3, 2, 2, StrToByteArray("E")); //long ref
er.setTag(4, 24, 4, StrToByteArray("33°24'51.8"));  //longitude
er.setTag(16, 2, 2, StrToByteArray("T")); //Gps image direction ref
er.setTag(36864, 4, 7, StrToByteArray("0221")); //exif version
public static byte[] StrToByteArray(string str)
{
System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
return encoding.GetBytes(str);
}

er.setTag on (id, length, type, byte[])

Tuossa muoden tallentaminen toimii paitsi latituden ja longituden, ne pitäs saada tallennettua rational64u[3] muodossa, olisikohan jollain tietoa miten tämä mahdollisesti tapahtuisi kun tallennetaan byte taulukkoon?

Toisesta kuvasta kattonu mallia ja koittanu pistää tarvittavia exif tietoja, ei kyl vieläkään näy mitään Gpsään liittyvää ite resurssien hallinnan avulla, mutta exif viewererillä tosiaan näkyy.

Synomi [05.01.2012 12:46:55]

#

Lähti toimiin ku tarpeeks päätä seinään hakkasi ja tyyppi muunnoksia pyöritteli. Latitude ja longitude häätyi perus muunnoksien jälkeen muuttaa rationaalisiksi luvuiksi ja sen jälkeen tavuiksi ja tallentaa. Tuossa osa koodista jos joillakin muilla tarvetta joskus:

System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(Path.Combine(Server.MapPath("~/App_Data/uploads/images"), filename + ".jpg"));

Goheer.EXIF.EXIFextractor er =
new Goheer.EXIF.EXIFextractor(ref bmp, "\n");

Coordinate cLongitude = new Coordinate(Convert.ToDouble(longitude, CultureInfo.InvariantCulture), CoordinatesPosition.N);
Coordinate cLatitude = new Coordinate(Convert.ToDouble(latitude, CultureInfo.InvariantCulture), CoordinatesPosition.E);

Rational longitudeDegrees = new Rational(cLongitude.Degrees,2);
Rational longitudeMinutes = new Rational(cLongitude.Minutes,2);
Rational longitudeSeconds = new Rational(cLongitude.Seconds,7);

Rational latitudeDegrees = new Rational(cLatitude.Degrees, 2);
Rational latitudeMinutes = new Rational(cLatitude.Minutes, 2);
Rational latitudeSeconds = new Rational(cLatitude.Seconds, 7);

byte[] latDegrees = BitConverter.GetBytes(latitudeDegrees.ToUlong());
byte[] latMinutes = BitConverter.GetBytes(latitudeMinutes.ToUlong());
byte[] latSeconds = BitConverter.GetBytes(latitudeSeconds.ToUlong());
byte[] latTot = new byte[24];

byte[] lotDegress = BitConverter.GetBytes(longitudeDegrees.ToUlong());
byte[] lotMinutes = BitConverter.GetBytes(longitudeMinutes.ToUlong());
byte[] lotSeconds = BitConverter.GetBytes(longitudeSeconds.ToUlong());
byte[] lotTot = new byte[24];

System.Buffer.BlockCopy(latDegrees, 0, latTot, 0, 8);
System.Buffer.BlockCopy(latMinutes, 0, latTot, 8, 8);
System.Buffer.BlockCopy(latSeconds, 0, latTot, 16, 8);

System.Buffer.BlockCopy(lotDegress, 0, lotTot, 0, 8);
System.Buffer.BlockCopy(lotMinutes, 0, lotTot, 8, 8);
System.Buffer.BlockCopy(lotSeconds, 0, lotTot, 16, 8);

er.setTag(2, 24, 5, latTot); //latitude
er.setTag(4, 24, 5, lotTot); //longitude

bmp.Save(Path.Combine(Server.MapPath("~/App_Data/uploads/images"), filename + "_en.jpg"));
bmp.Dispose();

Tossa nuo 2 luokkaa jotka löyty kait http://www.codeproject.com sivuilta, mut nyt alhaalla tuo sivusto.

//Coordinate Class
using System;
public class Coordinate
{
    public double Degrees { get; set; }
    public double Minutes { get; set; }
    public double Seconds { get; set; }
    public CoordinatesPosition Position { get; set; }

    public Coordinate() { }
    public Coordinate(double value, CoordinatesPosition position)
    {
        //sanity
        if (value < 0 && position == CoordinatesPosition.N)
            position = CoordinatesPosition.S;
        //sanity
        if (value < 0 && position == CoordinatesPosition.E)
            position = CoordinatesPosition.W;
        //sanity
        if (value > 0 && position == CoordinatesPosition.S)
            position = CoordinatesPosition.N;
        //sanity
        if (value > 0 && position == CoordinatesPosition.W)
            position = CoordinatesPosition.E;

        var decimalValue = Convert.ToDecimal(value);

        decimalValue = Math.Abs(decimalValue);

        var degrees = Decimal.Truncate(decimalValue);
        decimalValue = (decimalValue - degrees) * 60;

        var minutes = Decimal.Truncate(decimalValue);
        var seconds = (decimalValue - minutes) * 60;

        Degrees = Convert.ToDouble(degrees);
        Minutes = Convert.ToDouble(minutes);
        Seconds = Convert.ToDouble(seconds);
        Position = position;
    }
    public Coordinate(double degrees, double minutes, double seconds, CoordinatesPosition position)
    {
        Degrees = degrees;
        Minutes = minutes;
        Seconds = seconds;
        Position = position;
    }
    public double ToDouble()
    {
        var result = (Degrees) + (Minutes) / 60 + (Seconds) / 3600;
        return Position == CoordinatesPosition.W || Position == CoordinatesPosition.S ? -result : result;
    }
    public override string ToString()
    {
        return Degrees + "º " + Minutes + "' " + Seconds + "'' " + Position;
    }
}

public enum CoordinatesPosition
{
    N, E, S, W
}


//Rational Class
using System;
public class Rational
{
    private int numerator;
    private int denominator;
    /// <summary>
    /// Creates an Exif Rational
    /// </summary>
    /// <param name="numerator">The value you want to store in the Rational</param>
    /// <param name="accuracy">The number of decimal places of accuracy</param>
    public Rational(double numerator, int accuracy)
    {
        accuracy = (int)Math.Pow(10, accuracy);
        this.numerator = Convert.ToInt32(Math.Abs(numerator * accuracy));
        this.denominator = accuracy;
    }
    /// <summary>
    /// Creates an Exif Rational
    /// </summary>
    /// <param name="data">A ulong typically read from exif metadata</param>
    public Rational(ulong data)
    {
        this.numerator = (int)(data & 0xFFFFFFFFL);
        this.denominator = (int)((data & 0xFFFFFFFF00000000L) >> 32);
    }
    /// <summary>
    /// Returns the Rational as a Ulong, typically used to write back to exif metadata
    /// </summary>
    /// <returns>Ulong</returns>
    public ulong ToUlong()
    {
        return ((ulong)this.numerator) | (((ulong)this.denominator) << 32);
    }
    /// <summary>
    /// Returns the Rational as an Integer
    /// </summary>
    /// <returns>Int</returns>
    public int ToInt()
    {
        return Convert.ToInt32(Math.Round(Convert.ToDouble(this.numerator) / Convert.ToDouble(this.denominator)));
    }
    /// <summary>
    /// Returns the Rational as a Double
    /// </summary>
    /// <returns>Double</returns>
    public double ToDouble()
    {
        return this.ToDouble(0);
    }
    /// <summary>
    /// Returns the Rational as a Double
    /// </summary>
    /// <returns>Double, accurate to the specified number of decimal places</returns>
    /// <param name="decimalPlaces">The number of decimal places of accuracy</param>
    public double ToDouble(int decimalPlaces)
    {
        return Math.Round(Convert.ToDouble(this.numerator) / Convert.ToDouble(this.denominator), decimalPlaces);
    }
    /// <summary>
    /// Returns the Rational as a string
    /// </summary>
    /// <returns>A string in the format numerator/denominator</returns>
    public new string ToString()
    {
        return this.numerator.ToString() + "/" + this.denominator.ToString();
    }
}

Varmaan pientä viimeistelyä saattaa vaatia tuo. Eka testeillä kuvan näkyminen kartalla heitti joku pari sataa metriä. Ja tällä hetkellä käyttää vain Northern ja Eastern sijaintia. Joten muut ei välttämättä toimi.

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta