Ir al contenido

Reconocimiento facial y conteo de personas

Registra y administra personas conocidas, consulta eventos de coincidencia facial, ajusta el umbral de coincidencia y obtén analíticas de conteo de personas de las cámaras a través de la API de Rhombus.

En esta página

Descripción general

Esta guía cubre dos capacidades de analítica de cámara distintas que es fácil confundir:

  • Reconocimiento facial (identidad) — registra personas conocidas a partir de imágenes de rostros y luego recibe eventos de coincidencia facial cuando una cámara detecta un rostro que coincide. Cada evento incluye una puntuación de confianza y la persona que mejor coincide. Úsalo para reconocer quién aparece en la cámara.
  • Conteo de personas (aforo) — analítica de cámara agregada y no identificante que cuenta cuántas personas cruzan el campo de visión de una cámara a lo largo del tiempo. Proviene del servicio de reportes como series temporales de conteo y conteos de ocupación. No implica identidad alguna.

A través de la API puedes:

  • Registrar y administrar personas creando registros de persona y subiendo imágenes de rostros
  • Consultar eventos de coincidencia facial con filtros enriquecidos y paginación
  • Ajustar el umbral de coincidencia y corregir eventos mal identificados
  • Obtener analíticas de conteo de personas como series temporales o los conteos más recientes por cámara
Advertencia

Privacidad biométrica. El reconocimiento facial procesa datos biométricos. Muchas jurisdicciones regulan la recopilación, el almacenamiento y el uso de identificadores biométricos, y algunas exigen aviso o consentimiento. Revisa las leyes de privacidad biométrica que apliquen a tus ubicaciones antes de registrar rostros y confirma las políticas de tu organización. Esta guía describe únicamente la superficie de la API — no constituye asesoría legal.

Requisitos previos

Todas las solicitudes son POST a https://api2.rhombussystems.com y se autentican con los encabezados x-auth-scheme y x-auth-apikey. Las marcas de tiempo están en milisegundos epoch salvo que se indique lo contrario, y la confianza de coincidencia es un decimal entre 0 y 1.

Registrar y administrar personas conocidas

Una persona es un registro de identidad con nombre para el que registras rostros. Un matchmaker es una plantilla de rostro registrada asociada a una persona; el motor de reconocimiento compara los rostros detectados con tus matchmakers.

Crear una persona

Crea un registro de persona con nombre para registrar rostros contra él.

import requests

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY",
    "Content-Type": "application/json"
}

response = requests.post(
    "https://api2.rhombussystems.com/api/faceRecognition/person/createPerson",
    headers=headers,
    json={"name": "Jordan Rivera"}
)

person = response.json().get("person", {})
print(f"Created person {person.get('name')} -> {person.get('uuid')}")
namestringbodyobligatorio

Nombre visible de la persona.

La respuesta devuelve un objeto person:

uuidstring
Identificador único de la persona.
namestring
Nombre visible.
emailstring
Correo electrónico opcional asociado a la persona.
orgUuidstring
Organización a la que pertenece la persona.
createdOnstring
Cuándo se creó el registro de la persona.
updatedOnstring
Cuándo se actualizó por última vez el registro de la persona.

Subir una imagen de rostro

Registra un rostro subiendo una imagen .jpg o .png (máximo 5 MB) como multipart/form-data. El archivo debe enviarse bajo el nombre de campo de formulario file. Dos parámetros de consulta opcionales controlan la transacción:

  • transaction — un ID de transacción que proporcionas para rastrear y luego consultar la subida. Si se omite, el servidor genera uno y lo devuelve.
  • createPersonIfNotFound — establécelo en true para que el sistema cree una persona nueva automáticamente cuando el rostro subido no coincide con uno existente.
import requests

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY"
    # NO establezcas Content-Type manualmente; requests define el límite multipart
}

with open("jordan_rivera.jpg", "rb") as f:
    files = {"file": ("jordan_rivera.jpg", f, "image/jpeg")}
    response = requests.post(
        "https://api2.rhombussystems.com/api/faceRecognition/matchmaker/uploadFaceMatchmakers",
        headers=headers,
        params={"transaction": "enroll-jordan-001", "createPersonIfNotFound": "true"},
        files=files
    )

data = response.json()
print(f"Transaction: {data.get('transactionId')}")
for result in data.get("fileUploadResults", []):
    print(f"  {result.get('fileName')}: success={result.get('success')} {result.get('message', '')}")
