public void ShouldRemoveMeanColor()
            {
                using (var image = new MagickImage())
                {
                    var settings = new ConnectedComponentsSettings()
                    {
                        MeanColor = true,
                    };

                    settings.SetImageArtifacts(image);
                    settings.RemoveImageArtifacts(image);

                    Assert.IsNull(image.GetArtifact("connected-components:mean-color"));
                }
            }
                public void ShouldRemoveTheAngleThreshold()
                {
                    using (var image = new MagickImage())
                    {
                        var settings = new ConnectedComponentsSettings
                        {
                            AngleThreshold = new Threshold(1.5),
                        };

                        ArtifactsHelper.SetImageArtifacts(image, settings);
                        ArtifactsHelper.RemoveImageArtifacts(image, settings);

                        Assert.Empty(image.ArtifactNames);
                    }
                }
Ejemplo n.º 3
0
            public void ShouldSetThePerimeterThreshold()
            {
                using (var image = new MagickImage())
                {
                    var settings = new ConnectedComponentsSettings
                    {
                        PerimeterThreshold = new Threshold(1.5),
                    };

                    settings.SetImageArtifacts(image);

                    Assert.Single(image.ArtifactNames);
                    Assert.Equal("1.5", image.GetArtifact("connected-components:perimeter-threshold"));
                }
            }
Ejemplo n.º 4
0
            public void ShouldSetMeanColor()
            {
                using (var image = new MagickImage())
                {
                    var settings = new ConnectedComponentsSettings
                    {
                        MeanColor = true,
                    };

                    settings.SetImageArtifacts(image);

                    Assert.Single(image.ArtifactNames);
                    Assert.Equal("true", image.GetArtifact("connected-components:mean-color"));
                }
            }
Ejemplo n.º 5
0
            public void ShouldSetTheMinumunAndMaximumMinorAxisThreshold()
            {
                using (var image = new MagickImage())
                {
                    var settings = new ConnectedComponentsSettings
                    {
                        MinorAxisThreshold = new Threshold(1.2, 3.4),
                    };

                    settings.SetImageArtifacts(image);

                    Assert.Single(image.ArtifactNames);
                    Assert.Equal("1.2-3.4", image.GetArtifact("connected-components:minor-axis-threshold"));
                }
            }
Ejemplo n.º 6
0
            public void ShouldSetTheMinumunAndMaximumAreaThreshold()
            {
                using (var image = new MagickImage())
                {
                    var settings = new ConnectedComponentsSettings()
                    {
                        AreaThreshold = new Threshold(1.2, 3.4),
                    };

                    settings.SetImageArtifacts(image);

                    EnumerableAssert.IsSingle(image.ArtifactNames);
                    Assert.AreEqual("1.2-3.4", image.GetArtifact("connected-components:area-threshold"));
                }
            }
            public void ShouldRemoveTheEccentricityThreshold()
            {
                using (var image = new MagickImage())
                {
                    var settings = new ConnectedComponentsSettings()
                    {
                        EccentricityThreshold = new Threshold(1.5),
                    };

                    settings.SetImageArtifacts(image);
                    settings.RemoveImageArtifacts(image);

                    Assert.IsNull(image.GetArtifact("connected-components:eccentricity-threshold"));
                }
            }
Ejemplo n.º 8
0
            public void ShouldSetTheAngleThreshold()
            {
                using (var image = new MagickImage())
                {
                    var settings = new ConnectedComponentsSettings()
                    {
                        AngleThreshold = new Threshold(1.5),
                    };

                    settings.SetImageArtifacts(image);

                    EnumerableAssert.IsSingle(image.ArtifactNames);
                    Assert.AreEqual("1.5", image.GetArtifact("connected-components:angle-threshold"));
                }
            }
            public void ShouldRemoveTheEccentricityThreshold()
            {
                using (var image = new MagickImage())
                {
                    var settings = new ConnectedComponentsSettings
                    {
                        EccentricityThreshold = new Threshold(1.5),
                    };

                    settings.SetImageArtifacts(image);
                    settings.RemoveImageArtifacts(image);

                    Assert.Empty(image.ArtifactNames);
                }
            }
            public void ShouldRemoveTheDiameterThreshold()
            {
                using (var image = new MagickImage())
                {
                    var settings = new ConnectedComponentsSettings()
                    {
                        DiameterThreshold = new Threshold(1.5),
                    };

                    settings.SetImageArtifacts(image);
                    settings.RemoveImageArtifacts(image);

                    EnumerableAssert.IsEmpty(image.ArtifactNames);
                }
            }
            public void ShouldRemoveMeanColor()
            {
                using (var image = new MagickImage())
                {
                    var settings = new ConnectedComponentsSettings
                    {
                        MeanColor = true,
                    };

                    settings.SetImageArtifacts(image);
                    settings.RemoveImageArtifacts(image);

                    Assert.Empty(image.ArtifactNames);
                }
            }
