static void Main(string[] args) { PLog.OpenConsoleLogger(); PapillonSDK.Initialise(); string SAMPLE_DIR = PPath.Join(PUtils.GetEnv("PAPILLON_INSTALL_DIR"), "Data", "Samples"); //=========== // Enrollment //=========== // Enrollment is the process of turning detected faces of a subject into a unique key. In the biometrics world this is often // referred to as a biometric template. In this SDK we prefer the use of the term 'description'. Think of a description as a // description of an identity. In this SDK descriptions are fairly generic containers which can be used to store // a lot of other information as well as the information about the face of the identity. // The enrollment engine wraps up the whole face-recognition processing chain into an easy to use interface. // From it you can easily generate 'descriptions' from images and videos. However, all the functions provided // by the enrollment engine can also be done using the other classes available in this SDK as this enrollment // engine may not fit all use-cases. // The following code initialises an enrollment engine and sets it up with a set of sensible options PEnrollment enrollment = new PEnrollment(); enrollment.AutoConfigureForFaceRecognition(); // this sets-up a sensible set of options for the face-rec enrollment // Now we can generate two different descriptions of Kieron using two images that were taken at different times. // For each image, the enrollment engine will locate the face in the input image and generate the description. // Note, if an image is supplied with more than face, then the enrollment function will generate an error, as it is // unsure which face to use for the enrollment. PGuid kieronSubjectId = PGuid.CreateUniqueId(); PDescription descriptionOfKieron1 = new PDescription(); enrollment.EnrollFromImage(PPath.Join(SAMPLE_DIR, "kieron01.jpg"), descriptionOfKieron1, "Kieron", kieronSubjectId); PDescription descriptionOfKieron2 = new PDescription(); enrollment.EnrollFromImage(PPath.Join(SAMPLE_DIR, "kieron02.jpg"), descriptionOfKieron2, "Kieron", kieronSubjectId); // Generate two descriptions of Kjetil from images taken at different times PGuid kjetilSubjectId = PGuid.CreateUniqueId(); PDescription descriptionOfKjetil1 = new PDescription(); enrollment.EnrollFromImage(PPath.Join(SAMPLE_DIR, "kjetil01.jpg"), descriptionOfKjetil1, "Kjetil", kjetilSubjectId); PDescription descriptionOfKjetil2 = new PDescription(); enrollment.EnrollFromImage(PPath.Join(SAMPLE_DIR, "kjetil02.jpg"), descriptionOfKjetil2, "Kjetil", kjetilSubjectId); //=========================================================== // Face Verification on Images (also known as Authentication) //=========================================================== // Verification (commonly referred to as Authentication) is the process of comparing two identities (using their // 'descriptions' and comparing the result against a threshold. If the probability of match is higher than the // threshold, then the identity is said to be Verified/Authenticated. // The value of the threshold can be set using some pre-defined security levels. Alternatively, it can be set // manually by specifying the value yourself. Care should be taken in setting the right value for your application. // Papillon has a verification engine which is a helper class to enable you to easily perform verification on descriptions, images // and videos. All the functionalities of this class can also be done by using the other classes available in // the SDK. // The following code, initialises the verification engine, using the enrollment engine and sets the security level. PVerify verify = new PVerify(enrollment, EVerificationSecurityLevel.E_VERIFICATION_SECURITY_LEVEL_HIGH); // Next we can try to match a description of Kieron against another description of Kieron, we would expect this to authenticate // successfully with a high probability. DoMatch(descriptionOfKieron1, descriptionOfKieron2, verify); // However, if we match a description of Kieron against a description of Kjetil, we would NOT expect this to authenticate // successfully. The probability of match will be low. DoMatch(descriptionOfKieron1, descriptionOfKjetil1, verify); // We can also match a description of Kjetil against another description of Kjetil. Again, we would expect this to authenticate // successfully with a high probability. DoMatch(descriptionOfKjetil1, descriptionOfKjetil2, verify); //=========================== // Face Verification on Video //=========================== // Next lets perform enrollment from a video. This is done by processing each frame of the video in turn. // Because videos can contain multiple faces of the same identity in frames, a collection of faces // can be used to generate the description. The number of frames to process and the number of examples // to used to generate the description can be specified as parameters. // Note, only supply videos which are known to have a single identity present when performing enrollment. // If you have multiple faces in your video then you need to use the FaceLog2 analytic to generate descriptions // to use for enrollment. int maxFramesToProcess = -1; // Process all the frames (up until the maxExamples has been reached) int maxExamples = 10; // Use the first 5 examples found in the video to generate the description. PDescription descriptionOfKieronFromVideo = new PDescription(); enrollment.EnrollFromVideo(PPath.Join(SAMPLE_DIR, "kieron01.avi"), descriptionOfKieronFromVideo, maxFramesToProcess, maxExamples, "Kieron in Video", kieronSubjectId); PDescription descriptionOfKjetilFromVideo = new PDescription(); enrollment.EnrollFromVideo(PPath.Join(SAMPLE_DIR, "kjetil01.avi"), descriptionOfKjetilFromVideo, maxFramesToProcess, maxExamples, "Kjetil in Video", kjetilSubjectId); // Should authenticate DoMatch(descriptionOfKieron2, descriptionOfKieronFromVideo, verify); // Should fail to authenticate DoMatch(descriptionOfKjetil1, descriptionOfKieronFromVideo, verify); // Should authenticate DoMatch(descriptionOfKjetil1, descriptionOfKjetilFromVideo, verify); // Should fail to authenticate DoMatch(descriptionOfKieron2, descriptionOfKjetilFromVideo, verify); // ============================= // Merging Descriptions Together //============================== // In the above, 5 examples from the video have been used to build each description. // Each example used has been turned into a descriptor. // When enrolling from the image, only a single example face was used to generate the description. // The description will only contain 1 descriptor // We can see this by requesting the number of descriptors each description holds. Console.WriteLine("Number of descriptors in image description: " + descriptionOfKieron1.GetDescriptors().Size()); Console.WriteLine("Number of descriptors in video description: " + descriptionOfKieronFromVideo.GetDescriptors().Size()); // When a description is generated a thumbnail for each example (descriptor) is generated and stored in the description // We can get to these thumbnails and display them PImage thumbnail = new PImage(); PList thumbnails = descriptionOfKieron1.GetThumbnails(); thumbnails.Get(0, thumbnail); thumbnail.Display("Thumbnail Of Kieron", 5000); // display for 5 seconds // It is possible to merge descriptions of the same identity together, generating a more complete 'description' of someone if (descriptionOfKieron1.AddDescription(descriptionOfKieron2).Failed()) { Console.WriteLine("Failed to merge descriptions"); System.Environment.Exit(0); } // We would now expect there to be two descriptors in the description // Note, if you try to merge two descriptions with different IdentityId's together this will still work. // However, the id of base description will be used and the other discarded Console.WriteLine("Number of descriptors in description: " + descriptionOfKieron1.GetDescriptors().Size()); // Now when we perform the comparison, the match score could change as there is more information // held in one of the descriptions. You will notice the confidence has increased as we are more // certain the two identities are the same. DoMatch(descriptionOfKieron1, descriptionOfKieronFromVideo, verify); // We now also have two thumbnails associated with this description thumbnails = descriptionOfKieron1.GetThumbnails(); Console.WriteLine("Number of thumbnails in description: " + thumbnails.Size()); thumbnails.Get(0, thumbnail); thumbnail.Display("Thumbnail 1 Of Kieron", 5000); // display for 5 seconds thumbnails.Get(1, thumbnail); thumbnail.Display("Thumbnail 2 Of Kieron", 5000); // display for 5 seconds // ============================ // Description Input and Output //============================= // It is easy to save any Papillon object to file, including descriptions if (PFileIO.WriteToFile("descriptionOfKieron1.bin", descriptionOfKieron1).Failed()) { Console.WriteLine("Failed to save description to file"); System.Environment.Exit(0); } if (PFileIO.WriteToFile("descriptionOfKieronFromVideo.bin", descriptionOfKieronFromVideo).Failed()) { Console.WriteLine("Failed to save description to file"); System.Environment.Exit(0); } // We can also easily match descriptions that are stored in file PMatchScore matchScore = new PMatchScore(); if (verify.VerifyFromDescriptionFile("descriptionOfKieron1.bin", "descriptionOfKieronFromVideo.bin", matchScore).Failed()) { Console.WriteLine("Failed to match descriptions from file"); System.Environment.Exit(0); } Console.WriteLine("Match Score " + matchScore); // A common requirement is to save descriptions in a database such as MySQL, Postgresql, redis .... // This can easily be done using memory streams which allows a binary representation of a papillon // For example to get a binary blob of a description... PMemoryStream ms = new PMemoryStream(); ms.WriteObjectDescription(descriptionOfKieron1); PByteArray byteArray = ms.GetByteArray(); Console.WriteLine("Description size in bytes: " + byteArray.Size()); // Also, you can also convert the object to a string which can be stored as a text row of a database table String str = byteArray.ToString(); // To get from the string back to the byte-array back to your original object you perform the reverse operation PByteArray byteArray2 = new PByteArray(); PByteArray.FromString(str, byteArray2); // and then back to the description PDescription descriptionFromString = new PDescription(); PMemoryStream ms2 = new PMemoryStream(byteArray2); ms2.ReadObjectDescription(descriptionFromString); // And we can do our comparison and should get the same match score as before // and get to the thumbnail DoMatch(descriptionFromString, descriptionOfKieronFromVideo, verify); thumbnails = descriptionFromString.GetThumbnails(); thumbnails.Get(0, thumbnail); thumbnail.Display("Kieron Again", 5000); //=================================== // Face Identification and Watchlists //=================================== // Face identification is the process of ascertaining the identity by comparing it against a list of known identities. // This is done by comparing the 'unknown' description (often referred to as a probe) against a watchlist (sometimes referred // to as a gallery) of known descriptions and returning the top N matches, that pass the threshold value. // In Papillon we have a watchlist class that enables you to perform identification. PWatchlist watchlist = new PWatchlist(); // You can add descriptions to the watchlist // The descriptions of Kieron watchlist.Add(descriptionOfKieron1); watchlist.Add(descriptionOfKieron2); // The descriptions of Kjetil watchlist.Add(descriptionOfKjetil1); watchlist.Add(descriptionOfKjetil2); // To get the size of the watchlist // Note, this returns the number of subjects, not the number of descriptions. Console.WriteLine("Watchlist Size: " + watchlist.Size()); // To search a watch list you need to specify a comparer to use and threshold level to use PComparer comparer = new PComparer(); PComparer.Create(comparer, 0.55); // Lets generate an unknown description PDescription unknownDescription = new PDescription(); enrollment.EnrollFromImage(PPath.Join(SAMPLE_DIR, "kieron_20_years_ago.jpg"), unknownDescription); // And run a search against the watchlist PIdentifyResults identifyResults = new PIdentifyResults(); watchlist.Search(unknownDescription, comparer, identifyResults, 1, 0.55f); // In this example, the identity is returned as Kieron, eventhough there is over a 20 year gap // between the times the photos were taken Console.WriteLine(identifyResults.ToString()); thumbnails = unknownDescription.GetThumbnails(); thumbnails.Get(0, thumbnail); thumbnail.Display("Kieron 20 Years Ago", 5000); // We can of course load and save watchlists as well PFileIO.WriteToFileWatchlist("watchlist.bin", watchlist); //============================= // Face Identification in Video //============================= // There is another example which deals with the complicated task of performing face-identification in video. // This example is ExampleFaceLog2.cc }