static void Process2(string fileName) { Console.WriteLine(fileName); var meta = new OCR.Shared.ImageInfo { FileName = fileName, MaskName = fileName + ".mask.png", FilteredName = fileName + ".filtered.png", Words = new List <OCR.Shared.Blob>(), Areas = new List <OCR.Shared.Rect>() }; using (var img = System.Drawing.Image.FromFile(fileName)) using (var bmp = new Bitmap(img)) { var gray = new FiltersSequence(Grayscale.CommonAlgorithms.BT709, new GaussianSharpen(), new ContrastCorrection(20)) .Apply(UnmanagedImage.FromManagedImage(bmp)); gray.ToManagedImage().Save(fileName + ".gray.png", System.Drawing.Imaging.ImageFormat.Png); var bw = new FiltersSequence(new BradleyLocalThresholding { PixelBrightnessDifferenceLimit = 0.20f }, new Invert()) .Apply(gray); bw.ToManagedImage().Save(fileName + ".bw.png", System.Drawing.Imaging.ImageFormat.Png); var mask = new FiltersSequence(new Dilation(), new BlobsFiltering(5, 5, gray.Width / 2, gray.Height / 2, false)) .Apply(bw); mask.ToManagedImage().Save(meta.MaskName, System.Drawing.Imaging.ImageFormat.Png); { var ccs = new BlobCounter(mask).GetObjectsRectangles(); var sortedw = ccs.Select(p => p.Width).OrderBy(p => p).ToArray(); var sortedh = ccs.Select(p => p.Height).OrderBy(p => p).ToArray(); meta.MedianWordSize = new OCR.Shared.Point { X = sortedw[sortedw.Length / 2], Y = sortedh[sortedh.Length / 2] }; Console.WriteLine($"Median: {meta.MedianWordSize.X}, {meta.MedianWordSize.Y}"); } var filtered = new FiltersSequence(new Invert(), new Intersect(mask), new Invert()) .Apply(gray); var filteredBmp = filtered.ToManagedImage(); filteredBmp.Save(meta.FilteredName, System.Drawing.Imaging.ImageFormat.Png); var rgb = new GrayscaleToRGB().Apply(filtered); mask = new FiltersSequence(new HorizontalRunLengthSmoothing(meta.MedianWordSize.X * 2), new VerticalRunLengthSmoothing(meta.MedianWordSize.Y * 2)) .Apply(mask); mask.ToManagedImage().Save(fileName + ".area.png", System.Drawing.Imaging.ImageFormat.Png); foreach (Rectangle rect in new BlobCounter(mask).GetObjectsRectangles()) { Drawing.FillRectangle(mask, rect, Color.White); } mask.ToManagedImage().Save(fileName + ".rect.png", System.Drawing.Imaging.ImageFormat.Png); foreach (Rectangle rect in new BlobCounter(mask).GetObjectsRectangles()) { meta.Areas.Add(new OCR.Shared.Rect { X = rect.X, Y = rect.Y, W = rect.Width, H = rect.Height }); Drawing.Rectangle(rgb, rect, Color.Red); } new Intersect(mask).ApplyInPlace(bw); foreach (var blob in new BlobCounter(bw).GetObjects(bw, false)) { var outRect = new OCR.Shared.Rect { X = blob.Rectangle.X - 1, Y = blob.Rectangle.Y - 1, W = blob.Rectangle.Width + 2, H = blob.Rectangle.Height + 2 }; if (outRect.X < 0) { outRect.X = 0; outRect.W--; } if (outRect.Y < 0) { outRect.Y = 0; outRect.H--; } if (outRect.X + outRect.W > bw.Width) { outRect.W = bw.Width - outRect.X; } if (outRect.Y + outRect.H > bw.Height) { outRect.H = bw.Height - outRect.Y; } var gravityCenter = new OCR.Shared.Point { X = (int)blob.CenterOfGravity.X, Y = (int)blob.CenterOfGravity.Y }; var geometryCenter = new OCR.Shared.Point { X = blob.Rectangle.X + blob.Rectangle.Width / 2, Y = blob.Rectangle.Y + blob.Rectangle.Height / 2 }; var bytedata = blob.Image.ToByteArray(); var newbytedata = new byte[outRect.W * outRect.H]; for (var j = 0; j < outRect.H - 2; j++) { for (var i = 0; i < outRect.W - 2; i++) { newbytedata[j * outRect.W + i + 1] = bytedata[j * (outRect.W - 2) + i]; } } var blobImg = new FiltersSequence(new Dilation()).Apply(UnmanagedImage.FromByteArray(newbytedata, outRect.W, outRect.H, System.Drawing.Imaging.PixelFormat.Format8bppIndexed)); var area = new Rectangle(outRect.X, outRect.Y, outRect.W, outRect.H); Drawing.Rectangle(rgb, area, Color.Blue); Drawing.FillRectangle(rgb, new Rectangle(geometryCenter.X - 2, geometryCenter.Y - 2, 5, 5), Color.Magenta); Drawing.FillRectangle(rgb, new Rectangle(gravityCenter.X - 2, gravityCenter.Y - 2, 5, 5), Color.Cyan); var bits = filteredBmp.LockBits(area, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format8bppIndexed); meta.Words.Add( new OCR.Shared.Blob { Id = blob.ID, Data = new FiltersSequence(new Invert(), new Intersect(blobImg), new Invert()).Apply(new UnmanagedImage(bits)).ToByteArray(), Position = outRect, GravityCenter = gravityCenter, GeometryCenter = geometryCenter }); filteredBmp.UnlockBits(bits); } rgb.ToManagedImage().Save(fileName + ".marked.png", System.Drawing.Imaging.ImageFormat.Png); File.WriteAllText(fileName + ".meta.json", Newtonsoft.Json.JsonConvert.SerializeObject(meta, Newtonsoft.Json.Formatting.Indented)); } }