static BooleanMatrix Mask(BlockMap blocks, HistogramCube histogram) { var contrast = ClipContrast(blocks, histogram); var mask = FilterAbsoluteContrast(contrast); mask.Merge(FilterRelativeContrast(contrast, blocks)); // https://sourceafis.machinezoo.com/transparency/combined-mask FingerprintTransparency.Current.Log("combined-mask", mask); mask.Merge(FilterBlockErrors(mask)); mask.Invert(); mask.Merge(FilterBlockErrors(mask)); mask.Merge(FilterBlockErrors(mask)); mask.Merge(Vote(mask, null, Parameters.MaskVoteRadius, Parameters.MaskVoteMajority, Parameters.MaskVoteBorderDistance)); // https://sourceafis.machinezoo.com/transparency/filtered-mask FingerprintTransparency.Current.Log("filtered-mask", mask); return(mask); }
static HistogramCube Histogram(BlockMap blocks, DoubleMatrix image) { var histogram = new HistogramCube(blocks.Primary.Blocks, Parameters.HistogramDepth); foreach (var block in blocks.Primary.Blocks.Iterate()) { var area = blocks.Primary.Block(block); for (int y = area.Top; y < area.Bottom; ++y) { for (int x = area.Left; x < area.Right; ++x) { int depth = (int)(image[x, y] * histogram.Bins); histogram.Increment(block, histogram.Constrain(depth)); } } } // https://sourceafis.machinezoo.com/transparency/histogram FingerprintTransparency.Current.Log("histogram", histogram); return(histogram); }
static HistogramCube SmoothHistogram(BlockMap blocks, HistogramCube input) { var blocksAround = new IntPoint[] { new IntPoint(0, 0), new IntPoint(-1, 0), new IntPoint(0, -1), new IntPoint(-1, -1) }; var output = new HistogramCube(blocks.Secondary.Blocks, input.Bins); foreach (var corner in blocks.Secondary.Blocks.Iterate()) { foreach (var relative in blocksAround) { var block = corner + relative; if (blocks.Primary.Blocks.Contains(block)) { for (int i = 0; i < input.Bins; ++i) { output.Add(corner, i, input[block, i]); } } } } // https://sourceafis.machinezoo.com/transparency/smoothed-histogram FingerprintTransparency.Current.Log("smoothed-histogram", output); return(output); }
static DoubleMatrix ClipContrast(BlockMap blocks, HistogramCube histogram) { var result = new DoubleMatrix(blocks.Primary.Blocks); foreach (var block in blocks.Primary.Blocks.Iterate()) { int volume = histogram.Sum(block); int clipLimit = Doubles.RoundToInt(volume * Parameters.ClippedContrast); int accumulator = 0; int lowerBound = histogram.Bins - 1; for (int i = 0; i < histogram.Bins; ++i) { accumulator += histogram[block, i]; if (accumulator > clipLimit) { lowerBound = i; break; } } accumulator = 0; int upperBound = 0; for (int i = histogram.Bins - 1; i >= 0; --i) { accumulator += histogram[block, i]; if (accumulator > clipLimit) { upperBound = i; break; } } result[block] = (upperBound - lowerBound) * (1.0 / (histogram.Bins - 1)); } // https://sourceafis.machinezoo.com/transparency/contrast FingerprintTransparency.Current.Log("contrast", result); return(result); }
static DoubleMatrix Equalize(BlockMap blocks, DoubleMatrix image, HistogramCube histogram, BooleanMatrix blockMask) { const double rangeMin = -1; const double rangeMax = 1; const double rangeSize = rangeMax - rangeMin; double widthMax = rangeSize / histogram.Bins * Parameters.MaxEqualizationScaling; double widthMin = rangeSize / histogram.Bins * Parameters.MinEqualizationScaling; var limitedMin = new double[histogram.Bins]; var limitedMax = new double[histogram.Bins]; var dequantized = new double[histogram.Bins]; for (int i = 0; i < histogram.Bins; ++i) { limitedMin[i] = Math.Max(i * widthMin + rangeMin, rangeMax - (histogram.Bins - 1 - i) * widthMax); limitedMax[i] = Math.Min(i * widthMax + rangeMin, rangeMax - (histogram.Bins - 1 - i) * widthMin); dequantized[i] = i / (double)(histogram.Bins - 1); } var mappings = new Dictionary <IntPoint, double[]>(); foreach (var corner in blocks.Secondary.Blocks.Iterate()) { double[] mapping = new double[histogram.Bins]; mappings[corner] = mapping; if (blockMask.Get(corner, false) || blockMask.Get(corner.X - 1, corner.Y, false) || blockMask.Get(corner.X, corner.Y - 1, false) || blockMask.Get(corner.X - 1, corner.Y - 1, false)) { double step = rangeSize / histogram.Sum(corner); double top = rangeMin; for (int i = 0; i < histogram.Bins; ++i) { double band = histogram[corner, i] * step; double equalized = top + dequantized[i] * band; top += band; if (equalized < limitedMin[i]) { equalized = limitedMin[i]; } if (equalized > limitedMax[i]) { equalized = limitedMax[i]; } mapping[i] = equalized; } } } var result = new DoubleMatrix(blocks.Pixels); foreach (var block in blocks.Primary.Blocks.Iterate()) { var area = blocks.Primary.Block(block); if (blockMask[block]) { var topleft = mappings[block]; var topright = mappings[new IntPoint(block.X + 1, block.Y)]; var bottomleft = mappings[new IntPoint(block.X, block.Y + 1)]; var bottomright = mappings[new IntPoint(block.X + 1, block.Y + 1)]; for (int y = area.Top; y < area.Bottom; ++y) { for (int x = area.Left; x < area.Right; ++x) { int depth = histogram.Constrain((int)(image[x, y] * histogram.Bins)); double rx = (x - area.X + 0.5) / area.Width; double ry = (y - area.Y + 0.5) / area.Height; result[x, y] = Doubles.Interpolate(bottomleft[depth], bottomright[depth], topleft[depth], topright[depth], rx, ry); } } } else { for (int y = area.Top; y < area.Bottom; ++y) { for (int x = area.Left; x < area.Right; ++x) { result[x, y] = -1; } } } } // https://sourceafis.machinezoo.com/transparency/equalized-image FingerprintTransparency.Current.Log("equalized-image", result); return(result); }