Advertencia

Sube una imagen por solicitud. Envía exactamente una parte file, mantenla por debajo de 5 MB y usa .jpg o .png. Deja que tu cliente HTTP defina el límite de Content-Type: multipart/form-data — no lo establezcas manualmente.

La respuesta devuelve:

transactionIdstring
El ID de transacción de esta subida (devuelto tal cual, o generado si lo omitiste). Úsalo para consultar el estado de procesamiento.
fileUploadResultsarray
Resultados de subida por archivo, cada uno con fileName, success (booleano) y message.

Consultar la transacción de subida

Las imágenes de rostros se procesan de forma asíncrona. Consulta la transacción para conocer el faceId resultante y el personUuid al que se registró o vinculó el rostro.

import requests
import time

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY",
    "Content-Type": "application/json"
}

for _ in range(10):
    response = requests.post(
        "https://api2.rhombussystems.com/api/faceRecognition/matchmaker/findFaceUploadMetadataByTransaction",
        headers=headers,
        json={"transactionId": "enroll-jordan-001"}
    )
    metadata = response.json().get("faceUploadMetadata", [])
    if metadata and all(m.get("success") is not None for m in metadata):
        for m in metadata:
            print(f"faceId={m.get('faceId')} personUuid={m.get('personUuid')} "
                  f"success={m.get('success')} {m.get('errorMsg', '')}")
        break
    time.sleep(2)

Cada entrada de faceUploadMetadata incluye:

CampoTipoDescripción
transactionIdstringLa transacción a la que pertenece esta subida
faceIdstringIdentificador del matchmaker de rostro registrado (una vez procesado)
personUuidstringPersona a la que se registró o vinculó el rostro
successbooleanSi el procesamiento tuvo éxito
errorMsgstringDetalle del error cuando success es false
createdAtMillisintegerMarca de tiempo de la subida en milisegundos epoch
origS3KeystringClave de almacenamiento de la imagen original subida
Consejo

¿Prefieres registrar a una persona a partir de un rostro que la cámara ya capturó? Usa createFaceMatchmakerFromSighting con el faceEventUuid de un evento facial existente y el personUuid de destino para convertir ese avistamiento en una plantilla registrada — sin necesidad de subir nada.

Etiquetar a una persona

Adjunta etiquetas (por ejemplo, employee o visitor) para organizar a las personas y filtrar los eventos faciales más adelante.

import requests

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY",
    "Content-Type": "application/json"
}

response = requests.post(
    "https://api2.rhombussystems.com/api/faceRecognition/person/addPersonLabel",
    headers=headers,
    json={"personUuid": "YOUR_PERSON_UUID", "label": "employee"}
)
print(response.json())

Usa removePersonLabel con el mismo cuerpo para quitar una etiqueta, y findPersonLabelsByOrg (cuerpo vacío {}) para obtener un mapa labelsByPerson con las etiquetas de cada persona.

Listar personas y matchmakers

Lista todas las personas registradas con findPeopleByOrg, y lista las plantillas de rostro registradas con findFaceMatchmakersByOrg (o findFaceMatchmakersByPerson para una sola persona).

import requests

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY",
    "Content-Type": "application/json"
}

people = requests.post(
    "https://api2.rhombussystems.com/api/faceRecognition/person/findPeopleByOrg",
    headers=headers, json={}
).json().get("people", [])

for person in people:
    print(f"{person.get('name')} ({person.get('uuid')})")

matchmakers = requests.post(
    "https://api2.rhombussystems.com/api/faceRecognition/matchmaker/findFaceMatchmakersByOrg",
    headers=headers, json={}
).json().get("faceMatchmakers", [])

for mm in matchmakers:
    print(f"faceId={mm.get('id')} personUuid={mm.get('personUuid')} uploaded={mm.get('uploaded')}")

Cada matchmaker de rostro incluye id (el ID del rostro), personUuid, orgUuid, uploaded (booleano) y createdOn.

Actualizar o eliminar personas y rostros

