다음을 통해 공유


자습서: 얼굴의 활동성 감지

서버 쪽 논리 및 도우미 프런트 엔드 클라이언트 애플리케이션을 사용하여 얼굴 활동성 감지를 워크플로에 통합하는 방법을 알아봅니다.

팁 (조언)

얼굴 활동 감지에 대한 일반적인 내용은 개념 가이드를 참조하세요.

이 자습서에서는 앱 서버에서 프런트 엔드 애플리케이션을 실행하여 활동성 검색을 수행하는 방법을 알아봅니다. 다양한 플랫폼 및 언어에서 얼굴 확인을 추가할 수도 있습니다.

중요

활동성을 위한 Face 클라이언트 SDK는 제어된 기능입니다. 얼굴 인식 정보 입력 양식을 작성하여 라이브니스 기능에 대한 액세스를 요청해야 합니다. Azure 구독에 액세스 권한이 부여되면 Face 활동성 SDK를 다운로드할 수 있습니다.

필수 조건

  • Azure 구독 - 체험 구독 만들기
  • 책임 있는 AI 용어에 동의하고 리소스를 만들 수 있도록 Azure 계정에 Cognitive Services 기여자 역할이 할당되어 있어야 합니다. 이 역할을 계정에 할당하려면 역할 할당의 단계를 수행하거나 관리자에게 문의하세요.
  • Azure 구독을 보유한 후에는 Azure Portal에서 Face 리소스를 만들어 키와 엔드포인트를 받으세요. 배포 후 리소스로 이동을 선택합니다.
    • 애플리케이션을 Face 서비스에 연결하려면 생성한 리소스의 키와 엔드포인트가 필요합니다.
  • Mobile(iOS 및 Android) 및 웹에서 Foundry Tools Face Client SDK를 통해 Azure Vision에 필요한 제한된 아티팩트에 대한 액세스.
  • 얼굴 활동성 감지 기능에 대한 친숙함. 개념 가이드를 참조하세요.

팁 (조언)

필수 조건을 완료한 후 다음 플랫폼에서 라이브네스 기능을 사용해 볼 수 있습니다.

  • iOS: iOS 앱 스토어 - 설치 후 앱 화면을 10번 탭하여 개발자 모드를 사용하도록 설정합니다.
  • Android: Google Play 스토어 - 설치 후 앱 화면을 10번 탭하여 개발자 모드를 사용하도록 설정합니다.
  • 웹: Vision Studio에서 직접 사용해 보세요.

샘플 섹션에서 전체 프런트 엔드 샘플(iOS, Android 또는 웹)을 빌드하고 실행할 수도 있습니다.

프런트 엔드 애플리케이션 준비

프런트 엔드 애플리케이션과의 통합을 간소화하기 위해 여러 언어로 SDK를 제공합니다. 다음 섹션에서 선택한 SDK에 대한 추가 정보(README)를 참조하여 UI와 필수 코드를 통합합니다.

중요

각 프런트 엔드 SDK를 성공적으로 컴파일하려면 제어된 자산에 대한 액세스 권한이 필요합니다. 이 액세스를 설정하려면 다음 지침을 참조하세요.

Swift iOS의 경우:

Kotlin/Java Android의 경우:

JavaScript 웹의 경우:

프런트 엔드 애플리케이션에 통합되면 SDK는 카메라를 시작하고, 사용자가 자신의 위치를 조정하도록 안내하고, 활동성 페이로드를 구성하고, 처리를 위해 Azure AI Face 서비스로 보냅니다.

리포지토리의 릴리스 섹션에서 새 SDK 버전 업데이트를 모니터링하고 GitHub Dependabot(GitHub 리포지토리용) 또는 리노베이션(GitHub, GitLab, Bitbucket, Azure Repos)과 같은 자동화된 종속성 업데이트 경고를 사용하도록 설정합니다.

활동성 감지 수행

다음 단계에서는 활동성 오케스트레이션 프로세스에 대해 설명합니다.

