Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Face Comparison Block #684

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions inference/core/workflows/core_steps/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,18 @@
from inference.core.workflows.core_steps.models.roboflow.object_detection.v1 import (
RoboflowObjectDetectionModelBlockV1,
)
from inference.core.workflows.core_steps.models.third_party.seventh_sense.face_comparison.v1 import (
FaceComparisonBlockV1,
)
from inference.core.workflows.core_steps.models.third_party.barcode_detection.v1 import (
BarcodeDetectorBlockV1,
)
from inference.core.workflows.core_steps.models.third_party.qr_code_detection.v1 import (
QRCodeDetectorBlockV1,
)
from inference.core.workflows.core_steps.models.third_party.seventh_sense.face_comparison.v1 import (
FaceComparisonBlockV1,
)
from inference.core.workflows.core_steps.sinks.roboflow.custom_metadata.v1 import (
RoboflowCustomMetadataBlockV1,
)
Expand Down Expand Up @@ -297,6 +303,7 @@ def load_blocks() -> List[Type[WorkflowBlock]]:
RoboflowClassificationModelBlockV1,
RoboflowMultiLabelClassificationModelBlockV1,
RoboflowObjectDetectionModelBlockV1,
FaceComparisonBlockV1,
BarcodeDetectorBlockV1,
QRCodeDetectorBlockV1,
AbsoluteStaticCropBlockV1,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
from typing import List, Literal, Optional, Type, Union

from opencv.fr import FR
from opencv.fr.compare.schemas import CompareRequest
from opencv.fr.search.schemas import SearchMode
from pydantic import ConfigDict, Field

from inference.core.workflows.execution_engine.entities.base import (
OutputDefinition,
WorkflowImageData,
)
from inference.core.workflows.execution_engine.entities.types import (
FLOAT_ZERO_TO_ONE_KIND,
STRING_KIND,
StepOutputImageSelector,
WorkflowImageSelector,
WorkflowParameterSelector,
)
from inference.core.workflows.prototypes.block import (
BlockResult,
WorkflowBlock,
WorkflowBlockManifest,
)

LONG_DESCRIPTION = """
The OpenCV Face Comparison API accepts two images and returns a similarity score between 0 and 1
based on the similarity of the faces in the images. A score above 0.7 signifies a high likelihood
that the photos are of the same person.
"""

class BlockManifest(WorkflowBlockManifest):
model_config = ConfigDict(
json_schema_extra={
"name": "Face Comparison",
"version": "v1",
"short_description": "Determine if two faces are of the same person.",
"long_description": LONG_DESCRIPTION,
"license": "MIT",
"block_type": "model",
"search_keywords": ["facial", "identity", "seventh sense", "opencv"],
}
)
type: Literal["roboflow_core/seventh_sense/face_comparison@1"]
image_1: Union[WorkflowImageSelector, StepOutputImageSelector] = Field(
title="Image 1",
description="The first image to compare against the second image",
examples=["$inputs.image", "$steps.cropping.crops"],
)
image_2: Union[WorkflowImageSelector, StepOutputImageSelector] = Field(
title="Image 2",
description="The second image to compare against the first image",
examples=["$inputs.image", "$steps.cropping.crops"],
)
search_mode: Union[
Literal[
"FAST",
"ACCURATE",
],
WorkflowParameterSelector(kind=[STRING_KIND]),
] = Field(
description="Search mode for the face comparison",
default="FAST",
examples=["FAST", "ACCURATE", "$inputs.search_mode"],
)
backend_url: Union[
Literal[
"https://sg.opencv.fr",
"https://us.opencv.fr",
"https://eu.opencv.fr",
],
WorkflowParameterSelector(kind=[STRING_KIND]),
] = Field(
description="Region of your Seventh Sense account.",
default="https://us.opencv.fr",
examples=["https://sg.opencv.fr", "$inputs.seventh_sense_backend_url"],
)

api_key: Union[WorkflowParameterSelector(kind=[STRING_KIND]), str] = Field(
description="Your Seventh Sense API key",
examples=["xxxxxx", "$inputs.seventh_sense_api_key"],
private=True,
)
@classmethod
def describe_outputs(cls) -> List[OutputDefinition]:
return [
OutputDefinition(
name="score",
kind=[FLOAT_ZERO_TO_ONE_KIND],
description="Similarity score between 0 and 1",
),
]

@classmethod
def get_execution_engine_compatibility(cls) -> Optional[str]:
return ">=1.0.0,<2.0.0"


class FaceComparisonBlockV1(WorkflowBlock):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.sdkCache = {}

@classmethod
def get_manifest(cls) -> Type[WorkflowBlockManifest]:
return BlockManifest

@classmethod
def get_execution_engine_compatibility(cls) -> Optional[str]:
return ">=1.0.0,<2.0.0"
def getSdk(self, backend_url, api_key):
key = f"{backend_url}_{api_key}"

if key not in self.sdkCache:
self.sdkCache[key] = FR(backend_url, api_key)
return self.sdkCache[key]

def run(
self,
image_1: WorkflowImageData,
image_2: WorkflowImageData,
search_mode: str,
backend_url: str,
api_key: str,
) -> BlockResult:
sdk = self.getSdk(backend_url, api_key)

compare_request = CompareRequest(
[image_1.numpy_image],
[image_2.numpy_image],
search_mode=(
SearchMode.FAST if search_mode == "FAST" else SearchMode.ACCURATE
),
)
score = sdk.compare.compare_image_sets(compare_request)

return {"score": score}
3 changes: 2 additions & 1 deletion requirements/_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ pydot>=2.0.0
shapely>=2.0.0,<2.1.0
tldextract~=5.1.2
packaging~=24.0
anthropic~=0.34.2
anthropic~=0.34.2
opencv-face-recognition==1.1.1
Loading