diff --git a/include/RealSenseID/FaceAuthenticator.h b/include/RealSenseID/FaceAuthenticator.h index d0121ee..ab723b2 100644 --- a/include/RealSenseID/FaceAuthenticator.h +++ b/include/RealSenseID/FaceAuthenticator.h @@ -119,6 +119,17 @@ class RSID_API FaceAuthenticator */ EnrollStatus EnrollImage(const char* user_id, const unsigned char* buffer, unsigned int width, unsigned int height); + /** + * Extract features from RGB image. + * @param[in] user_id Null terminated C string of ascii chars. Max user id size is MAX_USERID_LENGTH bytes + * @param[in] buffer bgr24 image buffer of the enrolled user face. Max buffer size is 900KB(i.e. Width x Height x 3 should not exceed it) + * @param[in] width image width. + * @param[in] height image height. + * @param[out] the extracted faceprints from the image. + * @return EnrollStatus (EnrollStatus::Success on success). + */ + EnrollStatus EnrollImageFeatureExtraction(const char* user_id, const unsigned char* buffer, unsigned int width, unsigned int height, ExtractedFaceprints* pExtractedFaceprints); + /** * Attempt to authenticate. * Starts the authentication procedure, which starts the camera, captures frames and tries to match diff --git a/include/RealSenseID/Faceprints.h b/include/RealSenseID/Faceprints.h index fa286d2..4aeb805 100644 --- a/include/RealSenseID/Faceprints.h +++ b/include/RealSenseID/Faceprints.h @@ -26,16 +26,16 @@ static constexpr size_t RSID_MAX_USER_ID_LENGTH_IN_DB = 31; class ExtractedFaceprints { public: - RealSenseID::ExtractedFaceprints_t data; + RealSenseID::ExtractedFaceprintsElement data; }; // db layer faceprints element. -// a structure that is used in the DB layer, to save user faceprints plus additional metadata to the DB. +// a structure that is used in the DB layer, to save user DBFaceprintsElement plus additional metadata to the DB. // the struct includes several vectors and metadata to support all our internal matching mechanism (e.g. adaptive-learning etc..). class Faceprints { public: - RealSenseID::Faceprints_t data; + RealSenseID::DBFaceprintsElement data; }; // match element used during authentication flow, where we match between faceprints object received from the device @@ -43,7 +43,7 @@ class Faceprints class MatchElement { public: - RealSenseID::MatchElement_t data; + RealSenseID::ExtractedFaceprintsElement data; }; // faceprints plus username element. diff --git a/include/RealSenseID/FaceprintsDefines.h b/include/RealSenseID/FaceprintsDefines.h index a8b3944..dee7a67 100644 --- a/include/RealSenseID/FaceprintsDefines.h +++ b/include/RealSenseID/FaceprintsDefines.h @@ -54,7 +54,7 @@ typedef enum FaVectorFlags // a reduced structure that is used to represent the extracted faceprints been transferred from the device to the host // through the packet layer. #pragma pack(push, 1) -typedef struct ExtractedFaceprintsElement +struct ExtractedFaceprintsElement { int version; @@ -71,13 +71,13 @@ typedef struct ExtractedFaceprintsElement flags = 0; } #endif -} ExtractedFaceprints_t; +}; #pragma pack(pop) // db layer faceprints element. // a structure that is used in the DB layer, to save user faceprints and metadata to the DB. // the struct includes several vectors and metadata to support all our internal matching mechanism (e.g. adaptive-learning). -typedef struct DBFaceprintsElement +struct DBFaceprintsElement { int reserved[5]; // reserved placeholders (to minimize chance to re-create DB). @@ -102,22 +102,7 @@ typedef struct DBFaceprintsElement flags = 0; } #endif -} Faceprints_t; - -// [MatchElement_t] is a reduced structure that we use to match a single faceprints vector -// against DB faceprint objects during authentication (using Matcher Api functions). -typedef ExtractedFaceprints_t MatchElement_t; - -// [ExtractedSecureVersionDescriptor] - is used to transfer faceprints from device to host. -// [DBSecureVersionDescriptor] - is used to save user faceprints and metadata object to DB. -typedef Faceprints_t DBSecureVersionDescriptor; -typedef ExtractedFaceprints_t ExtractedSecureVersionDescriptor; - -// these definitions to be used in C clients (e.g. rsid_c_client.cc): -// rsid_extracted_faceprints_t - a packet layer element faceprints (as extracted from the device). -// rsid_faceprints_t - a db layer element faceprints (includes additional vectors and metadata). -typedef ExtractedFaceprints_t rsid_extracted_faceprints_t; -typedef Faceprints_t rsid_faceprints_t; +}; #ifdef __cplusplus } // close namespace RealSenseID diff --git a/include/RealSenseID/Version.h b/include/RealSenseID/Version.h index 0a327c1..943a059 100644 --- a/include/RealSenseID/Version.h +++ b/include/RealSenseID/Version.h @@ -8,13 +8,13 @@ #include #define RSID_VER_MAJOR 0 -#define RSID_VER_MINOR 24 +#define RSID_VER_MINOR 25 #define RSID_VER_PATCH 0 #define RSID_VERSION (RSID_VER_MAJOR * 10000 + RSID_VER_MINOR * 100 + RSID_VER_PATCH) #define RSID_FW_VER_MAJOR 4 -#define RSID_FW_VER_MINOR 2 +#define RSID_FW_VER_MINOR 3 namespace RealSenseID { diff --git a/release_notes.txt b/release_notes.txt index c7a11db..eac0520 100644 --- a/release_notes.txt +++ b/release_notes.txt @@ -1,3 +1,7 @@ +Realsense ID version 0.25.0 +----------------------------------- +* Support for enrollment from RGB image in host mode. + Realsense ID version 0.24.0 ----------------------------------- * Improved latency for most operations. diff --git a/src/FaceAuthenticator.cc b/src/FaceAuthenticator.cc index de3ed63..97c98b2 100644 --- a/src/FaceAuthenticator.cc +++ b/src/FaceAuthenticator.cc @@ -64,12 +64,16 @@ Status FaceAuthenticator::Enroll(EnrollmentCallback& callback, const char* user_ return _impl->Enroll(callback, user_id); } -EnrollStatus FaceAuthenticator::EnrollImage(const char* user_id, const unsigned char* buffer, unsigned int width, - unsigned int height) +EnrollStatus FaceAuthenticator::EnrollImage(const char* user_id, const unsigned char* buffer, unsigned int width, unsigned int height) { return _impl->EnrollImage(user_id, buffer, width, height); } +EnrollStatus FaceAuthenticator::EnrollImageFeatureExtraction(const char* user_id, const unsigned char* buffer, unsigned int width, unsigned int height, ExtractedFaceprints* pExtractedFaceprints) +{ + return _impl->EnrollImageFeatureExtraction(user_id, buffer, width, height, pExtractedFaceprints); +} + Status FaceAuthenticator::Authenticate(AuthenticationCallback& callback) { return _impl->Authenticate(callback); diff --git a/src/FaceAuthenticatorImpl.cc b/src/FaceAuthenticatorImpl.cc index 59e7947..d69057d 100644 --- a/src/FaceAuthenticatorImpl.cc +++ b/src/FaceAuthenticatorImpl.cc @@ -314,40 +314,25 @@ Status FaceAuthenticatorImpl::Enroll(EnrollmentCallback& callback, const char* u } -// Do enroll session with the device using the given bgr24 face image -// split to chunks and send to device as multiple 'e' DataPackets. -// image_size = Width x Height * 3 -// chunk_size = sizeof(PacketManager::DataMessage::data); -// image_chunk_size= chunk_size - 6 (6 bytes = [chunkN,W,H]) -// Number of chunks = image_size / image_chunk_size -// chunk format: [chunk-number (2 bytes)] [width (2 bytes)] [height (2 bytes)] [buffer (chunk size-6)] -// Wait for ack response ('e' packet) -// Send 'EnrollImage' Fa packet with the user id and return the response to the caller. -EnrollStatus FaceAuthenticatorImpl::EnrollImage(const char* user_id, const unsigned char* buffer, unsigned int width, - unsigned int height) +Status FaceAuthenticatorImpl::SendImageToDevice(const unsigned char* buffer, unsigned int width, unsigned int height) { - if (!ValidateUserId(user_id)) - { - return EnrollStatus::Failure; - } - if (buffer == nullptr) { LOG_ERROR(LOG_TAG, "Invalid buffer"); - return EnrollStatus::Failure; + return Status::Error; } if (width > 0xFFFF || height > 0xFFFF) { LOG_ERROR(LOG_TAG, "Invalid width/height"); - return EnrollStatus::Failure; + return Status::Error; } uint32_t image_size = ((uint32_t)width * (uint32_t)height) * 3; if (image_size == 0 || image_size > MAX_UPLOAD_IMG_SIZE) { LOG_ERROR(LOG_TAG, "Invalid image size %u", image_size); - return EnrollStatus::Failure; + return Status::Error; } @@ -378,7 +363,7 @@ EnrollStatus FaceAuthenticatorImpl::EnrollImage(const char* user_id, const unsig if (status != PacketManager::SerialStatus::Ok) { LOG_ERROR(LOG_TAG, "Session start failed with status %d", static_cast(status)); - return ToEnrollStatus(status); + return ToStatus(status); } auto chunk_number = static_cast(i); @@ -397,7 +382,7 @@ EnrollStatus FaceAuthenticatorImpl::EnrollImage(const char* user_id, const unsig if (status != PacketManager::SerialStatus::Ok) { LOG_ERROR(LOG_TAG, "Failed sending data packet (chunk %d status %d)", i, (int)status); - return ToEnrollStatus(status); + return ToStatus(status); } total_image_bytes_sent += bytes_to_send; @@ -408,13 +393,38 @@ EnrollStatus FaceAuthenticatorImpl::EnrollImage(const char* user_id, const unsig if (status != PacketManager::SerialStatus::Ok) { LOG_ERROR(LOG_TAG, "Failed receiving reply packet (status %d)", (int)status); - return ToEnrollStatus(status); + return ToStatus(status); } LOG_DEBUG(LOG_TAG, "Sent chunk %hu OK. %zu/%u bytes", chunk_number+1, total_image_bytes_sent, image_size); } assert(total_image_bytes_sent == image_size); + return Status::Ok; +} +// Do enroll session with the device using the given bgr24 face image +// split to chunks and send to device as multiple 'e' DataPackets. +// image_size = Width x Height * 3 +// chunk_size = sizeof(PacketManager::DataMessage::data); +// image_chunk_size= chunk_size - 6 (6 bytes = [chunkN,W,H]) +// Number of chunks = image_size / image_chunk_size +// chunk format: [chunk-number (2 bytes)] [width (2 bytes)] [height (2 bytes)] [buffer (chunk size-6)] +// Wait for ack response ('e' packet) +// Send 'EnrollImage' Fa packet with the user id and return the response to the caller. +EnrollStatus FaceAuthenticatorImpl::EnrollImage(const char* user_id, const unsigned char* buffer, unsigned int width, unsigned int height) +{ + if (!ValidateUserId(user_id)) + { + return EnrollStatus::Failure; + } + + Status imageSendingStatus = SendImageToDevice(buffer, width, height); + if (Status::Ok != imageSendingStatus) + { + LOG_ERROR(LOG_TAG, "Error sending the image to the device. status %d", static_cast(imageSendingStatus)); + return EnrollStatus::Failure; + } + // Now that the image was uploaded, send the enroll image request auto status = _session.Start(_serial.get()); if (status != PacketManager::SerialStatus::Ok) @@ -441,6 +451,104 @@ EnrollStatus FaceAuthenticatorImpl::EnrollImage(const char* user_id, const unsig return EnrollStatus(fa_packet.GetStatusCode()); } +EnrollStatus FaceAuthenticatorImpl::EnrollImageFeatureExtraction(const char* user_id, const unsigned char* buffer, unsigned int width, unsigned int height, ExtractedFaceprints* faceprints) +{ + if (!ValidateUserId(user_id)) + { + LOG_ERROR(LOG_TAG, "invalid user id"); + return EnrollStatus::Failure; + } + + if (nullptr == faceprints) { + LOG_ERROR(LOG_TAG, "the faceprints argument is null"); + return EnrollStatus::Failure; + } + + Status imageSendingStatus = SendImageToDevice(buffer, width, height); + if (Status::Ok != imageSendingStatus) + { + LOG_ERROR(LOG_TAG, "Error sending the image to the device. status %d", static_cast(imageSendingStatus)); + return EnrollStatus::Failure; + } + + // Now that the image was uploaded, send the enroll image request + auto status = _session.Start(_serial.get()); + if (status != PacketManager::SerialStatus::Ok) + { + LOG_ERROR(LOG_TAG, "Session start failed with status %d", static_cast(status)); + return ToEnrollStatus(status); + } + + //Getting the image enrollment result from the device(on success the faceprints are returned) + PacketManager::FaPacket fa_packet {PacketManager::MsgId::EnrollImageFeatureExtraction, user_id, 0}; + status = _session.SendPacket(fa_packet); + if (status != PacketManager::SerialStatus::Ok) + { + LOG_ERROR(LOG_TAG, "Failed sending fa packet (status %d)", (int)status); + return ToEnrollStatus(status); + } + + status = _session.RecvFaPacket(fa_packet); + if (status != PacketManager::SerialStatus::Ok) + { + LOG_ERROR(LOG_TAG, "Failed receiving fa packet (status %d)", (int)status); + return ToEnrollStatus(status); + } + + if (EnrollStatus(fa_packet.GetStatusCode()) != EnrollStatus::Success) + { + return EnrollStatus(fa_packet.GetStatusCode()); + } + + // expect features message + + PacketManager::DataPacket data_packet(PacketManager::MsgId::Faceprints); + status = _session.RecvDataPacket(data_packet); + if (status != PacketManager::SerialStatus::Ok) + { + LOG_ERROR(LOG_TAG, "Failed receiving data packet (status %d)", (int)status); + auto enroll_status = ToEnrollStatus(status); + return enroll_status; + } + + auto msg_id = data_packet.header.id; + if (msg_id == PacketManager::MsgId::Faceprints) + { + LOG_DEBUG(LOG_TAG, "Got faceprints from device!"); + ExtractedFaceprintsElement* desc = (ExtractedFaceprintsElement*)(data_packet.payload.message.data_msg.data); + + // + // read the mask-detector indicator: + feature_t vecFlags = desc->featuresVector[RSID_INDEX_IN_FEATURES_VECTOR_TO_FLAGS]; + feature_t hasMask = (vecFlags == FaVectorFlagsEnum::VecFlagValidWithMask) ? 1 : 0; + + LOG_DEBUG(LOG_TAG, "Enrollment flow : = %d, hasMask = %d.", vecFlags, hasMask); + + faceprints->data.version = desc->version; + faceprints->data.featuresType = desc->featuresType; + faceprints->data.flags = FaOperationFlagsEnum::OpFlagEnrollWithoutMask; + + size_t copySize = sizeof(desc->featuresVector); + + static_assert(sizeof(faceprints->data.featuresVector) == sizeof(desc->featuresVector), + "adaptive faceprints (without mask) sizes does not match"); + ::memcpy(faceprints->data.featuresVector, desc->featuresVector, copySize); + + // mark the enrolled vector flags as valid without mask. + faceprints->data.featuresVector[RSID_INDEX_IN_FEATURES_VECTOR_TO_FLAGS] = + FaVectorFlagsEnum::VecFlagValidWithoutMask; + + return EnrollStatus::Success; + } + else + { + LOG_ERROR(LOG_TAG, "Got unexpected message id when expecting faceprints to arrive: %c", (char)msg_id); + return EnrollStatus::SerialError; + } + + return EnrollStatus::Success; +} + // Do authenticate session with the device. Call user's authenticate callbacks in the process. // Wait for one of the following to happen: // We get 'reply' from device ('Y'). @@ -1095,8 +1203,8 @@ Status FaceAuthenticatorImpl::ExtractFaceprintsForEnroll(EnrollFaceprintsExtract if (msg_id == PacketManager::MsgId::Faceprints) { LOG_DEBUG(LOG_TAG, "Got faceprints from device!"); - ExtractedSecureVersionDescriptor* desc = - (ExtractedSecureVersionDescriptor*)(data_packet.payload.message.data_msg.data); + ExtractedFaceprintsElement* desc = + (ExtractedFaceprintsElement*)(data_packet.payload.message.data_msg.data); // // read the mask-detector indicator: @@ -1263,8 +1371,8 @@ Status FaceAuthenticatorImpl::ExtractFaceprintsForAuth(AuthFaceprintsExtractionC if (msg_id == PacketManager::MsgId::Faceprints) { LOG_DEBUG(LOG_TAG, "Got faceprints from device!"); - ExtractedSecureVersionDescriptor* received_desc = - (ExtractedSecureVersionDescriptor*)(data_packet.payload.message.data_msg.data); + ExtractedFaceprintsElement* received_desc = + (ExtractedFaceprintsElement*)(data_packet.payload.message.data_msg.data); // note that it's the withoutMask[] vector that was written during authentication. // @@ -1500,8 +1608,8 @@ Status FaceAuthenticatorImpl::GetUsersFaceprints(Faceprints* user_features, unsi if (get_features_return_packet.header.id == PacketManager::MsgId::GetUserFeatures) { LOG_DEBUG(LOG_TAG, "Got faceprints from device!"); - DBSecureVersionDescriptor* desc = - (DBSecureVersionDescriptor*)(get_features_return_packet.payload.message.data_msg.data); + DBFaceprintsElement* desc = + (DBFaceprintsElement*)(get_features_return_packet.payload.message.data_msg.data); user_features[i].data.version = desc->version; user_features[i].data.featuresType = (FaceprintsTypeEnum)(desc->featuresType); @@ -1569,10 +1677,10 @@ Status FaceAuthenticatorImpl::SetUsersFaceprints(UserFaceprints_t* user_features { return Status::Error; } - char buffer[sizeof(DBSecureVersionDescriptor) + PacketManager::MaxUserIdSize + 1] = {0}; + char buffer[sizeof(DBFaceprintsElement) + PacketManager::MaxUserIdSize + 1] = {0}; strncpy(buffer, user_id, PacketManager::MaxUserIdSize + 1); size_t offset = PacketManager::MaxUserIdSize + 1; - DBSecureVersionDescriptor* desc = (DBSecureVersionDescriptor*)&user_desc.faceprints; + DBFaceprintsElement* desc = (DBFaceprintsElement*)&user_desc.faceprints; memcpy(buffer + offset, (char*)desc, sizeof(*desc)); offset += sizeof(*desc); PacketManager::DataPacket data_packet {PacketManager::MsgId::SetUserFeatures, buffer, offset}; diff --git a/src/FaceAuthenticatorImpl.h b/src/FaceAuthenticatorImpl.h index 747c914..fc5d2d1 100644 --- a/src/FaceAuthenticatorImpl.h +++ b/src/FaceAuthenticatorImpl.h @@ -55,6 +55,7 @@ class FaceAuthenticatorImpl Status Enroll(EnrollmentCallback& callback, const char* user_id); EnrollStatus EnrollImage(const char* user_id, const unsigned char* buffer, unsigned int width, unsigned int height); + EnrollStatus EnrollImageFeatureExtraction(const char* user_id, const unsigned char* buffer, unsigned int width, unsigned int height, ExtractedFaceprints* faceprints); Status Authenticate(AuthenticationCallback& callback); Status AuthenticateLoop(AuthenticationCallback& callback); Status Cancel(); @@ -67,6 +68,7 @@ class FaceAuthenticatorImpl Status QueryNumberOfUsers(unsigned int& number_of_users); Status Standby(); + Status SendImageToDevice(const unsigned char* buffer, unsigned int width, unsigned int height); Status ExtractFaceprintsForEnroll(EnrollFaceprintsExtractionCallback& callback); Status ExtractFaceprintsForAuth(AuthFaceprintsExtractionCallback& callback); Status ExtractFaceprintsForAuthLoop(AuthFaceprintsExtractionCallback& callback); diff --git a/src/PacketManager/SerialPacket.h b/src/PacketManager/SerialPacket.h index 4322637..161eae0 100644 --- a/src/PacketManager/SerialPacket.h +++ b/src/PacketManager/SerialPacket.h @@ -47,6 +47,7 @@ enum class MsgId : char RemoveUser = 'D', Enroll = 'E', EnrollImage = 'I', + EnrollImageFeatureExtraction = 'J', Hint = 'H', SecureFaceprintsEnroll = 'N', SecureFaceprintsAuthenticate = 'Q', diff --git a/tools/rsid-android-app/app/build.gradle b/tools/rsid-android-app/app/build.gradle index ea02cc1..38dfb07 100644 --- a/tools/rsid-android-app/app/build.gradle +++ b/tools/rsid-android-app/app/build.gradle @@ -13,7 +13,7 @@ android { minSdkVersion 23 //noinspection ExpiredTargetSdkVersion targetSdkVersion 27 - versionName "0_23_0" + versionName "0_25_0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/tools/rsid-viewer/MainWindow.xaml.cs b/tools/rsid-viewer/MainWindow.xaml.cs index 1c68c0d..d83b919 100644 --- a/tools/rsid-viewer/MainWindow.xaml.cs +++ b/tools/rsid-viewer/MainWindow.xaml.cs @@ -175,8 +175,15 @@ private void EnrollImgButton_Click(object sender, RoutedEventArgs e) { UserId = enrollInput.Username, Filename = openFileDialog.FileName - }; - Task.Run(() => EnrollImageJob(enrollData, false)); + }; + if (FlowMode.Server == _flowMode) + { + Task.Run(() => EnrollImageHostJob(enrollData, false)); + } + else + { + Task.Run(() => EnrollImageJob(enrollData, false)); + } } private async void BatchEnrollImgButton_Click(object sender, RoutedEventArgs e) @@ -213,7 +220,14 @@ private async void BatchEnrollImgButton_Click(object sender, RoutedEventArgs e) _progressBar.Update(progress * 100); // perform the enroll var notLast = counter < enrollList.Count; - var success = await Task.Run(() => EnrollImageJob(record, notLast)); + var success = true; + if ( FlowMode.Server == _flowMode) { + success = await Task.Run(() => EnrollImageHostJob(record, notLast)); + } + else{ + success = await Task.Run(() => EnrollImageJob(record, notLast)); + } + if (success) { successCounter++; @@ -760,7 +774,7 @@ private void SetUiEnabled(bool isEnabled) ImportButton.IsEnabled = isEnabled && (_flowMode != FlowMode.Server); ExportButton.IsEnabled = isEnabled && (_flowMode != FlowMode.Server); EnrollButton.IsEnabled = isEnabled; - EnrollImgButton.IsEnabled = isEnabled && (_flowMode != FlowMode.Server); ; + EnrollImgButton.IsEnabled = isEnabled; BatchEnrollButton.IsEnabled = EnrollImgButton.IsEnabled; // auth button enabled if there are enrolled users or if we in spoof/face detection only mode var authBtnEnabled = AuthenticateButton.IsEnabled = @@ -2007,7 +2021,84 @@ private bool EnrollImageJob(EnrollImageRecord enrollRecord, bool isBatch) } return success; } - + + private bool EnrollImageHostJob(EnrollImageRecord enrollRecord, bool isBatch) + { + + var success = false; + IntPtr userIdCtx = IntPtr.Zero; + const int maxImageSize = 900 * 1024; + if (!ConnectAuth()) return false; + + try + { + var (buffer, w, h, bitmap) = ImageHelper.ToBgr(enrollRecord.Filename, maxImageSize); + + OnStartSession($"Enroll \"{enrollRecord.UserId}\"", true); + userIdCtx = Marshal.StringToHGlobalUni(enrollRecord.UserId); + + // validate file not bigger than max allowed + if (buffer.Length > maxImageSize) + throw new Exception("File too big"); + + // show uploaded image on preview panel + RenderDispatch(() => + { + var bi = ImageHelper.BitmapToImageSource(bitmap); + // flip back horizontally since the preview canvas is flipped + var transform = new ScaleTransform { ScaleX = -1 }; + var image = new Image + { + Source = bi, + RenderTransformOrigin = new Point(0.5, 0.5), + RenderTransform = transform, + Height = 250, + }; + var border = new Border + { + BorderThickness = new Thickness(2), + BorderBrush = Brushes.White, + Child = image + }; + + PreviewCanvas.Children.Add(border); + Canvas.SetRight(border, 16); + Canvas.SetTop(border, 205); + }); + + ShowProgressTitle("Uploading To Device.."); + _authloopRunning = true; + + var faceprints = new rsid.Faceprints(); + var status = _authenticator.EnrollImageFeatureExtraction(enrollRecord.UserId, buffer, w, h, ref faceprints); + if (status == EnrollStatus.Success && !isBatch) + { + _db.Push(faceprints, enrollRecord.UserId); + _db.Save(); + RefreshUserListServer(); + } + + var logMsg = status == EnrollStatus.Success ? "Enroll success" : status.ToString(); + VerifyResult(status == EnrollStatus.Success, logMsg, logMsg); + success = status == EnrollStatus.Success; + } + catch (Exception ex) + { + ShowFailedTitle(ex.Message); + OnStopSession(); + } + finally + { + if (!isBatch) OnStopSession(); + HideEnrollingLabelPanel(); + _authloopRunning = false; + _authenticator.Disconnect(); + if (userIdCtx != IntPtr.Zero) + Marshal.FreeHGlobal(userIdCtx); + } + return success; + } + // Enroll Job private void EnrollExtractFaceprintsJob(Object threadContext) { diff --git a/wrappers/c/include/rsid_c/rsid_client.h b/wrappers/c/include/rsid_c/rsid_client.h index 06b4a27..62f5668 100644 --- a/wrappers/c/include/rsid_c/rsid_client.h +++ b/wrappers/c/include/rsid_c/rsid_client.h @@ -48,23 +48,23 @@ extern "C" // Typedefs here are based on those in FaceprintsDefines.h: #ifdef __cplusplus - typedef RealSenseID::rsid_faceprints_t rsid_faceprints; + typedef RealSenseID::DBFaceprintsElement rsid_faceprints_t; #else - typedef rsid_faceprints_t rsid_faceprints; + typedef struct DBFaceprintsElement rsid_faceprints_t; #endif #ifdef __cplusplus - typedef RealSenseID::rsid_extracted_faceprints_t rsid_extracted_faceprints; + typedef RealSenseID::ExtractedFaceprintsElement rsid_extracted_faceprints_t; #else - typedef rsid_extracted_faceprints_t rsid_extracted_faceprints; + typedef struct ExtractedFaceprintsElement rsid_extracted_faceprints_t; #endif - typedef rsid_extracted_faceprints rsid_faceprints_match_element; + typedef rsid_extracted_faceprints_t rsid_faceprints_match_element_t; typedef struct { char * user_id; - rsid_faceprints faceprints; + rsid_faceprints_t faceprints; } rsid_user_faceprints_dble; /* @@ -118,18 +118,18 @@ extern "C" } rsid_enroll_args; /* rsid_extract_faceprints_for_auth() args */ - typedef void (*rsid_faceprints_ext_status_clbk)(rsid_auth_status status, const rsid_extracted_faceprints* faceprints, + typedef void (*rsid_faceprints_ext_status_clbk)(rsid_auth_status status, const rsid_extracted_faceprints_t* faceprints, void* ctx); typedef struct rsid_faceprints_ext_args // TODO: change name to rsid_auth_ext_args { rsid_faceprints_ext_status_clbk result_clbk; /* result callback */ rsid_auth_hint_clbk hint_clbk; /* hint callback */ rsid_face_detected_clbk face_detected_clbk; /* face detected callback (set to NULL if not needed)*/ - rsid_extracted_faceprints* faceprints; /* extracted faceprints*/ + rsid_extracted_faceprints_t* faceprints; /* extracted faceprints*/ void* ctx; /* user defined context (optional) */ } rsid_faceprints_ext_args; - typedef void (*rsid_enroll_ext_status_clbk)(rsid_enroll_status status, const rsid_faceprints* faceprints, + typedef void (*rsid_enroll_ext_status_clbk)(rsid_enroll_status status, const rsid_faceprints_t* faceprints, void* ctx); typedef struct rsid_enroll_ext_args @@ -144,9 +144,9 @@ extern "C" /* rsid_match_faceprints() args */ typedef struct rsid_match_args { - rsid_faceprints_match_element new_faceprints; - rsid_faceprints existing_faceprints; - rsid_faceprints updated_faceprints; + rsid_faceprints_match_element_t new_faceprints; + rsid_faceprints_t existing_faceprints; + rsid_faceprints_t updated_faceprints; rsid_matcher_confidence_level_type matcher_confidence_level; } rsid_match_args; @@ -202,6 +202,10 @@ RSID_C_API rsid_authenticator* rsid_create_authenticator(); RSID_C_API rsid_enroll_status rsid_enroll_image(rsid_authenticator* authenticator, const char* user_id, const unsigned char* buffer, unsigned width, unsigned height); + /* enroll a user with image and return the faceprints*/ + RSID_C_API rsid_enroll_status rsid_extract_faceprints_from_image(rsid_authenticator* authenticator, + const char* user_id, const unsigned char* buffer, unsigned width, unsigned height, rsid_faceprints_t* c_faceprints); + /* authenticate a user */ RSID_C_API rsid_status rsid_authenticate(rsid_authenticator* authenticator, const rsid_auth_args* args); @@ -262,7 +266,7 @@ RSID_C_API rsid_authenticator* rsid_create_authenticator(); * Get the feature descriptors associated with the given userID * On successful operation, the descriptors are copied to faceprints. */ - RSID_C_API rsid_status rsid_get_users_faceprints(rsid_authenticator* authenticator, rsid_faceprints* user_features); + RSID_C_API rsid_status rsid_get_users_faceprints(rsid_authenticator* authenticator, rsid_faceprints_t* user_features); /* * Insert (or update) all the users from the given array to the device's database. diff --git a/wrappers/c/src/rsid_c_client.cc b/wrappers/c/src/rsid_c_client.cc index 3b657f7..c069025 100644 --- a/wrappers/c/src/rsid_c_client.cc +++ b/wrappers/c/src/rsid_c_client.cc @@ -119,7 +119,7 @@ class AuthClbk : public RealSenseID::AuthenticationCallback }; static void copy_to_c_faceprints_ple_ple(const RealSenseID::ExtractedFaceprints& faceprints, - rsid_extracted_faceprints* c_faceprints) + rsid_extracted_faceprints_t* c_faceprints) { c_faceprints->version = faceprints.data.version; c_faceprints->featuresType = (int)faceprints.data.featuresType; @@ -131,7 +131,7 @@ static void copy_to_c_faceprints_ple_ple(const RealSenseID::ExtractedFaceprints& } static void copy_to_c_faceprints_ple_dble_for_enroll(const RealSenseID::ExtractedFaceprints& faceprints, - rsid_faceprints* c_faceprints) + rsid_faceprints_t* c_faceprints) { c_faceprints->version = faceprints.data.version; c_faceprints->featuresType = (int)faceprints.data.featuresType; @@ -151,7 +151,7 @@ static void copy_to_c_faceprints_ple_dble_for_enroll(const RealSenseID::Extracte c_faceprints->adaptiveDescriptorWithMask[RSID_INDEX_IN_FEATURES_VECTOR_TO_FLAGS] = FaVectorFlagsEnum::VecFlagNotSet; } -static void copy_to_cpp_faceprints_ple_ple(const rsid_extracted_faceprints* c_faceprints, +static void copy_to_cpp_faceprints_ple_ple(const rsid_extracted_faceprints_t* c_faceprints, RealSenseID::ExtractedFaceprints& faceprints) { faceprints.data.version = c_faceprints->version; @@ -163,7 +163,7 @@ static void copy_to_cpp_faceprints_ple_ple(const rsid_extracted_faceprints* c_fa ::memcpy(faceprints.data.featuresVector, c_faceprints->featuresVector, sizeof(c_faceprints->featuresVector)); } -static void copy_to_c_faceprints_dble_dble(const RealSenseID::Faceprints& faceprints, rsid_faceprints* c_faceprints) +static void copy_to_c_faceprints_dble_dble(const RealSenseID::Faceprints& faceprints, rsid_faceprints_t* c_faceprints) { c_faceprints->version = faceprints.data.version; c_faceprints->featuresType = (int)faceprints.data.featuresType; @@ -187,7 +187,7 @@ static void copy_to_c_faceprints_dble_dble(const RealSenseID::Faceprints& facepr sizeof(faceprints.data.enrollmentDescriptor)); } -static void copy_to_cpp_faceprints_dble_dble(const rsid_faceprints* c_faceprints, RealSenseID::Faceprints& faceprints) +static void copy_to_cpp_faceprints_dble_dble(const rsid_faceprints_t* c_faceprints, RealSenseID::Faceprints& faceprints) { faceprints.data.version = c_faceprints->version; faceprints.data.featuresType = (RealSenseID::FaceprintsTypeEnum)c_faceprints->featuresType; @@ -224,7 +224,7 @@ class AuthFaceprintsExtClbk : public RealSenseID::AuthFaceprintsExtractionCallba { if (_faceprints_ext_args.result_clbk) { - rsid_extracted_faceprints c_faceprints; + rsid_extracted_faceprints_t c_faceprints; if (status == RealSenseID::AuthenticateStatus::Success) { @@ -264,7 +264,7 @@ class AuthLoopFaceprintsExtClbk : public RealSenseID::AuthFaceprintsExtractionCa { if (_faceprints_ext_args.result_clbk) { - rsid_extracted_faceprints c_faceprints; + rsid_extracted_faceprints_t c_faceprints; if (status == RealSenseID::AuthenticateStatus::Success) { @@ -303,8 +303,7 @@ class EnrollFaceprintsExtClbk : public RealSenseID::EnrollFaceprintsExtractionCa { if (_enroll_ext_args.status_clbk) { - // rsid_extracted_faceprints c_faceprints; - rsid_faceprints c_faceprints; + rsid_faceprints_t c_faceprints; if (status == RealSenseID::EnrollStatus::Success) { @@ -588,11 +587,27 @@ rsid_status rsid_enroll(rsid_authenticator* authenticator, const rsid_enroll_arg return static_cast(status); } +rsid_enroll_status rsid_extract_faceprints_from_image(rsid_authenticator* authenticator, const char* user_id, const unsigned char* buffer, unsigned width, unsigned height, rsid_faceprints_t* c_faceprints) +{ + auto* auth_impl = get_auth_impl(authenticator); + ExtractedFaceprints extractedFaceprints; + RealSenseID::EnrollStatus status = auth_impl->EnrollImageFeatureExtraction(user_id, buffer, width, height, &extractedFaceprints); + + if (RealSenseID::EnrollStatus::Success == status) { + + copy_to_c_faceprints_ple_dble_for_enroll(extractedFaceprints, c_faceprints); + } + + return static_cast(status); +} + rsid_enroll_status rsid_enroll_image(rsid_authenticator* authenticator, const char* user_id, const unsigned char* buffer, unsigned width, unsigned height) { - auto* auth_impl = get_auth_impl(authenticator); - auto status = auth_impl->EnrollImage(user_id, buffer, width, height); + auto* auth_impl = get_auth_impl(authenticator); + RealSenseID::EnrollStatus status; + status = auth_impl->EnrollImage(user_id, buffer, width, height); + return static_cast(status); } @@ -604,7 +619,7 @@ rsid_status rsid_authenticate(rsid_authenticator* authenticator, const rsid_auth return static_cast(status); } -static RealSenseID::Faceprints convert_to_cpp_faceprints_dble(rsid_faceprints* rsid_faceprints_instance) +static RealSenseID::Faceprints convert_to_cpp_faceprints_dble(rsid_faceprints_t* rsid_faceprints_instance) { RealSenseID::Faceprints faceprints; faceprints.data.version = rsid_faceprints_instance->version; @@ -656,7 +671,7 @@ static RealSenseID::ThresholdsConfidenceEnum convert_to_confidence_level(rsid_ma } static RealSenseID::MatchElement convert_to_cpp_faceprints_match_element( - rsid_faceprints_match_element* rsid_faceprints_instance) + rsid_faceprints_match_element_t* rsid_faceprints_instance) { RealSenseID::MatchElement faceprints; faceprints.data.version = rsid_faceprints_instance->version; @@ -726,7 +741,7 @@ rsid_match_result rsid_match_faceprints(rsid_authenticator* authenticator, rsid_ // write the updated adaptive faceprints on the args->updated_faceprints so Authenticator.cs // will have the updated vector! - rsid_faceprints* rsid_updated_faceprints = &args->updated_faceprints; + rsid_faceprints_t* rsid_updated_faceprints = &args->updated_faceprints; // update withoutMask[] vector static_assert(sizeof(rsid_updated_faceprints->adaptiveDescriptorWithoutMask) == sizeof(updated_faceprints.data.adaptiveDescriptorWithoutMask), @@ -909,7 +924,7 @@ rsid_status rsid_query_number_of_users(rsid_authenticator* authenticator, unsign return static_cast(auth_impl->QueryNumberOfUsers(*number_of_users)); } -rsid_status rsid_get_users_faceprints(rsid_authenticator* authenticator, rsid_faceprints* user_features) +rsid_status rsid_get_users_faceprints(rsid_authenticator* authenticator, rsid_faceprints_t* user_features) { auto* auth_impl = get_auth_impl(authenticator); RealSenseID::Faceprints user_descriptors[1000]; //@TODO use a max_users macro diff --git a/wrappers/csharp/Authenticator.cs b/wrappers/csharp/Authenticator.cs index ed4269a..67bac1a 100644 --- a/wrappers/csharp/Authenticator.cs +++ b/wrappers/csharp/Authenticator.cs @@ -103,7 +103,7 @@ public enum MatcherConfidenceLevel // the struct includes several vectors and metadata to support all our internal matching mechanism (e.g. adaptive-learning etc..). // (1) this structure will be used to represent faceprints in the DB (and therefore contains // more vectors and info). - // (2) this structure must be aligned with struct DBSecureVersionDescriptor (FaceprintsDefines.h) and Faceprints (Faceprints.h)! + // (2) this structure must be aligned with struct DBSecureVersionDescriptor_t (FaceprintsDefines.h) and Faceprints (Faceprints.h)! // order and types matters (due to marshaling etc..). // public struct Faceprints @@ -143,7 +143,7 @@ public struct Faceprints // extracted faceprints element // a reduced structure that is used to represent the extracted faceprints been transferred from the device to the host // through the packet layer. - // (1) this structure must be aligned with struct ExtractedSecureVersionDescriptor (FaceprintsDefines.h) and ExtractedFaceprints (Faceprints.h)! + // (1) this structure must be aligned with struct ExtractedFaceprintsElement (FaceprintsDefines.h) and ExtractedFaceprints (Faceprints.h)! // order and types matters (due to marshaling etc..). // public struct ExtractedFaceprints @@ -434,6 +434,18 @@ public EnrollStatus EnrollImage(string userId, byte[] buffer, int width, int hei } finally { pinnedArray.Free(); } } + + public EnrollStatus EnrollImageFeatureExtraction(string userId, byte[] buffer, int width, int height, ref Faceprints userFaceprints) + { + var pinnedArray = GCHandle.Alloc(buffer, GCHandleType.Pinned); + try + { + var pointer = pinnedArray.AddrOfPinnedObject(); + return rsid_extract_faceprints_from_image(_handle, userId, pointer, width, height, ref userFaceprints); + + } + finally { pinnedArray.Free(); } + } public Status Authenticate(AuthArgs args) { @@ -650,6 +662,9 @@ public bool SetUsersFaceprints(List user_features) [DllImport(Shared.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] static extern EnrollStatus rsid_enroll_image(IntPtr rsid_authenticator, string userId, IntPtr buffer, int width, int height); + [DllImport(Shared.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + static extern EnrollStatus rsid_extract_faceprints_from_image(IntPtr rsid_authenticator, string userId, IntPtr buffer, int width, int height, ref Faceprints faceprints); + [DllImport(Shared.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] static extern Status rsid_authenticate(IntPtr rsid_authenticator, ref AuthArgs authArgs); diff --git a/wrappers/python/face_auth_py.cc b/wrappers/python/face_auth_py.cc index b9de7d7..0ed08a2 100644 --- a/wrappers/python/face_auth_py.cc +++ b/wrappers/python/face_auth_py.cc @@ -436,21 +436,21 @@ void init_face_authenticator(pybind11::module& m) py::enum_(m, "FaceprintsType").value("W10", FaceprintsType::W10).value("RGB", FaceprintsType::RGB); - py::class_(m, "Faceprints") + py::class_(m, "Faceprints") .def(py::init<>()) - .def_readwrite("version", &Faceprints_t::version) - .def_readwrite("features_type", &Faceprints_t::featuresType) - .def_readwrite("flags", &Faceprints_t::flags) + .def_readwrite("version", &DBFaceprintsElement::version) + .def_readwrite("features_type", &DBFaceprintsElement::featuresType) + .def_readwrite("flags", &DBFaceprintsElement::flags) /* adaptiveDescriptorWithoutMask getter/getter */ .def_property( "adaptive_descriptor_nomask", - [](Faceprints_t& self) { + [](DBFaceprintsElement& self) { // getter - return as vector of feature_t return std::vector {std::begin(self.adaptiveDescriptorWithoutMask), std::end(self.adaptiveDescriptorWithoutMask)}; }, // setter of avg descriptor list - [](Faceprints_t& self, const std::vector& new_descriptors) { + [](DBFaceprintsElement& self, const std::vector& new_descriptors) { constexpr size_t n_elements = std::extent::value; if (new_descriptors.size() != n_elements) { @@ -463,13 +463,13 @@ void init_face_authenticator(pybind11::module& m) /* adaptiveDescriptorWitMask getter/getter */ .def_property( "adaptive_descriptor_withmask", - [](Faceprints_t& self) { + [](DBFaceprintsElement& self) { // getter - return as vector of feature_t return std::vector {std::begin(self.adaptiveDescriptorWithMask), std::end(self.adaptiveDescriptorWithMask)}; }, // setter of avg descriptor list - [](Faceprints_t& self, const std::vector& new_descriptors) { + [](DBFaceprintsElement& self, const std::vector& new_descriptors) { constexpr size_t n_elements = std::extent::value; static_assert(n_elements == RSID_FEATURES_VECTOR_ALLOC_SIZE, "n_elements!=RSID_FEATURES_VECTOR_ALLOC_SIZE"); @@ -484,13 +484,13 @@ void init_face_authenticator(pybind11::module& m) /* adaptiveDescriptorWitMask getter/getter */ .def_property( "enroll_descriptor", - [](Faceprints_t& self) { + [](DBFaceprintsElement& self) { // getter - return as vector of feature_t return std::vector {std::begin(self.enrollmentDescriptor), std::end(self.enrollmentDescriptor)}; }, // setter of avg descriptor list - [](Faceprints_t& self, const std::vector& new_descriptors) { + [](DBFaceprintsElement& self, const std::vector& new_descriptors) { constexpr size_t n_elements = std::extent::value; static_assert(n_elements == RSID_FEATURES_VECTOR_ALLOC_SIZE, "n_elements!=RSID_FEATURES_VECTOR_ALLOC_SIZE"); @@ -505,12 +505,12 @@ void init_face_authenticator(pybind11::module& m) /* reserved array getter/getter */ .def_property( "reserved", - [](Faceprints_t& self) { + [](DBFaceprintsElement& self) { // getter - return as vector of feature_t return std::vector {std::begin(self.reserved), std::end(self.reserved)}; }, // setter of avg descriptor list - [](Faceprints_t& self, const std::vector& new_descriptors) { + [](DBFaceprintsElement& self, const std::vector& new_descriptors) { constexpr size_t n_elements = std::extent::value; if (new_descriptors.size() != n_elements) { @@ -519,7 +519,7 @@ void init_face_authenticator(pybind11::module& m) } std::copy(new_descriptors.begin(), new_descriptors.end(), self.reserved); }) - .def("__repr__", [](const Faceprints_t& fp) { + .def("__repr__", [](const DBFaceprintsElement& fp) { std::ostringstream oss; auto n_no_mask_descriptors = std::extent::value; auto n_with_mask_descriptors = std::extent::value; @@ -728,7 +728,7 @@ void init_face_authenticator(pybind11::module& m) py::arg("on_faces") = FaceDetectedClbkFun {}, py::call_guard()) .def("match_faceprints", [](FaceAuthenticator& self, ExtractedFaceprintsElement& new_faceprints, - Faceprints_t& existing_faceprints, Faceprints_t& updated_faceprints) { + DBFaceprintsElement& existing_faceprints, DBFaceprintsElement& updated_faceprints) { // wrap with needed classes MatchElement match_element; match_element.data = new_faceprints;