Azure AI Face의 활성 워크플로 다이어그램.

  1. 프런트 엔드 애플리케이션은 활성 확인을 시작하고 앱 서버에 알립니다.

  2. 앱 서버는 Azure AI Face Service를 사용하여 새 활동성 세션을 만듭니다. 서비스는 활동성 세션을 만들고 세션 권한 부여 토큰으로 응답합니다. 활동성 세션을 만드는 데 관련된 각 요청 매개 변수에 대한 자세한 내용은 Liveness 세션 만들기 작업을 참조하세요.

    var endpoint = new Uri(System.Environment.GetEnvironmentVariable("FACE_ENDPOINT"));
    var key = new AzureKeyCredential(System.Environment.GetEnvironmentVariable("FACE_APIKEY"));
    
    var body = JsonSerializer.Serialize(new
    {
        livenessOperationMode = "PassiveActive",
        deviceCorrelationId = "723d6d03-ef33-40a8-9682-23a1feb7bccd",
        enableSessionImage = true
    });
    
    using var client = new HttpClient();
    client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", key);
    
    var response = await client.PostAsync(
        $"{endpoint}/face/v1.2/detectLiveness-sessions",
        new StringContent(body, Encoding.UTF8, "application/json"));
    
    response.EnsureSuccessStatusCode();
    
    using var doc  = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
    var root       = doc.RootElement;
    
    Console.WriteLine("Session created");
    Console.WriteLine($"sessionId : {root.GetProperty("sessionId").GetString()}");
    Console.WriteLine($"authToken : {root.GetProperty("authToken").GetString()}");
    

    응답 본문의 예:

    {
        "sessionId": "a6e7193e-b638-42e9-903f-eaf60d2b40a5",
        "authToken": "<session-authorization-token>",
        "status": "NotStarted",
        "modelVersion": "2025-05-20",
        "results": {
            "attempts": []
        }
    }
    
  3. 앱 서버는 세션 권한 부여 토큰을 프런트 엔드 애플리케이션에 다시 제공합니다.

  4. 프런트 엔드 애플리케이션은 세션 권한 부여 토큰을 사용하여 얼굴 활동성 탐지기를 시작하고 이를 통해 활동성 흐름이 시작됩니다.

        FaceLivenessDetector(
            sessionAuthorizationToken = FaceSessionToken.sessionToken,
            verifyImageFileContent = FaceSessionToken.sessionSetInClientVerifyImage,
            deviceCorrelationId = "null",
            onSuccess = viewModel::onSuccess,
            onError = viewModel::onError
        )
    
  5. SDK는 카메라를 시작하고, 사용자가 올바르게 배치하도록 안내한 다음, 활동성 검색 서비스 엔드포인트를 호출하도록 페이로드를 준비합니다.

  6. SDK는 Azure Vision Face 서비스를 호출하여 생체 활동 감지를 수행합니다. 서비스가 응답하면 SDK는 프런트 엔드 애플리케이션에 활동성 검사가 완료되었다는 것을 알 수 있습니다. 참고: 서비스 응답에는 활동성 결정이 포함되지 않습니다. 앱 서버에서 이 정보를 쿼리해야 합니다.

  7. 프런트 엔드 애플리케이션은 활성 확인 완료를 앱 서버에 전달합니다.

  8. 앱 서버는 Azure Vision Face 서비스로부터 라이브니스 탐지 결과를 요청합니다.

    using var client = new HttpClient();
    client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", key);
    
    var response = await client.GetAsync(
        $"{endpoint}/face/v1.2/livenessSessions/{sessionId}/result");
    
    response.EnsureSuccessStatusCode();
    
    using var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
    var root = doc.RootElement;
    var attempts = root.GetProperty("results").GetProperty("attempts");
    var latestAttempt = attempts[attempts.GetArrayLength() - 1];
    var attemptStatus = latestAttempt.GetProperty("attemptStatus").GetString();
    
    Console.WriteLine($"Session id: {root.GetProperty("sessionId").GetString()}");
    Console.WriteLine($"Session status: {root.GetProperty("status").GetString()}");
    Console.WriteLine($"Latest attempt status: {attemptStatus}");
    
    if (attemptStatus == "Succeeded")
        Console.WriteLine($"Liveness detection decision: {latestAttempt.GetProperty("result").GetProperty("livenessDecision").GetString()}");
    else
    {
        var error = latestAttempt.GetProperty("error");
        Console.WriteLine($"Error: {error.GetProperty("code").GetString()} - {error.GetProperty("message").GetString()}");
    }
    

    응답 본문의 예:

    {
        "sessionId": "b12e033e-bda7-4b83-a211-e721c661f30e",
        "authToken": "eyJhbGciOiJFUzI1NiIsIm",
        "status": "NotStarted",
        "modelVersion": "2024-11-15",
        "results": {
            "attempts": [
                {
                    "attemptId": 2,
                    "attemptStatus": "Succeeded",
                    "result": {
                    "livenessDecision": "realface",
                    "targets": {
                        "color": {
                        "faceRectangle": {
                                "top": 669,
                                "left": 203,
                                "width": 646,
                                "height": 724
                            }
                        }
                    },
                    "digest": "B0A803BB7B26F3C8F29CD36030F8E63ED3FAF955FEEF8E01C88AB8FD89CCF761",
                    "sessionImageId": "Ae3PVWlXAmVAnXgkAFt1QSjGUWONKzWiSr2iPh9p9G4I"
                    }
                },
                {
                    "attemptId": 1,
                    "attemptStatus": "Failed",
                    "error": {
                    "code": "FaceWithMaskDetected",
                    "message": "Mask detected on face image.",
                    "targets": {
                            "color": {
                            "faceRectangle": {
                                "top": 669,
                                "left": 203,
                                "width": 646,
                                "height": 724
                            }
                            }
                        }
                    }
                }
            ]
        }
    }
    
  9. 앱 서버는 모든 세션 결과를 쿼리한 후 세션을 삭제합니다.

    using var client = new HttpClient();
    client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", key);
    
    await client.DeleteAsync($"{endpoint}/face/v1.2/livenessSessions/{sessionId}");
    Console.WriteLine($"Session deleted: {sessionId}");
    

