private static void ResizeImage(int width, int height, int quality, string options, IMagickImage magickImage) { magickImage.Quality = quality; magickImage.Strip(); if (options.Contains("g")) //grayscale { magickImage.Grayscale(PixelIntensityMethod.Average); } if (width == magickImage.BaseWidth && height == magickImage.BaseHeight) { return; } else if (options.Contains("f") || options.Contains("t")) { magickImage.Resize(width, height); } else { var magickGeometry = new MagickGeometry(width, height) { IgnoreAspectRatio = false, //保持长宽比 FillArea = true }; magickImage.Resize(magickGeometry); magickImage.Crop(magickGeometry, Gravity.Center); } }
/// <summary> /// Generate compress result from base64 image. /// </summary> /// <param name="base64Image"></param> /// <returns></returns> public CompressedImageModel CompressFromBase64(string base64Image) { MagickFormat format = MagickFormat.Jpeg; IMagickImage image = MagickImage.FromBase64(base64Image); MemoryStream compressedBuffer = new MemoryStream(); MemoryStream thumbnailBuffer = new MemoryStream(); image.Write(compressedBuffer, format); image.Resize(ImageService.ThumbnailAspectSize, ImageService.ThumbnailAspectSize); image.Write(thumbnailBuffer, format); compressedBuffer.Seek(0, SeekOrigin.Begin); thumbnailBuffer.Seek(0, SeekOrigin.Begin); this.ImageOptimizer.LosslessCompress(compressedBuffer); this.ImageOptimizer.LosslessCompress(thumbnailBuffer); return(new CompressedImageModel( compressedBuffer.GetBuffer(), thumbnailBuffer.GetBuffer(), ImageService.ImageFormat )); }
private static void ResizeImage(int requestWidth, int requestHeight, int quality, string options, IMagickImage image) { image.Quality = quality; image.Strip(); if (options.Contains("g")) //grayscale { image.Grayscale(PixelIntensityMethod.Average); } if (image.BaseWidth == requestWidth && image.BaseHeight == requestHeight) //requested image is same size { return; } if (requestWidth == 0 && requestHeight == 0) //requested image is same size { return; } if (options.Contains("f") || options.Contains("t")) //scale with aspect of image { var size = new MagickGeometry(requestWidth, requestHeight); image.Resize(size); } else if (requestWidth == 0 || requestHeight == 0) //scale with aspect of image { var size = new MagickGeometry(requestWidth, requestHeight); image.Resize(size); } else // This will resize the image to a fixed size without maintaining the aspect ratio. { var size = new MagickGeometry(requestWidth, requestHeight) { IgnoreAspectRatio = false, //keep aspect ratio! FillArea = true }; image.Resize(size); image.Crop(size, Gravity.Center); } }
public override void ShowPreview(Renderer renderer) { if (NeedleImage != null) { var pixelMagnification = 8.0; var magnification = Math.Min(1 / (double)pixelMagnification, 1.0); var resizeAmount = new Percentage(magnification * 100); IMagickImage newHaystack = null; var haystack = this.state.Image(this.haystack); lock (haystack) { newHaystack = haystack.Clone(); newHaystack.Resize(resizeAmount); }; var scaleFactor = newHaystack.Width / haystack.Width; haystack = newHaystack; if (this.initialCandidates != null) { foreach (var candidate in this.initialCandidates) { var rect = new Drawables() .StrokeWidth(1) .StrokeColor(new MagickColor("blue")) .FillOpacity(new Percentage(0)) .Rectangle(candidate.X * scaleFactor, candidate.Y * scaleFactor, (candidate.X + NeedleSize) * scaleFactor, (candidate.Y + NeedleSize) * scaleFactor); haystack.Draw(rect); } } if (this.searchResult != null && this.searchResult.Distance < SearchResult.MAX_DISTANCE) { var joinPoint = this.searchResult.HaystackPoint; var x = joinPoint.X; var y = joinPoint.Y; var rect = new Drawables() .StrokeWidth(1) .StrokeColor(searchResult.MeetsThreshold() ? new MagickColor("green") : new MagickColor("yellow")) .FillOpacity(new Percentage(0)) .Rectangle(x * scaleFactor, y * scaleFactor, (x + NeedleSize) * scaleFactor, (y + NeedleSize) * scaleFactor); haystack.Draw(rect); } var needle = NeedleImage.Clone(); needle.Resize(resizeAmount); renderer.DisplayImages(haystack, needle); } }
public Stream Compare() { IMagickImage modifiedHigh = ReadPDF(_modifiedImagePath, _magickSettingsHigh); IMagickImage original = ReadPDF(_originalImagePath, _magickSettings); IMagickImage modified = ReadPDF(_modifiedImagePath, _magickSettings); IMagickImage mask = GenerateMask(original, modified); mask.Resize(new Percentage((double)Density / (double)ReducedDensity * 100d)); modifiedHigh.Composite(mask, Gravity.Center, CompositeOperator.Over); MemoryStream ms = new MemoryStream(); modifiedHigh.Write(ms); ms.Position = 0; return(ms); }
void DoMagic(ImageEditMode mode, IMagickImage image, int originalWidth, int originalHeight) { switch (mode) { case ImageEditMode.Swirl: image.Swirl(360); break; case ImageEditMode.Rescale: image.LiquidRescale(image.Width / 2, image.Height / 2); image.LiquidRescale((image.Width * 3) / 2, (image.Height * 3) / 2); image.Resize(originalWidth, originalHeight); break; case ImageEditMode.Wave: image.BackgroundColor = MagickColor.FromRgb(0, 0, 0); image.Wave(image.Interpolate, 10.0, 150.0); break; case ImageEditMode.Implode: image.Implode(0.5d, PixelInterpolateMethod.Average); break; case ImageEditMode.JPEG: image.Quality = 10; break; case ImageEditMode.MoreJPEG: image.Quality = 5; break; case ImageEditMode.MostJPEG: image.Quality = 1; break; default: break; } }
private void Save(IMagickImage image, string device, string uploadPath, string identifier, MagickFormat format, int?size = null) { image.Format = format; if (size != null) { image.Resize(new MagickGeometry() { Width = size.Value, IgnoreAspectRatio = false }); } string path = Path.Combine(uploadPath, String.Format("{0}-{1}.{2}", identifier, device, format.ToString())); image.Write(path); if (format == MagickFormat.Jpg) { Compress(path); } }
private void DrawTreeCluster(MagickImage overlay, DrawableFixture fixture) { //MainForm.Log(string.Format("Image: {0} ({1}) ...", fixture.Name, fixture.TreeCluster.Tree), MainForm.LogLevel.notice); string fileName = System.IO.Path.GetFileNameWithoutExtension(fixture.TreeCluster.Tree); string defaultTree = "elm1"; // Load model image if (!m_modelImages.ContainsKey(fileName)) { string treeImageFile = string.Format("{0}\\data\\prerendered\\trees\\{1}.png", System.Windows.Forms.Application.StartupPath, fileName); if (System.IO.File.Exists(treeImageFile)) { MagickImage modelImage = new MagickImage(treeImageFile); modelImage.Blur(); m_modelImages.Add(fileName, modelImage); } else { MainForm.Log(string.Format("Can not find image for tree {0} ({1}), using default tree", fixture.TreeCluster.Tree, fixture.NifName), MainForm.LogLevel.warning); m_modelImages.Add(fileName, m_modelImages[defaultTree]); } } if (m_modelImages.ContainsKey(fileName) && m_modelImages[fileName] != null) { // Get the width of the orginal tree shape NifRow tree = FixturesLoader.NifRows.Where(n => n.Filename.ToLower() == fixture.TreeCluster.Tree.ToLower()).FirstOrDefault(); if (tree == null) { return; } System.Drawing.SizeF treeSize = tree.GetSize(0, 0); int dimensions = ((fixture.CanvasWidth > fixture.CanvasHeight) ? fixture.CanvasWidth : fixture.CanvasHeight) + 10; int extendedWidth = dimensions - fixture.CanvasWidth; int extendedHeight = dimensions - fixture.CanvasHeight; using (MagickImage treeCluster = new MagickImage(MagickColors.Transparent, dimensions, dimensions)) { double centerX = treeCluster.Width / 2d; double centerY = treeCluster.Height / 2d; foreach (SharpDX.Vector3 treeInstance in fixture.TreeCluster.TreeInstances) { using (IMagickImage treeImage = m_modelImages[fileName].Clone()) { double scaleWidthToTreeImage = treeSize.Width / treeImage.Width; double scaleHeightToTreeImage = treeSize.Height / treeImage.Height; int width = Convert.ToInt32(treeImage.Width * scaleWidthToTreeImage * fixture.Scale); int height = Convert.ToInt32(treeImage.Height * scaleHeightToTreeImage * fixture.Scale); treeImage.Resize(width, height); int x = Convert.ToInt32(centerX - width / 2d - zoneConfiguration.ZoneCoordinateToMapCoordinate(treeInstance.X) * (fixture.FixtureRow.Scale / 100)); int y = Convert.ToInt32(centerY - height / 2d - zoneConfiguration.ZoneCoordinateToMapCoordinate(treeInstance.Y) * (fixture.FixtureRow.Scale / 100)); treeCluster.Composite(treeImage, x, y, CompositeOperator.SrcOver); } } treeCluster.Rotate((360d * fixture.FixtureRow.AxisZ3D - fixture.FixtureRow.A) * -1); using (MagickImage modelCanvas = new MagickImage(MagickColors.Transparent, fixture.CanvasWidth, fixture.CanvasHeight)) { foreach (DrawableElement drawableElement in fixture.DrawableElements) { modelCanvas.Settings.FillColor = new MagickColor( Convert.ToUInt16(128 * 256 * drawableElement.lightning), Convert.ToUInt16(128 * 256 * drawableElement.lightning), Convert.ToUInt16(128 * 256 * drawableElement.lightning) ); DrawablePolygon polyDraw = new DrawablePolygon(drawableElement.coordinates); modelCanvas.Draw(polyDraw); } modelCanvas.Composite(treeCluster, Gravity.Center, CompositeOperator.DstIn); treeCluster.Composite(modelCanvas, Gravity.Center, CompositeOperator.Overlay); //treeCluster.Composite(modelCanvas, Gravity.Center, CompositeOperator.SrcOver); } if (fixture.RendererConf.HasShadow) { CastShadow( treeCluster, fixture.RendererConf.ShadowOffsetX, fixture.RendererConf.ShadowOffsetY, fixture.RendererConf.ShadowSize, new Percentage(100 - fixture.RendererConf.ShadowTransparency), fixture.RendererConf.ShadowColor, false ); } if (fixture.RendererConf.Transparency != 0) { treeCluster.Alpha(AlphaOption.Set); double divideValue = 100.0 / (100.0 - fixture.RendererConf.Transparency); treeCluster.Evaluate(Channels.Alpha, EvaluateOperator.Divide, divideValue); } overlay.Composite(treeCluster, Convert.ToInt32(fixture.CanvasX - extendedWidth / 2), Convert.ToInt32(fixture.CanvasY - extendedHeight / 2), CompositeOperator.SrcOver); } } }
private void DrawImage(MagickImage overlay, DrawableFixture fixture) { //MainForm.Log(string.Format("Image: {0} ({1}) ...", fixture.Name, fixture.NifName), MainForm.LogLevel.notice); string fileName = System.IO.Path.GetFileNameWithoutExtension(fixture.NifName); string defaultTree = "elm1"; // Load default tree if (!m_modelImages.ContainsKey(defaultTree)) { string defaultTreeImage = string.Format("{0}\\data\\prerendered\\trees\\{1}.png", System.Windows.Forms.Application.StartupPath, defaultTree); if (System.IO.File.Exists(defaultTreeImage)) { MagickImage treeImage = new MagickImage(defaultTreeImage); treeImage.Blur(); m_modelImages.Add(defaultTree, treeImage); } else { m_modelImages.Add(fileName, null); } } // TreeClusters are sets of trees in a specified arrangement // They need to be drawe separately if (fixture.IsTreeCluster) { DrawTreeCluster(overlay, fixture); return; } // Load model image if (!m_modelImages.ContainsKey(fileName)) { string objectImageFile = string.Format("{0}\\data\\prerendered\\objects\\{1}.png", System.Windows.Forms.Application.StartupPath, fileName); if (fixture.IsTree) { objectImageFile = string.Format("{0}\\data\\prerendered\\trees\\{1}.png", System.Windows.Forms.Application.StartupPath, fileName); } if (System.IO.File.Exists(objectImageFile)) { MagickImage objectImage = new MagickImage(objectImageFile); if (fixture.IsTree) { objectImage.Blur(); } m_modelImages.Add(fileName, objectImage); } else { if (fixture.IsTree) { MainForm.Log(string.Format("Can not find image for tree {0} ({1}), using default tree", fixture.Name, fixture.NifName), MainForm.LogLevel.warning); m_modelImages.Add(fileName, m_modelImages[defaultTree]); } else { m_modelImages.Add(fileName, null); } } } // Draw the image if (m_modelImages.ContainsKey(fileName) && m_modelImages[fileName] != null) { NifRow orginalNif = FixturesLoader.NifRows.Where(n => n.NifId == fixture.FixtureRow.NifId).FirstOrDefault(); if (orginalNif == null) { MainForm.Log(string.Format("Error with imaged nif ({0})!", fixture.FixtureRow.TextualName), MainForm.LogLevel.warning); } System.Drawing.SizeF objectSize = orginalNif.GetSize(0, 0); // The final image using (MagickImage modelImage = new MagickImage(MagickColors.Transparent, fixture.CanvasWidth, fixture.CanvasHeight)) { // Place the replacing image using (IMagickImage newModelImage = m_modelImages[fileName].Clone()) { newModelImage.BackgroundColor = MagickColors.Transparent; double scaleWidthToTreeImage = objectSize.Width / newModelImage.Width; double scaleHeightToTreeImage = objectSize.Height / newModelImage.Height; int width = Convert.ToInt32(newModelImage.Width * scaleWidthToTreeImage * fixture.Scale); int height = Convert.ToInt32(newModelImage.Height * scaleHeightToTreeImage * fixture.Scale); // Resize to new size newModelImage.FilterType = FilterType.Gaussian; newModelImage.VirtualPixelMethod = VirtualPixelMethod.Transparent; newModelImage.Resize(width, height); // Rotate the image //newModelImage.Rotate(fixture.FixtureRow.A * -1 * fixture.FixtureRow.AxisZ3D); newModelImage.Rotate((360d * fixture.FixtureRow.AxisZ3D - fixture.FixtureRow.A) * -1); // Place in center of modelImage modelImage.Composite(newModelImage, Gravity.Center, CompositeOperator.SrcOver); } // Draw the shaped model if wanted if (fixture.RendererConf.HasLight) { using (MagickImage modelShaped = new MagickImage(MagickColors.Transparent, fixture.CanvasWidth, fixture.CanvasHeight)) { foreach (DrawableElement drawableElement in fixture.DrawableElements) { var light = 1 - drawableElement.lightning; modelShaped.Settings.FillColor = new MagickColor( Convert.ToUInt16(ushort.MaxValue * light), Convert.ToUInt16(ushort.MaxValue * light), Convert.ToUInt16(ushort.MaxValue * light) ); DrawablePolygon polyDraw = new DrawablePolygon(drawableElement.coordinates); modelShaped.Draw(polyDraw); } using (MagickImage modelMask = new MagickImage(MagickColors.Transparent, fixture.CanvasWidth, fixture.CanvasHeight)) { modelShaped.Blur(); modelMask.Composite(modelShaped, 0, 0, CompositeOperator.DstAtop); modelMask.Composite(modelImage, 0, 0, CompositeOperator.DstIn); modelMask.Level(new Percentage(20), new Percentage(100), Channels.All); modelImage.Composite(modelMask, 0, 0, CompositeOperator.ColorDodge); } } } // Add the shadow if not a tree (tree shadow are substituted by a treeoverlay) if (fixture.RendererConf.HasShadow && !fixture.IsTree) { CastShadow( modelImage, fixture.RendererConf.ShadowOffsetX, fixture.RendererConf.ShadowOffsetY, fixture.RendererConf.ShadowSize, new Percentage(100 - fixture.RendererConf.ShadowTransparency), fixture.RendererConf.ShadowColor ); // Update the canvas position to match the new border fixture.CanvasX -= fixture.RendererConf.ShadowSize; fixture.CanvasY -= fixture.RendererConf.ShadowSize; } // Set transprency if not a tree (see shadow) if (fixture.RendererConf.Transparency != 0 && !fixture.IsTree) { double divideValue = 100.0 / (100.0 - fixture.RendererConf.Transparency); modelImage.Evaluate(Channels.Alpha, EvaluateOperator.Divide, divideValue); } // Place the image on the right position overlay.Composite(modelImage, Convert.ToInt32(fixture.CanvasX), Convert.ToInt32(fixture.CanvasY), CompositeOperator.SrcOver); } } }
async Task DoImageMagickCommandForGif(CommandContext ctx, byte[] buffer, ImageEditMode mode) { if (mode == ImageEditMode.Rescale) { await ctx.RespondAsync("이 모드는 속도가 느리고 이미지사이즈에 기반해서 느려지기 때문에 gif는 지원하지 않습니다."); return; } MagickImageCollection image; try { image = new MagickImageCollection(buffer); } catch (MagickMissingDelegateErrorException) { await ctx.RespondAsync("이미지 파일확장자를 알아볼 수 없다."); return; } int originalWidth = image[0].Width, originalHeight = image[0].Height; if (originalHeight * originalWidth > 1000000) { await ctx.RespondAsync($"Gif exceeds maximum size of 1000000 pixels (Actual size: {originalHeight * originalWidth})"); return; } if (image.Count > 100) { await ctx.RespondAsync($"Gif exceeds maximum frame count of 100 pixels (Actual count: {image.Count})"); return; } image.Coalesce(); long rawLength; using (MemoryStream stream = new MemoryStream()) { image.Write(stream); rawLength = stream.Length; } double exceed = rawLength / 4194304d; double rescale = 1f; if (exceed > 1.0) { rescale = Math.Sqrt(exceed); } await ctx.TriggerTypingAsync(); for (int i = 0; i < image.Count; i++) { IMagickImage frame = image[i]; if (rescale > 1f) { if (rescale > 2f) { frame.AdaptiveResize((int)(frame.Width / rescale), (int)(frame.Height / rescale)); } else { frame.Resize((int)(frame.Width / rescale), (int)(frame.Height / rescale)); } } DoMagic(mode, frame, originalWidth, originalHeight); } await ctx.TriggerTypingAsync(); image.OptimizeTransparency(); using (Stream stream = new MemoryStream()) { image.Write(stream); stream.Seek(0, SeekOrigin.Begin); await ctx.RespondWithFileAsync(stream, "magic.gif"); } }
// TODO: Name of this method + signature is a mess private SearchResult FindTemplateInImage(IMagickImage needleImage, Gravity needleGravity, IMagickImage haystack, StitchTask task, Point anchor) { // Resize needle var pixelMagnification = 8.0; var magnification = Math.Min(1 / (double)pixelMagnification, 1.0); var progress = (IProgress <double>)task; var resizeAmount = new Percentage(magnification * 100); var template = needleImage.Clone(); template.Resize(resizeAmount); template.RePage(); IMagickImage searchArea = null; var resized = false; while (!resized) { try { lock (haystack) { searchArea = haystack.Clone(); searchArea.Resize(resizeAmount); searchArea.RePage(); } resized = true; } catch (AccessViolationException) { Console.WriteLine("Corrupt Memory, trying again"); Thread.Sleep(500); } } // We need to get the actual values here, since Resize() has unexpected rounding logic // (only supports whole digit percentages) and if we don't use the actual values then // scaling math won't work properly. var oldWidth = searchArea.Width; var oldHeight = searchArea.Height; var bounds = new MagickGeometry(0, 0, searchArea.Width, searchArea.Height); var candidates = FindTemplateCandidates(searchArea, template, bounds, progress, 2000, new HashSet <Point>()); if (candidates.Any()) { var bestScore = candidates.First().Key; this.initialCandidates = candidates.Where(x => x.Key < bestScore * 1.05).Select(x => { return(new Point(x.Value.X / magnification, x.Value.Y / magnification)); }).ToList(); } while (pixelMagnification > 1 && candidates.Any()) { var newCandidates = new SortedList <double, Point>(new DuplicateKeyComparer <double>()); var newPixelMagnification = pixelMagnification / 2; var newMagnification = Math.Min(1 / (double)newPixelMagnification, 1.0); var newResizeAmount = new Percentage(newMagnification * 100); var threshold = 2000.0; var bestSeen = threshold; var bestScore = candidates.First().Key; var toLoop = candidates.Where(x => x.Key < bestScore * 1.05); Console.WriteLine("Considering {0} candidates at {1}", toLoop.Count(), newMagnification); IMagickImage newHaystack = null; lock (haystack) { newHaystack = haystack.Clone(); newHaystack.Resize(newResizeAmount); newHaystack.RePage(); } var t2 = needleImage.Clone(); t2.Resize(newResizeAmount); t2.RePage(); var cache = new HashSet <Point>(); foreach (var candidate in toLoop) { var point = new Point(candidate.Value.X / oldWidth * newHaystack.Width, candidate.Value.Y / oldHeight * newHaystack.Height); var margin = newPixelMagnification; var clampedBounds = new MagickGeometry( (int)(point.X - margin), (int)(point.Y - margin), (int)(NeedleSize * newMagnification + margin * 2), (int)(NeedleSize * newMagnification + margin * 2) ); clampedBounds.X = Math.Max(0, clampedBounds.X); clampedBounds.Y = Math.Max(0, clampedBounds.Y); clampedBounds.Width = Math.Min(newHaystack.Width - clampedBounds.X, clampedBounds.Width); clampedBounds.Height = Math.Min(newHaystack.Height - clampedBounds.Y, clampedBounds.Height); var toAdd = FindTemplateCandidates(newHaystack, t2, clampedBounds, this, threshold, cache); foreach (var add in toAdd) { newCandidates.Add(add.Key, add.Value); if (add.Key < bestSeen) { bestSeen = add.Key; Console.WriteLine("Updating best score: {0}", bestSeen); } } } candidates = newCandidates; magnification = newMagnification; pixelMagnification = newPixelMagnification; oldWidth = newHaystack.Width; oldHeight = newHaystack.Height; } Console.WriteLine("============ Final: {0}", candidates.Count); if (candidates.Any()) { var bestCandidate = candidates.First(); return(new SearchResult() { Distance = bestCandidate.Key, HaystackPoint = bestCandidate.Value, NeedlePoint = anchor }); } return(SearchResult.Null); }
public static void Run([BlobTrigger("inbox/{inputFilename}", Connection = "StorageAccountConnString")] Stream inputStream, string inputFilename, [Blob("outbox/out-{inputFilename}", FileAccess.Write, Connection = "StorageAccountConnString")] Stream outputStream, ILogger log) { log.LogInformation($"STEP: trigger fired for file:{inputFilename}, Size: {inputStream.Length} Bytes"); using (MagickImage image = new MagickImage(inputStream)) { log.LogInformation($"STEP: Read image {inputFilename} with {image.BaseWidth} x {image.BaseHeight} in {image.Format.ToString()}"); // Step 0 - does nothing in the sample images I used image.AutoOrient(); // Step 1 - Deskew image.BackgroundColor = MagickColor.FromRgb(0, 0, 0); image.Deskew(new Percentage(1)); // documentation suggests "A threshold of 40% works for most images" but 1% seems to work well IMagickImage rotatedImage = image.Clone(); log.LogInformation($"STEP: Deskewed {inputFilename}"); // Step 2 - Apply threshold to transform in black and white image image.AutoThreshold(AutoThresholdMethod.OTSU); log.LogInformation($"STEP: Applied OTSU to {inputFilename}"); // Step 3 - find the regions (blocs) in the image and group them if large enough IEnumerable <ConnectedComponent> components = null; ConnectedComponentsSettings ccs = new ConnectedComponentsSettings(); ccs.AreaThreshold = 500 * 500.0; // 500x500 -- seems to be pointless, many more regions are returned ccs.Connectivity = 8; components = image.ConnectedComponents(ccs); // if there are multiple blocs, consolidate them in a larger block if (components != null && components.Count() > 0) { log.LogInformation($"STEP: Looked for regions in {inputFilename}, there are {components.Count()}"); // filter out the smaller rectangles, as the AreaThreshold parameter seems not to be working List <ConnectedComponent> biggerComponents = components.Where(cc => cc.Height * cc.Width >= 250000 && cc.Height * cc.Width != image.Width * image.Height) /*.OrderByDescending(i => i.Height * i.Width)*/.ToList(); int topLeftX = biggerComponents[0].X, topLeftY = biggerComponents[0].Y, bottomRightX = biggerComponents[0].Width + topLeftX, bottomRightY = biggerComponents[0].Height + topLeftY; foreach (ConnectedComponent cc in biggerComponents) { #region Debug -- draw the regions on the image //DrawableStrokeColor strokeColor = new DrawableStrokeColor(new MagickColor("yellow")); //DrawableStrokeWidth stokeWidth = new DrawableStrokeWidth(3); //DrawableFillColor fillColor = new DrawableFillColor(new MagickColor(50, 50, 50, 128)); //DrawableRectangle dr = new DrawableRectangle(cc.X, cc.Y, cc.X + cc.Width, cc.Y + cc.Height); //rotatedImage.Draw(dr, strokeColor, stokeWidth, fillColor); #endregion if (cc.X < topLeftX) { topLeftX = cc.X; } if (cc.Y < topLeftY) { topLeftY = cc.Y; } if (cc.X + cc.Width > bottomRightX) { bottomRightX = cc.X + cc.Width; } if (cc.Y + cc.Height > bottomRightY) { bottomRightY = cc.Y + cc.Height; } } #region Debug -- draw the bounding box on the image //DrawableStrokeColor strokeColor2 = new DrawableStrokeColor(new MagickColor("purple")); //DrawableStrokeWidth stokeWidth2 = new DrawableStrokeWidth(3); //DrawableFillColor fillColor2 = new DrawableFillColor(new MagickColor(50, 50, 50, 128)); //DrawableRectangle dr2 = new DrawableRectangle(topLeftX, topLeftY, bottomRightX, bottomRightY); //rotatedImage.Draw(dr2, strokeColor2, stokeWidth2, fillColor2); #endregion // Step 4 - Crop the image MagickGeometry mg = new MagickGeometry(topLeftX, topLeftY, bottomRightX - topLeftX, bottomRightY - topLeftY); rotatedImage.RePage(); // this is needed because otherwise the crop is relative to the page information and sometimes this leads to an incorrect crop rotatedImage.Crop(mg); log.LogInformation($"STEP: Cropped {inputFilename} to fit existing large regions"); } else { log.LogInformation($"STEP: Looked for large regions in {inputFilename}, none were found, skipping crop"); } // Step 5 - Resize the image to 1200px width (todo: move to configuration) int originalWidth = rotatedImage.BaseWidth; int originalHeight = rotatedImage.BaseHeight; rotatedImage.Resize(1200, 0); // make width 1200, height proportional log.LogInformation($"STEP: Resized {inputFilename} from {originalWidth}x{originalHeight} to {image.BaseWidth}x{image.BaseHeight}"); // Step 6 - write out as Jpeg with 70% quality rotatedImage.Format = MagickFormat.Jpeg; rotatedImage.Quality = 70; rotatedImage.Write(outputStream); log.LogInformation($"STEP: Wrote out {inputFilename} as JPEG"); } log.LogInformation($"STEP: Processing of {inputFilename} done"); }