Ejemplo n.º 12
0
                public void ShouldSetTheMinumunAndMaximumPerimeterThreshold()
                {
                    using (var image = new MagickImage())
                    {
                        var settings = new ConnectedComponentsSettings
                        {
                            PerimeterThreshold = new Threshold(1.2, 3.4),
                        };

                        ArtifactsHelper.SetImageArtifacts(image, settings);

                        Assert.Single(image.ArtifactNames);
                        Assert.Equal("1.2-3.4", image.GetArtifact("connected-components:perimeter-threshold"));
                    }
                }
Ejemplo n.º 13
0
                public void ShouldSetTheMinorAxisThreshold()
                {
                    using (var image = new MagickImage())
                    {
                        var settings = new ConnectedComponentsSettings
                        {
                            MinorAxisThreshold = new Threshold(1.5),
                        };

                        ArtifactsHelper.SetImageArtifacts(image, settings);

                        Assert.Single(image.ArtifactNames);
                        Assert.Equal("1.5", image.GetArtifact("connected-components:minor-axis-threshold"));
                    }
                }
Ejemplo n.º 14
0
            public void ShouldNotSetTheAttributesWhenTheyAreNotSpecified()
            {
                using (var image = new MagickImage())
                {
                    var settings = new ConnectedComponentsSettings();

                    settings.SetImageArtifacts(image);

                    Assert.IsNull(image.GetArtifact("connected-components:angle-threshold"));
                    Assert.IsNull(image.GetArtifact("connected-components:area-threshold"));
                    Assert.IsNull(image.GetArtifact("connected-components:circularity-threshold"));
                    Assert.IsNull(image.GetArtifact("connected-components:diameter-threshold"));
                    Assert.IsNull(image.GetArtifact("connected-components:eccentricity-threshold"));
                    Assert.IsNull(image.GetArtifact("connected-components:major-axis-threshold"));
                    Assert.IsNull(image.GetArtifact("connected-components:mean-color"));
                    Assert.IsNull(image.GetArtifact("connected-components:minor-axis-threshold"));
                    Assert.IsNull(image.GetArtifact("connected-components:perimeter-threshold"));
                }
            }