얼굴인식을 사용하여 활동성 감지 수행

얼굴인식과 활동성 감지를 결합하면 관심 있는 특정인의 생체 인식 확인과 그 사람이 시스템에 실제로 존재한다는 추가 보장이 가능합니다.

Azure AI Face의 실시간 얼굴 확인 워크플로 다이어그램.

생체 감지를 검증과 통합하는 것은 다음 두 부분으로 구성됩니다.

1단계 - 참조 이미지 선택

가장 정확한 인식 결과를 얻으려면 ID 확인 시나리오에 대한 컴퍼지션 요구 사항에 나열된 팁을 따르세요.

2단계 - 검증을 통한 활동성 오케스트레이션을 설정합니다.

다음 상위 단계에서는 확인을 통해 생동감을 오케스트레이션하는 방법을 보여 줍니다.

  1. 다음 두 가지 방법 중 하나를 사용하여 확인 참조 이미지를 제공합니다.

    • 앱 서버는 라이브니스 세션을 만들 때 참조 이미지를 제공합니다. 확인을 사용하여 활동성 세션을 만드는 데 관련된 각 요청 매개 변수에 대한 자세한 내용은 Liveness With Verify 세션 만들기 작업을 참조하세요.

      var endpoint = new Uri(System.Environment.GetEnvironmentVariable("FACE_ENDPOINT"));
      var key      = System.Environment.GetEnvironmentVariable("FACE_APIKEY");
      
      // Create the JSON part
      var jsonPart = new StringContent(
          JsonSerializer.Serialize(new
          {
              livenessOperationMode = "PassiveActive",
              deviceCorrelationId = "723d6d03-ef33-40a8-9682-23a1feb7bcc",
              enableSessionImage = true
          }),
          Encoding.UTF8,
          "application/json"
      );
      jsonPart.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
      {
          Name = "CreateLivenessWithVerifySessionRequest"
      };
      
      // Create the file part
      using var fileStream = File.OpenRead("test.png");
      var filePart = new StreamContent(fileStream);
      filePart.Headers.ContentType = new MediaTypeHeaderValue("image/png");
      filePart.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
      {
          Name = "VerifyImage",
          FileName = "test.png"
      };
      
      // Build multipart form data
      using var formData = new MultipartFormDataContent();
      formData.Add(jsonPart);
      formData.Add(filePart);
      
      using var client = new HttpClient();
      client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", key);
      
      var response = await client.PostAsync($"{endpoint}/face/v1.2/createLivenessWithVerifySession", formData);
      response.EnsureSuccessStatusCode();
      
      using var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
      var root = doc.RootElement;
      
      Console.WriteLine("Session created.");
      Console.WriteLine($"Session id: {root.GetProperty("sessionId").GetString()}");
      Console.WriteLine($"Auth token: {root.GetProperty("authToken").GetString()}");
      

      응답 본문의 예:

      {
          "sessionId": "3847ffd3-4657-4e6c-870c-8e20de52f567",
          "authToken": "<session-authorization-token>",
          "status": "NotStarted",
          "modelVersion": "2024-11-15",
          "results": {
              "attempts": [],
              "verifyReferences": [
              {
                  "referenceType": "image",
                  "faceRectangle": {
                  "top": 98,
                  "left": 131,
                  "width": 233,
                  "height": 300
                  },
                  "qualityForRecognition": "high"
              }
              ]
          }
      }
      
    • 프런트 엔드 애플리케이션은 모바일 SDK를 초기화할 때 참조 이미지를 제공합니다. 이 시나리오는 웹 솔루션에서 지원되지 않습니다.

          FaceLivenessDetector(
              sessionAuthorizationToken = FaceSessionToken.sessionToken,
              verifyImageFileContent = FaceSessionToken.sessionSetInClientVerifyImage,
              deviceCorrelationId = "null",
              onSuccess = viewModel::onSuccess,
              onError = viewModel::onError
          )
      
  2. 이제 앱 서버는 활동성 결과 외에도 확인 결과를 쿼리할 수 있습니다.

    using var client = new HttpClient();
    client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", key);
    
    var response = await client.GetAsync($"{endpoint}/face/v1.2/livenessSessions/{sessionId}/result");
    response.EnsureSuccessStatusCode();
    
    using var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
    var root = doc.RootElement;
    
    var attempts = root.GetProperty("results").GetProperty("attempts");
    var latestAttempt = attempts[attempts.GetArrayLength() - 1];
    var attemptStatus = latestAttempt.GetProperty("attemptStatus").GetString();
    
    Console.WriteLine($"Session id: {root.GetProperty("sessionId").GetString()}");
    Console.WriteLine($"Session status: {root.GetProperty("status").GetString()}");
    Console.WriteLine($"Latest attempt status: {attemptStatus}");
    
    if (attemptStatus == "Succeeded")
    {
        var decision = latestAttempt.GetProperty("result").GetProperty("livenessDecision").GetString();
        var verify   = latestAttempt.GetProperty("verifyResult");
        Console.WriteLine($"Liveness detection decision: {decision}");
        Console.WriteLine($"Verify isIdentical: {verify.GetProperty("isIdentical").GetBoolean()}");
        Console.WriteLine($"Verify matchConfidence: {verify.GetProperty("matchConfidence").GetDouble()}");
    }
    else
    {
        var err = latestAttempt.GetProperty("error");
        Console.WriteLine($"Error: {err.GetProperty("code").GetString()} - {err.GetProperty("message").GetString()}");
    }
    

    응답 본문의 예:

    {
        "sessionId": "b12e033e-bda7-4b83-a211-e721c661f30e",
        "authToken": "eyJhbGciOiJFUzI1NiIsIm",
        "status": "NotStarted",
        "modelVersion": "2024-11-15",
        "results": {
            "attempts": [
            {
                "attemptId": 2,
                "attemptStatus": "Succeeded",
                "result": {
                "livenessDecision": "realface",
                "targets": {
                    "color": {
                    "faceRectangle": {
                        "top": 669,
                        "left": 203,
                        "width": 646,
                        "height": 724
                    }
                    }
                },
                "verifyResult": {
                    "matchConfidence": 0.08871888,
                    "isIdentical": false
                },
                "digest": "B0A803BB7B26F3C8F29CD36030F8E63ED3FAF955FEEF8E01C88AB8FD89CCF761",
                "sessionImageId": "Ae3PVWlXAmVAnXgkAFt1QSjGUWONKzWiSr2iPh9p9G4I",
                "verifyImageHash": "43B7D8E8769533C3290DBD37A84D821B2C28CB4381DF9C6784DBC4AAF7E45018"
                }
            },
            {
                "attemptId": 1,
                "attemptStatus": "Failed",
                "error": {
                    "code": "FaceWithMaskDetected",
                    "message": "Mask detected on face image.",
                    "targets": {
                        "color": {
                        "faceRectangle": {
                                "top": 669,
                                "left": 203,
                                "width": 646,
                                "height": 724
                            }
                        }
                    }
                }
            }
            ],
            "verifyReferences": [
                {
                    "referenceType": "image",
                    "faceRectangle": {
                    "top": 316,
                    "left": 131,
                    "width": 498,
                    "height": 677
                    },
                    "qualityForRecognition": "high"
                }
            ]
            }
        }
    
  3. 더 이상 결과가 필요하지 않은 경우 앱 서버에서 세션을 삭제할 수 있습니다.

    using var client = new HttpClient();
    client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", key);
    
    await client.DeleteAsync($"{endpoint}/face/v1.2/livenessWithVerifySessions/{sessionId}");
    Console.WriteLine($"Liveness-with-Verify session deleted: {sessionId}");
    