EndpointCuerpoPropósito
/api/faceRecognition/person/getPerson{ "personUuid": "..." }Obtener una persona
/api/faceRecognition/person/updatePerson{ "personSelectiveUpdate": { "uuid": "...", "name": "..." } }Actualizar nombre/correo (selectivo)
/api/faceRecognition/person/deletePerson{ "personUuid": "..." }Eliminar una persona
/api/faceRecognition/matchmaker/getFaceMatchmaker{ "faceId": "..." }Obtener un rostro registrado
/api/faceRecognition/matchmaker/deleteFaceMatchmaker{ "faceId": "..." }Eliminar una plantilla de rostro registrada

Consultar eventos de coincidencia facial

Cuando una cámara detecta un rostro, genera un evento facial. Recupera eventos con findFaceEventsByOrg, que toma un searchFilter y un pageRequest.

import requests
import time

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY",
    "Content-Type": "application/json"
}

now_ms = int(time.time() * 1000)
one_day_ago_ms = now_ms - (24 * 60 * 60 * 1000)

response = requests.post(
    "https://api2.rhombussystems.com/api/faceRecognition/faceEvent/findFaceEventsByOrg",
    headers=headers,
    json={
        "searchFilter": {
            "hasName": True,
            "timestampFilter": {
                "rangeStart": str(one_day_ago_ms),
                "rangeEnd": str(now_ms)
            }
        },
        "pageRequest": {"maxPageSize": 50}
    }
)

data = response.json()
for event in data.get("faceEvents", []):
    match = event.get("selectedPersonMatch") or {}
    print(f"{event.get('faceName')} on {event.get('deviceUuid')} "
          f"confidence={match.get('confidence')} at {event.get('eventTimestamp')}")

# Obtén la página siguiente si hay lastEvaluatedKey presente
next_key = data.get("lastEvaluatedKey")

Filtro de búsqueda

Todos los campos de searchFilter son opcionales. Cuando se omite timestampFilter, la búsqueda usa por defecto los últimos 7 días.

personUuidsstring[]body
Filtra por un conjunto de personas.
faceNamesstring[]body
Filtra por nombres exactos de personas.
faceNameContainsstringbody
Filtra por una subcadena del nombre (mínimo 3 caracteres tras recortar). Tiene precedencia sobre faceNames si ambos están definidos.
labelsstring[]body
Filtra por etiquetas de persona.
deviceUuidsstring[]body
Filtra por un conjunto de cámaras.
locationUuidsstring[]body
Filtra por un conjunto de ubicaciones.
hasNamebooleanbody
Filtra por la presencia (true) o ausencia (false) de un nombre de persona coincidente.
hasEmbeddingbooleanbody
Filtra por la presencia o ausencia de una firma facial.
timestampFilterobjectbody
Ventana temporal con rangeStart y rangeEnd, cada uno una cadena que contiene un valor en milisegundos epoch (inclusive). Por defecto usa los últimos 7 días.

Solicitud de página

maxPageSizeintegerbody
Número máximo de eventos a devolver en una página.
lastEvaluatedKeystringbody
Cursor de paginación. Pasa el lastEvaluatedKey de la respuesta anterior para obtener la página siguiente. Ausente cuando no hay más resultados.

Interpretar un evento facial

Cada evento en faceEvents es un objeto con estos campos:

uuidstring
Identificador único del evento facial.
eventTimestampinteger
Cuándo se detectó el rostro, en milisegundos epoch.
faceNamestring
Nombre de la persona coincidente. Igual al name de selectedPersonMatch cuando se seleccionó una coincidencia.
personUuidstring
UUID de la persona coincidente, si la hay.
deviceUuidstring
Cámara que generó el evento.
locationUuidstring
Ubicación de la cámara.
detectionConfidencenumber
Confianza (0–1) de que la imagen detectada es un rostro.
embeddingConfidencenumber
Confianza (0–1) asociada a la firma facial generada.
hasEmbeddingboolean
Si el evento tiene una firma facial.
selectedPersonMatchobject
La coincidencia de persona elegida, con uuid, name, faceId y confidence (0–1). Nula cuando no se seleccionó ninguna coincidencia.
topPersonMatchesarray
Las mejores coincidencias candidatas para el rostro, cada una con uuid, name, faceId y confidence (0–1). Útil para revisar coincidencias cercanas.
imageS3Keystring
Clave de almacenamiento de la imagen del evento.
thumbnailS3Keystring
Clave de almacenamiento de la miniatura del evento.
Consejo

Obtén un solo evento por su UUID con getFaceEvent (cuerpo { "eventUuid": "..." }).

Ajustar la coincidencia y corregir eventos

