Click here to Skip to main content
15,891,375 members
Articles / Web Development / HTML

A Framework in C# for Fingerprint Verification

2 Jan 2019CPOL11 min read 1.6M   143.6K   672  
In this article, we introduce a framework in C# for fingerprint verification, we briefly explain how to perform fingerprint verification experiments and how to integrate your algorithms to the framework.
/*
 * Created by: Miguel Angel Medina Pérez (miguel.medina.perez@gmail.com)
 * Created: 
 * Comments by: Miguel Angel Medina Pérez (miguel.medina.perez@gmail.com)
 */

using System;
using System.Drawing;
using ImageProcessingTools;
using PatternRecognition.FingerprintRecognition.Core;

namespace PatternRecognition.FingerprintRecognition.FeatureExtractors
{
    /// <summary>
    ///     An implementation of the <see cref="OrientationImage"/> extractor proposed by Ratha et al. in 1995.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         This is an implementation of the <see cref="OrientationImage"/> extractor proposed by Ratha et al. [1] in 1995.
    ///     </para>
    ///     <para>
    ///         We apply a 3x3 median filter to improve the computed <see cref="OrientationImage"/>.
    ///     </para>
    ///     <para>
    ///         In order to better deal with diverse fingerprint databases, we modified the algorithm proposed by Ratha et al. [1] to detect foreground. 
    ///     </para>
    ///     <para>
    ///         Take into account that this algorithm is created to work with fingerprint images at 500 dpi. Proper modifications have to be made for different image resolutions.
    ///     </para>
    ///     <para>
    ///         References:
    ///     </para>
    ///     <para>
    ///         <list type="number">
    ///             <item>
    ///                Ratha N.K., Chen S.Y. and Jain A.K., "Adaptive flow orientation-based feature extraction in fingerprint images," Pattern Recognition, vol. 28, no. 11, pp. 1657–1672, 1995.
    ///             </item>             
    ///         </list>
    ///     </para>
    /// </remarks>
    public class Ratha1995OrImgExtractor : FeatureExtractor<OrientationImage>
    {
        /// <summary>
        ///     The block size in pixels.
        /// </summary>
        public byte BlockSize
        {
            get { return bSize; }
            set { bSize = value; }
        }

        /// <summary>
        ///     Extract an orientation image from the specified bitmap image.
        /// </summary>
        /// <param name="image">The source bitmap image to extract features from.</param>
        /// <returns>The features extracted from the specified bitmap image.</returns>
        public override OrientationImage ExtractFeatures(Bitmap image)
        {
            ImageMatrix matrix = new ImageMatrix(image);

            ImageMatrix Gx = yFilter.Apply(matrix);
            ImageMatrix Gy = xFilter.Apply(matrix);

            byte width = Convert.ToByte(image.Width / BlockSize);
            byte height = Convert.ToByte(image.Height / BlockSize);
            OrientationImage oi = new OrientationImage(width, height, BlockSize);
            for (int row = 0; row < height; row++)
                for (int col = 0; col < width; col++)
                {
                    int x, y;
                    oi.GetPixelCoordFromBlock(row, col, out x, out y);
                    int x0 = Math.Max(x - BlockSize / 2, 0);
                    int x1 = Math.Min(image.Width - 1, x + BlockSize / 2);
                    int y0 = Math.Max(y - BlockSize / 2, 0);
                    int y1 = Math.Min(image.Height - 1, y + BlockSize / 2);

                    int Gxy = 0, Gxx = 0, Gyy = 0;
                    for (int yi = y0; yi <= y1; yi++)
                        for (int xi = x0; xi <= x1; xi++)
                        {
                            Gxy += Gx[yi, xi] * Gy[yi, xi];
                            Gxx += Gx[yi, xi] * Gx[yi, xi];
                            Gyy += Gy[yi, xi] * Gy[yi, xi];
                        }

                    if (Gxx - Gyy == 0 && Gxy == 0)
                        oi[row, col] = OrientationImage.Null;
                    else
                    {
                        double angle = Angle.ToDegrees(Angle.ComputeAngle(Gxx - Gyy, 2 * Gxy));
                        angle = angle / 2 + 90;
                        if (angle > 180)
                            angle = angle - 180;
                        oi[row, col] = Convert.ToByte(Math.Round(angle));
                    }
                }

            RemoveBadBlocksVariance(oi, matrix);
            RemoveBadBlocks(oi);
            OrientationImage smoothed = SmoothOrImg(oi);
            return smoothed;
        }

        #region private

