← 가이드로 돌아가기
프라이빗 테스트Face Recognition평가MOU프라이버시

InsightFace 비공개 인식 모델 프라이빗 테스트 가이드

MOU 요건, 이미지 전처리, Python 클라이언트, 프라이버시 규칙까지 포함해 자체 데이터로 InsightFace 오픈소스/비공개 모델을 평가하는 단계별 가이드입니다.

8분 분량

이 가이드에서 구축할 내용

InsightFace는 상용 계약 전에 고객이 자체 검증 데이터로 정확도를 평가할 수 있도록 오픈소스/비공개 인식 모델을 호스팅형 특징 추출 API로 제공합니다.

이 가이드는 평가 전 과정을 다룹니다: 엔드포인트 수신 방법, 이미지 준비, 최소 구성 Python 클라이언트, 당사의 프라이버시·데이터 처리 규칙, 무료 평가 기간의 계약적 범위까지 포함합니다.

시작하기 전에

  • 협력 의향, 평가 기간, 그리고 공유되는 모델 식별자에 대한 비밀유지 범위를 정의한 서명된 MOU. 엔드포인트 URL은 MOU 체결 후에만 발급됩니다.
  • Python 3.9 이상에서 numpy, requests, OpenCV (opencv-python)이 설치된 환경.
  • RetinaFace, SCRFD 등의 검출 결과(바운딩 박스)를 포함한, 운영 분포를 반영하는 검증 셋.
  • 명확한 평가용 데이터셋과 정답 레이블, 그리고 과학적인 평가 방법론.

1. MOU 체결 및 액세스 신청

모든 비공개 모델 테스트는 귀사와 InsightFace 사이의 서명된 MOU에서 시작됩니다. MOU는 협력 의향, 평가 기간, 그리고 당사가 공유하는 모델 식별자에 대한 비밀유지 범위를 정의합니다.

API 엔드포인트 URL, 귀사가 권한을 받은 모델 이름(MOU에서 허용된 오픈소스 또는 비공개 모델), 계정별 레이트 리밋은 MOU에 명시된 기술 담당자에게 이메일로 발송됩니다. 엔드포인트는 공개되지 않으며, 권한 있는 담당자 외에는 공유해서는 안 됩니다.

  • 평가 기간은 엔드포인트 발급일로부터 최대 2주입니다. 이후 사용은 상용 계약이 필요합니다.
  • 각 MOU에 명시된 모델 이름만 활성화되며, 다른 이름의 요청은 서버에서 거부합니다.
  • 엔드포인트가 교체되어야 하는 경우 새 URL은 동일한 담당자 목록에 다시 발송됩니다.

2. 대표성 있는 검증 셋 준비

비공개 모델은 인식 backbone이며 검출기는 포함되어 있지 않습니다. 한 번의 요청에는 단일 인물의 타이트하게 잘린 얼굴 한 장이 들어가고, 그에 대응하는 임베딩 한 개가 반환됩니다. 먼저 자체 검출기를 돌리고 각 크롭을 한 장씩 전송하세요.

다만, 서버에서도 제출된 헤드샷에 대해 임베딩을 추출하기 전에 2차 검출 및 정렬을 수행하므로 약간의 프레이밍 오차는 허용됩니다.

  • 이미지당 얼굴 1개. 원본에 여러 명이 있으면 각각 별도의 파일로 잘라서 전송하세요.
  • MOU의 데이터 조항에 따라 사용된 모든 이미지의 동의와 합법적 근거를 문서화하세요.

3. 얼굴 이미지 크롭 및 인코딩

전송 전에 검출 박스 주위에 약간의 마진을 더하세요. 0.2 정도의 expansion factor(좌·우·상·하 각 10%)를 더하면 헤어라인, 턱, 귀처럼 인식에 유용한 컨텍스트가 보존됩니다. 업로드 용량을 줄이고 싶다면 가장 긴 변을 256 픽셀 정도로 제한해도 됩니다.

반드시 PNG 같은 무손실 포맷으로 전송하세요. JPEG, 저품질 WebP 등 손실 포맷은 눈과 입 주변에 압축 아티팩트를 만들어 임베딩 품질을 측정 가능한 수준으로 떨어뜨립니다. 아래 헬퍼는 출발점이며, 사용 중인 검출기의 박스 규약에 맞게 조정해 사용하세요.

  • 검출 박스 외부에 마진(expansion factor)을 추가하세요. 박스에 딱 붙은 크롭은 정확도를 떨어뜨립니다.
  • 업로드 전에는 PNG로 저장하세요. API 호출 직전에 다시 JPEG으로 인코딩하는 것이 예기치 않은 저조한 점수의 가장 흔한 원인입니다.
단일 인물 헤드샷에 마진을 더해 자르기 (Python)
import cv2


def crop_face_with_expansion(image, bbox, expansion_factor=0.2, max_side=256):
    """
    Crop a single-person headshot with a small margin around the detected
    face box, and optionally downscale so the longest side is <= max_side.

    image:             input image as a numpy array (BGR or RGB).
    bbox:              face bounding box [x, y, width, height] from your
                       detector of choice (RetinaFace, SCRFD, etc.).
    expansion_factor:  total margin ratio (0.2 means +10% on each side).
    max_side:          if set, the longest side of the result is bounded.
    """
    img_h, img_w = image.shape[:2]
    x, y, w, h = bbox

    # 1. Margin distributed evenly to both sides.
    dw = int(w * expansion_factor / 2)
    dh = int(h * expansion_factor / 2)

    # 2. Expanded box, clamped to image bounds.
    x1 = max(0, x - dw)
    y1 = max(0, y - dh)
    x2 = min(img_w, x + w + dw)
    y2 = min(img_h, y + h + dh)

    face_img = image[y1:y2, x1:x2]

    # 3. Optional bounded downscale with a high-quality filter.
    if max_side is not None:
        curr_h, curr_w = face_img.shape[:2]
        longest = max(curr_h, curr_w)
        if longest > max_side:
            scale = max_side / longest
            new_w = int(curr_w * scale)
            new_h = int(curr_h * scale)
            face_img = cv2.resize(
                face_img, (new_w, new_h), interpolation=cv2.INTER_AREA,
            )

    return face_img