Leer y actualizar el umbral de coincidencia

El umbral de coincidencia controla qué tan estricta es la coincidencia facial. Es un valor de confianza entre 0 y 1 — un valor más alto exige una coincidencia más cercana (menos falsos positivos, más omisiones); un valor más bajo es más permisivo.

import requests

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Leer el umbral actual
current = requests.post(
    "https://api2.rhombussystems.com/api/faceRecognition/matchmaker/getFaceMatchingConfig",
    headers=headers, json={}
).json()
print(f"Current threshold: {current.get('faceMatchConfidenceThreshold')}")

# Subir el umbral para reducir falsos positivos
updated = requests.post(
    "https://api2.rhombussystems.com/api/faceRecognition/matchmaker/updateFaceMatchingConfig",
    headers=headers,
    json={"faceMatchConfidenceThreshold": 0.8}
).json()
print(f"New threshold: {updated.get('faceMatchConfidenceThreshold')}")
faceMatchConfidenceThresholdnumberbody
Confianza mínima de coincidencia (0–1) requerida para considerar que un rostro detectado es una coincidencia.

Corregir un evento mal identificado

Reasigna un evento facial a la persona correcta (o establece un nombre directamente) con updateFaceEvent. Así corriges coincidencias erróneas.

import requests

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY",
    "Content-Type": "application/json"
}

response = requests.post(
    "https://api2.rhombussystems.com/api/faceRecognition/faceEvent/updateFaceEvent",
    headers=headers,
    json={
        "eventUuid": "YOUR_FACE_EVENT_UUID",
        "personUuid": "CORRECT_PERSON_UUID"
    }
)
print(response.json())
eventUuidstringbodyobligatorio
El evento facial a actualizar.
personUuidstringbody
UUID de la persona correcta a asociar con el evento.
personNamestringbody
Nombre a asociar con el evento. Se ignora si personUuid coincide con una de las mejores coincidencias de persona del evento.

Para eliminar un evento por completo, llama a deleteFaceEvent con { "eventUuid": "..." }.

Analíticas de conteo de personas

El conteo de personas es analítica de cámara agregada — cuenta personas sin identificarlas, servida por el servicio de reportes. Úsalo para tráfico de personas, aforo y paneles de tendencias.

Serie temporal de conteo

getCountReportV2 devuelve una serie temporal de conteos agrupados por intervalo. Para el conteo de personas, establece types en ["PEOPLE"].

import requests
import time

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY",
    "Content-Type": "application/json"
}

now_ms = int(time.time() * 1000)
week_ago_ms = now_ms - (7 * 24 * 60 * 60 * 1000)

response = requests.post(
    "https://api2.rhombussystems.com/api/report/getCountReportV2",
    headers=headers,
    json={
        "startTimeMs": week_ago_ms,
        "endTimeMs": now_ms,
        "interval": "DAILY",
        "scope": "DEVICE",
        "types": ["PEOPLE"],
        "uuid": "YOUR_CAMERA_UUID",
        "timeZone": "America/Los_Angeles"
    }
)

data = response.json()
for point in data.get("timeSeriesDataPoints", []):
    counts = point.get("eventCountMap", {})
    print(f"{point.get('dateLocal')}: {counts.get('PEOPLE', 0)} people")
startTimeMsintegerbodyobligatorio
Inicio del rango en milisegundos epoch.
endTimeMsintegerbodyobligatorio
Fin del rango en milisegundos epoch.
intervalstringbodyobligatorio
Tamaño del bucket. Uno de MINUTELY, QUARTERHOURLY, HOURLY, DAILY, WEEKLY, MONTHLY.
scopestringbodyobligatorio
Alcance de agregación. Uno de REGION, DEVICE, LOCATION, ORG.
typesstring[]bodyobligatorio
Tipos de reporte a incluir. Usa ["PEOPLE"] para el conteo de personas.
uuidstringbody
UUID de destino para el alcance elegido (por ejemplo, un UUID de cámara cuando scope es DEVICE). Omítelo para el alcance ORG.
timeZonestringbody
Zona horaria IANA usada para agrupar los datos (por ejemplo, America/Los_Angeles).

Cada entrada en timeSeriesDataPoints incluye:

dateUtcstring
Inicio del bucket en UTC.
dateLocalstring
Inicio del bucket en la zona horaria solicitada.
eventCountMapobject
Mapa de tipo de reporte a conteo para el bucket (por ejemplo, { "PEOPLE": 42 }).
reportingDevicesMapobject
Mapa que describe qué dispositivos reportaron en el bucket.

