Exemplo n.º 1
0
        public static SharpMapillaryInfo LoadGPX(this String                                               GPXFile,
                                                 Int32?                                                    TimeOffset           = null,
                                                 Func<String, DateTime, Double, Double, Double, DateTime>  OnDupliateTimestamp  = null,
                                                 Action<DateTime, DateTime, DateTimeKind>                  OnResult             = null)
        {
            var Mapillary = new SharpMapillaryInfo(GPXFile.Substring(0, GPXFile.LastIndexOf(Path.DirectorySeparatorChar)));

            return LoadGPX(GPXFile, ref Mapillary, TimeOffset, OnDupliateTimestamp, OnResult);
        }
Exemplo n.º 2
0
        public static SharpMapillaryInfo LoadJPEG(this String                       JPEGFile,
                                                  DateTimeKind                      DateTimeType         = DateTimeKind.Utc,
                                                  Int32?                            TimeOffset           = null,
                                                  Func<String, DateTime, DateTime>  OnDupliateTimestamp  = null)
        {
            var Mapillary = new SharpMapillaryInfo(JPEGFile.Substring(0, JPEGFile.LastIndexOf(Path.DirectorySeparatorChar)));

            return LoadJPEG(JPEGFile, ref Mapillary, DateTimeType, TimeOffset, OnDupliateTimestamp);
        }
Exemplo n.º 3
0
        public static SharpMapillaryInfo LoadJPEG(String                            JPEGFile,
                                                  ref SharpMapillaryInfo            MapillaryInfo,
                                                  DateTimeKind                      DateTimeType         = DateTimeKind.Utc,
                                                  Int32?                            TimeOffset           = null,
                                                  Func<String, DateTime, DateTime>  OnDupliateTimestamp  = null)
        {
            #region Initial checks...

            if (JPEGFile == null)
                throw new ArgumentNullException("Illegal path!");

            if (MapillaryInfo == null)
                lock (SharpMapillaryLock)
                {
                    MapillaryInfo = new SharpMapillaryInfo(JPEGFile.Substring(0, JPEGFile.LastIndexOf(Path.DirectorySeparatorChar)));
                }

            #endregion

            #region Init...

            ImageFile               EXIFFile        = null;
            ImageEXIFInfo           MapillaryImage  = null;

            var                     Timestamp       = DateTime.MinValue;
            var                     Latitude        = 0.0;
            var                     Longitude       = 0.0;
            var                     Altitude        = 0.0;
            var                     Direction       = 0.0;

            MathEx.UFraction32[]    EXIF_Latitude;
            MathEx.UFraction32[]    EXIF_Longitude;
            DMSLatitudeType         EXIF_LatitudeO;
            DMSLongitudeType        EXIF_LongitudeO;

            #endregion

            DateTimeType = DateTimeKind.Local;

            try
            {

                EXIFFile                = ImageFile.FromFile(JPEGFile);

                if (EXIFFile.Properties.ContainsKey(ExifTag.DateTime))
                {

                    Timestamp = DateTime.SpecifyKind((DateTime) EXIFFile.Properties[ExifTag.DateTime].Value, DateTimeType).ToUniversalTime();

                    if (TimeOffset.HasValue)
                    {
                        if (TimeOffset.Value > 0)
                            Timestamp += TimeSpan.FromSeconds(TimeOffset.Value);
                        else
                            Timestamp -= TimeSpan.FromSeconds(-TimeOffset.Value);
                    }

                }

                //if (EXIFFile.Properties.ContainsKey(ExifTag.GPSLatitude))
                //{
                //    EXIF_Latitude       = (MathEx.UFraction32[]) EXIFFile.Properties[ExifTag.GPSLatitude].   Value;
                //    EXIF_LatitudeO      = (DMSLatitudeType)         EXIFFile.Properties[ExifTag.GPSLatitudeRef].Value; //ToDo: Wrong cast!
                //    Latitude            = SharpMapillary.ToLatitude(EXIF_Latitude[0].Numerator / (Double) EXIF_Latitude[0].Denominator,
                //                                                    EXIF_Latitude[1].Numerator / (Double) EXIF_Latitude[1].Denominator,
                //                                                    EXIF_Latitude[2].Numerator / (Double) EXIF_Latitude[2].Denominator,
                //                                                    EXIF_LatitudeO);
                //}

                //if (EXIFFile.Properties.ContainsKey(ExifTag.GPSLongitude))
                //{
                //    EXIF_Longitude      = (MathEx.UFraction32[]) EXIFFile.Properties[ExifTag.GPSLongitude].Value;
                //    EXIF_LongitudeO     = (DMSLongitudeType)        EXIFFile.Properties[ExifTag.GPSLongitudeRef].Value; //ToDo: Wrong cast!
                //    Longitude           = SharpMapillary.ToLongitude(EXIF_Longitude[0].Numerator / (Double) EXIF_Longitude[0].Denominator,
                //                                                     EXIF_Longitude[1].Numerator / (Double) EXIF_Longitude[1].Denominator,
                //                                                     EXIF_Longitude[2].Numerator / (Double) EXIF_Longitude[2].Denominator,
                //                                                     EXIF_LongitudeO);
                //}

                //if (EXIFFile.Properties.ContainsKey(ExifTag.GPSAltitude))
                //    Altitude            = (Double) EXIFFile.Properties[ExifTag.GPSAltitude].Value;

                //if (EXIFFile.Properties.ContainsKey(ExifTag.GPSImgDirection))
                //    Direction           = (Double) EXIFFile.Properties[ExifTag.GPSImgDirection].Value;

                lock (SharpMapillaryLock)
                {

                    MapillaryInfo.NumberOfImages++;

                    if (!MapillaryInfo.Images.TryGetValue(Timestamp, out MapillaryImage))
                        MapillaryInfo.Images.Add(Timestamp, new ImageEXIFInfo(JPEGFile,
                                                                              Timestamp,
                                                                              Latitude,
                                                                              Longitude,
                                                                              Altitude,
                                                                              Direction));

                    else
                    {

                        if (!MapillaryImage.Timestamp.HasValue)
                        {
                            MapillaryImage.FileName   = JPEGFile;
                            MapillaryImage.Timestamp  = Timestamp;
                            MapillaryImage.Latitude   = Latitude;
                            MapillaryImage.Longitude  = Longitude;
                            MapillaryImage.Altitude   = Altitude;
                        }

                        else
                        {

                            MapillaryInfo.NumberOfDuplicateEXIFTimestamps++;

                            if (OnDupliateTimestamp != null)
                            {

                                var FixedTimestamp = OnDupliateTimestamp(JPEGFile, Timestamp);

                                MapillaryInfo.Images.Add(FixedTimestamp, new ImageEXIFInfo(JPEGFile,
                                                                                           FixedTimestamp,
                                                                                           Latitude,
                                                                                           Longitude,
                                                                                           Altitude,
                                                                                           Direction));

                            }

                        }

                    }

                }

            }
            catch (Exception e)
            {

                Console.WriteLine("There is something wrong in file: " + JPEGFile + Environment.NewLine + e.Message);
                Console.WriteLine("Moving it to the 'errors'-directory!");
                Directory.CreateDirectory(MapillaryInfo.FilePath + Path.DirectorySeparatorChar + "errors");

                File.Move(JPEGFile, MapillaryInfo.FilePath + Path.DirectorySeparatorChar + "errors" + Path.DirectorySeparatorChar + JPEGFile.Remove(0, JPEGFile.LastIndexOf(Path.DirectorySeparatorChar) + 1));

            }

            return MapillaryInfo;
        }