Ejemplo n.º 15
0
            public void ShouldReturnTheConnectedComponents()
            {
                using (var image = new MagickImage(Files.ConnectedComponentsPNG))
                {
                    using (var temp = image.Clone())
                    {
                        temp.Blur(0, 10);
                        temp.Threshold((Percentage)50);

                        var components = temp.ConnectedComponents(4).OrderBy(component => component.X).ToArray();
                        Assert.Equal(7, components.Length);

                        var color = MagickColors.Black;

#if Q8
                        AssertComponent(image, components[1], 2, 94, 297, 128, 151, 11783, color, 157, 371);
                        AssertComponent(image, components[2], 5, 99, 554, 128, 150, 11772, color, 162, 628);
                        AssertComponent(image, components[3], 4, 267, 432, 89, 139, 11792, color, 310, 501);
                        AssertComponent(image, components[4], 1, 301, 202, 148, 143, 11801, color, 374, 272);
                        AssertComponent(image, components[5], 6, 341, 622, 136, 150, 11793, color, 408, 696);
                        AssertComponent(image, components[6], 3, 434, 411, 88, 139, 11835, color, 477, 480);
#else
                        AssertComponent(image, components[1], 2, 94, 297, 128, 151, 11737, color, 157, 371);
                        AssertComponent(image, components[2], 5, 99, 554, 128, 150, 11734, color, 162, 628);
                        AssertComponent(image, components[3], 4, 267, 432, 89, 139, 11749, color, 310, 501);
                        AssertComponent(image, components[4], 1, 301, 202, 148, 143, 11755, color, 374, 272);
                        AssertComponent(image, components[5], 6, 341, 622, 136, 150, 11746, color, 408, 696);
                        AssertComponent(image, components[6], 3, 434, 411, 88, 139, 11793, color, 477, 480);
#endif
                    }

#if !Q8
                    using (var temp = image.Clone())
                    {
                        var settings = new ConnectedComponentsSettings
                        {
                            Connectivity  = 4,
                            MeanColor     = true,
                            AreaThreshold = new Threshold(400),
                        };

                        var components = temp.ConnectedComponents(settings).OrderBy(component => component.X).ToArray();
                        Assert.Equal(12, components.Length);

                        var color = new MagickColor("#010101010101");

                        AssertComponent(image, components[1], 597, 90, 293, 136, 162, 11624, color, 157, 372);
                        AssertComponent(image, components[2], 3439, 96, 550, 138, 162, 11739, color, 162, 628);
                        AssertComponent(image, components[3], 4122, 103, 604, 4, 2, 4, new MagickColor("#0B0B0B0B0B0B"), 104, 606);
                        AssertComponent(image, components[4], 4157, 107, 612, 3, 1, 4, new MagickColor("#080808080808"), 108, 613);
                        AssertComponent(image, components[5], 4233, 111, 620, 3, 1, 4, new MagickColor("#020202020202"), 112, 621);
                        AssertComponent(image, components[6], 5085, 150, 698, 3, 1, 4, new MagickColor("#424242424242"), 150, 698);
                        AssertComponent(image, components[7], 5132, 152, 702, 3, 1, 4, new MagickColor("#262626262626"), 153, 703);
                        AssertComponent(image, components[8], 2105, 268, 433, 89, 139, 11645, color, 311, 502);
                        AssertComponent(image, components[9], 17, 298, 198, 155, 151, 11622, color, 375, 273);
                        AssertComponent(image, components[10], 4202, 337, 618, 144, 158, 11675, color, 409, 696);
                        AssertComponent(image, components[11], 1703, 435, 412, 87, 138, 11629, color, 478, 481);
                    }
#endif
                }
            }
            public void ShouldReturnTheConnectedComponents()
            {
                using (IMagickImage image = new MagickImage(Files.ConnectedComponentsPNG))
                {
                    using (IMagickImage temp = image.Clone())
                    {
                        temp.Blur(0, 10);
                        temp.Threshold((Percentage)50);

                        var components = temp.ConnectedComponents(4).OrderBy(component => component.X).ToArray();
                        Assert.AreEqual(7, components.Length);

                        var color = MagickColors.Black;

#if Q8
                        AssertComponent(image, components[1], 2, 94, 297, 128, 151, 11783, color, 157, 371);
                        AssertComponent(image, components[2], 5, 99, 554, 128, 150, 11772, color, 162, 628);
                        AssertComponent(image, components[3], 4, 267, 432, 89, 139, 11792, color, 310, 501);
                        AssertComponent(image, components[4], 1, 301, 202, 148, 143, 11801, color, 374, 272);
                        AssertComponent(image, components[5], 6, 341, 622, 136, 150, 11793, color, 408, 696);
                        AssertComponent(image, components[6], 3, 434, 411, 88, 139, 11835, color, 477, 480);
#else
                        AssertComponent(image, components[1], 2, 94, 297, 128, 151, 11737, color, 157, 371);
                        AssertComponent(image, components[2], 5, 99, 554, 128, 150, 11734, color, 162, 628);
                        AssertComponent(image, components[3], 4, 267, 432, 89, 139, 11749, color, 310, 501);
                        AssertComponent(image, components[4], 1, 301, 202, 148, 143, 11755, color, 374, 272);
                        AssertComponent(image, components[5], 6, 341, 622, 136, 150, 11746, color, 408, 696);
                        AssertComponent(image, components[6], 3, 434, 411, 88, 139, 11793, color, 477, 480);
#endif
                    }

#if !Q8
                    using (IMagickImage temp = image.Clone())
                    {
                        var settings = new ConnectedComponentsSettings()
                        {
                            Connectivity  = 4,
                            MeanColor     = true,
                            AreaThreshold = 400,
                        };

                        var components = temp.ConnectedComponents(settings).OrderBy(component => component.X).ToArray();
                        Assert.AreEqual(13, components.Length);

                        var color1 = new MagickColor("#010101010101");
                        var color2 = MagickColors.Black;

                        AssertComponent(image, components[1], 597, 90, 293, 139, 162, 11902, color1, 157, 372);
                        AssertComponent(image, components[2], 3439, 96, 550, 138, 162, 11999, color1, 162, 628);
                        AssertComponent(image, components[3], 4367, 213, 633, 1, 2, 1, color2, 213, 633);
                        AssertComponent(image, components[4], 4412, 215, 637, 3, 1, 1, color2, 215, 637);
                        AssertComponent(image, components[5], 4453, 217, 641, 3, 1, 1, color2, 217, 641);
                        AssertComponent(image, components[6], 4495, 219, 645, 3, 1, 1, color2, 219, 645);
                        AssertComponent(image, components[7], 4538, 221, 647, 3, 1, 1, color2, 221, 649);
                        AssertComponent(image, components[8], 2105, 268, 433, 89, 139, 11808, color1, 311, 502);
                        AssertComponent(image, components[9], 17, 298, 198, 155, 151, 11927, color1, 375, 273);
                        AssertComponent(image, components[10], 4202, 337, 618, 148, 158, 11974, color1, 409, 696);
                        AssertComponent(image, components[11], 314, 410, 247, 2, 1, 2, color2, 410, 247);
                        AssertComponent(image, components[12], 1703, 434, 411, 88, 140, 11763, color1, 477, 480);
                    }
#endif
                }
            }
        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");
        }