/// <inheritdoc /> /// <exception cref="ArgumentNullException"> /// <paramref name="source"/> is <b>null</b>. /// </exception> public PointsOfInterestFeatures BuildFeatures(ImageSource source, CancellationToken cancellationToken) { if (source == null) { throw new ArgumentNullException(nameof(source)); } Image image = ImagePreprocessing.Process(source.Image, this.ImagePreprocessingOptions, 8); image = image ////.Scale(null, 100.0 / image.HorizontalResolution, 100.0 / image.VerticalResolution, ScalingOptions.None) ////.Binarize() .Convert8To1(null, 128) .CleanOverscan(0.5f, 0.5f) .Deskew(null) .Despeckle(null); ISet <ConnectedComponent> components = image.FindConnectedComponents(8); image.RemoveConnectedComponents(components.Where(x => x.Power <= 16)); image = image .CropBlackArea(0, 0) ////.Dilate(null, StructuringElement.Square(3), 1, BorderType.BorderConst, image.WhiteColor) ////.CropBlackArea(0, 0) .Convert1To8(null) .Erode(null, StructuringElement.Square(2), 1, BorderType.BorderRepl, 0) .ScaleByDownsampling2(null) .FilterLowpass(null, 3, BorderType.BorderRepl, 0); FeatureDetectors.Features features = this.detector.Detect(image, cancellationToken); return(new PointsOfInterestFeatures(source.Id, features)); }
public void ErodeTest1() { const int Width = 150; const int Height = 20; for (int ix = 0; ix < Width; ix++) { for (int iy = 0; iy < Height; iy++) { Image image = new Image(Width, Height, 1, 200, 200); image.SetBlack(); image.SetPixel(ix, iy, 0); Image dilatedImage = image.Erode(null, StructuringElement.Square(3), 1, BorderType.BorderConst, uint.MaxValue); if ((ix == 0 || ix == Width - 1) && (iy == 0 || iy == Height - 1)) { Assert.AreEqual( (ulong)((Width * Height) - 4), dilatedImage.Power(), string.Format(CultureInfo.InvariantCulture, "{0} {1}", ix, iy)); } else if ((ix == 0 || ix == Width - 1) || (iy == 0 || iy == Height - 1)) { Assert.AreEqual( (ulong)((Width * Height) - 6), dilatedImage.Power(), string.Format(CultureInfo.InvariantCulture, "{0} {1}", ix, iy)); } else { Assert.AreEqual( (ulong)((Width * Height) - 9), dilatedImage.Power(), string.Format(CultureInfo.InvariantCulture, "{0} {1}", ix, iy)); } } } }
public void DilateTest_3x3() { const int Width = 150; const int Height = 20; for (int ix = 0; ix < Width; ix++) { for (int iy = 0; iy < Height; iy++) { Image image = new Image(Width, Height, 1, 200, 200); image.SetPixel(ix, iy, 1); Image dilatedImage = image.Dilate(null, StructuringElement.Square(3), 1, BorderType.BorderConst, image.WhiteColor); if ((ix == 0 || ix == Width - 1) && (iy == 0 || iy == Height - 1)) { Assert.AreEqual( 4ul, dilatedImage.Power(), string.Format(CultureInfo.InvariantCulture, "{0} {1}", ix, iy)); } else if ((ix == 0 || ix == Width - 1) || (iy == 0 || iy == Height - 1)) { Assert.AreEqual( 6ul, dilatedImage.Power(), string.Format(CultureInfo.InvariantCulture, "{0} {1}", ix, iy)); } else { Assert.AreEqual( 9ul, dilatedImage.Power(), string.Format(CultureInfo.InvariantCulture, "{0} {1}", ix, iy)); } } } }
public void DilateTest2() { StructuringElement[] ses = new[] { StructuringElement.Brick(3, 1), StructuringElement.Brick(1, 3), StructuringElement.Brick(1, 4), StructuringElement.Brick(1, 4, new Point(0, 1)), StructuringElement.Square(7), StructuringElement.Brick(8, 6), StructuringElement.Brick(8, 6, new Point(6, 5)), StructuringElement.Brick(8, 6, new Point(10, 8)), StructuringElement.Cross(10, 5), StructuringElement.Cross(10, 5, new Point(-3, -2)), }; foreach (int bitsPerPixel in new[] { 1, /*2, 4,*/ 8, 16, 24, 32 }) { foreach (int width in new[] { 64 * 2, 131 }) { Image src = new Image(width, 50, bitsPerPixel, 200, 200); foreach (StructuringElement se in ses) { // constant border foreach (uint borderValue in new[] { src.BlackColor, src.WhiteColor, (uint)(((ulong)src.BlackColor + (ulong)src.WhiteColor) / 2) }) { src.Randomize(); Image dst = src.Dilate(null, se, 1, BorderType.BorderConst, borderValue); for (int x = 0; x < src.Width; x++) { for (int y = 0; y < src.Height; y++) { Assert.AreEqual(ComputePixelBorder(x, y, borderValue), dst.GetPixel(x, y)); } } } // replica border { src.Randomize(); Image dst = src.Dilate(null, se, 1, BorderType.BorderRepl, 0); for (int x = 0; x < src.Width; x++) { for (int y = 0; y < src.Height; y++) { Assert.AreEqual(ComputePixel(x, y), dst.GetPixel(x, y)); } } } uint ComputePixel(int x, int y) { uint maxcolor = uint.MinValue; foreach (Point point in se.GetElements()) { Point pt = new Point(x + point.X, y + point.Y); if (src.Bounds.Contains(pt)) { maxcolor = Color.Max(maxcolor, src.GetPixel(pt), bitsPerPixel); } } return(maxcolor); } uint ComputePixelBorder(int x, int y, uint borderValue) { uint maxcolor = uint.MinValue; foreach (Point point in se.GetElements()) { Point pt = new Point(x + point.X, y + point.Y); uint color = src.Bounds.Contains(pt) ? src.GetPixel(pt) : borderValue; maxcolor = Color.Max(maxcolor, color, bitsPerPixel); } return(maxcolor); } } } } }
public void XXXTest() { #if false const int Count = 10000000; Stopwatch stopwatch = new Stopwatch(); RandomGenerator random = new RandomGenerator(); int length = 128; float[] dense1 = random.Generate(length); float[] dense2 = new float[length]; for (int i = 0; i < 16; i++) { dense2[(int)random.Generate(0, length)] = random.Generate(); } SparseVectorF sparse = SparseVectorF.FromDense(length, dense2, 0); stopwatch.Restart(); for (int i = 0; i < Count; i++) { Math32f.EuclideanDistance(length, dense1, 0, dense2, 0); } stopwatch.Stop(); Console.WriteLine("{0:F4} ms", stopwatch.ElapsedMilliseconds /* / Count*/); stopwatch.Restart(); for (int i = 0; i < Count; i++) { sparse.EuclideanDistance(dense1, 0); } stopwatch.Stop(); Console.WriteLine("{0:F4} ms", stopwatch.ElapsedMilliseconds /* / Count*/); #else const int Count = 5; Stopwatch stopwatch = new Stopwatch(); ////Image image = new Image(2000, 3000, 8, 200, 200); ////image.Randomize(); ////foreach ((Image image, _, _) in Image.FromFile(@"C:\DNN\dnn\test.jpg")) ////foreach ((Image image, _, _) in Image.FromFile(@"C:\DNN\dnn\363978.tif")) foreach ((Image image, _, _) in Imaging.Image.FromFile(@"L:\FormXtra\Receipts\Concur\Full\jpg\Work\08240EB488FE324E83C7C54042D9813C.jpg")) { stopwatch.Restart(); Image xxx = image.ConvertTo(null, 8); for (int i = 0; i < Count; i++) { xxx.Erode(xxx, StructuringElement.Square(2), 1, BorderType.BorderRepl, 0); xxx.ScaleByDownsampling2(xxx); ////image.Binarize(null, 0, 0, 0, 0, true, 0, 0); ////IntegralImage.FromImage(image); ////long power = image.Power(); ////Histogram hyst = image.GrayHistogram(); /*Image workImage = image * .ConvertTo(null, 8) * .Scale(null, 100.0 / image.HorizontalResolution, 100.0 / image.VerticalResolution, ScalingOptions.None) * ////.Binarize(null) * .Convert8To1(null, 128) * .CleanOverscan(0.5f, 0.5f) * .Deskew(null) * .Despeckle(null); * * ISet<ConnectedComponent> components = workImage.FindConnectedComponents(8); * workImage.RemoveConnectedComponents(components);*/ /*workImage = workImage.Scale(100.0 / image.HorizontalResolution, 100.0 / image.VerticalResolution, Imaging.ScalingOptions.None); * workImage = workImage.Binarize(); * ////workImage = workImage.Convert8To1(128); * workImage = workImage.Dilate(StructuringElement.Rectangle(5, 1), 1); * workImage = workImage.Dilate(StructuringElement.Rectangle(1, 5), 1); * workImage = workImage.Convert1To8(); * * DenseVectorPackF vectors = workImage.HOG(8, 2, 1, 9, 0.2f); * * vectors = DenseVectorPackF.Pack(vectors.Unpack().Where(x => x.Sum() != 0.0f).ToList());*/ ////Image temp = image.Binarize(); } stopwatch.Stop(); } Console.WriteLine("{0:F4} ms", stopwatch.ElapsedMilliseconds / Count); #endif }
/// <summary> /// Finds the horizontal and vertical lines on the <see cref="Image"/>. /// The type of lines to find is determined by the class parameters. /// </summary> /// <param name="image">The source <see cref="Image"/>.</param> /// <param name="cancellationToken">The cancellationToken token used to notify the <see cref="LineDetector"/> that operation should be canceled.</param> /// <returns> /// The detected lines. /// </returns> /// <exception cref="ArgumentNullException"> /// <paramref name="image"/> is <b>null</b>. /// </exception> /// <exception cref="NotImplementedException"> /// <see cref="Image{T}.BitsPerPixel"/> is not one. /// </exception> /// <remarks> /// <para>This method works with binary (1bpp) images only.</para> /// </remarks> public ISet <Shape> FindLines(Image image, CancellationToken cancellationToken) { if (image == null) { throw new ArgumentNullException(nameof(image)); } if (image.BitsPerPixel != 1) { throw new NotImplementedException(Properties.Resources.E_UnsupportedDepth_1bpp); } // find check boxes ISet <CheckboxShape> checkboxResult = null; BoundedObjectGrid <CheckboxShape> checkboxGrid = null; if (this.FindCheckboxes) { checkboxResult = this.FindBoxes(image, cancellationToken); checkboxGrid = new BoundedObjectGrid <CheckboxShape>(image.Bounds, 5, 10); checkboxGrid.AddRange(checkboxResult, true, true); } HashSet <LineShape> hlinesResult = null; HashSet <LineShape> vlinesResult = null; if (this.LineTypes != LineTypes.None) { // masks we would like to find Image hlines = null; Image vlines = null; FindLines(); if (hlines != null || vlines != null) { // filter out false positives FilterFalsePositives(out Image nonHLines, out Image nonVLines); if (this.RemoveLines) { if (hlines != null) { RemoveLines(hlines, nonHLines); } if (vlines != null) { RemoveLines(vlines, nonVLines); } if (hlines != null && vlines != null) { RemoveIntersections(); } } } void FindLines() { // close up small holes int maxLineWidth = LineDetector.MaxLineWidth.MulDiv(image.HorizontalResolution, 200); Image closedImage = image.MorphClose(null, StructuringElement.Brick(maxLineWidth / 3, 2), 1, BorderType.BorderConst, image.WhiteColor); // open up to detect big solid areas Image openedImage = closedImage.MorphOpen(null, StructuringElement.Square(maxLineWidth), 1, BorderType.BorderConst, image.WhiteColor); Image hollowImage = closedImage.Sub(null, openedImage, 0); // open up in both directions to find lines int minHorLineLength = LineDetector.MinHorLineLength.MulDiv(image.HorizontalResolution, 200); hlines = hollowImage.MorphOpen(null, StructuringElement.Brick(minHorLineLength, 1), 1, BorderType.BorderConst, image.WhiteColor); if (hlines.IsAllWhite()) { hlines = null; } else { hlinesResult = this.FilterHorizontalLines(hlines, checkboxGrid); if (hlinesResult.Count == 0) { hlines = null; } } cancellationToken.ThrowIfCancellationRequested(); int minVerLineLength = LineDetector.MinVerLineLength.MulDiv(image.HorizontalResolution, 200); vlines = hollowImage.MorphOpen(null, StructuringElement.Brick(1, minVerLineLength), 1, BorderType.BorderConst, image.WhiteColor); if (vlines.IsAllWhite()) { vlines = null; } else { vlinesResult = this.FilterVerticalLines(vlines, hlines, checkboxGrid); if (vlinesResult.Count == 0) { vlines = null; } } cancellationToken.ThrowIfCancellationRequested(); } void FilterFalsePositives(out Image nonHLines, out Image nonVLines) { nonHLines = null; nonVLines = null; Image nonLines = null; if (hlines != null) { nonLines = image.Sub(nonLines, hlines, 0); } if (vlines != null) { nonLines = (nonLines ?? image).Sub(nonLines, vlines, 0); } Image intersections = vlines != null && hlines != null ? hlines & vlines : null; int maxLineResidue = 6; //// LineDetector.MaxLineResidue.MulDiv(image.HorizontalResolution, 200); if (hlines != null) { nonHLines = nonLines.Erode(null, StructuringElement.Brick(1, maxLineResidue), 1, BorderType.BorderConst, image.WhiteColor); nonHLines.FloodFill(nonHLines, 8, nonLines); if (vlines != null) { nonHLines += vlines; nonHLines -= intersections; } } ////int maxLineResidue = 6; //// LineDetector.MaxLineResidue.MulDiv(image.HorizontalResolution, 200); if (vlines != null) { nonVLines = nonLines.Erode(null, StructuringElement.Brick(maxLineResidue, 1), 1, BorderType.BorderConst, image.WhiteColor); nonVLines.FloodFill(nonVLines, 8, nonLines); if (hlines != null) { nonVLines += hlines; nonVLines -= intersections; } } int removedCount = 0; do { removedCount = 0; // horizontal lines if (hlines != null) { int count = this.RemoveHorizontalLinesFalsePositives(hlinesResult, hlines, nonHLines, intersections); if (count > 0) { removedCount += count; if (vlines != null) { // recompute intersections intersections = hlines.And(intersections, vlines); } if (hlinesResult.Count == 0) { hlines = null; } } cancellationToken.ThrowIfCancellationRequested(); } // vertical lines if (vlines != null) { int count = this.RemoveVerticalLinesFalsePositives(vlinesResult, vlines, nonVLines, intersections); if (count > 0) { removedCount += count; if (hlines != null) { // recompute intersections intersections = hlines.And(intersections, vlines); } if (vlinesResult.Count == 0) { vlines = null; } } cancellationToken.ThrowIfCancellationRequested(); } }while (removedCount != 0); } void RemoveLines(Image lines, Image nonLines) { // remove the lines image.Xand(image, lines); // dilate the lines so they touch the residue // then flood fill then to get all the residue (image less non-lines) Image fatLines = lines.Dilate3x3(null, BorderType.BorderConst, image.WhiteColor); fatLines.FloodFill(fatLines, 8, image.Xand(null, nonLines)); // remove the residue image.Xand(image, fatLines); cancellationToken.ThrowIfCancellationRequested(); } void RemoveIntersections() { // TODO: // get the intersection residue /*Image residue = hlines * .And(null, vlines) * .Dilate(null, StructuringElement.Square(5), 1, BorderType.BorderConst, image.WhiteColor); * * residue.FloodFill(residue, 8, image); * * // remove the residue * image.Xand(image, residue); * * cancellationToken.ThrowIfCancellationRequested();*/ } } // delete check boxes from the image if (this.RemoveCheckboxes && checkboxResult != null) { foreach (CheckboxShape shape in checkboxResult) { image.SetWhite(Rectangle.Inflate(shape.Bounds, 1, 1)); } } // create a draft that would show found check boxes #if DEBUG Image draft = image.ConvertTo(null, 32); if (checkboxResult != null) { foreach (CheckboxShape shape in checkboxResult) { draft.DrawRectangle(shape.Bounds, Color.Green); } } if (hlinesResult != null) { foreach (LineShape shape in hlinesResult) { draft.DrawRectangle(shape.Bounds, Color.Red); } } if (vlinesResult != null) { foreach (LineShape shape in vlinesResult) { draft.DrawRectangle(shape.Bounds, Color.Red); } } #endif // create answer HashSet <Shape> answer = new HashSet <Shape>(); if (hlinesResult != null) { answer.UnionWith(hlinesResult); } if (vlinesResult != null) { answer.UnionWith(vlinesResult); } if (checkboxResult != null) { answer.UnionWith(checkboxResult); } return(answer); }