protected override void ExecuteEffect(ref MagickImage input) { float otherStrength = 1.0f - strenght; MagickImage otherImage = new MagickImage(input); Texture2D otherTex = new Texture2D(base.tex.width, base.tex.height, base.tex.format, false); ImageProcessingHelper.WriteToTexture(otherImage, ref base.tex); input.AutoThreshold((AutoThresholdMethod)Mode); ImageProcessingHelper.WriteToTexture(input, ref otherTex); Texture2D newTex = new Texture2D(otherTex.width, otherTex.height, otherTex.format, false); for (int x = 0; x < newTex.width; x++) { for (int y = 0; y < newTex.height; y++) { Color a = otherTex.GetPixel(x, y) * strenght + otherStrength * base.tex.GetPixel(x, y); newTex.SetPixel(x, y, a); } } newTex.Apply(); input = ImageProcessingHelper.GenerateMagicImage(newTex); }
public void ShouldThresholdImageWithTriangleMethod() { using (var image = new MagickImage(Files.FujiFilmFinePixS1ProPNG)) { image.AutoThreshold(AutoThresholdMethod.Triangle); var colors = image.Histogram(); Assert.Equal(ColorType.Bilevel, image.DetermineColorType()); Assert.Equal(210553, colors[MagickColors.Black]); Assert.Equal(29447, colors[MagickColors.White]); } }
public void ShouldThresholdImageWithOTSUMethod() { using (var image = new MagickImage(Files.FujiFilmFinePixS1ProPNG)) { image.AutoThreshold(AutoThresholdMethod.OTSU); var colors = image.Histogram(); Assert.Equal(ColorType.Bilevel, image.DetermineColorType()); Assert.Equal(67844, colors[MagickColors.Black]); Assert.Equal(172156, colors[MagickColors.White]); } }
public void ShouldThresholdImageWithKapurMethod() { using (IMagickImage image = new MagickImage(Files.FujiFilmFinePixS1ProPNG)) { image.AutoThreshold(AutoThresholdMethod.Kapur); Dictionary <MagickColor, int> colors = image.Histogram(); Assert.AreEqual(ColorType.Bilevel, image.DetermineColorType()); Assert.AreEqual(236359, colors[MagickColors.Black]); Assert.AreEqual(3641, colors[MagickColors.White]); } }
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"); }