public void ApplyFilters(ImageProcessingSettings imageProcessingSettings, Bitmap bitmap) { var filters = this.GetFilterList(imageProcessingSettings); foreach (IInPlaceFilter filter in filters) { lock (bitmap) { filter.ApplyInPlace(bitmap); } } }
public IList <IFilter> GetFilterList(ImageProcessingSettings imageProcessingSettings) { var filterList = new List <IFilter>(); // Brightness if (imageProcessingSettings.BrightnessAdjustment.Value != 0) { BrightnessCorrection filter = new BrightnessCorrection(imageProcessingSettings.BrightnessAdjustment.Value); filterList.Add(filter); } // Contrast if (imageProcessingSettings.ContrastAdjustment.Value != 0) { ContrastCorrection filter = new ContrastCorrection(imageProcessingSettings.ContrastAdjustment.Value); filterList.Add(filter); } // Saturation if (imageProcessingSettings.SaturationAdjustment.Value != 0) { // Only use 60% of the available correction range float adjustedValue = ((float)imageProcessingSettings.SaturationAdjustment.Value / 100) * 60; float value = (float)adjustedValue / 100; SaturationCorrection filter = new SaturationCorrection(value); filterList.Add(filter); } // Sharpness if (imageProcessingSettings.SharpnessAdjustment.Value != 0) { double sigma = 1.5; GaussianSharpen filter = new GaussianSharpen(sigma, imageProcessingSettings.SharpnessAdjustment.Value); filterList.Add(filter); } // Red, Green, Blue if (imageProcessingSettings.RedAdjustment.Value != 0 || imageProcessingSettings.GreenAdjustment.Value != 0 || imageProcessingSettings.BlueAdjustment.Value != 0) { LevelsLinear filter = new LevelsLinear(); if (imageProcessingSettings.RedAdjustment.Value != 0) { float val = ((float)imageProcessingSettings.RedAdjustment.Value / 100) * 255; if (imageProcessingSettings.RedAdjustment.Value > 0) { var finalVal = 255 - (int)val; filter.InRed = new IntRange(0, finalVal); } else { val = val * -1; filter.InRed = new IntRange((int)val, 255); } } if (imageProcessingSettings.GreenAdjustment.Value != 0) { float val = ((float)imageProcessingSettings.GreenAdjustment.Value / 100) * 255; if (imageProcessingSettings.GreenAdjustment.Value > 0) { var finalVal = 255 - (int)val; filter.InGreen = new IntRange(0, finalVal); } else { val = val * -1; filter.InGreen = new IntRange((int)val, 255); } } if (imageProcessingSettings.BlueAdjustment.Value != 0) { float val = ((float)imageProcessingSettings.BlueAdjustment.Value / 100) * 255; if (imageProcessingSettings.BlueAdjustment.Value > 0) { var finalVal = 255 - (int)val; filter.InBlue = new IntRange(0, finalVal); } else { val = val * -1; filter.InBlue = new IntRange((int)val, 255); } } filterList.Add(filter); } return(filterList); }
public async Task StitchImageTilesAsync(string tileDownloadDirectory, string stitchedTilesDirectory, bool deleteOriginals, IProgress <TileStitcherProgress> progress) { await Task.Run(() => { var tileStitcherProgress = new TileStitcherProgress(); this.imageProcessingFilters = new ImageProcessingFilters(); this.xmlSerializer = new XmlSerializer(typeof(ImageTile)); this.stitchedImageXmlSerializer = new XmlSerializer(typeof(StitchedImage)); int startTileX; int startTileY; int endTileX; int endTileY; // Get the top left tile X & Y and the bottom right tile X & Y // We can work everything else out from this this.GetStartingAndEndTileXY(tileDownloadDirectory, out startTileX, out startTileY, out endTileX, out endTileY); //Debug.WriteLine("startTileX " + startTileX); //Debug.WriteLine("startTileY " + startTileY); //Debug.WriteLine("endTileX " + endTileX); //Debug.WriteLine("endTileY " + endTileY); int numberOfTilesX = (endTileX - startTileX) + 1; int numberOfTilesY = (endTileY - startTileY) + 1; //Debug.WriteLine("Tiles X " + numberOfTilesX); //Debug.WriteLine("Tiles Y " + numberOfTilesY); //Debug.WriteLine("Filename prefix " + filenamePrefix); var firstImageTile = this.LoadImageTile(tileDownloadDirectory, startTileX, startTileY); // Get some info from the first tile. We can assume all tiles have these // properties or something is very wrong var imageSource = firstImageTile.Source; var zoomLevel = firstImageTile.ZoomLevel; var tileWidth = firstImageTile.Width; var tileHeight = firstImageTile.Height; int maxTilesPerStitchedImageX = AeroSceneryManager.Instance.Settings.MaximumStitchedImageSize.Value; int maxTilesPerStitchedImageY = AeroSceneryManager.Instance.Settings.MaximumStitchedImageSize.Value; // Calculate the size of our stitched images in each direction var imageSizeX = maxTilesPerStitchedImageX * tileWidth; var imageSizeY = maxTilesPerStitchedImageY * tileHeight; // Calculate how many images we need var requiredStitchedImagesX = (int)Math.Ceiling((float)numberOfTilesX / (float)maxTilesPerStitchedImageX); var requiredStitchedImagesY = (int)Math.Ceiling((float)numberOfTilesY / (float)maxTilesPerStitchedImageY); var requiredStichedImages = requiredStitchedImagesX * requiredStitchedImagesY; int imageTileOffsetX = 0; int imagetileOffsetY = 0; tileStitcherProgress.TotalStitchedImages = requiredStichedImages; // Loop through each stitched image that we will need for (int stitchedImagesYIx = 0; stitchedImagesYIx < requiredStitchedImagesY; stitchedImagesYIx++) { for (int stitchedImagesXIx = 0; stitchedImagesXIx < requiredStitchedImagesX; stitchedImagesXIx++) { imageTileOffsetX = stitchedImagesXIx * maxTilesPerStitchedImageX; imagetileOffsetY = stitchedImagesYIx * maxTilesPerStitchedImageY; // This might not be right, but it's a reasonable estimate. We wont know until we read each file tileStitcherProgress.TotalImageTilesForCurrentStitchedImage = maxTilesPerStitchedImageX * maxTilesPerStitchedImageY; int columnsUsed = 0; int rowsUsed = 0; // By giving these incorrect values, we can be sure they will we overwritten without having // to make them nullable and do null checks double northLatitude = -500; double westLongitude = 500; double southLatitude = 500; double eastLongitude = -500; using (Bitmap bitmap = new System.Drawing.Bitmap(imageSizeX, imageSizeY)) { bitmap.MakeTransparent(); using (Graphics g = Graphics.FromImage(bitmap)) { tileStitcherProgress.CurrentTilesRenderedForCurrentStitchedImage = 0; // Work left to right, top to bottom // Loop through rows for (int yIx = 0; yIx < maxTilesPerStitchedImageY; yIx++) { bool rowHasImages = false; // Loop through columns for (int xIx = 0; xIx < maxTilesPerStitchedImageX; xIx++) { int currentTileX = xIx + imageTileOffsetX + startTileX; int currentTileY = yIx + imagetileOffsetY + startTileY; var imageTileData = this.LoadImageTile(tileDownloadDirectory, currentTileX, currentTileY); if (imageTileData != null) { // Even if all the images in this row are invalid, the aero files are present // so an attempt was made to download something rowHasImages = true; // Update our overall stitched image lat and long maxima and minima // We want the highest NorthLatitude value of any image tile for this stitched image if (imageTileData.NorthLatitude > northLatitude) { northLatitude = imageTileData.NorthLatitude; } // We want the lowest SouthLatitude value of any image tile for this stitched image if (imageTileData.SouthLatitude < southLatitude) { southLatitude = imageTileData.SouthLatitude; } // We want the lowest WestLongitude value of any image tile for this stitched image if (imageTileData.WestLongitude < westLongitude) { westLongitude = imageTileData.WestLongitude; } // We want the highest EastLongitude value of any image tile for this stitched image if (imageTileData.EastLongitude > eastLongitude) { eastLongitude = imageTileData.EastLongitude; } var imageTileFilename = tileDownloadDirectory + imageTileData.FileName + "." + imageTileData.ImageExtension; Image tile = null; try { tile = Image.FromFile(imageTileFilename); if (tile != null) { var imagePointX = (xIx * imageTileData.Width); var imagePointY = (yIx * imageTileData.Width); g.DrawImage(tile, new PointF(imagePointX, imagePointY)); tileStitcherProgress.CurrentTilesRenderedForCurrentStitchedImage++; progress.Report(tileStitcherProgress); } tile.Dispose(); } catch (Exception) { // The image file was probably invalid, but there's not a lot we can do // Leave it transparent } finally { // Even if the image was invalid, we still had an aero file for it // so it counts as a used column var colsUsedInThisRow = xIx + 1; if (columnsUsed < colsUsedInThisRow) { columnsUsed = colsUsedInThisRow; } if (tile != null) { tile.Dispose(); } } } } if (rowHasImages) { rowsUsed++; } } } var stitchFilename = String.Format("{0}_{1}_stitch_{2}_{3}.png", imageSource, zoomLevel, stitchedImagesXIx + 1, stitchedImagesYIx + 1); var stitchedImage = new StitchedImage(); stitchedImage.ImageExtension = "png"; stitchedImage.NorthLatitude = northLatitude; stitchedImage.WestLongitude = westLongitude; stitchedImage.SouthLatitude = southLatitude; stitchedImage.EastLongitude = eastLongitude; stitchedImage.Width = columnsUsed * tileWidth; stitchedImage.Height = rowsUsed * tileHeight; stitchedImage.Source = imageSource; stitchedImage.ZoomLevel = zoomLevel; stitchedImage.StichedImageSetX = stitchedImagesXIx + 1; stitchedImage.StichedImageSetY = stitchedImagesYIx + 1; //Debug.WriteLine("Rows Used " + rowsUsed); //Debug.WriteLine("Columns Used " + columnsUsed); var settings = AeroSceneryManager.Instance.Settings; if (settings.EnableImageProcessing.Value) { var imageProcessingSettings = new ImageProcessingSettings(); imageProcessingSettings.BrightnessAdjustment = settings.BrightnessAdjustment; imageProcessingSettings.ContrastAdjustment = settings.ContrastAdjustment; imageProcessingSettings.SaturationAdjustment = settings.SaturationAdjustment; imageProcessingSettings.SharpnessAdjustment = settings.SharpnessAdjustment; imageProcessingSettings.RedAdjustment = settings.RedAdjustment; imageProcessingSettings.GreenAdjustment = settings.GreenAdjustment; imageProcessingSettings.BlueAdjustment = settings.BlueAdjustment; this.imageProcessingFilters.ApplyFilters(imageProcessingSettings, bitmap); } // Have we drawn an image to the maximum number of rows and columns for this image? if (columnsUsed == maxTilesPerStitchedImageX && rowsUsed == maxTilesPerStitchedImageY) { // Save the bitmap as it is log.InfoFormat("Saving stitched image {0}", stitchFilename); bitmap.Save(stitchedTilesDirectory + stitchFilename, ImageFormat.Png); tileStitcherProgress.CurrentStitchedImage++; } else { // Resize the bitmap down to the used number of rows and columns log.InfoFormat("Cropping stitched image {0}", stitchFilename); CropBitmap(bitmap, new Rectangle(0, 0, columnsUsed *tileWidth, rowsUsed *tileHeight), stitchedTilesDirectory, stitchFilename); tileStitcherProgress.CurrentStitchedImage++; } this.SaveStitchedImageAeroFile(this.stitchedImageXmlSerializer, stitchedImage, stitchedTilesDirectory); } } } if (deleteOriginals) { } }); }