Exemplo n.º 4
0
        public static SharpMapillaryInfo LoadJPEGs(String                                    Path,
                                                   SearchOption                              SearchOption         = SearchOption.TopDirectoryOnly,
                                                   SharpMapillaryInfo                        MapillaryInfo        = null,
                                                   DateTimeKind                              DateTimeType         = DateTimeKind.Utc,
                                                   Int32?                                    TimeOffset           = null,
                                                   Action<UInt32, UInt32, Double>            OnProcessed          = null,
                                                   Func<String, DateTime, DateTime>          OnDupliateTimestamp  = null,
                                                   ParallelOptions                           ParallelOptions      = null,
                                                   Action<DateTime, DateTime, DateTimeKind>  OnResult             = null,
                                                   Boolean                                   ParallelProcessing   = false)
        {
            #region Initial checks...

            if (Path == null)
                throw new ArgumentNullException("Illegal path!");

            #endregion

            #region Init...

            var AllJPegs                = Directory.EnumerateFiles(Path, "*.JPG", SearchOption).OrderBy(a => a).ToArray();
            var NumberOfJPEGsFound      = (UInt32) AllJPegs.Length;
            var NumberOfJPEGsProcessed  = 0;
            var OnProcessedLocal        = OnProcessed;

            var MinDateTime             = DateTime.MaxValue;
            var MaxDateTime             = DateTime.MinValue;

            #endregion

            if (ParallelProcessing)
            {

                Parallel.ForEach(AllJPegs,
                                 ParallelOptions != null ? ParallelOptions : new ParallelOptions() { MaxDegreeOfParallelism = 1 },
                                 JPegFile => {

                                     LoadJPEG(JPegFile, ref MapillaryInfo, DateTimeType, TimeOffset, OnDupliateTimestamp);
                                     Interlocked.Increment(ref NumberOfJPEGsProcessed);

                                     OnProcessedLocal = OnProcessed;
                                     if (OnProcessedLocal != null)
                                         OnProcessedLocal(NumberOfJPEGsFound, (UInt32) NumberOfJPEGsProcessed, (Double) NumberOfJPEGsProcessed / (Double) NumberOfJPEGsFound * 100);

                                 });

            }

            else
            {

                foreach (var JPegFile in AllJPegs)
                {

                    LoadJPEG(JPegFile, ref MapillaryInfo, DateTimeType, TimeOffset, OnDupliateTimestamp);
                    Interlocked.Increment(ref NumberOfJPEGsProcessed);

                    OnProcessedLocal = OnProcessed;
                    if (OnProcessedLocal != null)
                        OnProcessedLocal(NumberOfJPEGsFound, (UInt32) NumberOfJPEGsProcessed, (Double) NumberOfJPEGsProcessed / (Double) NumberOfJPEGsFound * 100);

                }

            }

            #region Process OnResult-delegate...

            if (OnResult != null)
            {

                foreach (var ImageInfo in MapillaryInfo.Images.Values)
                {

                    if (ImageInfo.Timestamp.Value < MinDateTime)
                        MinDateTime = ImageInfo.Timestamp.Value;

                    if (ImageInfo.Timestamp.Value > MaxDateTime)
                        MaxDateTime = ImageInfo.Timestamp.Value;

                }

                OnResult(MinDateTime, MaxDateTime, MinDateTime.Kind);

            }

            #endregion

            return MapillaryInfo;
        }
