static PointF[,] SmoothOutOrientationMap(PointF[,] orientation, bool[,] mask) { const int radius = 1; var size = Point.SizeOf(mask); PointF[,] smoothed = size.Allocate <PointF>(); for (int y = 0; y < size.Y; ++y) { for (int x = 0; x < size.X; ++x) { if (mask[y, x]) { Rectangle neighbors = Rectangle.Between( new Point(Math.Max(0, x - radius), Math.Max(0, y - radius)), new Point(Math.Min(size.X, x + radius + 1), Math.Min(size.Y, y + radius + 1))); PointF sum = new PointF(); for (int ny = neighbors.Bottom; ny < neighbors.Top; ++ny) { for (int nx = neighbors.Left; nx < neighbors.Right; ++nx) { if (mask[ny, nx]) { sum += orientation[ny, nx]; } } } smoothed[y, x] = sum; } } } return(smoothed); }
static bool[,] ComputeInnerMask(bool[,] outer) { const int minBorderDistance = 14; var size = Point.SizeOf(outer); var inner = size.Allocate <bool>(); for (int y = 1; y < size.Y - 1; ++y) { for (int x = 1; x < size.X - 1; ++x) { inner[y, x] = outer[y, x]; } } if (minBorderDistance >= 1) { inner = ShrinkMask(inner, 1); } int total = 1; for (int step = 1; total + step <= minBorderDistance; step *= 2) { inner = ShrinkMask(inner, step); total += step; } if (total < minBorderDistance) { inner = ShrinkMask(inner, minBorderDistance - total); } return(inner); }
static bool[,] Binarize(double[,] input, double[,] baseline, bool[,] mask, BlockMap blocks) { var size = Point.SizeOf(input); var binarized = size.Allocate <bool>(); for (int blockY = 0; blockY < blocks.AllBlocks.Height; ++blockY) { for (int blockX = 0; blockX < blocks.AllBlocks.Width; ++blockX) { if (mask[blockY, blockX]) { Rectangle rect = blocks.BlockAreas[blockY, blockX]; for (int y = rect.Bottom; y < rect.Top; ++y) { for (int x = rect.Left; x < rect.Right; ++x) { if (input[y, x] - baseline[y, x] > 0) { binarized[y, x] = true; } } } } } } return(binarized); }
static void MergeMask(bool[,] mask, bool[,] merged) { var size = Point.SizeOf(mask); for (int y = 0; y < size.Y; ++y) { for (int x = 0; x < size.X; ++x) { mask[y, x] |= merged[y, x]; } } }
static bool[,] ShrinkMask(bool[,] mask, int amount) { var size = Point.SizeOf(mask); var shrunk = size.Allocate <bool>(); for (int y = amount; y < size.Y - amount; ++y) { for (int x = amount; x < size.X - amount; ++x) { shrunk[y, x] = mask[y - amount, x] && mask[y + amount, x] && mask[y, x - amount] && mask[y, x + amount]; } } return(shrunk); }
static bool[,] Invert(bool[,] binary, bool[,] mask) { var size = Point.SizeOf(binary); var inverted = size.Allocate <bool>(); for (int y = 0; y < size.Y; ++y) { for (int x = 0; x < size.X; ++x) { inverted[y, x] = !binary[y, x] && mask[y, x]; } } return(inverted); }
static bool[,] InvertMask(bool[,] mask) { var size = Point.SizeOf(mask); var inverted = size.Allocate <bool>(); for (int y = 0; y < size.Y; ++y) { for (int x = 0; x < size.X; ++x) { inverted[y, x] = !mask[y, x]; } } return(inverted); }
static void CleanupBinarized(bool[,] binary) { var size = Point.SizeOf(binary); var islands = FilterBinarized(InvertMask(binary)); var holes = FilterBinarized(binary); for (int y = 0; y < size.Y; ++y) { for (int x = 0; x < size.X; ++x) { binary[y, x] = binary[y, x] && !islands[y, x] || holes[y, x]; } } RemoveCrosses(binary); }
static byte[,] ConvertOrientationVectorsToAngles(PointF[,] vectors, bool[,] mask) { var size = Point.SizeOf(mask); byte[,] angles = size.Allocate <byte>(); for (int y = 0; y < size.Y; ++y) { for (int x = 0; x < size.X; ++x) { if (mask[y, x]) { angles[y, x] = Angle.ToByte(Angle.Atan(vectors[y, x])); } } } return(angles); }
static bool[,] ComputeAbsoluteContrast(byte[,] contrast) { const int limit = 17; var size = Point.SizeOf(contrast); bool[,] result = size.Allocate <bool>(); for (int y = 0; y < size.Y; ++y) { for (int x = 0; x < size.X; ++x) { if (contrast[y, x] < limit) { result[y, x] = true; } } } return(result); }
static void RemoveCrosses(bool[,] input) { var size = Point.SizeOf(input); bool any = true; while (any) { any = false; for (int y = 0; y < size.Y - 1; ++y) { for (int x = 0; x < size.X - 1; ++x) { if (input[y, x] && input[y + 1, x + 1] && !input[y + 1, x] && !input[y, x + 1] || input[y + 1, x] && input[y, x + 1] && !input[y, x] && !input[y + 1, x + 1]) { input[y, x] = false; input[y + 1, x] = false; input[y, x + 1] = false; input[y + 1, x + 1] = false; any = true; } } } } }
static bool[,] ApplyVotingFilter(bool[,] input, int radius = 1, double majority = 0.51, int borderDist = 0) { var size = Point.SizeOf(input); Rectangle rect = new Rectangle(new Point(borderDist, borderDist), new Point(size.X - 2 * borderDist, size.Y - 2 * borderDist)); var output = size.Allocate <bool>(); for (int y = rect.RangeY.Begin; y < rect.RangeY.End; ++y) { for (int x = rect.Left; x < rect.Right; ++x) { Rectangle neighborhood = Rectangle.Between( new Point(Math.Max(x - radius, 0), Math.Max(y - radius, 0)), new Point(Math.Min(x + radius + 1, size.X), Math.Min(y + radius + 1, size.Y))); int ones = 0; for (int ny = neighborhood.Bottom; ny < neighborhood.Top; ++ny) { for (int nx = neighborhood.Left; nx < neighborhood.Right; ++nx) { if (input[ny, nx]) { ++ones; } } } double voteWeight = 1.0 / neighborhood.TotalArea; if (ones * voteWeight >= majority) { output[y, x] = true; } } } return(output); }
static byte[,] ScaleImage(byte[,] input, int newX, int newY) { var oldSize = Point.SizeOf(input); var output = new byte[newY, newX]; double scaleX = newX / (double)oldSize.X; double scaleY = newY / (double)oldSize.Y; double descaleX = 1 / scaleX; double descaleY = 1 / scaleY; for (int y = 0; y < newY; ++y) { double y1 = y * descaleY; double y2 = y1 + descaleY; int y1i = (int)y1; int y2i = (int)Math.Ceiling(y2); for (int x = 0; x < newX; ++x) { double x1 = x * descaleX; double x2 = x1 + descaleX; int x1i = (int)x1; int x2i = (int)Math.Ceiling(x2); double sum = 0; for (int oy = y1i; oy < y2i; ++oy) { var ry = Math.Min(oy + 1, y2) - Math.Max(oy, y1); for (int ox = x1i; ox < x2i; ++ox) { var rx = Math.Min(ox + 1, x2) - Math.Max(ox, x1); sum += rx * ry * input[oy, ox]; } } output[y, x] = Convert.ToByte(sum * (scaleX * scaleY)); } } return(output); }
static byte[,] ScaleImage(byte[,] input, int dpi) { var size = Point.SizeOf(input); return(ScaleImage(input, Convert.ToInt32(500.0 / dpi * size.X), Convert.ToInt32(500.0 / dpi * size.Y))); }