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); } }
private void CropImage(IMagickImage image) { if (!CropOffset.IsSet) { return; } int width = image.Width - (CropOffset.Left + CropOffset.Right); int height = image.Height - (CropOffset.Top + CropOffset.Bottom); image.Crop(new MagickGeometry(CropOffset.Left, CropOffset.Top, width, height)); }
public IMagickImage Execute(IMagickImage image, ValueItemCollection values) { Variables = values; if (!CropVisible) { return(image); } var dw = image.Width / 1000.0; var dh = image.Height / 1000.0; image.Crop((int)(CropX * dw), (int)(CropY * dh), (int)(CropWidth * dw), (int)(CropHeight * dh)); image.RePage(); return(image); }
private static void CropImage(CustomRatio ratio, IMagickImage image) { var cropWidth = Math.Abs(ratio.X2 - ratio.X1); var cropHeight = Math.Abs(ratio.Y2 - ratio.Y1); if (image.BaseWidth == cropWidth && image.BaseHeight == cropHeight) //requested image is same size { return; } var cropSize = new MagickGeometry(cropWidth, cropHeight) { IgnoreAspectRatio = false, //keep aspect ratio! FillArea = false, X = ratio.X1, Y = ratio.Y1 }; image.Crop(cropSize); }
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); } }
private void AssertComponent(IMagickImage image, ConnectedComponent component, int id, int x, int y, int width, int height, int area, MagickColor color, int centroidX, int centroidY) { var delta = 2; Assert.AreEqual(id, component.Id); Assert.AreEqual(x, component.X, delta); Assert.AreEqual(y, component.Y, delta); Assert.AreEqual(width, component.Width, delta); Assert.AreEqual(height, component.Height, delta); Assert.AreEqual(area, component.Area, delta); ColorAssert.AreEqual(color, component.Color); Assert.AreEqual(centroidX, component.Centroid.X, delta); Assert.AreEqual(centroidY, component.Centroid.Y, delta); using (IMagickImage componentImage = image.Clone()) { componentImage.Crop(component.ToGeometry(10)); Assert.AreEqual(width + 20, componentImage.Width, delta); Assert.AreEqual(height + 20, componentImage.Height, delta); } }
private static void CropImage(ImageCustomization imageCustomization, IMagickImage magickImage) { var cropWidth = Math.Abs(imageCustomization.X2 - imageCustomization.X1); var cropHeight = Math.Abs(imageCustomization.Y2 - imageCustomization.Y1); if ((cropWidth <= 0 || cropHeight <= 0) || cropWidth == magickImage.BaseWidth && cropHeight == magickImage.BaseHeight) { return; } else { var magickGeometry = new MagickGeometry(cropWidth, cropHeight) { IgnoreAspectRatio = false, //保持长宽比 FillArea = false, X = imageCustomization.X1, Y = imageCustomization.Y1 }; magickImage.Crop(magickGeometry); } }
private static void Process(string png, string mesh) { var rebuildCoord = new List <Point>(); var rebuildMeta = new List <FragmentDataInfo>(); var cropCoord = new List <PointF>(); string[] txtRaw = File.ReadAllLines(mesh); int nLine = 0; int widthLow = 0; int widthHigh = 0; int heightLow = 0; int heightHigh = 0; for (nLine = 0; nLine < txtRaw.Length; nLine++) { // Start of coord data if (txtRaw[nLine][0] == 'v') { break; } } while (true) { int i = 0; string[] tmp = txtRaw[nLine + i + 0].Split(' '); Point rd; PointF cd; if (tmp[0][0] == '#') { break; } rd = new Point(Math.Abs(Convert.ToInt32(Convert.ToDouble(tmp[1]))), Convert.ToInt32(Convert.ToDouble(tmp[2]))); rebuildCoord.Add(rd); if (widthLow > Convert.ToInt32(Convert.ToDouble(tmp[1]))) { widthLow = Convert.ToInt32(Convert.ToDouble(tmp[1])); } if (widthHigh < rd.X) { widthHigh = rd.X; } if (heightLow > rd.Y) { heightLow = rd.Y; } if (heightHigh < rd.Y) { heightHigh = rd.Y; } tmp = txtRaw[nLine + i + 1].Split(' '); cd = new PointF(float.Parse(tmp[1]), 1.0f - float.Parse(tmp[2])); cropCoord.Add(cd); nLine += 2; } for (; nLine < txtRaw.Length; nLine++) { // Start of meta data if (txtRaw[nLine][0] == 'f') { break; } } while (true) { int i = 0; string[] tmp = txtRaw[nLine + i + 0].Split(' ', '/'); FragmentDataInfo fdi = new FragmentDataInfo(); if (tmp[0] == String.Empty || nLine > txtRaw.Length) { break; } fdi.F1 = Math.Abs(Convert.ToInt32(Convert.ToDouble(tmp[1]))); fdi.F2 = Math.Abs(Convert.ToInt32(Convert.ToDouble(tmp[3]))); fdi.F3 = Math.Abs(Convert.ToInt32(Convert.ToDouble(tmp[5]))); rebuildMeta.Add(fdi); nLine++; } MagickImage mi = new MagickImage(png); int baseWidth = mi.Width; int baseHeight = mi.Height; int padding = 1024; MagickImage ri = new MagickImage(MagickColor.FromRgb(255, 255, 255), 4096, 4096); ri.Resize(4096, 4096); ri.Transparent(MagickColors.White); for (int i = 0; i < rebuildMeta.Count; i++) { Point[] rebuildInfo = { new Point(rebuildCoord[rebuildMeta[i].F1 - 1].X, rebuildCoord[rebuildMeta[i].F1 - 1].Y), new Point(rebuildCoord[rebuildMeta[i].F2 - 1].X, rebuildCoord[rebuildMeta[i].F2 - 1].Y), new Point(rebuildCoord[rebuildMeta[i].F3 - 1].X, rebuildCoord[rebuildMeta[i].F3 - 1].Y) }; PointF[] cropInfo = { new PointF(baseWidth * cropCoord[rebuildMeta[i].F1 - 1].X, baseHeight * cropCoord[rebuildMeta[i].F1 - 1].Y), new PointF(baseWidth * cropCoord[rebuildMeta[i].F2 - 1].X, baseHeight * cropCoord[rebuildMeta[i].F2 - 1].Y), new PointF(baseWidth * cropCoord[rebuildMeta[i].F3 - 1].X, baseHeight * cropCoord[rebuildMeta[i].F3 - 1].Y) }; int cropStartX = (int)Math.Round(Math.Min(Math.Min(cropInfo[0].X, cropInfo[1].X), cropInfo[2].X)); int cropStartY = (int)Math.Round(Math.Min(Math.Min(cropInfo[0].Y, cropInfo[1].Y), cropInfo[2].Y)); int cropEndX = (int)Math.Round(Math.Max(Math.Max(cropInfo[0].X, cropInfo[1].X), cropInfo[2].X)); int cropEndY = (int)Math.Round(Math.Max(Math.Max(cropInfo[0].Y, cropInfo[1].Y), cropInfo[2].Y)); int rebuildStartX = Math.Min(Math.Min(rebuildInfo[0].X, rebuildInfo[1].X), rebuildInfo[2].X); int rebuildStartY = baseWidth - Math.Min(Math.Min(rebuildInfo[0].Y, rebuildInfo[1].Y), rebuildInfo[2].Y) - (cropEndY - cropStartY); IMagickImage clone = mi.Clone(); MagickGeometry mg = new MagickGeometry { X = cropStartX, Y = cropStartY, Width = cropEndX - cropStartX, Height = cropEndY - cropStartY }; clone.Crop(mg, Gravity.Northwest); ri.Composite(clone, rebuildStartX + padding, rebuildStartY + padding, CompositeOperator.Copy, Channels.All); Console.Write(((double)i / (double)rebuildMeta.Count * 100.0d).ToString("0.00")); Console.Write("\r"); } //ri.Transparent(MagickColor.FromRgb(0, 0, 0)); ri.BackgroundColor = MagickColors.Transparent; ri.Trim(); ri.Write(Path.GetFileName(png) + "_d.png", MagickFormat.Png32); mi.Dispose(); ri.Dispose(); Console.Write("100.0"); Console.WriteLine(Environment.NewLine + "Output: " + Environment.NewLine + "PNG=" + Path.GetFileName(png) + "_d.png" + Environment.NewLine); return; }
private void Crop(IMagickImage image, MagickGeometry area) { ShiftGeometry(area); image.Crop(area.X, area.Y, area.Width, area.Height); image.RePage(); }
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"); }