        private void RemoveBadBlocksVariance(OrientationImage oi, ImageMatrix matrix)
        {
            int maxLength = oi.WindowSize / 2;
            int[,] varianceMatrix = new int[oi.Height, oi.Width];
            double max = 0;
            double min = double.MaxValue;
            for (int row = 0; row < oi.Height; row++)
                for (int col = 0; col < oi.Width; col++)
                {
                    int x, y;
                    oi.GetPixelCoordFromBlock(row, col, out x, out y);

                    // Computing Average
                    int sum = 0;
                    int count = 0;
                    for (int xi = x - maxLength; xi < x + maxLength; xi++)
                        for (int yi = y - maxLength; yi < y + maxLength; yi++)
                            if (xi >= 0 && xi < matrix.Width && yi >= 0 && yi < matrix.Height)
                            {
                                sum += matrix[yi, xi];
                                count++;
                            }
                    double avg = 1.0 * sum / count;

                    // Computing Variance
                    double sqrSum = 0;
                    for (int xi = x - maxLength; xi < x + maxLength; xi++)
                        for (int yi = y - maxLength; yi < y + maxLength; yi++)
                            if (xi >= 0 && xi < matrix.Width && yi >= 0 && yi < matrix.Height)
                            {
                                double diff = matrix[yi, xi] - avg;
                                sqrSum += diff * diff;
                            }
                    varianceMatrix[row, col] = Convert.ToInt32(Math.Round(sqrSum / (count - 1)));

                    // Computing de max variance
                    if (varianceMatrix[row, col] > max)
                        max = varianceMatrix[row, col];
                    if (varianceMatrix[row, col] < min)
                        min = varianceMatrix[row, col];
                }

            for (int row = 0; row < oi.Height; row++)
                for (int col = 0; col < oi.Width; col++)
                    varianceMatrix[row, col] = Convert.ToInt32(Math.Round(254.0 * (varianceMatrix[row, col] - min) / (max - min)));

            const int t = 15;
            for (int row = 0; row < oi.Height; row++)
                for (int col = 0; col < oi.Width; col++)
                    if (!oi.IsNullBlock(row, col) && varianceMatrix[row, col] <= t)
                        oi[row, col] = OrientationImage.Null;
        }

        private void RemoveBadBlocks(OrientationImage oi)
        {
            int[,] neighborsMatrix = new int[oi.Height, oi.Width];
            for (int row0 = 0; row0 < oi.Height; row0++)
                for (int col0 = 0; col0 < oi.Width; col0++)
                    if (oi[row0, col0] != OrientationImage.Null)
                    {
                        int lowRow = Math.Max(0, row0 - 1);
                        int lowCol = Math.Max(0, col0 - 1);
                        int highRow = Math.Min(row0 + 1, oi.Height - 1);
                        int highCol = Math.Min(col0 + 1, oi.Width - 1);
                        for (int row1 = lowRow; row1 <= highRow; row1++)
                            for (int col1 = lowCol; col1 <= highCol; col1++)
                                if (oi[row1, col1] != OrientationImage.Null)
                                    neighborsMatrix[row0, col0]++;
                    }

            for (int row0 = 0; row0 < oi.Height; row0++)
                for (int col0 = 0; col0 < oi.Width; col0++)
                    if (oi[row0, col0] != OrientationImage.Null)
                    {
                        bool shouldRemove = true;
                        int lowRow = Math.Max(0, row0 - 1);
                        int lowCol = Math.Max(0, col0 - 1);
                        int highRow = Math.Min(row0 + 1, oi.Height - 1);
                        int highCol = Math.Min(col0 + 1, oi.Width - 1);
                        for (int row1 = lowRow; row1 <= highRow && shouldRemove; row1++)
                            for (int col1 = lowCol; col1 <= highCol && shouldRemove; col1++)
                                if (neighborsMatrix[row1, col1] == 9)
                                    shouldRemove = false;
                        if (shouldRemove)
                            oi[row0, col0] = OrientationImage.Null;
                    }
        }

        /// <summary>
        ///     Apply 3x3 median filter to the specified <see cref="OrientationImage"/>.
        /// </summary>
        /// <param name="img">The image to be smoothed.</param>
        /// <returns>The smoothed image.</returns>
        private OrientationImage SmoothOrImg(OrientationImage img)
        {
            OrientationImage smoothed = new OrientationImage(img.Width, img.Height, img.WindowSize);
            double xSum, ySum, xAvg, yAvg, angle;
            int count;
            byte wSize = 3;
            for (int row = 0; row < img.Height; row++)
                for (int col = 0; col < img.Width; col++)
                    if (!img.IsNullBlock(row, col))
                    {
                        xSum = ySum = count = 0;
                        for (int y = row - wSize / 2; y <= row + wSize / 2; y++)
                            for (int x = col - wSize / 2; x <= col + wSize / 2; x++)
                                if (y >= 0 && y < img.Height && x >= 0 && x < img.Width && !img.IsNullBlock(y, x))
                                {
                                    angle = img.AngleInRadians(y, x);
                                    xSum += Math.Cos(2 * angle);
                                    ySum += Math.Sin(2 * angle);
                                    count++;
                                }
                        if (count == 0 || (xSum == 0 && ySum == 0))
                            smoothed[row, col] = OrientationImage.Null;
                        else
                        {
                            xAvg = xSum / count;
                            yAvg = ySum / count;
                            angle = Angle.ToDegrees(Angle.ComputeAngle(xAvg, yAvg)) / 2;

                            smoothed[row, col] = Convert.ToByte(Math.Round(angle));
                        }
                    }
                    else
                        smoothed[row, col] = OrientationImage.Null;

            return smoothed;
        }

