How to Run a Private InsightFace Recognition Model Test
A step-by-step guide for evaluators to benchmark InsightFace open-source and private recognition models against their own data, covering MOU prerequisites, image preparation, the Python client, and privacy controls.
What you will build
InsightFace exposes its open-source and private recognition models through a hosted feature extraction API so that customers can run a self-directed accuracy benchmark on their own validation data before signing a commercial agreement.
This guide describes the end-to-end test workflow: how to receive the API endpoint, how to prepare images, how to call the service from a minimal Python client, the privacy and data-handling rules we follow, and the contractual scope of the free evaluation window.
Before you start
- An executed Memorandum of Understanding (MOU) covering the cooperation intent, the evaluation period, and the confidentiality scope around the model identifiers we share. The endpoint URL is only released after the MOU is signed.
- A Python 3.9+ environment with numpy, requests, and OpenCV (opencv-python) installed.
- A representative validation set with face detection results (bounding boxes from RetinaFace, SCRFD, or your existing detector) so that crops can be prepared per identity.
- A clearly defined evaluation dataset with ground-truth labels, and a scientific evaluation methodology.
1. Sign the MOU and request access
All private model tests start with a signed MOU between your organization and InsightFace. The MOU covers the cooperation intent, the evaluation period, and the confidentiality scope around the model identifiers we share with you.
The API endpoint URL, the list of model names you are entitled to test (the open-source or private models authorized in your MOU), and any per-account rate limits are delivered by email to the named technical contact in the MOU. We never publish the test endpoint, and the URL must not be shared outside the authorized contacts.
- The maximum evaluation window is two weeks from the date the endpoint is delivered. Longer engagements require a commercial agreement.
- Each MOU lists the specific model names that are unlocked for testing; requests with other model names are rejected by the server.
- If the endpoint must be rotated (for example, after the test window closes), we will send the new URL to the same contact list.
2. Prepare a representative validation set
The private models are recognition backbones, not detectors. Each request takes one tightly cropped single-person headshot and returns one face embedding. Run your own face detector first, then pass each cropped face to the API one image at a time.
That said, the server still performs a secondary detection and alignment pass on the submitted headshot before extracting the embedding, so small framing imperfections are tolerated.
- One face per image. If the source frame contains multiple people, crop each face into its own file before sending.
- Document consent and lawful basis for every image used, in line with the data-handling clause of the MOU.
3. Crop and encode each face image
Apply a small margin around the detected face box before sending. A symmetric expansion factor of about 0.2 (10% on each side) gives the recognition backbone enough hairline, jaw, and ear context to be robust without becoming a full upper-body shot. Optionally bound the longest side (for example, 256 pixels) so that uploads stay small.
Always send a lossless image format such as PNG. Lossy formats (JPEG, low-quality WebP) introduce compression artifacts that can measurably lower embedding quality, especially around the eye and mouth regions that dominate face recognition similarity. Use the helper below as a starting point and adapt it to your detector's bounding-box convention.
- Use a margin (expansion factor) around the detector box; a tight crop directly on the face box hurts accuracy.
- Save crops as PNG before upload. Re-encoding through JPEG before the API call is the most common cause of unexpectedly low scores.
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. Call the feature extraction API
The API accepts a multipart/form-data POST: a single image file under the image field and the model_name string. It returns a JSON payload that includes the model name and a feature list, which is the L2-normalized embedding for that face. Save each embedding as a numpy .npy file so you can compute cosine similarity offline.
The example client below mirrors the production protocol exactly. You can run it directly, or import extract_feature from your own evaluation pipeline. Pass the endpoint URL we sent you over email through --server-url; do not hard-code it in scripts that are committed to a shared repository.
- model_name must be one of the values listed in your MOU. Unlisted names will be rejected.
- An empty feature list means no face was detected in the crop; check that your bounding box and margin actually contain a face.
- Compare embeddings with cosine similarity over the L2-normalized vectors; do not re-normalize on the client side.
"""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. Privacy and data handling
Uploaded images are used only to compute and return the requested embedding for the duration of the request. We do not retain raw images after the response is sent, and we do not use evaluation traffic to train or fine-tune any model.
- Do not share the endpoint URL, model names, or returned scores with parties not named in the MOU.
- Report suspected leakage of the endpoint or credentials to your InsightFace contact within 24 hours.
6. Evaluation scope and next steps
The free evaluation window is capped at two weeks from the day the endpoint is delivered. Within that window you can run as many comparisons as your rate limit allows; we recommend planning the test set, preprocessing, and metric definitions before requesting the endpoint so the time is spent on numbers, not on setup.
After the test, please assess your results and share your conclusion on commercial cooperation as soon as possible.
Need help with production deployment?
Contact InsightFace for model licensing, runtime optimization, and deployment support for your target hardware.
Contact Us