public BinaryMap DetectLowContrast(byte[,] contrast, BlockMap blocks) { List<byte> sortedContrast = new List<byte>(); foreach (byte contrastItem in contrast) sortedContrast.Add(contrastItem); sortedContrast.Sort(); sortedContrast.Reverse(); int pixelsPerBlock = Calc.GetArea(blocks.PixelCount) / blocks.AllBlocks.TotalArea; int sampleCount = Math.Min(sortedContrast.Count, SampleSize / pixelsPerBlock); int consideredBlocks = Math.Max(Convert.ToInt32(sampleCount * SampleFraction), 1); int averageContrast = 0; for (int i = 0; i < consideredBlocks; ++i) averageContrast += sortedContrast[i]; averageContrast /= consideredBlocks; byte limit = Convert.ToByte(averageContrast * RelativeLimit); BinaryMap result = new BinaryMap(blocks.BlockCount.Width, blocks.BlockCount.Height); for (int y = 0; y < result.Height; ++y) for (int x = 0; x < result.Width; ++x) if (contrast[y, x] < limit) result.SetBitOne(x, y); Logger.Log(result); return result; }
public float[,] Smooth(float[,] input, byte[,] orientation, BinaryMap mask, BlockMap blocks) { Point[][] lines = Lines.Construct(); float[,] output = new float[input.GetLength(0), input.GetLength(1)]; Parallel.ForEach(blocks.AllBlocks, delegate(Point block) { if (mask.GetBit(block)) { Point[] line = lines[Angle.Quantize(Angle.Add(orientation[block.Y, block.X], AngleOffset), lines.Length)]; foreach (Point linePoint in line) { RectangleC target = blocks.BlockAreas[block]; RectangleC source = target.GetShifted(linePoint); source.Clip(new RectangleC(blocks.PixelCount)); target = source.GetShifted(Calc.Negate(linePoint)); for (int y = target.Bottom; y < target.Top; ++y) for (int x = target.Left; x < target.Right; ++x) output[y, x] += input[y + linePoint.Y, x + linePoint.X]; } RectangleC blockArea = blocks.BlockAreas[block]; for (int y = blockArea.Bottom; y < blockArea.Top; ++y) for (int x = blockArea.Left; x < blockArea.Right; ++x) output[y, x] *= 1f / line.Length; } }); Logger.Log(output); return output; }
float[,] PerformEqualization(BlockMap blocks, byte[,] image, float[, ,] equalization, BinaryMap blockMask) { float[,] result = new float[blocks.PixelCount.Height, blocks.PixelCount.Width]; Parallel.ForEach(blocks.AllBlocks, delegate(Point block) { if (blockMask.GetBit(block)) { RectangleC area = blocks.BlockAreas[block]; for (int y = area.Bottom; y < area.Top; ++y) for (int x = area.Left; x < area.Right; ++x) { byte pixel = image[y, x]; float bottomLeft = equalization[block.Y, block.X, pixel]; float bottomRight = equalization[block.Y, block.X + 1, pixel]; float topLeft = equalization[block.Y + 1, block.X, pixel]; float topRight = equalization[block.Y + 1, block.X + 1, pixel]; PointF fraction = area.GetFraction(new Point(x, y)); result[y, x] = Calc.Interpolate(topLeft, topRight, bottomLeft, bottomRight, fraction); } } }); Logger.Log(result); return result; }
public byte[,] Detect(float[,] image, BinaryMap mask, BlockMap blocks) { PointF[,] accumulated = AccumulateOrientations(image, mask, blocks); PointF[,] byBlock = SumBlocks(accumulated, blocks, mask); PointF[,] smooth = Smooth(byBlock, mask); byte[,] angles = ToAngles(smooth, mask); Logger.Log(angles); return angles; }
public short[, ,] Analyze(BlockMap blocks, byte[,] image) { short[, ,] histogram = new short[blocks.BlockCount.Height, blocks.BlockCount.Width, 256]; Parallel.ForEach(blocks.AllBlocks, delegate(Point block) { RectangleC area = blocks.BlockAreas[block]; for (int y = area.Bottom; y < area.Top; ++y) for (int x = area.Left; x < area.Right; ++x) ++histogram[block.Y, block.X, image[y, x]]; }); return histogram; }
public BinaryMap FillBlocks(BlockMap blocks) { BinaryMap result = new BinaryMap(blocks.PixelCount); Parallel.For(0, blocks.BlockCount.Height, delegate(int blockY) { for (int blockX = 0; blockX < blocks.BlockCount.Width; ++blockX) { if (GetBit(blockX, blockY)) { result.Fill(blocks.BlockAreas[blockY, blockX]); } } }); return(result); }
public BinaryMap FillCornerAreas(BlockMap blocks) { BinaryMap result = new BinaryMap(blocks.PixelCount); Parallel.For(0, blocks.CornerCount.Height, delegate(int cornerY) { for (int cornerX = 0; cornerX < blocks.CornerCount.Width; ++cornerX) { if (GetBit(cornerX, cornerY)) { result.Fill(blocks.CornerAreas[cornerY, cornerX]); } } }); return(result); }
public short[, ,] SmoothAroundCorners(BlockMap blocks, short[, ,] input) { Point[] blocksAround = new Point[] { new Point(0, 0), new Point(-1, 0), new Point(0, -1), new Point(-1, -1) }; short[, ,] output = new short[blocks.CornerCount.Height, blocks.CornerCount.Width, 256]; Parallel.ForEach(blocks.AllCorners, delegate(Point corner) { foreach (Point relative in blocksAround) { Point block = Calc.Add(corner, relative); if (blocks.AllBlocks.Contains(block)) { for (int i = 0; i < 256; ++i) output[corner.Y, corner.X, i] += input[block.Y, block.X, i]; } } }); return output; }
float[, ,] ComputeEqualization(BlockMap blocks, short[, ,] histogram, BinaryMap blockMask) { float widthMax = RangeSize / 256f * MaxScaling; float widthMin = RangeSize / 256f * MinScaling; float[] limitedMin = new float[256]; float[] limitedMax = new float[256]; for (int i = 0; i < 256; ++i) { limitedMin[i] = Math.Max(i * widthMin + RangeMin, RangeMax - (255 - i) * widthMax); limitedMax[i] = Math.Min(i * widthMax + RangeMin, RangeMax - (255 - i) * widthMin); } float[, ,] equalization = new float[blocks.CornerCount.Height, blocks.CornerCount.Width, 256]; Parallel.ForEach(blocks.AllCorners, delegate(Point corner) { if (blockMask.GetBitSafe(corner.X, corner.Y, false) || blockMask.GetBitSafe(corner.X - 1, corner.Y, false) || blockMask.GetBitSafe(corner.X, corner.Y - 1, false) || blockMask.GetBitSafe(corner.X - 1, corner.Y - 1, false)) { int area = 0; for (int i = 0; i < 256; ++i) area += histogram[corner.Y, corner.X, i]; float widthWeigth = RangeSize / area; float top = RangeMin; for (int i = 0; i < 256; ++i) { float width = histogram[corner.Y, corner.X, i] * widthWeigth; float equalized = top + ToFloatTable[i] * width; top += width; float limited = equalized; if (limited < limitedMin[i]) limited = limitedMin[i]; if (limited > limitedMax[i]) limited = limitedMax[i]; equalization[corner.Y, corner.X, i] = limited; } } }); return equalization; }
public short[, ,] Smooth(BlockMap blocks, short[, ,] input) { short[, ,] output = new short[blocks.CornerCount.Height, blocks.CornerCount.Width, 256]; Parallel.ForEach(blocks.AllCorners, delegate(Point corner) { for (int i = 0; i < 256; ++i) output[corner.Y, corner.X, i] = input[corner.Y, corner.X, i]; foreach (Point neigborRelative in Neighborhood.CornerNeighbors) { Point neighbor = Calc.Add(corner, neigborRelative); if (blocks.AllCorners.Contains(neighbor)) { for (int i = 0; i < 256; ++i) output[corner.Y, corner.X, i] += input[neighbor.Y, neighbor.X, i]; } } }); return output; }
public BinaryMap Binarize(float[,] input, float[,] baseline, BinaryMap mask, BlockMap blocks) { BinaryMap binarized = new BinaryMap(input.GetLength(1), input.GetLength(0)); Parallel.For(0, blocks.AllBlocks.Height, delegate(int blockY) { for (int blockX = 0; blockX < blocks.AllBlocks.Width; ++blockX) { if (mask.GetBit(blockX, blockY)) { RectangleC 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.SetBitOne(x, y); } } }); Logger.Log(binarized); return binarized; }
public byte[,] Compute(BlockMap blocks, short[, ,] histogram) { byte[,] result = new byte[blocks.BlockCount.Height, blocks.BlockCount.Width]; Parallel.ForEach(blocks.AllBlocks, delegate(Point block) { int area = 0; for (int i = 0; i < 256; ++i) area += histogram[block.Y, block.X, i]; int clipLimit = Convert.ToInt32(area * ClipFraction); int accumulator = 0; int lowerBound = 255; for (int i = 0; i < 256; ++i) { accumulator += histogram[block.Y, block.X, i]; if (accumulator > clipLimit) { lowerBound = i; break; } } accumulator = 0; int upperBound = 0; for (int i = 255; i >= 0; --i) { accumulator += histogram[block.Y, block.X, i]; if (accumulator > clipLimit) { upperBound = i; break; } } result[block.Y, block.X] = (byte)(upperBound - lowerBound); }); Logger.Log(result); return result; }
PointF[,] AccumulateOrientations(float[,] input, BinaryMap mask, BlockMap blocks) { List<List<NeighborInfo>> neighbors = PrepareNeighbors(); PointF[,] orientation = new PointF[input.GetLength(0), input.GetLength(1)]; Parallel.For(0, mask.Height, delegate(int blockY) { Range validMaskRange = GetMaskLineRange(mask, blockY); if (validMaskRange.Length > 0) { Range validXRange = new Range(blocks.BlockAreas[blockY, validMaskRange.Begin].Left, blocks.BlockAreas[blockY, validMaskRange.End - 1].Right); for (int y = blocks.BlockAreas[blockY, 0].Bottom; y < blocks.BlockAreas[blockY, 0].Top; ++y) { foreach (NeighborInfo neighbor in neighbors[y % neighbors.Count]) { int radius = Math.Max(Math.Abs(neighbor.Position.X), Math.Abs(neighbor.Position.Y)); if (y - radius >= 0 && y + radius < input.GetLength(0)) { Range xRange = new Range(Math.Max(radius, validXRange.Begin), Math.Min(input.GetLength(1) - radius, validXRange.End)); for (int x = xRange.Begin; x < xRange.End; ++x) { float before = input[y - neighbor.Position.Y, x - neighbor.Position.X]; float at = input[y, x]; float after = input[y + neighbor.Position.Y, x + neighbor.Position.X]; float strength = at - Math.Max(before, after); if (strength > 0) orientation[y, x] = Calc.Add(orientation[y, x], Calc.Multiply(strength, neighbor.Orientation)); } } } } } }); Logger.Log("Raw", orientation); return orientation; }
public BinaryMap FillBlocks(BlockMap blocks) { BinaryMap result = new BinaryMap(blocks.PixelCount); Parallel.For(0, blocks.BlockCount.Height, delegate(int blockY) { for (int blockX = 0; blockX < blocks.BlockCount.Width; ++blockX) if (GetBit(blockX, blockY)) result.Fill(blocks.BlockAreas[blockY, blockX]); }); return result; }
public void LogRectangleGrid(BlockMap.RectangleGrid grid, BinaryWriter binWrite, TextWriter tw, string name) { LogPointGrid(grid.Corners, binWrite, tw, name); }
PointF[,] SumBlocks(PointF[,] orientation, BlockMap blocks, BinaryMap mask) { PointF[,] sums = new PointF[blocks.BlockCount.Height, blocks.BlockCount.Width]; Parallel.ForEach(blocks.AllBlocks, delegate(Point block) { if (mask.GetBit(block)) { PointF sum = new PointF(); RectangleC area = blocks.BlockAreas[block]; for (int y = area.Bottom; y < area.Top; ++y) for (int x = area.Left; x < area.Right; ++x) sum = Calc.Add(sum, orientation[y, x]); sums[block.Y, block.X] = sum; } }); return sums; }
public float[,] Equalize(BlockMap blocks, byte[,] image, short[, ,] histogram, BinaryMap blockMask) { float[, ,] equalization = ComputeEqualization(blocks, histogram, blockMask); return PerformEqualization(blocks, image, equalization, blockMask); }
public void LogPointGrid(BlockMap.PointGrid grid, BinaryWriter binWrite, TextWriter tw, string name) { binWrite.Write(grid.AllX.Count()); tw.WriteLine("{1} AllX Length: {0}", grid.AllX.Count(), name); foreach (var i in grid.AllX) { binWrite.Write(i); tw.WriteLine(i); } binWrite.Write(grid.AllY.Count()); tw.WriteLine("{1} AllY Length: {0}", grid.AllY.Count(), name); foreach (var i in grid.AllY) { binWrite.Write(i); tw.WriteLine(i); } }
public TemplateBuilder Extract(byte[,] invertedImage, int dpi) { TemplateBuilder template = null; DpiAdjuster.Adjust(this, dpi, delegate() { byte[,] image = ImageInverter.GetInverted(invertedImage); BlockMap blocks = new BlockMap(new Size(image.GetLength(1), image.GetLength(0)), BlockSize); Logger.Log("BlockMap", blocks); short[, ,] histogram = Histogram.Analyze(blocks, image); short[, ,] smoothHistogram = Histogram.SmoothAroundCorners(blocks, histogram); BinaryMap mask = Mask.ComputeMask(blocks, histogram); float[,] equalized = Equalizer.Equalize(blocks, image, smoothHistogram, mask); byte[,] orientation = Orientation.Detect(equalized, mask, blocks); float[,] smoothed = RidgeSmoother.Smooth(equalized, orientation, mask, blocks); float[,] orthogonal = OrthogonalSmoother.Smooth(smoothed, orientation, mask, blocks); BinaryMap binary = Binarizer.Binarize(smoothed, orthogonal, mask, blocks); binary.AndNot(BinarySmoother.Filter(binary.GetInverted())); binary.Or(BinarySmoother.Filter(binary)); Logger.Log("BinarySmoothingResult", binary); CrossRemover.Remove(binary); BinaryMap pixelMask = mask.FillBlocks(blocks); BinaryMap innerMask = InnerMask.Compute(pixelMask); BinaryMap inverted = binary.GetInverted(); inverted.And(pixelMask); SkeletonBuilder ridges = null; SkeletonBuilder valleys = null; Parallel.Invoke( () => { ridges = ProcessSkeleton("Ridges", binary); }, () => { valleys = ProcessSkeleton("Valleys", inverted); }); template = new TemplateBuilder(); template.OriginalDpi = dpi; template.OriginalWidth = invertedImage.GetLength(1); template.OriginalHeight = invertedImage.GetLength(0); MinutiaCollector.Collect(ridges, TemplateBuilder.MinutiaType.Ending, template); MinutiaCollector.Collect(valleys, TemplateBuilder.MinutiaType.Bifurcation, template); MinutiaMask.Filter(template, innerMask); StandardDpiScaling.Scale(template); MinutiaCloudRemover.Filter(template); UniqueMinutiaSorter.Filter(template); MinutiaSorter.Shuffle(template); Logger.Log("FinalTemplate", template); }); return template; }
public BinaryMap FillCornerAreas(BlockMap blocks) { BinaryMap result = new BinaryMap(blocks.PixelCount); Parallel.For(0, blocks.CornerCount.Height, delegate(int cornerY) { for (int cornerX = 0; cornerX < blocks.CornerCount.Width; ++cornerX) if (GetBit(cornerX, cornerY)) result.Fill(blocks.CornerAreas[cornerY, cornerX]); }); return result; }
public TemplateBuilder Extract(byte[,] invertedImage, int dpi) { TemplateBuilder template = null; DpiAdjuster.Adjust(this, dpi, delegate () { byte[,] image = ImageInverter.GetInverted(invertedImage); BlockMap blocks = new BlockMap(new Size(image.GetLength(1), image.GetLength(0)), BlockSize); Logger.Log("BlockMap", blocks); //Testing Start var outFileDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "bloackMapOut" + DateTime.Now.Second + ".bin"); var outFileText = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "blockMapTextOut" + DateTime.Now.Second + ".txt"); var file = new FileStream(outFileDir, FileMode.CreateNew); var binWrite = new BinaryWriter(file); TextWriter tw = new StreamWriter(outFileText); LogSize(blocks.PixelCount, binWrite, tw, "PixelCount"); LogSize(blocks.BlockCount, binWrite, tw, "BlockCount"); LogSize(blocks.CornerCount, binWrite, tw, "CornerCount"); LogRectangleC(blocks.AllBlocks, binWrite, tw, "AllBlocks"); LogRectangleC(blocks.AllCorners, binWrite, tw, "AllCorners"); LogPointGrid(blocks.Corners, binWrite, tw, "Corners"); LogRectangleGrid(blocks.BlockAreas, binWrite, tw, "BlockAreas"); LogPointGrid(blocks.BlockCenters, binWrite, tw, "BlockCenters"); LogRectangleGrid(blocks.CornerAreas, binWrite, tw, "CornerAreas"); binWrite.Close(); tw.Close(); //Testing End short[,,] histogram = Histogram.Analyze(blocks, image); ////testing //var outFileDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "histogramImageInput" + ".bin"); //var file = new FileStream(outFileDir, FileMode.CreateNew); //var binWrite = new BinaryWriter(file); //binWrite.Write(image.GetLength(0)); //binWrite.Write(image.GetLength(1)); //for (var i = 0; i < image.GetLength(0); i++) //{ // for (var j = 0; j < image.GetLength(1); j++) // { // binWrite.Write(image[i, j]); // } //} //binWrite.Close(); ////End testing ////Testing Start //Count++; //var outFileDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "histogramOut" + Count + ".bin"); //var outFileText = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), // "histogramTextOut" + Count + ".txt"); //var file = new FileStream(outFileDir, FileMode.CreateNew); //var binWrite = new BinaryWriter(file); //TextWriter tw = new StreamWriter(outFileText); //binWrite.Write(histogram.GetLength(0)); //tw.WriteLine(histogram.GetLength(0)); //binWrite.Write(histogram.GetLength(1)); //tw.WriteLine(histogram.GetLength(1)); //binWrite.Write(histogram.GetLength(2)); //tw.WriteLine(histogram.GetLength(2)); //for (var i = 0; i < histogram.GetLength(0); i++) //{ // for (var j = 0; j < histogram.GetLength(1); j++) // { // for (var k = 0; k < histogram.GetLength(2); k++) // { // binWrite.Write(histogram[i, j, k]); // tw.WriteLine(histogram[i, j, k]); // } // } //} //binWrite.Close(); //file.Close(); //tw.Close(); //Testing Finish //Testing Start //outFileDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "bloackMapOutPostHis" + DateTime.Now.Second + ".bin"); //outFileText = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), // "blockMapTextOutPostHis" + DateTime.Now.Second + ".txt"); //file = new FileStream(outFileDir, FileMode.CreateNew); //binWrite = new BinaryWriter(file); //tw = new StreamWriter(outFileText); //LogSize(blocks.PixelCount, binWrite, tw); //LogSize(blocks.BlockCount, binWrite, tw); //LogSize(blocks.CornerCount, binWrite, tw); //LogRectangleC(blocks.AllBlocks, binWrite, tw); //LogRectangleC(blocks.AllCorners, binWrite, tw); //LogPointGrid(blocks.Corners, binWrite, tw); //LogRectangleGrid(blocks.BlockAreas, binWrite, tw); //LogPointGrid(blocks.BlockCenters, binWrite, tw); //LogRectangleGrid(blocks.CornerAreas, binWrite, tw); //binWrite.Close(); //tw.Close(); //Testing End short[,,] smoothHistogram = Histogram.SmoothAroundCorners(blocks, histogram); BinaryMap mask = Mask.ComputeMask(blocks, histogram); float[,] equalized = Equalizer.Equalize(blocks, image, smoothHistogram, mask); byte[,] orientation = Orientation.Detect(equalized, mask, blocks); float[,] smoothed = RidgeSmoother.Smooth(equalized, orientation, mask, blocks); float[,] orthogonal = OrthogonalSmoother.Smooth(smoothed, orientation, mask, blocks); BinaryMap binary = Binarizer.Binarize(smoothed, orthogonal, mask, blocks); binary.AndNot(BinarySmoother.Filter(binary.GetInverted())); binary.Or(BinarySmoother.Filter(binary)); Logger.Log("BinarySmoothingResult", binary); CrossRemover.Remove(binary); BinaryMap pixelMask = mask.FillBlocks(blocks); BinaryMap innerMask = InnerMask.Compute(pixelMask); BinaryMap inverted = binary.GetInverted(); inverted.And(pixelMask); SkeletonBuilder ridges = null; SkeletonBuilder valleys = null; Parallel.Invoke( () => { ridges = ProcessSkeleton("Ridges", binary); }, () => { valleys = ProcessSkeleton("Valleys", inverted); }); template = new TemplateBuilder(); template.OriginalDpi = dpi; template.OriginalWidth = invertedImage.GetLength(1); template.OriginalHeight = invertedImage.GetLength(0); MinutiaCollector.Collect(ridges, TemplateBuilder.MinutiaType.Ending, template); MinutiaCollector.Collect(valleys, TemplateBuilder.MinutiaType.Bifurcation, template); MinutiaMask.Filter(template, innerMask); StandardDpiScaling.Scale(template); MinutiaCloudRemover.Filter(template); UniqueMinutiaSorter.Filter(template); MinutiaSorter.Shuffle(template); Logger.Log("FinalTemplate", template); }); return template; }