赤外線カメラで Windows Hello を有効にする
[一部の情報はリリース前の製品に関することであり、正式版がリリースされるまでに大幅に変更される可能性があります。本書に記載された情報について、Microsoft は明示または黙示を問わずいかなる保証をするものでもありません。]
Windows Hello を利用すると、顔認証や指紋認証を使って Windows 10 のシステムやデバイスにログオンすることができます。このトピックでは、赤外線ビデオ カメラで Windows Hello を有効にする方法について説明します。また、このトピックは、デバイスでこのログオン機能を提供することを計画している相手先ブランド供給 (OEM) および独立系ハードウェア ベンダー (IHV) を対象としています。特に、アプリ向けの認識 API で使うために調整された色センサー、赤外線センサー、深度センサーを製造する IHV や、さまざまな IHV 製のセンサーの組み込みや調整を行って最終的なデバイスを製造する OEM を対象としています。
注 このトピックでは、基になるデバイスからデータを読み取るしくみについては説明しません。 IHV に独自のドライバーがある場合は、IPerceptionFrameProvider の実装をそのドライバーに対して行ってください。 デバイスが UVC デバイスの場合、IHV は、メディア ファンデーションとクラス ドライバーを活用して、デバイスからデータを取得してください。 メディア ファンデーションについて詳しくは、Microsoft メディア ファンデーションに関するページをご覧ください。
Windows Hello チェック リスト
Windows Hello をサポートするには、次の手順を実行する必要があります。
赤外線カメラ用のプロバイダー モジュールを登録する
ドライバーをインストールするとき、各ソース プロバイダー DLL を次のレジストリ キーに登録する必要があります。
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Analog\Providers
また、各ソース プロバイダーは、Providers
の下に独自のサブキーで登録してください。 各ソース プロバイダーのエントリには、次の 2 つの値を含める必要があります。
名前 | データ型 | 説明 |
ModulePath | REG_EXPAND_SZ | IPerceptionFrameProviderManager オブジェクトをインスタンス化する DLL への完全なパス。 |
ActivatableClass | REG_SZ | IPerceptionFrameProviderManager インターフェイスを実装するランタイム クラスの完全修飾名。 |
1 つの DLL には、複数の異なるソース プロバイダーを実装できます。そのためには、同じ ModulePath を共有するが、ActivatableClass 値が異なる複数のキーを登録します。 たとえば、Kinect for Windows v2 SDK を使うソース プロバイダーは、次のようになる場合があります。
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Analog\Providers\KinectV2]
"ModulePath"="%SystemRoot%\System32\Kinect\KinectV2SourceProvider.dll"
"ActivatableClass"="Microsoft.Kinect.KinectV2SourceProvider"
Windows Hello 対応デバイスであることを示すようにレジストリ キーを設定する
Windows Hello の登録ユーザー インターフェイスが、カメラの読み込みと初期化の後、すぐに表示されるようにするには、プロバイダー DLL をインストールするときに、次に示すレジストリ値が必要になります。
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Analog\Providers]
"FaceAuthenticationSourcesHint"=dword:00000001
IPerceptionFrameProviderManager を実装する
システム サービスは、プロバイダー DLL を読み込んで、マネージャーを初期化します。 Dll には IPerceptionFrameProviderManager を実装しているアクティブ化可能なクラスが含まれている必要があります。このクラスはシステムによってアクティブ化されます。
namespace WDDP = Windows::Devices::Perception::Provider;
public ref class SampleFrameProviderManager sealed : public WDPP::IPerceptionFrameProviderManager
{
public:
SampleFrameProviderManager();
virtual ~SampleFrameProviderManager();
// IFrameProviderManager interface methods
virtual WDPP::IPerceptionFrameProvider^ GetFrameProvider(
WDPP::PerceptionFrameProviderInfo^ frameProviderInfo);
private:
}
デバイスを初期化し、登録する
デバイスを接続したら、プロバイダーを作成し、RegisterFrameProviderInfo を使ってそのプロバイダーを登録する必要があります。
また、UpdateAvailabilityForProvider を使って、ソース プロバイダーが利用可能になったことを通知する必要もあります。
{// Initialize our IPerceptionFrameProvider object and add it to the Manager's internal map
void SampleFrameProvider^ provider = ref new SampleFrameProvider(targetDeviceId);
_providerMap->Insert(provider->FrameProviderInfo->Id, provider);
// Register our Provider object with the service
WDPP::PerceptionFrameProviderManagerService::RegisterFrameProviderInfo(
this, provider->FrameProviderInfo);
// Notify the provider is avalible because device is connected
WDPP::PerceptionFrameProviderManagerService:: UpdateAvailabilityForProvider(
provider, true);
// Initialize a ControlGroup object which allows the clients to set/change
// properties on the Provider
auto controllerModeIds = ref new Platform::Collections::Vector<Platform::String^>();
controllerModeIds->Append(provider->FrameProviderInfo->Id);
_controlGroup = ref new WDPP::PerceptionControlGroup(controllerModeIds->GetView());
WDPP::PerceptionFrameProviderManagerService::RegisterControlGroup(this, _controlGroup);
// Register our Provider for FaceAuthentication. only a single Provider in this implementation
auto faceAuthProviderIds = ref new Platform::Collections::Vector<Platform::String^>();
faceAuthProviderIds->Append(provider->FrameProviderInfo->Id);
// Create handlers for the "Start" and "Stop" FaceAuthentication events
auto startFaceAuthHandler = ref new WDPP::PerceptionStartFaceAuthenticationHandler(
this, &SampleFrameProviderManager::StartFaceAuthentication);
auto stopFaceAuthHandler = ref new WDPP::PerceptionStopFaceAuthenticationHandler(
this, &SampleFrameProviderManager::StopFaceAuthentication);
// Create a FaceAuthentication group containing our Providers list and event handlers
_faceAuthGroup = ref new WDPP::PerceptionFaceAuthenticationGroup(
faceAuthProviderIds->GetView(),
startFaceAuthHandler,
stopFaceAuthHandler);
// Finally register our FaceAuthentication group with the service
WDPP::PerceptionFrameProviderManagerService::RegisterFaceAuthenticationGroup(
this, _faceAuthGroup);
}
デバイスの登録を解除する
デバイスをコンピューターから取り外したら、UnregisterFrameProviderInfo を使ってプロバイダーの登録を解除し、デバイスが利用できなくなったことをシステムに通知します。
void SampleFrameProviderManager::OnDeviceDisconnect()
{
// Unregister the FaceAuthentication group
if (_faceAuthGroup != nullptr)
{
WDPP::PerceptionFrameProviderManagerService::UnregisterFaceAuthenticationGroup(
this, _faceAuthGroup);
_faceAuthGroup = nullptr;
}
// Unregister the ControlGroup
if (_controlGroup != nullptr)
{
WDPP::PerceptionFrameProviderManagerService::UnregisterControlGroup(this, _controlGroup);
_controlGroup = nullptr;
}
// Unregister the Provider object and release it. Following sample assume there’s only 1 provider.
if (_providerMap->Size > 0)
{
auto firstItem = _providerMap->First();
WDPP::PerceptionFrameProviderInfo^ providerInfo =
firstItem->Current->Value->FrameProviderInfo;
if (providerInfo != nullptr)
{
WDPP::PerceptionFrameProviderManagerService::UnregisterFrameProviderInfo(
this, providerInfo);
}
_providerMap->Clear();
利用可能なフレーム ソースを列挙し、フィルター処理する
次のコードは、利用可能なフレーム ソースを列挙し、フィルター処理する方法を示しています。ここでは、フレーム ソースの登録や登録解除が行われると通知されます。
void SampleFrameProviderManager::OnDeviceDisconnect()
{
// Unregister the FaceAuthentication group
if (_faceAuthGroup != nullptr)
{
WDPP::PerceptionFrameProviderManagerService::UnregisterFaceAuthenticationGroup(
this, _faceAuthGroup);
_faceAuthGroup = nullptr;
}
// Unregister the ControlGroup
if (_controlGroup != nullptr)
{
WDPP::PerceptionFrameProviderManagerService::UnregisterControlGroup(this, _controlGroup);
_controlGroup = nullptr;
}
// Unregister the Provider object and release it. Following sample assume there’s only 1 provider.
if (_providerMap->Size > 0)
{
auto firstItem = _providerMap->First();
WDPP::PerceptionFrameProviderInfo^ providerInfo =
firstItem->Current->Value->FrameProviderInfo;
if (providerInfo != nullptr)
{
WDPP::PerceptionFrameProviderManagerService::UnregisterFrameProviderInfo(
this, providerInfo);
}
_providerMap->Clear();
}
}
Start 要求に応答する
Start 要求を受け取ってから 400 ミリ秒未満で、プロバイダーはカメラをオンにして、PerceptionFrameProviderManagerService.PublishFrameForProvider を使ってフレームの発行を開始する必要があります。Start 要求が到着したときにデバイスが利用できない場合、プロバイダーはこの要求をキューに登録し、デバイスが利用可能になったときに、フレームの発行を開始します。
void SampleFrameProvider::Start()
{
// Normally, a source would communicate to underlying device that actually produces
// the desired data. For simplicity this sample frameprovider is a synthetic
// source backed by a timer.
_threadPoolTimer = WST::ThreadPoolTimer::CreatePeriodicTimer(
ref new WST::TimerElapsedHandler(this, &SampleFrameProvider::OnFrameArrived),
_frameDuration);
}
Stop 要求に応答する
プロバイダーは、フレームの発行を停止し、カメラをオフにする必要があります。
void SampleFrameProvider::Stop()
{
// Normally, a source would communicate to underlying device that actually produces
// the desired data. For simplicity this sample frameprovider is a synthetic
// source backed by a timer.
if (_threadPoolTimer)
{
_threadPoolTimer->Cancel();
_threadPoolTimer = nullptr;
}
}
顔認証モードを開始または終了する
顔認証が実際に開始または終了されると、プロバイダーに通知されます。開始と終了はそれぞれ、PerceptionStartFaceAuthenticationHandler イベントと PerceptionStopFaceAuthenticationHandler イベントによって通知されます。デバイスで顔認証をサポートするために状態やモードを変更する必要がある場合は、これらのイベントに応答します。たとえば、カメラを “RGB モード” から “IR モード” に切り替えたり、モードを元に戻したりする場合です。
void SampleFrameProvider:: StartFaceAuthentication ()
{
// A request from the system to Start face authentication. For some sensors,
// this can be a no-op, while other sensors must fine tune behavior
// NOTE: if this device exposes multiple FrameProviders to support face auth,
// StartFaceAuthentication and StopFaceAuthentication mode should be
// atomic between the 2 providers
if (!_inFaceMode)
{
for (WDPP::IFrameProvider^ provider: _frameProviders)
{
safe_cast<SampleFrameProvider^>(provider)->SetFaceMode(true);
}
_inFaceMode = true;
}
}
顔認証モードを終了する要求に応答する方法を次に示します。
void SampleFrameProvider::ExitFaceAuthenticationMode()
{
// A request from the system to exit face authentication. For some sensors,
// this can be a no-op, while other sensors must fine tune behavior
// NOTE: if this device exposes multiple FrameProviders to support face auth
// StartFaceAuthentication and StopFaceAuthentication mode should be
// atomic between the 2 providers
if (_inFaceMode)
{
for (WDPP::IFrameProvider^ provider: _frameProviders)
{
safe_cast<SampleFrameProvider^>(provider)->SetFaceMode(false);
}
_inFaceMode = false;
}
}
IPerceptionFrameProvider.Properties を使って AmbientSubtractionEnabled を報告する
Windows Hello では、高い精度で顔認証を実行するには、"クリーン IR" が必要になります。"クリーン IR" は、照射されたフレームと非照射されたフレームのペアによる環境光の除去によって生成されます。AmbientSubtractionEnabled プロパティを設定して、環境光の除去がどのように実行されたかをシステムに通知するには、IPerceptionFrameProvider が必要になります。
- デバイス自体が環境光の除去を実行する場合 (クリーン IR フレームが確実に提供されます): AmbientSubtractionEnabled = True に設定します。
- Windows が環境光の除去を実行する場合 (照射されたフレームと非照射されたフレームのペアがデバイスによって送信されます): AmbientSubtractionEnabled = False に設定します。
強力な赤外線 (直射日光など) が照射される環境では、環境光の除去を行うことができません。これは、環境赤外線が強力で、デバイスの赤外線発信機が正しく機能しないためです。このような場合、デバイスが独自に環境光の除去を実行するときは、AmbientSubtractionEnabled = False に設定します。
またプロバイダーでは、システムに渡す前に、各フレームに対して “照射” または "非照射" のタグを付ける必要があります。これを行うには、次の状況に応じて、ActiveIlluminationEnabled プロパティを設定してください。
- デバイスでは環境光の除去を実行せず、LED 発信機をオンにする場合 (フレームは照射されます): ActiveIlluminationEnabled = True に設定します。
- デバイスでは環境光の除去を実行せず、LED 発信機をオフにする場合 (フレームは照射されません): ActiveIlluminationEnabled = False に設定します。
- デバイスが独自に環境光の除去を実行する場合 (クリーン IR フレームが提供されます): ActiveIlluminationEnabled = True に設定します。
- • デバイスが独自に環境光の除去を実行するが、明るい環境で実行される場合 (AmbientSubtractionEnabled は現在 False に設定されている): ActiveIlluminationEnabled = True に設定します。
重要 これらのプロパティを正しく設定しないと、Windows Hello では顔認識が失敗します (顔は認識されません)。その結果、カスタマー エクスペリエンスが低下します。
注 Windows Hello の赤外線カメラ用サンプルでは、照射/非照射のフレームのペア向けに AmbientSubtractionEnabled プロパティと ActiveIlluminationEnabled プロパティを設定する方法を示します。
フレーム ソースで新しいフレームが利用可能になったことをシステムに通知する
void SampleFrameProvider::OnFrameArrived(/* NewFrame */)
{
WDPP::PerceptionFrame^ frame = _allocator->AllocateFrame();
if (frame != nullptr)
{
BYTE* buffer = nullptr;
UINT32 capacity = 0;
MemoryBufferByteAccess bufferByteAccess;
bufferByteAccess.GetBuffer(
reinterpret_cast<IInspectable*>(frame->FrameData), &buffer, &capacity);
// copy the new content into buffer, this is just a sample
// Ideally, the driver would fill this buffer directly
memset(buffer, _frameCounter & 0xFF, capacity);
_frameCounter++;
// When frame is illuminated/unilluminated pairs, make sure set property
outputFrame->Properties->Insert(
WDP::KnownPerceptionInfraredFrameSourceProperties::ActiveIlluminationEnabled,
illuminationEnabled != 0 ? true : false);
WDPP::PerceptionFrameProviderManagerService::PublishFrameForProvider(this, outputFrame);
}
}
プロパティの変更に応答する
次のコード例のように、ExposureCompensation などのプロパティの変更に応答することができます。
void SampleFrameProvider::SetProperty(_In_ WWP::PerceptionPropertyChangeRequest^ request)
{
WDP::PerceptionFrameSourcePropertyChangeStatus status =
WDP::PerceptionFrameSourcePropertyChangeStatus::PropertyNotSupported;
auto propertyName = request->Name;
auto value = request->Value;
// An actual device backed provider will likely require additional validation
if (propertyName == WDP::KnownPerceptionVideoFrameSourceProperties::VideoProfile)
{
// NOTE: videoprofiles are also wired via properties. Setting VideoProfile
// should also update VideoProfilesSupported and VideoProfilesAvailable,
// if necessary.
if (_TrySetVideoProfile(safe_cast<WFC::IPropertySet^>(request->Value)))
{
status = WDP::PerceptionFrameSourcePropertyChangeStatus::Accepted;
}
else
{
status = WDP::PerceptionFrameSourcePropertyChangeStatus::ValueOutOfRange;
}
}
else if ((propertyName == WDP::KnownPerceptionVideoFrameSourceProperties::SupportedVideoProfiles)
|| (propertyName == WDP::KnownPerceptionVideoFrameSourceProperties::AvailableVideoProfiles))
{
status = WDP::PerceptionFrameSourcePropertyChangeStatus::PropertyReadOnly;
}
else if (propertyName == WDP::KnownPerceptionInfraredFrameSourceProperties::ExposureCompensation)
{
// NOTE: actually control the sensor device and change exposure compensation
_properties->Insert(request->Name, request->Value);
status = WDP::PerceptionFrameSourcePropertyChangeStatus::Accepted;
}
else
{
// NOTE: If this provider does not explicitly support the property,
// it should return PropertyNotSupported.
status = WDP::PerceptionFrameSourcePropertyChangeStatus::PropertyNotSupported;
}
request->Status = status;
}
ライフ サイクルのメソッドを処理する
IPerceptionFrameProvider と IPerceptionFrameProviderManager では、Windows.Foundation.IClosable インターフェイスを使います。このインターフェイスは、Close メソッドを C++ クラスのデストラクターに投影します。このメソッドは、プロバイダー オブジェクトは初期化されていないが、オブジェクト RefCounts が 0 に達していないときに、サービスによって直接呼び出すことができます。つまり、クラスのデストラクターは 2 回呼び出すことができます。サービスでオブジェクトの Close メソッドを呼び出すときと、RefCount が 0 に達したときです。 したがって、プロバイダーが独自の初期化状態を追跡する (つまり、オブジェクトやリソースがリリース前にクリーンアップされていないことを確認する) ようにプロバイダーをコーディングすることが重要です 。
ref class FrameProvider sealed : Windows::Devices::Perception::IPerceptionFrameProvider
{
public:
virtual ~FrameProvider()
{
Cleanup();
}
void Cleanup()
{
if (!_alreadyCleaned)
{
_alreadyCleaned = true;
// cleanup code
}
}
private:
bool _alreadyCleaned { false };
ISoftwareBitmap の要件
Windows Hello で赤外線カメラを正しく機能させるには、次の要件満たす必要があります。
PixelWidth は、「Windows Hello 生体認証の要件」に示されている最大値以下である必要があります。
PixelHeight は、「Windows Hello 生体認証の要件」に示されている最大値以下である必要があります。
BitmapPixelFormat は、 Gray8 である必要があります。
PerceptionVideoProfile.FrameDuration の要件
PerceptionVideoProfile.FrameDuration の最小要件は 15 fps です (30 fps 以上を推奨)。
IVideoFrame::RelativeTime の要件
RelativeTime は、フレームが 100 ナノ秒単位でキャプチャされたときにかかった時間と同じである必要があります。
KnownPerceptionVideoFrameSourceProperties.IsMirrored による報告
プロパティが設定されていない場合の既定値は false です。
KnownPerceptionInfraredFrameSourceProperties.AutoExposureEnabled による報告
プロパティが設定されていない場合の既定値は true です。
生体認証フレームワークの拡張機能
次の機能は、この機能拡張 API を使う IHV や OEM で利用することができます。 以下に示されている通知は、デバイスに対する変更 (周辺機器の接続や切り離しなど)、またはシステムによって集計される、アプリからの要求 (IR フレーム ソースからの読み取りの開始など) によってトリガーされます。これらの機能は、Windows Hello では必要ありません。
深度、赤外線、および色の間の関連付けを設定する
void SampleFrameProviderManager::SetCorrelation (Platform::String^ serialNumber)
{
// infrared and depth are backed by the same sensor.
// As such, they are in identical positons, and the offsets are all 0.
// the IR (and Depth) camera forms the origin of the device
WF::Numerics::Vector3 depthAndInfraredPosition;
depthAndInfraredPosition.X = 0;
depthAndInfraredPosition.Y = 0;
depthAndInfraredPosition.Z = 0;
WF::Numerics::Quaternion depthAndInfraredOrientation;
depthAndInfraredOrientation.W = 1;
depthAndInfraredOrientation.X = 0;
depthAndInfraredOrientation.Y = 0;
depthAndInfraredOrientation.Z = 0;
// Color is offset from the the IR/Depth camera by 5 cm to the right
// In a real implementation this would likely load a factory calibrated value from
// device firmware rather than relying on a nominal value
WF::Numerics::Vector3 colorPosition;
colorPosition.X = 0.05;
colorPosition.Y = 0;
colorPosition.Z = 0;
WF::Numerics::Quaternion colorOrientation;
colorOrientation.W = 1;
colorOrientation.X = 0;
colorOrientation.Y = 0;
colorOrientation.Z = 0;
auto depthCorrelation = ref new WDPP::PerceptionCorrelation(
_depthFrameProviderId, depthAndInfraredPosition, depthAndInfraredOrientation);
auto infraredCorrelation = ref new WDPP::PerceptionCorrelation(
_InfraredFrameProviderId, depthAndInfraredPosition, depthAndInfraredOrientation);
auto colorExtrinsics = ref new WDPP::FrameProviderTransform(
_colorFrameProviderId, colorPosition, colorOrientation);
auto correlations = ref new Platform::Collections::Vector<WWP::PerceptionCorrelation^>();
correlations->Append(depthCorrelation);
correlations->Append(infraredCorrelation);
correlations->Append(infraredHiddenLocation);
correlations->Append(colorExtrinsics);
_correlationGroup = ref new WWP::PerceptionCorrelationGroup(correlations->GetView());
WWP::PerceptionFrameProviderManagerService::RegisterCorrelationGroup(this, _correlationGroup);
}
特定のフレーム ソース カメラのレンズゆがみモデルをシステムに通知する
SampleFrameProvider::SampleFrameProvider()
{
// ... ctor
// Initial CameraModel
auto cameraIntrinsicsData = ref new Windows::Foundation::Collections::PropertySet();
{
auto fl = ref new Platform::Array<float>{ 300.0f, 200.0f };
cameraIntrinsicsData->Insert(
WDP::KnownCameraIntrinsicsProperties::FocalLength,
fl);
auto pp = ref new Platform::Array<float>{ 150.0f, 100.0f };
cameraIntrinsicsData->Insert(
WDP::KnownCameraIntrinsicsProperties::PrincipalPoint,
pp);
auto rd = ref new Platform::Array<float>{ 0.5f, 0.25f, 0.125f};
cameraIntrinsicsData->Insert(
WDP::KnownCameraIntrinsicsProperties::RadialDistortion,
rd);
auto td = ref new Platform::Array<float>{ 0.1f, 0.2f };
cameraIntrinsicsData->Insert(
WDP::KnownCameraIntrinsicsProperties::TangentialDistortion,
td);
}
_properties->Insert(
WDP::KnownPerceptionVideoFrameSourceProperties::CameraIntrinsics,
cameraIntrinsicsData);
}
フレーム ソースの間の調整をシステムに通知する
すべての機能に関する例については、このトピックの「"深度、赤外線、および色の間の関連付けを設定する"」セクションをご覧ください。
WF::Numerics::Vector3 colorPosition;
colorPosition.X = 0.05;
colorPosition.Y = 0;
colorPosition.Z = 0;
WF::Numerics::Quaternion colorOrientation;
colorOrientation.W = 1;
colorOrientation.X = 0;
colorOrientation.Y = 0;
colorOrientation.Z = 0;
auto depthCorrelation = ref new WDPP::PerceptionCorrelation(
_depthFrameProviderId, depthAndInfraredPosition, depthAndInfraredOrientation);
auto infraredCorrelation = ref new WDPP::PerceptionCorrelation(
_InfraredFrameProviderId, depthAndInfraredPosition, depthAndInfraredOrientation);
auto colorExtrinsics = ref new WDPP::FrameProviderTransform(
_colorFrameProviderId, colorPosition, colorOrientation);
auto correlations = ref new Platform::Collections::Vector<WWP::PerceptionCorrelation^>();
correlations->Append(depthCorrelation);
correlations->Append(infraredCorrelation);
correlations->Append(infraredHiddenLocation);
correlations->Append(colorExtrinsics);
_correlationGroup = ref new WWP::PerceptionCorrelationGroup(correlations->GetView());
WWP::PerceptionFrameProviderManagerService::RegisterCorrelationGroup(this, _correlationGroup);
}