        private SobelHorizontalFilter xFilter = new SobelHorizontalFilter();

        private SobelVerticalFilter yFilter = new SobelVerticalFilter();

        private byte bSize = 16;

        #endregion

    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Tecnológico de Monterrey
Mexico Mexico
I received my B.S. and M.S. degrees from the University of Ciego de Ávila, Cuba, in 2007 and I received my PhD. degree in 2014 from the National Institute of Astrophysics, Optics and Electronics (INAOE), Mexico.

I have developed software to solve pattern recognition problems. A successful example is the algorithm DMC which is the most accurate (according to EER) among those which compare both fingerprints and palmprints in the international competition FVC-onGoing.

I have been involved in several research projects about pattern recognition and I have published tens of papers in referenced journals such as "Pattern Recognition," "Knowledge-Based Systems," "Information Sciences", and "IEEE Transactions on Information Forensics and Security."

Written By
Cuba Cuba
Milton García-Borroto is graduated from Las Villas Central University, Cuba, in 2000. He received the M.S. degree in 2007 from the National Institute of Astrophisics, Optics and Electronics, Mexico, where he continues his studies toward a Ph.D. degree. His research interests are pattern recognition and biometry.

Relevant papers:
1. M. García-Borroto, J. F. Martinez Trinidad, J. A. Carrasco Ochoa, M. A. Medina-Pérez, and J. Ruiz-Shulcloper. LCMine: An efficient algorithm for mining discriminative regularities and its application in supervised classification. Pattern Recognition vol. 43, pp. 3025-3034, 2010.
2. M. García-Borroto, J. F. Martinez Trinidad, J. A. Carrasco Ochoa. A New Emerging Pattern Mining Algorithm and Its Application in Supervised Classification. M.J. Zaki et al. (Eds.): PAKDD 2010, Part I, Lecture Notes in Artificial Intelligence, vol. 6118, pp. 150–157, 2010.
3. M. A. Medina-Pérez, A. Gutiérrez-Rodríguez, and M. García-Borroto, "Improving Fingerprint Matching Using an Orientation-Based Minutia Descriptor," Lecture Notes in Computer Science, vol. 5856, pp. 121-128, 2009.
4. M. García-Borroto, Y. Villuendas-Rey, J. A. Carrasco-Ochoa, and J. F. Martínez-Trinidad, "Finding Small Consistent Subset for the Nearest Neighbor Classifier Based on Support Graphs," Lecture Notes in Computer Science, vol. 5856, pp. 465-472, 2009.
5. M. García-Borroto, Y. Villuendas-Rey, J. A. Carrasco-Ochoa, and J. F. Martínez-Trinidad, "Using Maximum Similarity Graphs to Edit Nearest Neighbor Classifiers," Lecture Notes in Computer Science, vol. 5856, pp. 489-496, 2009.
6. M. A. Medina-Pérez, M. García-Borroto, and J. Ruiz-Shulcloper, "Object Selection Based on Subclass Error Correcting for ALVOT," Lecture Notes in Computer Science, vol. 4756, pp. 496-505, 2007.

Andres Eduardo Gutierrez Rodriguez is graduated from Las Villas Central University, Cuba, in 2006. He received the M.S. degree in 2009 from the University of Ciego de Ávila, Cuba. His research interests are pattern recognition and biometry.

Relevant papers:

-M. A. Medina-Pérez, A. Gutiérrez-Rodríguez, and M. García-Borroto, "Improving Fingerprint Matching Using an Orientation-Based Minutia Descriptor," Lecture Notes in Computer Science, vol. 5856, pp. 121-128, 2009.
-A. E. Gutierrez-Rodriguez, M. A. Medina-Perez, J. F. Martinez-Trinidad, J. A. Carrasco-Ochoa, and M. Garcia-Borroto, "New Dissimilarity Measures for Ultraviolet Spectra Identification," Lecture Notes in Computer Science, vol. 6256, pp. 220-229, 2010.

Written By
Program Manager
Spain Spain
Octavio Loyola-González received his PhD degree in Computer Science from the National Institute for Astrophysics, Optics, and Electronics, Mexico. He has won several awards from different institutions due to his research work on applied projects; consequently, he is a Member of the National System of Researchers in Mexico (Rank1). He worked as a distinguished professor and researcher at Tecnologico de Monterrey, Campus Puebla, for undergraduate and graduate programs of Computer Sciences. Currently, he is responsible for running Machine Learning & Artificial Intelligence practice inside Stratesys., where he is involved in the development and implementation using analytics and data mining. He has outstanding experience in the fields of big data & pattern recognition, cloud computing, IoT, and analytical tools to apply them in sectors where he has worked for as Banking & Insurance, Retail, Oil&Gas, Agriculture, Cybersecurity, Biotechnology, and Dactyloscopy. From these applied projects, Dr. Loyola-González has published several books and papers in well-known journals, and he has several ongoing patents as manager and researcher in Stratesys.

Comments and Discussions