Exemplo n.º 5
0
        public static SharpMapillaryInfo SyncGPS(ref SharpMapillaryInfo  MapillaryInfo,
                                                 GPSInterpolation        GPSInterpolation  = GPSInterpolation.LinearInterpolation,
                                                 Double                  DirectionOffset   = 0.0)
        {
            #region Data

            //var Ratio               = Math.Round((Double) GPXLookup.Data.Count() / (Double) NumberOfImages, 2);

            GPSInfo GPSEarly;
            GPSInfo GPSLate;
            GPSInfo NextGPSTrackpoint;

            var GPSEarlyTimestamp           = DateTime.MinValue;
            var GPSLateTimestamp            = DateTime.MinValue;
            var EarlyDiff                   = 0.0;
            var LateDiff                    = 0.0;
            var GPSTimeRange                = 0.0;
            var GPSEarly2Image_TimeOffset   = 0.0;
            var dx                          = 0.0;
            var dy                          = 0.0;

            //var GPXArray = GPXLookup.Keys.ToArray();
            //var GPXList  = GPXLookup.Keys.ToList();

            #endregion

            foreach (var ImageInfo in MapillaryInfo.Images.Values)
            {

                // == is an image!
                if (ImageInfo.FileName != null &&
                    ImageInfo.Timestamp.HasValue)
                {

                    // ToDo: Currently a bit inefficient!
                    GPSEarly                  = MapillaryInfo.GPSData.Values.Where(v => ImageInfo.Timestamp >= v.Timestamp).LastOrDefault();
                    GPSLate                   = MapillaryInfo.GPSData.Values.Where(v => ImageInfo.Timestamp <= v.Timestamp).FirstOrDefault();
                    //var GPXIndex               = GPXList.BinarySearch(timestamp);
                    //LateTimestamp              = (GPXIndex >= 0) ? GPXArray[GPXIndex] : GPXArray[-GPXIndex - 1]; // Returns the next timestamp if no exact match was found!

                    if (GPSEarly == null)
                    {
                        Console.WriteLine("Could not find a GPSEarly timestamp!");
                        Environment.Exit(1);
                    }

                    if (GPSLate == null)
                    {
                        Console.WriteLine("Could not find a GPSLate timestamp!");
                        Environment.Exit(1);
                    }

                    // Ignore GPS-less images without GPS coordinates
                    // before and after the image was taken!
                    if (GPSEarly != null && GPSLate != null)
                    {

                        GPSEarlyTimestamp                   = GPSEarly != null ? GPSEarly.Timestamp.Value : DateTime.MinValue;
                        GPSLateTimestamp                    = GPSLate  != null ? GPSLate. Timestamp.Value : DateTime.MaxValue;

                        EarlyDiff                           = (ImageInfo.Timestamp.Value - GPSEarlyTimestamp)              .TotalSeconds;
                        LateDiff                            = (GPSLateTimestamp          - ImageInfo.Timestamp.Value).TotalSeconds;

                        ImageInfo.Image2GPS_TimeDifference  = (EarlyDiff < LateDiff) ? -EarlyDiff : LateDiff;

                        #region Update DiffHistogram

                        if (!MapillaryInfo.DiffHistogram.ContainsKey(ImageInfo.Image2GPS_TimeDifference))
                            MapillaryInfo.DiffHistogram.Add(ImageInfo.Image2GPS_TimeDifference, 1);

                        else
                            MapillaryInfo.DiffHistogram[ImageInfo.Image2GPS_TimeDifference]++;

                        #endregion

                        #region A image sharing its timestamp with a GPS coordinate!

                        if (ImageInfo.Image2GPS_TimeDifference == 0)
                        {

                            #region Set Lat/Lng/Alt

                            ImageInfo.Latitude    = GPSEarly.Latitude;
                            ImageInfo.Longitude   = GPSEarly.Longitude;
                            ImageInfo.Altitude    = GPSEarly.Altitude;

                            #endregion

                            #region Calculate image direction

                            // ToDo: Currently a bit inefficient!
                            NextGPSTrackpoint       = MapillaryInfo.GPSData.Values.Where(v => ImageInfo.Timestamp < v.Timestamp).FirstOrDefault();

                            if (NextGPSTrackpoint  != null)
                            {
                                dy                      = GPSEarly.Latitude - NextGPSTrackpoint.Latitude;
                                dx                      = Math.Cos(Math.PI / 180 * NextGPSTrackpoint.Latitude) * (NextGPSTrackpoint.Longitude - GPSEarly.Longitude);
                            }

                            #endregion

                        }

                        #endregion

                        else
                        {

                            #region GPSInterpolation

                            switch (GPSInterpolation)
                            {

                                case GPSInterpolation.NearestMatch:
                                    ImageInfo.Latitude         = (EarlyDiff < LateDiff) ? GPSEarly.Latitude  : GPSLate.Latitude;
                                    ImageInfo.Longitude        = (EarlyDiff < LateDiff) ? GPSEarly.Longitude : GPSLate.Longitude;
                                    ImageInfo.Altitude         = (EarlyDiff < LateDiff) ? GPSEarly.Altitude  : GPSLate.Altitude;
                                    break;

                                case GPSInterpolation.LinearInterpolation:
                                    GPSTimeRange               = (GPSLateTimestamp - GPSEarlyTimestamp).TotalSeconds;
                                    GPSEarly2Image_TimeOffset  = (ImageInfo.Timestamp.Value - GPSEarlyTimestamp).TotalSeconds;
                                    ImageInfo.Latitude         = GPSEarly.Latitude  - (GPSEarly.Latitude  - GPSLate.Latitude)  / GPSTimeRange * GPSEarly2Image_TimeOffset;
                                    ImageInfo.Longitude        = GPSEarly.Longitude - (GPSEarly.Longitude - GPSLate.Longitude) / GPSTimeRange * GPSEarly2Image_TimeOffset;
                                    ImageInfo.Altitude         = GPSEarly.Altitude  - (GPSEarly.Altitude  - GPSLate.Altitude)  / GPSTimeRange * GPSEarly2Image_TimeOffset;
                                    break;

                            }

                            #endregion

                            #region Calculate image direction

                            dy                         = GPSEarly.Latitude - GPSLate.Latitude;
                            dx                         = Math.Cos(Math.PI / 180 * GPSLate.Latitude) * (GPSLate.Longitude - GPSEarly.Longitude);

                            #endregion

                        }

                        #region Calculate image direction

                        ImageInfo.ViewingDirection = (90 + Math.Atan2(dy, dx) * 180 / Math.PI + DirectionOffset) % 360;

                        if (ImageInfo.ViewingDirection < 0)
                            ImageInfo.ViewingDirection += 360;

                        #endregion

                        ImageInfo.NoValidGPSFound = false;

                    }

                    else
                    {
                        ImageInfo.NoValidGPSFound = true;
                        MapillaryInfo.NumberOfImagesWithoutGPS++;
                    }

                }

            }

            #region Store DiffHistogram

            using (var DiffHistogramFile = new StreamWriter(MapillaryInfo.FilePath + Path.DirectorySeparatorChar + @"DiffHistogram.txt"))
            {
                foreach (var KVPair in MapillaryInfo.DiffHistogram)
                    DiffHistogramFile.WriteLine(String.Concat(KVPair.Key, ";", KVPair.Value));
            }

            #endregion

            return MapillaryInfo;
        }
