diff --git a/src/groundlight/client.py b/src/groundlight/client.py index c4609bf5..38e9df29 100644 --- a/src/groundlight/client.py +++ b/src/groundlight/client.py @@ -16,6 +16,7 @@ from groundlight_openapi_client.api.user_api import UserApi from groundlight_openapi_client.exceptions import NotFoundException, UnauthorizedException from groundlight_openapi_client.model.b_box_geometry_request import BBoxGeometryRequest +from groundlight_openapi_client.model.bounding_box_mode_configuration import BoundingBoxModeConfiguration from groundlight_openapi_client.model.count_mode_configuration import CountModeConfiguration from groundlight_openapi_client.model.detector_creation_input_request import DetectorCreationInputRequest from groundlight_openapi_client.model.detector_group_request import DetectorGroupRequest @@ -396,9 +397,8 @@ def create_detector( # noqa: PLR0913 By default will create a binary detector but alternate modes can be created by passing in a mode argument. - Text and Bounding box detectors are in Beta, and can be created through the - ExperimentalApi via the :meth:`ExperimentalApi.create_text_recognition_detector` and - :meth:`ExperimentalApi.create_bounding_box_detector` methods. + Text recognition detectors are in Beta, and can be created through the + ExperimentalApi via the :meth:`ExperimentalApi.create_text_recognition_detector` method. **Example usage**:: @@ -448,6 +448,8 @@ def create_detector( # noqa: PLR0913 :param class_names: The name or names of the class to use for the detector. Only used for multi-class and counting detectors. :param priming_group_id: Optional ID of an existing PrimingGroup to associate with this detector. + PrimingGroup IDs are provided by Groundlight representatives. If you would like + to use a priming_group_id, please reach out to your Groundlight representative. :return: The created Detector object """ @@ -1605,6 +1607,8 @@ def create_counting_detector( # noqa: PLR0913 # pylint: disable=too-many-argume information like location, purpose, or related system IDs. You can retrieve this metadata later by calling `get_detector()`. :param priming_group_id: Optional ID of an existing PrimingGroup to associate with this detector. + PrimingGroup IDs are provided by Groundlight representatives. If you would like + to use a priming_group_id, please reach out to your Groundlight representative. :return: The created Detector object """ @@ -1720,6 +1724,8 @@ def create_multiclass_detector( # noqa: PLR0913 # pylint: disable=too-many-argu information like location, purpose, or related system IDs. You can retrieve this metadata later by calling `get_detector()`. :param priming_group_id: Optional ID of an existing PrimingGroup to associate with this detector. + PrimingGroup IDs are provided by Groundlight representatives. If you would like + to use a priming_group_id, please reach out to your Groundlight representative. :return: The created Detector object """ @@ -1739,3 +1745,84 @@ def create_multiclass_detector( # noqa: PLR0913 # pylint: disable=too-many-argu detector_creation_input.mode_configuration = mode_config obj = self.detectors_api.create_detector(detector_creation_input, _request_timeout=DEFAULT_REQUEST_TIMEOUT) return Detector.parse_obj(obj.to_dict()) + + def create_bounding_box_detector( # noqa: PLR0913 # pylint: disable=too-many-arguments, too-many-locals + self, + name: str, + query: str, + class_name: str, + *, + max_num_bboxes: Optional[int] = None, + group_name: Optional[str] = None, + confidence_threshold: Optional[float] = None, + patience_time: Optional[float] = None, + pipeline_config: Optional[str] = None, + metadata: Union[dict, str, None] = None, + priming_group_id: Optional[str] = None, + ) -> Detector: + """ + Creates a bounding box detector that can detect objects in images up to a specified maximum number of bounding + boxes. + + **Example usage**:: + + gl = Groundlight() + + # Create a detector that counts people up to 5 + detector = gl.create_bounding_box_detector( + name="people_counter", + query="Draw a bounding box around each person in the image", + class_name="person", + max_num_bboxes=5, + confidence_threshold=0.9, + patience_time=30.0 + ) + + # Use the detector to find people in an image + image_query = gl.ask_ml(detector, "path/to/image.jpg") + print(f"Confidence: {image_query.result.confidence}") + print(f"Label: {image_query.result.label}") + print(f"Bounding boxes: {image_query.rois}") + + :param name: A short, descriptive name for the detector. + :param query: A question about the object to detect in the image. + :param class_name: The class name of the object to detect. + :param max_num_bboxes: Maximum number of bounding boxes to detect (default: 10) + :param group_name: Optional name of a group to organize related detectors together. + :param confidence_threshold: A value that sets the minimum confidence level required for the ML model's + predictions. If confidence is below this threshold, the query may be sent for human review. + :param patience_time: The maximum time in seconds that Groundlight will attempt to generate a + confident prediction before falling back to human review. Defaults to 30 seconds. + :param pipeline_config: Advanced usage only. Configuration string needed to instantiate a specific + prediction pipeline for this detector. + :param metadata: A dictionary or JSON string containing custom key/value pairs to associate with + the detector (limited to 1KB). This metadata can be used to store additional + information like location, purpose, or related system IDs. You can retrieve this + metadata later by calling `get_detector()`. + :param priming_group_id: Optional ID of an existing PrimingGroup to associate with this detector. + PrimingGroup IDs are provided by Groundlight representatives. If you would like + to use a priming_group_id, please reach out to your Groundlight representative. + + :return: The created Detector object + """ + + detector_creation_input = self._prep_create_detector( + name=name, + query=query, + group_name=group_name, + confidence_threshold=confidence_threshold, + patience_time=patience_time, + pipeline_config=pipeline_config, + metadata=metadata, + priming_group_id=priming_group_id, + ) + detector_creation_input.mode = ModeEnum.BOUNDING_BOX + + if max_num_bboxes is None: + mode_config = BoundingBoxModeConfiguration(class_name=class_name) + else: + mode_config = BoundingBoxModeConfiguration(max_num_bboxes=max_num_bboxes, class_name=class_name) + + detector_creation_input.mode_configuration = mode_config + obj = self.detectors_api.create_detector(detector_creation_input, _request_timeout=DEFAULT_REQUEST_TIMEOUT) + return Detector.parse_obj(obj.to_dict()) diff --git a/src/groundlight/experimental_api.py b/src/groundlight/experimental_api.py index 5282871d..b50d7fb0 100644 --- a/src/groundlight/experimental_api.py +++ b/src/groundlight/experimental_api.py @@ -19,7 +19,6 @@ from groundlight_openapi_client.api.edge_api import EdgeApi from groundlight_openapi_client.api.notes_api import NotesApi from groundlight_openapi_client.model.action_request import ActionRequest -from groundlight_openapi_client.model.bounding_box_mode_configuration import BoundingBoxModeConfiguration from groundlight_openapi_client.model.channel_enum import ChannelEnum from groundlight_openapi_client.model.condition_request import ConditionRequest from groundlight_openapi_client.model.patched_detector_request import PatchedDetectorRequest @@ -632,85 +631,6 @@ def update_detector_name(self, detector: Union[str, Detector], name: str) -> Non detector = detector.id self.detectors_api.update_detector(detector, patched_detector_request=PatchedDetectorRequest(name=name)) - def create_bounding_box_detector( # noqa: PLR0913 # pylint: disable=too-many-arguments, too-many-locals - self, - name: str, - query: str, - class_name: str, - *, - max_num_bboxes: Optional[int] = None, - group_name: Optional[str] = None, - confidence_threshold: Optional[float] = None, - patience_time: Optional[float] = None, - pipeline_config: Optional[str] = None, - metadata: Union[dict, str, None] = None, - priming_group_id: Optional[str] = None, - ) -> Detector: - """ - Creates a bounding box detector that can detect objects in images up to a specified maximum number of bounding - boxes. - - **Example usage**:: - - gl = ExperimentalApi() - - # Create a detector that counts people up to 5 - detector = gl.create_bounding_box_detector( - name="people_counter", - query="Draw a bounding box around each person in the image", - class_name="person", - max_num_bboxes=5, - confidence_threshold=0.9, - patience_time=30.0 - ) - - # Use the detector to find people in an image - image_query = gl.ask_ml(detector, "path/to/image.jpg") - print(f"Confidence: {image_query.result.confidence}") - print(f"Label: {image_query.result.label}") - print(f"Bounding boxes: {image_query.rois}") - - :param name: A short, descriptive name for the detector. - :param query: A question about the object to detect in the image. - :param class_name: The class name of the object to detect. - :param max_num_bboxes: Maximum number of bounding boxes to detect (default: 10) - :param group_name: Optional name of a group to organize related detectors together. - :param confidence_threshold: A value that sets the minimum confidence level required for the ML model's - predictions. If confidence is below this threshold, the query may be sent for human review. - :param patience_time: The maximum time in seconds that Groundlight will attempt to generate a - confident prediction before falling back to human review. Defaults to 30 seconds. - :param pipeline_config: Advanced usage only. Configuration string needed to instantiate a specific - prediction pipeline for this detector. - :param metadata: A dictionary or JSON string containing custom key/value pairs to associate with - the detector (limited to 1KB). This metadata can be used to store additional - information like location, purpose, or related system IDs. You can retrieve this - metadata later by calling `get_detector()`. - :param priming_group_id: Optional ID of an existing PrimingGroup to associate with this detector. - - :return: The created Detector object - """ - - detector_creation_input = self._prep_create_detector( - name=name, - query=query, - group_name=group_name, - confidence_threshold=confidence_threshold, - patience_time=patience_time, - pipeline_config=pipeline_config, - metadata=metadata, - priming_group_id=priming_group_id, - ) - detector_creation_input.mode = ModeEnum.BOUNDING_BOX - - if max_num_bboxes is None: - mode_config = BoundingBoxModeConfiguration(class_name=class_name) - else: - mode_config = BoundingBoxModeConfiguration(max_num_bboxes=max_num_bboxes, class_name=class_name) - - detector_creation_input.mode_configuration = mode_config - obj = self.detectors_api.create_detector(detector_creation_input, _request_timeout=DEFAULT_REQUEST_TIMEOUT) - return Detector.parse_obj(obj.to_dict()) - def create_text_recognition_detector( # noqa: PLR0913 # pylint: disable=too-many-arguments, too-many-locals self, name: str, @@ -750,6 +670,8 @@ def create_text_recognition_detector( # noqa: PLR0913 # pylint: disable=too-man information like location, purpose, or related system IDs. You can retrieve this metadata later by calling `get_detector()`. :param priming_group_id: Optional ID of an existing PrimingGroup to associate with this detector. + PrimingGroup IDs are provided by Groundlight representatives. If you would like + to use a priming_group_id, please reach out to your Groundlight representative. :return: The created Detector object """ diff --git a/test/integration/test_groundlight.py b/test/integration/test_groundlight.py index 988386a1..8f06251a 100644 --- a/test/integration/test_groundlight.py +++ b/test/integration/test_groundlight.py @@ -942,6 +942,9 @@ def test_delete_detector(gl: Groundlight): def test_create_detector_with_invalid_priming_group_id(gl: Groundlight): """ Test that creating a detector with a non-existent priming_group_id returns an appropriate error. + + Note: PrimingGroup IDs are provided by Groundlight representatives. If you would like to + use a priming_group_id, please reach out to your Groundlight representative. """ name = f"Test invalid priming {datetime.utcnow()}" query = "Is there a dog?"