static BooleanMatrix FilterRelativeContrast(DoubleMatrix contrast, BlockMap blocks) { var sortedContrast = new List <double>(); foreach (var block in contrast.Size.Iterate()) { sortedContrast.Add(contrast[block]); } sortedContrast.Sort(); sortedContrast.Reverse(); int pixelsPerBlock = blocks.Pixels.Area / blocks.Primary.Blocks.Area; int sampleCount = Math.Min(sortedContrast.Count, Parameters.RelativeContrastSample / pixelsPerBlock); int consideredBlocks = Math.Max(Doubles.RoundToInt(sampleCount * Parameters.RelativeContrastPercentile), 1); double averageContrast = 0; for (int i = 0; i < consideredBlocks; ++i) { averageContrast += sortedContrast[i]; } averageContrast /= consideredBlocks; var limit = averageContrast * Parameters.MinRelativeContrast; var result = new BooleanMatrix(blocks.Primary.Blocks); foreach (var block in blocks.Primary.Blocks.Iterate()) { if (contrast[block] < limit) { result[block] = true; } } // https://sourceafis.machinezoo.com/transparency/relative-contrast-mask FingerprintTransparency.Current.Log("relative-contrast-mask", result); return(result); }
public IntPoint[] LineTo(IntPoint to) { IntPoint[] result; var relative = to - this; if (Math.Abs(relative.X) >= Math.Abs(relative.Y)) { result = new IntPoint[Math.Abs(relative.X) + 1]; if (relative.X > 0) { for (int i = 0; i <= relative.X; ++i) { result[i] = new IntPoint(X + i, Y + Doubles.RoundToInt(i * (relative.Y / (double)relative.X))); } } else if (relative.X < 0) { for (int i = 0; i <= -relative.X; ++i) { result[i] = new IntPoint(X - i, Y - Doubles.RoundToInt(i * (relative.Y / (double)relative.X))); } } else { result[0] = this; } } else { result = new IntPoint[Math.Abs(relative.Y) + 1]; if (relative.Y > 0) { for (int i = 0; i <= relative.Y; ++i) { result[i] = new IntPoint(X + Doubles.RoundToInt(i * (relative.X / (double)relative.Y)), Y + i); } } else if (relative.Y < 0) { for (int i = 0; i <= -relative.Y; ++i) { result[i] = new IntPoint(X - Doubles.RoundToInt(i * (relative.X / (double)relative.Y)), Y - i); } } else { result[0] = this; } } return(result); }
static EdgeShape() { for (int y = 0; y < PolarCacheRadius; ++y) { for (int x = 0; x < PolarCacheRadius; ++x) { PolarDistanceCache[y * PolarCacheRadius + x] = Doubles.RoundToInt(Math.Sqrt(Doubles.Sq(x) + Doubles.Sq(y))); if (y > 0 || x > 0) { PolarAngleCache[y * PolarCacheRadius + x] = DoubleAngle.Atan(x, y); } else { PolarAngleCache[y * PolarCacheRadius + x] = 0; } } } }
static ConsideredOrientation[][] PlanOrientations() { var random = new OrientationRandom(); var splits = new ConsideredOrientation[Parameters.OrientationSplit][]; for (int i = 0; i < Parameters.OrientationSplit; ++i) { var orientations = splits[i] = new ConsideredOrientation[Parameters.OrientationsChecked]; for (int j = 0; j < Parameters.OrientationsChecked; ++j) { var sample = orientations[j] = new ConsideredOrientation(); while (true) { double angle = random.Next() * Math.PI; double distance = Doubles.InterpolateExponential(Parameters.MinOrientationRadius, Parameters.MaxOrientationRadius, random.Next()); sample.Offset = (distance * DoubleAngle.ToVector(angle)).Round(); if (sample.Offset == IntPoint.Zero) { continue; } if (sample.Offset.Y < 0) { continue; } bool duplicate = false; for (int jj = 0; jj < j; ++jj) { if (orientations[jj].Offset == sample.Offset) { duplicate = true; } } if (duplicate) { continue; } break; } sample.Orientation = DoubleAngle.ToVector(DoubleAngle.Add(DoubleAngle.ToOrientation(DoubleAngle.Atan((DoublePoint)sample.Offset)), Math.PI)); } } return(splits); }
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); }
public IntPoint Round() { return(new IntPoint(Doubles.RoundToInt(X), Doubles.RoundToInt(Y))); }
public void Compute(MatcherThread thread) { MinutiaCount = thread.Count; MinutiaScore = Parameters.MinutiaScore * MinutiaCount; MinutiaFractionInProbe = thread.Count / (double)thread.Probe.Minutiae.Length; MinutiaFractionInCandidate = thread.Count / (double)thread.Candidate.Minutiae.Length; MinutiaFraction = 0.5 * (MinutiaFractionInProbe + MinutiaFractionInCandidate); MinutiaFractionScore = Parameters.MinutiaFractionScore * MinutiaFraction; SupportingEdgeSum = 0; SupportedMinutiaCount = 0; MinutiaTypeHits = 0; for (int i = 0; i < thread.Count; ++i) { var pair = thread.Tree[i]; SupportingEdgeSum += pair.SupportingEdges; if (pair.SupportingEdges >= Parameters.MinSupportingEdges) { ++SupportedMinutiaCount; } if (thread.Probe.Minutiae[pair.Probe].Type == thread.Candidate.Minutiae[pair.Candidate].Type) { ++MinutiaTypeHits; } } EdgeCount = thread.Count + SupportingEdgeSum; EdgeScore = Parameters.EdgeScore * EdgeCount; SupportedMinutiaScore = Parameters.SupportedMinutiaScore * SupportedMinutiaCount; MinutiaTypeScore = Parameters.MinutiaTypeScore * MinutiaTypeHits; int innerDistanceRadius = Doubles.RoundToInt(Parameters.DistanceErrorFlatness * Parameters.MaxDistanceError); double innerAngleRadius = Parameters.AngleErrorFlatness * Parameters.MaxAngleError; DistanceErrorSum = 0; AngleErrorSum = 0; for (int i = 1; i < thread.Count; ++i) { var pair = thread.Tree[i]; var probeEdge = new EdgeShape(thread.Probe.Minutiae[pair.ProbeRef], thread.Probe.Minutiae[pair.Probe]); var candidateEdge = new EdgeShape(thread.Candidate.Minutiae[pair.CandidateRef], thread.Candidate.Minutiae[pair.Candidate]); DistanceErrorSum += Math.Max(innerDistanceRadius, Math.Abs(probeEdge.Length - candidateEdge.Length)); AngleErrorSum += Math.Max(innerAngleRadius, DoubleAngle.Distance(probeEdge.ReferenceAngle, candidateEdge.ReferenceAngle)); AngleErrorSum += Math.Max(innerAngleRadius, DoubleAngle.Distance(probeEdge.NeighborAngle, candidateEdge.NeighborAngle)); } DistanceAccuracyScore = 0; AngleAccuracyScore = 0; int distanceErrorPotential = Parameters.MaxDistanceError * Math.Max(0, thread.Count - 1); DistanceAccuracySum = distanceErrorPotential - DistanceErrorSum; DistanceAccuracyScore = Parameters.DistanceAccuracyScore * (distanceErrorPotential > 0 ? DistanceAccuracySum / (double)distanceErrorPotential : 0); double angleErrorPotential = Parameters.MaxAngleError * Math.Max(0, thread.Count - 1) * 2; AngleAccuracySum = angleErrorPotential - AngleErrorSum; AngleAccuracyScore = Parameters.AngleAccuracyScore * (angleErrorPotential > 0 ? AngleAccuracySum / angleErrorPotential : 0); TotalScore = MinutiaScore + MinutiaFractionScore + SupportedMinutiaScore + EdgeScore + MinutiaTypeScore + DistanceAccuracyScore + AngleAccuracyScore; ShapedScore = Shape(TotalScore); }
static DoubleMatrix ScaleImage(DoubleMatrix input, double dpi) { return(ScaleImage(input, Doubles.RoundToInt(500.0 / dpi * input.Width), Doubles.RoundToInt(500.0 / dpi * input.Height))); }
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); }