public void Scale(TemplateBuilder template) { float dpiFactor = 500 / (float)DpiScaling; foreach (var minutia in template.Minutiae) minutia.Position = Calc.Round(Calc.Multiply(dpiFactor, minutia.Position)); Logger.Log(template); }
public void Shuffle(TemplateBuilder template) { int seed = 0; foreach (var minutia in template.Minutiae) seed += minutia.Direction + minutia.Position.X + minutia.Position.Y + (int)minutia.Type; template.Minutiae = Calc.Shuffle(template.Minutiae, new Random(seed)).ToList(); }
public void Filter(TemplateBuilder template) { var radiusSq = Calc.Sq(NeighborhoodRadius); template.Minutiae = template.Minutiae.Except( (from minutia in template.Minutiae where template.Minutiae.Count(neighbor => Calc.DistanceSq(neighbor.Position, minutia.Position) <= radiusSq) - 1 > MaxNeighbors select minutia).ToList()).ToList(); Logger.Log(template); }
public TemplateBuilder ToTemplateBuilder() { TemplateBuilder builder = new TemplateBuilder(); builder.OriginalDpi = OriginalDpi; builder.OriginalWidth = OriginalWidth; builder.OriginalHeight = OriginalHeight; foreach (Minutia minutia in Minutiae) builder.Minutiae.Add(minutia.ToBuilderMinutia()); return builder; }
public Template(TemplateBuilder builder) { OriginalDpi = builder.OriginalDpi; OriginalWidth = builder.OriginalWidth; OriginalHeight = builder.OriginalHeight; StandardDpiWidth = builder.StandardDpiWidth; StandardDpiHeight = builder.StandardDpiHeight; Minutiae = new Minutia[builder.Minutiae.Count]; for (int i = 0; i < builder.Minutiae.Count; ++i) Minutiae[i] = new Minutia(builder.Minutiae[i]); }
public Minutia(TemplateBuilder.Minutia builderMinutia) { Position = new PointS(builderMinutia.Position); Direction = builderMinutia.Direction; switch (builderMinutia.Type) { case TemplateBuilder.MinutiaType.Ending: Type = MinutiaType.Ending; break; case TemplateBuilder.MinutiaType.Bifurcation: Type = MinutiaType.Bifurcation; break; case TemplateBuilder.MinutiaType.Other: Type = MinutiaType.Other; break; default: throw new ApplicationException(); } }
public void Filter(TemplateBuilder template) { if (template.Minutiae.Count > MaxMinutiae) { template.Minutiae = (from minutia in template.Minutiae let radiusSq = (from neighbor in template.Minutiae let distanceSq = Calc.DistanceSq(minutia.Position, neighbor.Position) orderby distanceSq select distanceSq).Skip(NeighborhoodSize).First() orderby radiusSq descending select minutia).Take(MaxMinutiae).ToList(); } Logger.Log(template); }
public override TemplateBuilder Import(XElement template) { int version = (int)template.Attribute("Version"); if (version < 1 || version > 2) { throw new ApplicationException("Unknown template version."); } TemplateBuilder builder = new TemplateBuilder() { Minutiae = (from minutia in template.Elements("Minutia") select new TemplateBuilder.Minutia() { Position = new Point( (int)minutia.Attribute("X"), (int)minutia.Attribute("Y")), Direction = (byte)(uint)minutia.Attribute("Direction"), Type = (TemplateBuilder.MinutiaType)Enum.Parse( typeof(TemplateBuilder.MinutiaType), (string)minutia.Attribute("Type"), false) }).ToList() }; if (version >= 2) { builder.OriginalDpi = (int)template.Attribute("OriginalDpi"); builder.OriginalWidth = (int)template.Attribute("OriginalWidth"); builder.OriginalHeight = (int)template.Attribute("OriginalHeight"); } else { builder.OriginalDpi = 500; builder.StandardDpiWidth = template.Elements("Minutia").Max(e => (int)e.Attribute("X")) + 1; builder.StandardDpiHeight = template.Elements("Minutia").Max(e => (int)e.Attribute("Y")) + 1; } return(builder); }
public abstract T Export(TemplateBuilder builder);
public byte[] SerializeBuilder(TemplateBuilder builder) { return(Serialize(Export(builder))); }
public override Template Export(TemplateBuilder builder) { return(new Template(builder)); }
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; }
// References: // http://www.italdata-roma.com/PDF/Norme%20ISO-IEC%20Minutiae%20Data%20Format%2019794-2.pdf // https://biolab.csr.unibo.it/fvcongoing/UI/Form/Download.aspx (ISO section, sample ZIP, ISOTemplate.pdf) // // Format (all numbers are big-endian): // 4B magic "FMR\0" // 4B version (ignored, set to " 20\0" // 4B total length (including header) // 2B rubbish (zeroed) // 2B image size in pixels X // 2B image size in pixels Y // 2B rubbish (pixels per cm X, set to 196 = 500dpi) // 2B rubbish (pixels per cm Y, set to 196 = 500dpi) // 1B rubbish (number of fingerprints, set to 1) // 1B rubbish (zeroed) // 1B rubbish (finger position, zeroed) // 1B rubbish (zeroed) // 1B rubbish (fingerprint quality, set to 100) // 1B minutia count // N*6B minutiae // 2B minutia position X in pixels // 2b (upper) minutia type (01 ending, 10 bifurcation, 00 other) // 2B minutia position Y in pixels (upper 2b ignored, zeroed) // 1B direction, compatible with SourceAFIS angles // 1B quality (ignored, zeroed) // 2B rubbish (extra data length, zeroed) // N*1B rubbish (extra data) public override byte[] Export(TemplateBuilder builder) { MemoryStream stream = new MemoryStream(); BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8); checked { // 4B magic "FMR\0" writer.Write("FMR\0".ToCharArray()); // 4B version (ignored, set to " 20\0" writer.Write(" 20\0".ToCharArray()); // 4B total length (including header, will be updated later) writer.Write(0); // 2B rubbish (zeroed) writer.Write((short)0); // 2B image size in pixels X writer.Write(IPAddress.HostToNetworkOrder((short)builder.StandardDpiWidth)); // 2B image size in pixels Y writer.Write(IPAddress.HostToNetworkOrder((short)builder.StandardDpiHeight)); // 2B rubbish (pixels per cm X, set to 196 = 500dpi) writer.Write(IPAddress.HostToNetworkOrder((short)196)); // 2B rubbish (pixels per cm Y, set to 196 = 500dpi) writer.Write(IPAddress.HostToNetworkOrder((short)196)); // 1B rubbish (number of fingerprints, set to 1) writer.Write((byte)1); // 1B rubbish (zeroed) writer.Write((byte)0); // 1B rubbish (finger position, zeroed) writer.Write((byte)0); // 1B rubbish (zeroed) writer.Write((byte)0); // 1B rubbish (fingerprint quality, set to 100) writer.Write((byte)100); // 1B minutia count writer.Write((byte)builder.Minutiae.Count); // N*6B minutiae foreach (var minutia in builder.Minutiae) { // 2B minutia position X in pixels // 2b (upper) minutia type (01 ending, 10 bifurcation, 00 other (considered ending)) int x = minutia.Position.X; AssertException.Check(x <= 0x3fff, "X position is out of range"); int type; switch (minutia.Type) { case TemplateBuilder.MinutiaType.Ending: type = 0x4000; break; case TemplateBuilder.MinutiaType.Bifurcation: type = 0x8000; break; case TemplateBuilder.MinutiaType.Other: type = 0; break; default: throw new ApplicationException(); } writer.Write(IPAddress.HostToNetworkOrder(unchecked ((short)(x | type)))); // 2B minutia position Y in pixels (upper 2b ignored, zeroed) int y = builder.StandardDpiHeight - minutia.Position.Y - 1; AssertException.Check(y <= 0x3fff, "Y position is out of range"); writer.Write(IPAddress.HostToNetworkOrder((short)y)); // 1B direction, compatible with SourceAFIS angles writer.Write(minutia.Direction); // 1B quality (ignored, zeroed) writer.Write((byte)0); } // 2B rubbish (extra data length, zeroed) // N*1B rubbish (extra data) writer.Write((short)0); } writer.Dispose(); // update length byte[] template = stream.ToArray(); BitConverter.GetBytes(IPAddress.HostToNetworkOrder(template.Length)).CopyTo(template, 8); return(template); }
public override TemplateBuilder Import(byte[] template) { TemplateBuilder builder = new TemplateBuilder(); builder.OriginalDpi = 500; MemoryStream stream = new MemoryStream(template); BinaryReader reader = new BinaryReader(stream, Encoding.UTF8); // 4B magic "FMR\0" AssertException.Check(new String(reader.ReadChars(4)) == "FMR\0", "This is not an ISO template."); // 4B version (ignored, set to " 20\0" reader.ReadChars(4); // 4B total length (including header) AssertException.Check(IPAddress.NetworkToHostOrder(reader.ReadInt32()) == template.Length, "Invalid template length."); // 2B rubbish (zeroed) reader.ReadInt16(); // 2B image size in pixels X builder.StandardDpiWidth = IPAddress.NetworkToHostOrder(reader.ReadInt16()); // 2B image size in pixels Y builder.StandardDpiHeight = IPAddress.NetworkToHostOrder(reader.ReadInt16()); // 2B rubbish (pixels per cm X, set to 196 = 500dpi) reader.ReadInt16(); // 2B rubbish (pixels per cm Y, set to 196 = 500dpi) reader.ReadInt16(); // 1B rubbish (number of fingerprints, set to 1) AssertException.Check(reader.ReadByte() == 1, "Only single-fingerprint ISO templates are supported."); // 1B rubbish (zeroed) reader.ReadByte(); // 1B rubbish (finger position, zeroed) reader.ReadByte(); // 1B rubbish (zeroed) reader.ReadByte(); // 1B rubbish (fingerprint quality, set to 100) reader.ReadByte(); // 1B minutia count int minutiaCount = reader.ReadByte(); // N*6B minutiae for (int i = 0; i < minutiaCount; ++i) { TemplateBuilder.Minutia minutia = new TemplateBuilder.Minutia(); // 2B minutia position X in pixels // 2b (upper) minutia type (01 ending, 10 bifurcation, 00 other (considered ending)) ushort xPacked = (ushort)IPAddress.NetworkToHostOrder(reader.ReadInt16()); minutia.Position.X = xPacked & (ushort)0x3fff; switch (xPacked & (ushort)0xc000) { case 0x4000: minutia.Type = TemplateBuilder.MinutiaType.Ending; break; case 0x8000: minutia.Type = TemplateBuilder.MinutiaType.Bifurcation; break; case 0: minutia.Type = TemplateBuilder.MinutiaType.Other; break; default: throw new ApplicationException(); } // 2B minutia position Y in pixels (upper 2b ignored, zeroed) minutia.Position.Y = builder.StandardDpiHeight - 1 - ((ushort)IPAddress.NetworkToHostOrder(reader.ReadInt16()) & (ushort)0x3fff); // 1B direction, compatible with SourceAFIS angles minutia.Direction = reader.ReadByte(); // 1B quality (ignored, zeroed) reader.ReadByte(); builder.Minutiae.Add(minutia); } // 2B rubbish (extra data length, zeroed) // N*1B rubbish (extra data) return(builder); }
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; }