IEnumerable<MinutiaPair> GetRoots() { const int minEdgeLength = 58; const int maxEdgeLookups = 1633; int counter = 0; var filters = new Predicate<EdgeShape>[] { shape => shape.Length >= minEdgeLength, shape => shape.Length < minEdgeLength }; foreach (var shapeFilter in filters) { for (int step = 1; step < Candidate.Minutiae.Count; ++step) for (int pass = 0; pass < step + 1; ++pass) for (int candidateReference = pass; candidateReference < Candidate.Minutiae.Count; candidateReference += step + 1) { int candidateNeighbor = (candidateReference + step) % Candidate.Minutiae.Count; var candidateEdge = new EdgeShape(Candidate, candidateReference, candidateNeighbor); if (shapeFilter(candidateEdge)) { List<IndexedEdge> matches; if (EdgeHash.TryGetValue(HashShape(candidateEdge), out matches)) { foreach (var match in matches) { if (MatchingShapes(match.Shape, candidateEdge)) { var pair = new MinutiaPair() { Probe = match.Reference, Candidate = candidateReference }; yield return pair; ++counter; if (counter >= maxEdgeLookups) yield break; } } } } } } }
static int HashShape(EdgeShape edge) { return((edge.ReferenceAngle / MaxAngleError << 24) + (edge.NeighborAngle / MaxAngleError << 16) + edge.Length / MaxDistanceError); }
double ComputeScore() { const int minSupportingEdges = 1; const double distanceErrorFlatness = 0.69; const double angleErrorFlatness = 0.27; const double pairCountFactor = 0.032; const double pairFractionFactor = 8.98; const double correctTypeFactor = 0.629; const double supportedCountFactor = 0.193; const double edgeCountFactor = 0.265; const double distanceAccuracyFactor = 9.9; const double angleAccuracyFactor = 2.79; double score = pairCountFactor * PairCount; score += pairFractionFactor * (PairCount / (double)Template.Minutiae.Count + PairCount / (double)Candidate.Minutiae.Count) / 2; for (int i = 0; i < PairCount; ++i) { PairInfo pair = PairList[i]; if (pair.SupportingEdges >= minSupportingEdges) score += supportedCountFactor; score += edgeCountFactor * (pair.SupportingEdges + 1); if (Template.Minutiae[pair.Pair.Probe].Type == Candidate.Minutiae[pair.Pair.Candidate].Type) score += correctTypeFactor; } var innerDistanceRadius = Convert.ToInt32(distanceErrorFlatness * FingerprintMatcher.MaxDistanceError); var innerAngleRadius = Convert.ToInt32(angleErrorFlatness * FingerprintMatcher.MaxAngleError); var distanceErrorSum = 0; var angleErrorSum = 0; for (int i = 1; i < PairCount; ++i) { PairInfo pair = PairList[i]; var probeEdge = new EdgeShape(Template, pair.Reference.Probe, pair.Pair.Probe); var candidateEdge = new EdgeShape(Candidate, pair.Reference.Candidate, pair.Pair.Candidate); distanceErrorSum += Math.Abs(probeEdge.Length - candidateEdge.Length); angleErrorSum += Math.Max(innerDistanceRadius, Angle.Distance(probeEdge.ReferenceAngle, candidateEdge.ReferenceAngle)); angleErrorSum += Math.Max(innerAngleRadius, Angle.Distance(probeEdge.NeighborAngle, candidateEdge.NeighborAngle)); } if (PairCount >= 2) { var maxDistanceError = FingerprintMatcher.MaxDistanceError * (PairCount - 1); score += distanceAccuracyFactor * (maxDistanceError - distanceErrorSum) / maxDistanceError; var maxAngleError = FingerprintMatcher.MaxAngleError * (PairCount - 1) * 2; score += angleAccuracyFactor * (maxAngleError - angleErrorSum) / maxAngleError; } return score; }
static bool MatchingShapes(EdgeShape probe, EdgeShape candidate) { int lengthDelta = probe.Length - candidate.Length; if (lengthDelta >= -MaxDistanceError && lengthDelta <= MaxDistanceError) { byte complementaryAngleError = Angle.Complementary(MaxAngleError); byte referenceDelta = Angle.Difference(probe.ReferenceAngle, candidate.ReferenceAngle); if (referenceDelta <= MaxAngleError || referenceDelta >= complementaryAngleError) { byte neighborDelta = Angle.Difference(probe.NeighborAngle, candidate.NeighborAngle); if (neighborDelta <= MaxAngleError || neighborDelta >= complementaryAngleError) return true; } } return false; }
static int HashShape(EdgeShape edge) { return (edge.ReferenceAngle / MaxAngleError << 24) + (edge.NeighborAngle / MaxAngleError << 16) + edge.Length / MaxDistanceError; }
static IEnumerable<int> GetShapeCoverage(EdgeShape edge) { int minLengthBin = (edge.Length - MaxDistanceError) / MaxDistanceError; int maxLengthBin = (edge.Length + MaxDistanceError) / MaxDistanceError; int angleBins = MathEx.DivRoundUp(256, MaxAngleError); int minReferenceBin = Angle.Difference(edge.ReferenceAngle, MaxAngleError) / MaxAngleError; int maxReferenceBin = Angle.Add(edge.ReferenceAngle, MaxAngleError) / MaxAngleError; int endReferenceBin = (maxReferenceBin + 1) % angleBins; int minNeighborBin = Angle.Difference(edge.NeighborAngle, MaxAngleError) / MaxAngleError; int maxNeighborBin = Angle.Add(edge.NeighborAngle, MaxAngleError) / MaxAngleError; int endNeighborBin = (maxNeighborBin + 1) % angleBins; for (int lengthBin = minLengthBin; lengthBin <= maxLengthBin; ++lengthBin) for (int referenceBin = minReferenceBin; referenceBin != endReferenceBin; referenceBin = (referenceBin + 1) % angleBins) for (int neighborBin = minNeighborBin; neighborBin != endNeighborBin; neighborBin = (neighborBin + 1) % angleBins) yield return (referenceBin << 24) + (neighborBin << 16) + lengthBin; }
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); }