Exemplo n.º 6
0
        public static SharpMapillaryInfo LoadGPX(String                                                    GPXFile,
                                                 ref SharpMapillaryInfo                                    MapillaryInfo,
                                                 Int32?                                                    TimeOffset           = null,
                                                 Func<String, DateTime, Double, Double, Double, DateTime>  OnDupliateTimestamp  = null,
                                                 Action<DateTime, DateTime, DateTimeKind>                  OnResult             = null)
        {
            #region GPX XML docu...

            // <?xml version="1.0" encoding="UTF-8"?>
            // <gpx version="1.1"
            //      creator="RunKeeper - http://www.runkeeper.com"
            //      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            //      xmlns="http://www.topografix.com/GPX/1/1"
            //      xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"
            //      xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1">
            //   <trk>
            //     <name><![CDATA[Cycling 7/22/14 1:29 pm]]></name>
            //     <time>2014-07-22T13:29:07Z</time>
            //     <trkseg>
            //       <trkpt lat="50.927267000" lon="11.612756000"><ele>236.0</ele><time>2014-07-22T13:29:07Z</time></trkpt>
            //       <trkpt lat="50.927267000" lon="11.612754000"><ele>236.0</ele><time>2014-07-22T13:29:09Z</time></trkpt>
            //       <trkpt lat="50.927367000" lon="11.612731000"><ele>236.0</ele><time>2014-07-22T13:29:33Z</time></trkpt>
            //       <trkpt lat="50.927454000" lon="11.612754000"><ele>235.0</ele><time>2014-07-22T13:29:35Z</time></trkpt>
            //       <trkpt lat="50.927563000" lon="11.612748000"><ele>234.2</ele><time>2014-07-22T13:29:37Z</time></trkpt>
            //     </trkseg>
            //   </trk>
            // </gpx>

            #endregion

            #region Initial checks...

            if (GPXFile == null)
                throw new ArgumentNullException("Illegal path!");

            if (MapillaryInfo == null)
                MapillaryInfo = new SharpMapillaryInfo(GPXFile.Substring(0, GPXFile.LastIndexOf(Path.DirectorySeparatorChar)));

            #endregion

            #region Init...

            XNamespace      NS              = "http://www.topografix.com/GPX/1/1";
            GPSInfo         GPSInfoElement  = null;

            DateTime        Timestamp;
            Double          Latitude;
            Double          Longitude;
            Double          Altitude;

            var MinDateTime = DateTime.MaxValue;
            var MaxDateTime = DateTime.MinValue;

            #endregion

            try
            {

                foreach (var GPSTrackPoint in XDocument.Load(GPXFile).
                                                             Root.
                                                             Elements(NS + "trk").
                                                             Elements(NS + "trkseg").
                                                             Elements(NS + "trkpt"))

                {

                    Timestamp  = DateTime.Parse(GPSTrackPoint.Element(NS + "time").Value).ToUniversalTime();

                    if (TimeOffset.HasValue)
                    {
                        if (TimeOffset.Value > 0)
                            Timestamp += TimeSpan.FromSeconds(TimeOffset.Value);
                        else
                            Timestamp -= TimeSpan.FromSeconds(-TimeOffset.Value);
                    }

                    if (Timestamp < MinDateTime)
                        MinDateTime = Timestamp;

                    if (Timestamp > MaxDateTime)
                        MaxDateTime = Timestamp;

                    Latitude   = Double.  Parse(GPSTrackPoint.Attribute("lat").Value,    CultureInfo.InvariantCulture);
                    Longitude  = Double.  Parse(GPSTrackPoint.Attribute("lon").Value,    CultureInfo.InvariantCulture);
                    Altitude   = Double.  Parse(GPSTrackPoint.Element(NS + "ele").Value, CultureInfo.InvariantCulture);

                    MapillaryInfo.NumberOfGPSPoints++;

                    if (!MapillaryInfo.GPSData.TryGetValue(Timestamp, out GPSInfoElement))
                        MapillaryInfo.GPSData.Add(Timestamp, new GPSInfo(Timestamp,
                                                                         Latitude,
                                                                         Longitude,
                                                                         Altitude));

                    else
                    {

                        MapillaryInfo.NumberOfDuplicateGPSTimestamps++;

                        if (OnDupliateTimestamp != null)
                        {

                            var FixedTimestamp = OnDupliateTimestamp(GPXFile, Timestamp, Latitude, Longitude, Altitude);

                            MapillaryInfo.GPSData.Add(FixedTimestamp, new GPSInfo(FixedTimestamp,
                                                                                  Latitude,
                                                                                  Longitude,
                                                                                  Altitude));

                        }

                    }

                }

            }
            catch (Exception e)
            {
                Console.WriteLine("There is something wrong in file: " + GPXFile + Environment.NewLine + e.Message);
            }

            if (OnResult != null)
                OnResult(MinDateTime, MaxDateTime, MinDateTime.Kind);

            return MapillaryInfo;
        }