Conteos de ocupación a lo largo del tiempo

getOccupancyCountsV2 devuelve una serie temporal de conteos de ocupación derivados de la cámara para una sola cámara.

import requests
import time

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY",
    "Content-Type": "application/json"
}

now_ms = int(time.time() * 1000)
day_ago_ms = now_ms - (24 * 60 * 60 * 1000)

response = requests.post(
    "https://api2.rhombussystems.com/api/report/getOccupancyCountsV2",
    headers=headers,
    json={
        "deviceUuid": "YOUR_CAMERA_UUID",
        "startTimeMs": day_ago_ms,
        "endTimeMs": now_ms,
        "interval": "HOURLY"
    }
)

for point in response.json().get("timeSeriesDataPoints", []):
    print(f"{point.get('dateLocal')}: {point.get('eventCountMap')}")
deviceUuidstringbodyobligatorio
Cámara de la que leer los conteos de ocupación.
startTimeMsintegerbodyobligatorio
Inicio del rango en milisegundos epoch.
endTimeMsintegerbodyobligatorio
Fin del rango en milisegundos epoch.
intervalstringbodyobligatorio
Tamaño del bucket (MINUTELY, QUARTERHOURLY, HOURLY, DAILY, WEEKLY, MONTHLY).

Cada punto de datos añade timestampMs y approximateTimestampMsMap a los campos estándar dateUtc, dateLocal, eventCountMap y reportingDevicesMap.

Referencia: enums de reportes

EnumValores
Tipo de reporteCROWD, PEOPLE, FACES, MOTION, BANDWIDTH, VEHICLES, LICENSEPLATES, ALERTS, AM_VERIFICATION, DWELL
IntervaloMINUTELY, QUARTERHOURLY, HOURLY, DAILY, WEEKLY, MONTHLY
AlcanceREGION, DEVICE, LOCATION, ORG

Casos de uso

  • Alertas de lista de vigilancia — registra personas de interés y luego consulta findFaceEventsByOrg filtrando por personUuids o labels para marcar avistamientos.
  • Reconciliación de acceso — cruza los eventos faciales con la actividad de control de acceso para confirmar que la persona que usó su credencial coincide con la que vio la cámara.
  • Ciclo de calidad del modelo — revisa topPersonMatches en eventos de baja confianza, corrígelos con updateFaceEvent y ajusta faceMatchConfidenceThreshold para equilibrar falsos positivos y omisiones.
  • Paneles de tráfico de personas — grafica getCountReportV2 con types: ["PEOPLE"] para tendencias de tráfico diarias u horarias por cámara o ubicación.
  • Mosaicos de aforo — grafica getOccupancyCountsV2 para una cámara y muestra la ocupación a lo largo del día.

Solución de problemas

Los endpoints de rostros devuelven un error o no están disponibles

El reconocimiento facial es una función licenciada y restringida. Confirma que tu organización esté habilitada y que tu clave de API tenga permisos de administración de rostros. Contacta a tu equipo de cuenta de Rhombus si los endpoints no están disponibles.

uploadFaceMatchmakers rechaza mi archivo

Envía exactamente un archivo por solicitud bajo el nombre de campo de formulario file, mantenlo por debajo de 5 MB y usa .jpg o .png. Deja que tu cliente HTTP defina el límite multipart de Content-Type — no establezcas Content-Type manualmente.

findFaceUploadMetadataByTransaction aún no devuelve nada

Las subidas se procesan de forma asíncrona. Consulta la transacción hasta que cada entrada reporte un valor de success. Cuando success es false, lee errorMsg para conocer el motivo.

No regresan eventos faciales

Si omites timestampFilter, la búsqueda solo cubre los últimos 7 días. Amplía la ventana con rangeStart/rangeEnd (cadenas en milisegundos epoch) y relaja filtros como hasName o personUuids. Recuerda paginar con lastEvaluatedKey.

Demasiadas coincidencias falsas (o muy pocas)

Ajusta faceMatchConfidenceThreshold con updateFaceMatchingConfig. Súbelo (más cerca de 1) para reducir falsos positivos; bájalo para capturar más coincidencias potenciales. Corrige los errores individuales con updateFaceEvent.

Próximos pasos