Skip to content

Commit

Permalink
Version 0.25.0. Support for enrollment from RGB image in host mode
Browse files Browse the repository at this point in the history
  • Loading branch information
gabime committed Aug 19, 2021
1 parent a1dd1c3 commit f29171b
Show file tree
Hide file tree
Showing 15 changed files with 347 additions and 107 deletions.
11 changes: 11 additions & 0 deletions include/RealSenseID/FaceAuthenticator.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions include/RealSenseID/Faceprints.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,24 @@ 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
// to user objects read from the DB.
class MatchElement
{
public:
RealSenseID::MatchElement_t data;
RealSenseID::ExtractedFaceprintsElement data;
};

// faceprints plus username element.
Expand Down
23 changes: 4 additions & 19 deletions include/RealSenseID/FaceprintsDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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).

Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions include/RealSenseID/Version.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
#include <string>

#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
{
Expand Down
4 changes: 4 additions & 0 deletions release_notes.txt
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
8 changes: 6 additions & 2 deletions src/FaceAuthenticator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
168 changes: 138 additions & 30 deletions src/FaceAuthenticatorImpl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}


Expand Down Expand Up @@ -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<int>(status));
return ToEnrollStatus(status);
return ToStatus(status);
}

auto chunk_number = static_cast<uint16_t>(i);
Expand All @@ -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;
Expand All @@ -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<int>(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)
Expand All @@ -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<int>(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<int>(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').
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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.
//
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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};
Expand Down
Loading

0 comments on commit f29171b

Please sign in to comment.