활동성 감지 후 다른 얼굴 작업 수행

필요에 따라 얼굴 분석(얼굴 특성 가져오기) 및 얼굴 ID 작업과 같은 활동성 검사 후 추가 얼굴 작업을 수행할 수 있습니다.

  1. 세션 생성 단계에서 enableSessionImage 매개 변수를 true로 설정합니다.
  2. sessionImageId을(를) 세션-Get-Result 단계에서 추출합니다.
  3. 세션 이미지를 다운로드하거나(활동성 세션 이미지 가져오기 작업 API에서 참조됨) 세션 이미지 ID에서 검색 API 작업에서 sessionImageId를 제공하여 다른 얼굴 분석이나 얼굴 식별 작업을 계속 진행합니다. 이러한 작업에 대한 자세한 내용은 얼굴 감지 개념얼굴 인식 개념을 참조하세요.

지원 옵션

기본 Foundry 도구 지원 옵션을 사용하는 것 외에도 SDK 리포지토리의 문제 섹션에 질문을 게시할 수도 있습니다.

활동성 솔루션을 기존 애플리케이션에 통합하는 방법을 알아보려면 Azure Vision SDK 참조를 참조하세요.

활성 솔루션을 오케스트레이션하는 데 사용할 수 있는 기능에 대해 자세히 알아보려면 세션 REST API 참조를 확인합니다.