Exemplo n.º 7
0
        public static SharpMapillaryInfo LoadGPXs(String                                                    Path,
                                                  SharpMapillaryInfo                                        MapillaryInfo        = null,
                                                  Int32?                                                    TimeOffset           = null,
                                                  Func<String, DateTime, Double, Double, Double, DateTime>  OnDupliateTimestamp  = null,
                                                  Action<DateTime, DateTime, DateTimeKind>                  OnResult             = null)
        {
            #region Initial checks...

            if (Path == null)
                throw new ArgumentNullException("Illegal path!");

            #endregion

            foreach (var GPXFile in Directory.EnumerateFiles(Path, "*.gpx"))
                LoadGPX(GPXFile, ref MapillaryInfo, TimeOffset, OnDupliateTimestamp, OnResult);

            return MapillaryInfo;
        }
Exemplo n.º 8
0
        /// <summary>
        /// Store the given SharpMapillaryInfo within the given subdirectory.
        /// </summary>
        /// <param name="MapillaryInfo">A SharpMapillaryInfo data structure.</param>
        /// <param name="SubDirectory">A subdirectory for storing the processed Mapillary images.</param>
        /// <param name="SubDirectoryNoGPS">A subdirectory for storing Mapillary images without valid GPS data.</param> </param>
        /// <param name="OnProcessed">An optional delegate to be invoked for every processed SharpMapillaryInfo.</param>
        /// <param name="ParallelOptions">Optional for controlling the multi-threading behaviour.</param>
        public static SharpMapillaryInfo Store(ref SharpMapillaryInfo          MapillaryInfo,
                                               String                          SubDirectory       = "fixed",
                                               String                          SubDirectoryNoGPS  = "noGPS",
                                               Action<UInt32, UInt32, Double>  OnProcessed        = null,
                                               ParallelOptions                 ParallelOptions    = null)
        {
            #region Data

            if (MapillaryInfo == null)
                throw new ArgumentNullException("MapillaryInfo", "The given parameter must not be null!");

            // ref/out parameters are not allowed inside Parallel.ForEach!
            var _MapillaryInfo              = MapillaryInfo;

            var EXIFFile                    = new ThreadLocal<ImageFile>();
            var LatitudeDMS                 = new ThreadLocal<DMS>();
            var LongitudeDMS                = new ThreadLocal<DMS>();
            var newImage                    = new ThreadLocal<Bitmap>();
            var oldImage                    = new ThreadLocal<Bitmap>();
            var g                           = new ThreadLocal<Graphics>();
            var NewFilePath                 = new ThreadLocal<String>();

            var NumberOfJPEGs               = (UInt32) MapillaryInfo.Images.Count;
            var NumberOfJPEGsProcessed      = 0;
            var NumberOfImagesWithoutGPS    = 0;
            var OnProcessedLocal            = OnProcessed;

            #endregion

            Directory.CreateDirectory(MapillaryInfo.FilePath + Path.DirectorySeparatorChar + SubDirectory);

            Parallel.ForEach(MapillaryInfo.Images.Values,
                             ParallelOptions != null ? ParallelOptions : new ParallelOptions() { MaxDegreeOfParallelism = 8 },
                             ImageInfo => {

                // == is an image!
                if (ImageInfo.FileName != null &&
                    ImageInfo.Timestamp.HasValue)
                {

                    #region Valid GPS data...

                    if (ImageInfo.NoValidGPSFound.HasValue &&
                        ImageInfo.NoValidGPSFound.Value == false &&
                        ImageInfo.Latitude. HasValue &&
                        ImageInfo.Longitude.HasValue &&
                        ImageInfo.Altitude. HasValue)
                    {

                        #region Load image and update EXIF metadata...

                        var MS = new MemoryStream();

                        try
                        {

                            EXIFFile.Value = ImageFile.FromFile(ImageInfo.FileName);

                            #region Set/update EXIF data...

                            LatitudeDMS. Value  = ImageInfo.Latitude. Value.ToDMSLat();
                            LongitudeDMS.Value  = ImageInfo.Longitude.Value.ToDMSLng();

                            //if (!EXIFFile.Value.Properties.ContainsKey(ExifTag.GPSLatitude))
                                EXIFFile.Value.Properties.Set(ExifTag.GPSLatitude, LatitudeDMS.Value.Degree, LatitudeDMS.Value.Minute, LatitudeDMS.Value.Second);

                            if (!EXIFFile.Value.Properties.ContainsKey(ExifTag.GPSLatitudeRef))
                                EXIFFile.Value.Properties.Set(ExifTag.GPSLatitudeRef, ImageInfo.Latitude > 0 ? GPSLatitudeRef.North : GPSLatitudeRef.South);

                            //if (!EXIFFile.Value.Properties.ContainsKey(ExifTag.GPSLongitude))
                                EXIFFile.Value.Properties.Set(ExifTag.GPSLongitude, LongitudeDMS.Value.Degree, LongitudeDMS.Value.Minute, LongitudeDMS.Value.Second);

                            if (!EXIFFile.Value.Properties.ContainsKey(ExifTag.GPSLongitudeRef))
                                EXIFFile.Value.Properties.Set(ExifTag.GPSLongitudeRef, ImageInfo.Longitude > 0 ? GPSLongitudeRef.East : GPSLongitudeRef.West);

                            //if (!EXIFFile.Value.Properties.ContainsKey(ExifTag.GPSAltitude))
                                EXIFFile.Value.Properties.Set(ExifTag.GPSAltitude, ImageInfo.Altitude.Value);

                            if (!EXIFFile.Value.Properties.ContainsKey(ExifTag.GPSAltitudeRef))
                                EXIFFile.Value.Properties.Set(ExifTag.GPSAltitudeRef, GPSAltitudeRef.AboveSeaLevel);

                            //if (!EXIFFile.Value.Properties.ContainsKey(ExifTag.GPSImgDirection))
                                EXIFFile.Value.Properties.Add(new ExifURational(ExifTag.GPSImgDirection, new MathEx.UFraction32(ImageInfo.ViewingDirection)));

                            if (!EXIFFile.Value.Properties.ContainsKey(ExifTag.Artist))
                                EXIFFile.Value.Properties.Set(ExifTag.Artist, "Achim 'ahzf' Friedland <*****@*****.**>");

                            //// Used by GoPro
                            //if (!EXIFFile.Properties.ContainsKey(ExifTag.ImageDescription))
                            //    EXIFFile.Properties.Set(ExifTag.ImageDescription, "ImageDescription");

                            //// Used by GoPro
                            //if (!EXIFFile.Properties.ContainsKey(ExifTag.Software))
                            //    EXIFFile.Properties.Set(ExifTag.Software, "Software");

                            if (!EXIFFile.Value.Properties.ContainsKey(ExifTag.UserComment))
                                EXIFFile.Value.Properties.Set(ExifTag.UserComment, "Mapillary");

                            if (!EXIFFile.Value.Properties.ContainsKey(ExifTag.WindowsAuthor))
                                EXIFFile.Value.Properties.Set(ExifTag.WindowsAuthor, "Achim 'ahzf' Friedland <*****@*****.**>");

                            //if (!EXIFFile.Properties.ContainsKey(ExifTag.WindowsComment))
                            //    EXIFFile.Properties.Set(ExifTag.WindowsComment, "WindowsComment");

                            if (!EXIFFile.Value.Properties.ContainsKey(ExifTag.WindowsKeywords))
                                EXIFFile.Value.Properties.Set(ExifTag.WindowsKeywords, "Mapillary; Deutschland; Thüringen; Jena; GPSLinearInterpolation");

                            if (!EXIFFile.Value.Properties.ContainsKey(ExifTag.WindowsSubject))
                                EXIFFile.Value.Properties.Set(ExifTag.WindowsSubject, "Mapillary");

                            //if (!EXIFFile.Properties.ContainsKey(ExifTag.WindowsTitle))
                            //    EXIFFile.Properties.Set(ExifTag.WindowsTitle, "WindowsTitle");

                            if (!EXIFFile.Value.Properties.ContainsKey(ExifTag.Copyright))
                                EXIFFile.Value.Properties.Set(ExifTag.Copyright, "Creative Commons Attribution-NonCommercial 4.0 International License (CC BY-NC)");

                            //if (!EXIFFile.Properties.ContainsKey(ExifTag.ImageUniqueID))
                            //    EXIFFile.Properties.Set(ExifTag.ImageUniqueID, "4711");

                            #endregion

                            EXIFFile.Value.Save(MS);

                        }

                        catch (Exception e)
                        {
                            Console.WriteLine("Exception during 'Load image and update EXIF metadata': " + e.Message);
                        }

                        #endregion

                        #region Resize and store the image...

                        try
                        {

                            #region Load Image from memory stream and resize it...

                            oldImage.Value = new Bitmap(MS);
                            newImage.Value = new Bitmap((Int32)_MapillaryInfo.FinalImageWidth,
                                                        (Int32)_MapillaryInfo.FinalImageHeight);

                            g.Value = Graphics.FromImage((Image)newImage.Value);
                            g.Value.InterpolationMode = InterpolationMode.HighQualityBicubic;
                            g.Value.CompositingQuality = CompositingQuality.HighQuality;
                            g.Value.CompositingMode = CompositingMode.SourceCopy;
                            g.Value.DrawImage(oldImage.Value,
                                              0, 0,
                                              (Int32)_MapillaryInfo.FinalImageWidth,
                                              (Int32)_MapillaryInfo.FinalImageHeight);

                            g.Value.Dispose();

                            #endregion

                            #region Copy EXIF metadata

                            // Copy all metadata...
                            foreach (var PropertyItem in oldImage.Value.PropertyItems)
                                newImage.Value.SetPropertyItem(PropertyItem);

                            //// Get an ImageCodecInfo object that represents the JPEG codec.
                            //var imageCodecInfo = GetEncoderInfo(ImageFormat.Jpeg);

                            //// Create an Encoder object for the Quality parameter.
                            //var encoder        = Encoder.Quality;

                            //// Create an EncoderParameters object.
                            //EncoderParameters encoderParameters = new EncoderParameters(1);

                            //// Save the image as a JPEG file with quality level.
                            //EncoderParameter encoderParameter = new EncoderParameter(encoder, quality);
                            //encoderParameters.Param[0] = encoderParameter;
                            //newImage.Save(filePath, imageCodecInfo, encoderParameters);

                            #endregion

                            #region Store resized image on disc...

                            // Generate new file name including a possible subdirectory...
                            NewFilePath.Value = String.Concat(_MapillaryInfo.FilePath,
                                                              Path.DirectorySeparatorChar,
                                                              SubDirectory,
                                                              ImageInfo.FileName.Replace(_MapillaryInfo.FilePath, ""));

                            // Check if the possible subdirectory already exists... or create it!
                            Directory.CreateDirectory(NewFilePath.Value.Substring(0, NewFilePath.Value.LastIndexOf(Path.DirectorySeparatorChar)));

                            newImage.Value.Save(NewFilePath.Value,
                                                ImageFormat.Jpeg);

                            newImage.Value.Dispose();
                            MS.Dispose();

                            #endregion

                        }

                        catch (Exception e)
                        {
                            Console.WriteLine("Exception during 'Resize and store the image': " + e.Message);
                        }

                        #endregion

                        Interlocked.Increment(ref NumberOfJPEGsProcessed);

                        OnProcessedLocal = OnProcessed;
                        if (OnProcessedLocal != null)
                            OnProcessedLocal(NumberOfJPEGs, (UInt32) NumberOfJPEGsProcessed, (Double) NumberOfJPEGsProcessed / (Double) NumberOfJPEGs * 100);

                    }

                    #endregion

                    #region ...no valid GPS data!

                    else
                    {

                        if (ImageInfo.FileName != null)
                        {

                            Directory.CreateDirectory(_MapillaryInfo.FilePath + Path.DirectorySeparatorChar + SubDirectoryNoGPS);

                            File.Copy(ImageInfo.FileName,
                                      _MapillaryInfo.FilePath +
                                      Path.DirectorySeparatorChar + SubDirectoryNoGPS +
                                      Path.DirectorySeparatorChar + ImageInfo.FileName.Remove(0, ImageInfo.FileName.LastIndexOf(Path.DirectorySeparatorChar) + 1));

                            Interlocked.Increment(ref NumberOfImagesWithoutGPS);

                        }

                    }

                    #endregion

                }

            });

            _MapillaryInfo.NumberOfImagesWithoutGPS = (UInt32) NumberOfImagesWithoutGPS;

            return _MapillaryInfo;
        }