public void ToByteArray_test8pp(PixelFormat pixelFormat, int w, int h, int expected) { int[,] values = Vector.Range(0, 255).Get(0, h * w).Reshape(h, w); UnmanagedImage image = values.ToBitmap().ToUnmanagedImage(); int formatBytes = pixelFormat.GetPixelFormatSizeInBytes(); byte[] b = image.ToByteArray(); Assert.AreEqual(w * h * formatBytes, b.Length); Assert.AreEqual(expected, b.Length); // Reconstruct the original matrix UnmanagedImage r = UnmanagedImage.FromByteArray(b, w, h, pixelFormat); byte[,] actual = r.ToManagedImage().ToMatrix(0, 0, 255); Assert.AreEqual(values, actual); }
public void ToByteArray_test_general(PixelFormat pixelFormat, int w, int h, int expected) { int c = pixelFormat.GetNumberOfChannels(); byte[,,] values = (byte[, , ])Vector.Range((byte)0, (byte)255).Get(0, h * w).Reshape(new[] { h, w, c }); UnmanagedImage image = values.ToBitmap().ToUnmanagedImage(); int formatBytes = pixelFormat.GetPixelFormatSizeInBytes(); byte[] b = image.ToByteArray(); Assert.AreEqual(w * h * formatBytes, b.Length); Assert.AreEqual(expected, b.Length); // Reconstruct the original matrix UnmanagedImage r = UnmanagedImage.FromByteArray(b, w, h, pixelFormat); byte[,,] actual = r.ToManagedImage().ToMatrix((byte)0, (byte)255); Assert.AreEqual(values, actual); }
public void ToByteArray_test_general(PixelFormat pixelFormat, int w, int h, int expected) { int c = pixelFormat.GetNumberOfChannels(); byte[,,] values = (byte[, , ])Vector.Range((byte)0, (byte)255).Get(0, c * h * w).Reshape(new[] { h, w, c }); UnmanagedImage image = values.ToBitmap().ToUnmanagedImage(); int formatBytes = pixelFormat.GetPixelFormatSizeInBytes(); byte[] b = image.ToByteArray(); Assert.AreEqual(w * h * formatBytes, b.Length, "{0} * {1} * {2}, {3}", w, h, formatBytes, b.Length); Assert.AreEqual(expected, b.Length, "{0}, {1}", expected, b.Length); // Reconstruct the original matrix UnmanagedImage r = UnmanagedImage.FromByteArray(b, w, h, pixelFormat); byte[,,] actual = r.ToManagedImage().ToMatrix((byte)0, (byte)255); string a = String.Join(" ", (string[])Accord.Math.Matrix.ToString(actual).DeepFlatten()); string e = String.Join(" ", (string[])Accord.Math.Matrix.ToString(values).DeepFlatten()); Assert.AreEqual(e, a, "{0} == {1}", e, a); }
static void Main(string[] args) { var metapath = ".\\..\\..\\..\\demo\\4.input.jpg.meta.json"; var meta = Newtonsoft.Json.JsonConvert.DeserializeObject <OCR.Shared.ImageInfo>(File.ReadAllText(metapath)); while (true) { var pos = Console.ReadLine().Split(' ').Select(p => int.Parse(p)).ToArray(); var rect = new OCR.Shared.Rect { X = pos[0], Y = pos[1], W = pos[2], H = pos[3] }; var filtered = meta.Words.Where( p => p.GravityCenter.X >= rect.X && p.GravityCenter.X <= rect.X + rect.W && p.GravityCenter.Y >= rect.Y && p.GravityCenter.Y <= rect.Y + rect.H || p.GeometryCenter.X >= rect.X && p.GeometryCenter.X <= rect.X + rect.W && p.GeometryCenter.Y >= rect.Y && p.GeometryCenter.Y <= rect.Y + rect.H).ToArray(); var newrect = new OCR.Shared.Rect { X = filtered.Min(p => p.Position.X), Y = filtered.Min(p => p.Position.Y), W = 0, H = 0 }; foreach (var word in filtered) { var width = word.Position.X - newrect.X + word.Position.W; var height = word.Position.Y - newrect.Y + word.Position.H; if (newrect.W < width) { newrect.W = width; } if (newrect.H < height) { newrect.H = height; } } var bytes = new byte[newrect.W * newrect.H]; for (var i = 0; i < newrect.W * newrect.H; i++) { bytes[i] = 0xFF; } foreach (var word in filtered) { var offsetx = word.Position.X - newrect.X; var offsety = word.Position.Y - newrect.Y; for (var j = 0; j < word.Position.H; j++) { for (var i = 0; i < word.Position.W; i++) { var d = word.Data[j * word.Position.W + i]; if (d == 0xFF) { continue; } bytes[(offsety + j) * newrect.W + offsetx + i] = d; } } } var umbmp = UnmanagedImage.FromByteArray(bytes, newrect.W, newrect.H, System.Drawing.Imaging.PixelFormat.Format8bppIndexed); umbmp.ToManagedImage().Save(meta.FileName + "." + newrect.X + "." + newrect.Y + "." + newrect.W + "." + newrect.H + ".crop.jpg", System.Drawing.Imaging.ImageFormat.Png); } }
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)); } }
private UnmanagedImage CreateImage(int frame, TifFileInfo fi, int threshold) { byte[] image = new byte[fi.sizeX * fi.sizeY]; int position = 0; byte zeroInd = 0; if (fi.fillHoles) { zeroInd = 1; } if (fi.bitsPerPixel == 8) { byte[][] oldImage = fi.image8bitFilter[frame]; for (int y = 0; y < fi.sizeY; y++) { image[position] = zeroInd; position++; for (int x = 1; x < fi.sizeX - 1; x++, position++) { if (oldImage[y][x] < threshold) { image[position] = 0; } else { image[position] = 255; } } image[position] = zeroInd; position++; } } else if (fi.bitsPerPixel == 16) { ushort[][] oldImage = fi.image16bitFilter[frame]; for (int y = 0; y < fi.sizeY; y++) { image[position] = zeroInd; position++; for (int x = 1; x < fi.sizeX - 1; x++, position++) { if (oldImage[y][x] < threshold) { image[position] = 0; } else { image[position] = 255; } } image[position] = zeroInd; position++; } } //start fill holes if (zeroInd == 1) { FillHoles(image, fi.sizeX); } //return image return(UnmanagedImage.FromByteArray(image, fi.sizeX, fi.sizeY, System.Drawing.Imaging.PixelFormat.Format8bppIndexed)); }
private bool watershedSegment(byte[] pixels, int width, int height) { // Create an array with the coordinates of all points between value 1 and 254 // This method, suggested by Stein Roervik (stein_at_kjemi-dot-unit-dot-no), // greatly speeds up the watershed segmentation routine. ImageStatistics @is = new ImageStatistics(UnmanagedImage.FromByteArray(pixels, width, height, PixelFormat.Format8bppIndexed)); int[] histogram = @is.Gray.Values; int arraySize = width * height - histogram[0] - histogram[255]; int[] coordinates = new int[arraySize]; //from pixel coordinates, low bits x, high bits y int highestValue = 0; int maxBinSize = 0; int offset = 0; int[] levelStart = new int[256]; for (int v = 1; v < 255; v++) { levelStart[v] = offset; offset += histogram[v]; if (histogram[v] > 0) { highestValue = v; } if (histogram[v] > maxBinSize) { maxBinSize = histogram[v]; } } int[] levelOffset = new int[highestValue + 1]; for (int y = 0, i = 0; y < height; y++) { for (int x = 0; x < width; x++, i++) { int v = pixels[i] & 255; if (v > 0 && v < 255) { offset = levelStart[v] + levelOffset[v]; coordinates[offset] = x | y << intEncodeShift; levelOffset[v]++; } } // for x } // for y // Create an array of the points (pixel offsets) that we set to 255 in one pass. // If we remember this list we need not create a snapshot of the ImageProcessor. int[] setPointList = new int[Math.Min(maxBinSize, (width * height + 2) / 3)]; // now do the segmentation, starting at the highest level and working down. // At each level, dilate the particle (set pixels to 255), constrained to pixels // whose values are at that level and also constrained (by the fateTable) // to prevent features from merging. int[] table = makeFateTable(); int[] directionSequence = new int[] { 7, 3, 1, 5, 0, 4, 2, 6 }; // diagonal directions first for (int level = highestValue; level >= 1; level--) { int remaining = histogram[level]; //number of points in the level that have not been processed int idle = 0; while (remaining > 0 && idle < 8) { int dIndex = 0; do { // expand each level in 8 directions int n = processLevel(directionSequence[dIndex % 8], pixels, table, levelStart[level], remaining, coordinates, setPointList, width, height); //IJ.log("level="+level+" direction="+directionSequence[dIndex%8]+" remain="+remaining+"-"+n); remaining -= n; // number of points processed if (n > 0) { idle = 0; // nothing processed in this direction? } dIndex++; } while (remaining > 0 && idle++ < 8); } // any pixels that we have not reached? if (remaining > 0 && level > 1) { int nextLevel = level; // find the next level to process do { nextLevel--; }while (nextLevel > 1 && histogram[nextLevel] == 0); // in principle we should add all unprocessed pixels of this level to the // tasklist of the next level. This would make it very slow for some images, // however. Thus we only add the pixels if they are at the border (of the // image or a thresholded area) and correct unprocessed pixels at the very // end by CleanupExtraLines if (nextLevel > 0) { int newNextLevelEnd = levelStart[nextLevel] + histogram[nextLevel]; for (int i = 0, p = levelStart[level]; i < remaining; i++, p++) { int xy = coordinates[p]; int x = xy & intEncodeXMask; int y = (xy & intEncodeYMask) >> intEncodeShift; int pOffset = x + y * width; bool addToNext = false; if (x == 0 || y == 0 || x == width - 1 || y == height - 1) { addToNext = true; // image border } else { for (int d = 0; d < 8; d++) { if (isWithin(x, y, d, width, height) && pixels[pOffset + dirOffset[d]] == 0) { addToNext = true; // border of area below threshold break; } } } if (addToNext) { coordinates[newNextLevelEnd++] = xy; } } // tasklist for the next level to process becomes longer by this: histogram[nextLevel] = newNextLevelEnd - levelStart[nextLevel]; } } } return(true); }