# Save the crop with a lossless format (PNG) before sending it to the API.
# Lossy formats (JPEG, WebP at low quality) introduce compression artifacts
# that can measurably reduce face recognition accuracy.
# cv2.imwrite("probe.png", crop)

4. 특징 추출 API 호출

API는 multipart/form-data POST를 받습니다: image 필드에 이미지 한 장, model_name에 문자열을 넣어 보내고, 응답으로 model_name과 feature(L2 정규화된 임베딩)를 포함한 JSON을 받습니다. 각 임베딩은 numpy .npy로 저장하면 오프라인에서 cosine similarity 계산이 자유롭습니다.

아래 예시 클라이언트는 운영 프로토콜을 그대로 반영합니다. 그대로 실행하거나 extract_feature를 자체 평가 파이프라인에서 import하여 사용하세요. 이메일로 받은 엔드포인트 URL은 --server-url로 전달하고, 공유 저장소에 커밋되는 스크립트에 하드코딩하지 마세요.

  • model_name은 MOU에 명시된 값만 허용되며, 그 외의 이름은 서버에서 거부됩니다.
  • feature가 빈 배열이면 크롭에서 얼굴이 검출되지 않은 것입니다. 박스와 마진이 실제로 얼굴을 포함하는지 확인하세요.
  • 이미 L2 정규화된 벡터에 대한 cosine similarity로 비교하세요. 클라이언트에서 추가로 정규화할 필요는 없습니다.
비공개 엔드포인트용 최소 Python 클라이언트
"""Example client for the private InsightFace feature extraction service.

Uploads a single-person face image to the test endpoint, reads the returned
embedding, and saves it locally with np.save for offline evaluation.
"""

import argparse
import os.path as osp

import numpy as np
import requests


def extract_feature(image_path, output_path, url, model_name, timeout=120.0):
    filename = osp.basename(image_path)
    data = {"model_name": model_name}

    with open(image_path, "rb") as infile:
        files = {
            "image": (filename, infile, "application/octet-stream"),
        }
        try:
            resp = requests.post(
                url=url, files=files, data=data, timeout=timeout,
            )
            resp.raise_for_status()
        except requests.RequestException as ex:
            raise SystemExit(
                "request failed: %s: %s" % (type(ex).__name__, str(ex))
            )

    payload = resp.json()
    feature = payload.get("feature")
    if not isinstance(feature, list):
        raise SystemExit("server response feature must be a list")
    if len(feature) == 0:
        raise SystemExit(
            "server returned no features; verify the image contains a "
            "detectable face"
        )

    feat = np.asarray(feature, dtype=np.float32)
    np.save(output_path, feat)
    print("saved feature:", output_path, feat.shape, payload.get("model_name"))
    return feat


def main():
    parser = argparse.ArgumentParser(
        description=(
            "Upload a face image to the private InsightFace test server and "
            "save the embedding with np.save."
        ),
    )
    # The --server-url value is delivered to evaluators by email.
    parser.add_argument(
        "--server-url", required=True,
        help="Feature extraction API URL (sent privately by InsightFace)",
    )
    # The set of choices below is illustrative; the models actually unlocked
    # for your account are listed in your MOU and may evolve over time.
    parser.add_argument(
        "--model-name", required=True,
        choices=["buffalo", "dingo", "eagle"],
        help="Recognition model to evaluate (must match an entry in your MOU)",
    )
    parser.add_argument("--image", required=True, help="Input face image path (PNG recommended)")
    parser.add_argument("--output", required=True, help="Output .npy file path")
    parser.add_argument("--timeout", type=float, default=120.0, help="HTTP timeout in seconds")
    args = parser.parse_args()

    extract_feature(
        args.image, args.output, args.server_url, args.model_name,
        timeout=args.timeout,
    )


if __name__ == "__main__":
    main()

5. 프라이버시와 데이터 처리

업로드된 이미지는 요청 처리 동안 임베딩을 계산해 응답으로 돌려주는 데에만 사용됩니다. 응답 이후에는 원본 이미지를 보관하지 않으며, 평가 트래픽을 모델 학습이나 fine-tuning에 사용하지 않습니다.

  • 엔드포인트 URL, 모델 이름, 반환 점수는 MOU에 명시된 인원 외에 공유하지 마세요.
  • 엔드포인트나 자격 정보 유출이 의심되면 24시간 이내에 InsightFace 담당자에게 알려주세요.

6. 평가 범위 및 다음 단계

무료 평가 기간은 URL 발급일로부터 최대 2주입니다. 기간 내에는 레이트 리밋 범위에서 자유롭게 호출할 수 있습니다. 엔드포인트 발급 전에 테스트 셋, 전처리, 지표 정의를 미리 정해 두면 기간을 수치 산출에 집중해서 사용할 수 있습니다.

테스트가 끝나면 결과를 평가하여 가능한 한 빨리 상용 협력에 대한 결론을 공유해 주세요.

프로덕션 배포 지원이 필요하신가요?

모델 라이선스, runtime 최적화, 대상 하드웨어 배포 지원은 InsightFace에 문의하세요.

문의하기