-
Notifications
You must be signed in to change notification settings - Fork 3
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
Improve the plumbing and documentation for some of the ask* methods #125
Changes from 5 commits
f8f3245
5b7c63e
35013f7
c661b67
54885dd
945a4bc
867ffac
76212eb
8de7f3a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
import logging | ||
import os | ||
import time | ||
from functools import partial | ||
from io import BufferedReader, BytesIO | ||
from typing import Callable, Optional, Union | ||
|
||
|
@@ -304,9 +305,20 @@ def submit_image_query( # noqa: PLR0913 # pylint: disable=too-many-arguments, t | |
Any pixel format will get converted to JPEG at high quality before sending to service. | ||
:type image: str or bytes or Image.Image or BytesIO or BufferedReader or np.ndarray | ||
|
||
:param wait: How long to wait (in seconds) for a confident answer. | ||
:param wait: How long to poll (in seconds) for a confident answer. This is a client-side timeout. | ||
:type wait: float | ||
|
||
:param patience_time: How long to wait (in seconds) for a confident answer for this image query. | ||
The longer the patience_time, the more likely Groundlight will arrive at a confident answer. | ||
Within patience_time, Groundlight will update ML predictions based on stronger findings, | ||
and, additionally, Groundlight will prioritize human review of the image query if necessary. | ||
This is a soft server-side timeout. If not set, use the detector's patience_time. | ||
:type patience_time: float | ||
|
||
:param confidence_threshold: The confidence threshold to wait for. | ||
If not set, use the detector's confidence threshold. | ||
:type confidence_threshold: float | ||
|
||
:param human_review: If `None` or `DEFAULT`, send the image query for human review | ||
only if the ML prediction is not confident. | ||
If set to `ALWAYS`, always send the image query for human review. | ||
|
@@ -375,8 +387,10 @@ def ask_confident( | |
confidence_threshold: Optional[float] = None, | ||
wait: Optional[float] = None, | ||
) -> ImageQuery: | ||
"""Evaluates an image with Groundlight waiting until an answer above the confidence threshold | ||
of the detector is reached or the wait period has passed. | ||
""" | ||
Evaluates an image with Groundlight waiting until an answer above the confidence threshold | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Address some formatting issues |
||
of the detector is reached or the wait period has passed. | ||
|
||
:param detector: the Detector object, or string id of a detector like `det_12345` | ||
:type detector: Detector or str | ||
|
||
|
@@ -405,6 +419,8 @@ def ask_confident( | |
image, | ||
confidence_threshold=confidence_threshold, | ||
wait=wait, | ||
patience_time=wait, | ||
human_review=None, | ||
) | ||
|
||
def ask_ml( | ||
|
@@ -413,7 +429,9 @@ def ask_ml( | |
image: Union[str, bytes, Image.Image, BytesIO, BufferedReader, np.ndarray], | ||
wait: Optional[float] = None, | ||
) -> ImageQuery: | ||
"""Evaluates an image with Groundlight, getting the first answer Groundlight can provide. | ||
""" | ||
Evaluates an image with Groundlight, getting the first answer Groundlight can provide. | ||
|
||
:param detector: the Detector object, or string id of a detector like `det_12345` | ||
:type detector: Detector or str | ||
|
||
|
@@ -447,8 +465,9 @@ def ask_async( | |
self, | ||
detector: Union[Detector, str], | ||
image: Union[str, bytes, Image.Image, BytesIO, BufferedReader, np.ndarray], | ||
patience_time: Optional[float] = None, | ||
confidence_threshold: Optional[float] = None, | ||
human_review: Optional[str] = None, | ||
inspection_id: Optional[str] = None, | ||
) -> ImageQuery: | ||
""" | ||
Convenience method for submitting an `ImageQuery` asynchronously. This is equivalent to calling | ||
|
@@ -469,6 +488,17 @@ def ask_async( | |
|
||
:type image: str or bytes or Image.Image or BytesIO or BufferedReader or np.ndarray | ||
|
||
:param patience_time: How long to wait (in seconds) for a confident answer for this image query. | ||
The longer the patience_time, the more likely Groundlight will arrive at a confident answer. | ||
Within patience_time, Groundlight will update ML predictions based on stronger findings, | ||
and, additionally, Groundlight will prioritize human review of the image query if necessary. | ||
This is a soft server-side timeout. If not set, use the detector's patience_time. | ||
:type patience_time: float | ||
|
||
:param confidence_threshold: The confidence threshold to wait for. | ||
If not set, use the detector's confidence threshold. | ||
:type confidence_threshold: float | ||
|
||
Comment on lines
+491
to
+501
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I absolutely love this description of patience time |
||
:param human_review: If `None` or `DEFAULT`, send the image query for human review | ||
only if the ML prediction is not confident. | ||
If set to `ALWAYS`, always send the image query for human review. | ||
|
@@ -500,26 +530,35 @@ def ask_async( | |
assert image_query.id is not None | ||
|
||
# Do not attempt to access the result of this query as the result for all async queries | ||
# will be None. Your result is being computed asynchronously and will be available | ||
# later | ||
# will be None. Your result is being computed asynchronously and will be available later | ||
assert image_query.result is None | ||
|
||
# retrieve the result later or on another machine by calling gl.get_image_query() | ||
# with the id of the image_query above | ||
image_query = gl.get_image_query(image_query.id) | ||
# retrieve the result later or on another machine by calling gl.wait_for_confident_result() | ||
# with the id of the image_query above. This will block until the result is available. | ||
image_query = gl.wait_for_confident_result(image_query.id) | ||
|
||
# now the result will be available for your use | ||
assert image_query.result is not None | ||
|
||
# alternatively, you can check if the result is available (without blocking) by calling | ||
# gl.get_image_query() with the id of the image_query above. This will return the image_query, | ||
# but the result may still be None. | ||
tyler-romero marked this conversation as resolved.
Show resolved
Hide resolved
tyler-romero marked this conversation as resolved.
Show resolved
Hide resolved
|
||
image_query = gl.get_image_query(image_query.id) | ||
""" | ||
return self.submit_image_query( | ||
detector, image, wait=0, human_review=human_review, want_async=True, inspection_id=inspection_id | ||
detector, | ||
image, | ||
wait=0, | ||
patience_time=patience_time, | ||
confidence_threshold=confidence_threshold, | ||
human_review=human_review, | ||
want_async=True, | ||
) | ||
|
||
def wait_for_confident_result( | ||
self, | ||
image_query: Union[ImageQuery, str], | ||
confidence_threshold: float, | ||
confidence_threshold: Optional[float] = None, | ||
timeout_sec: float = 30.0, | ||
) -> ImageQuery: | ||
""" | ||
|
@@ -529,7 +568,8 @@ def wait_for_confident_result( | |
:param image_query: An ImageQuery object to poll | ||
:type image_query: ImageQuery or str | ||
|
||
:param confidence_threshold: The minimum confidence level required to return before the timeout. | ||
:param confidence_threshold: The confidence threshold to wait for. | ||
If not set, use the detector's confidence threshold. | ||
:type confidence_threshold: float | ||
|
||
:param timeout_sec: The maximum number of seconds to wait. | ||
|
@@ -538,10 +578,12 @@ def wait_for_confident_result( | |
:return: ImageQuery | ||
:rtype: ImageQuery | ||
""" | ||
if confidence_threshold is None: | ||
if isinstance(image_query, str): | ||
image_query: ImageQuery = self.get_image_query(image_query) | ||
confidence_threshold = self.get_detector(image_query.detector_id).confidence_threshold | ||
|
||
def confidence_above_thresh(iq): | ||
return iq_is_confident(iq, confidence_threshold=confidence_threshold) | ||
|
||
confidence_above_thresh = partial(iq_is_confident, confidence_threshold=confidence_threshold) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks for teaching me about There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Haha its honestly really nice sometimes. I love it because its a bit of a functional holdover |
||
return self._wait_for_result(image_query, condition=confidence_above_thresh, timeout_sec=timeout_sec) | ||
|
||
def wait_for_ml_result(self, image_query: Union[ImageQuery, str], timeout_sec: float = 30.0) -> ImageQuery: | ||
|
@@ -551,9 +593,6 @@ def wait_for_ml_result(self, image_query: Union[ImageQuery, str], timeout_sec: f | |
:param image_query: An ImageQuery object to poll | ||
:type image_query: ImageQuery or str | ||
|
||
:param confidence_threshold: The minimum confidence level required to return before the timeout. | ||
:type confidence_threshold: float | ||
|
||
:param timeout_sec: The maximum number of seconds to wait. | ||
:type timeout_sec: float | ||
|
||
|
@@ -623,6 +662,7 @@ def add_label(self, image_query: Union[ImageQuery, str], label: Union[Label, str | |
else: | ||
image_query_id = str(image_query) | ||
# Some old imagequery id's started with "chk_" | ||
# TODO: handle iqe | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure what you mean here, so I'm concerned that others might not as well, but maybe I'm just out of the loop. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Edge detectors return image queries with the prefix There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This what the edge server will return, right? This just opens up the sdk to work with the edge There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. got it! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exactly Brandon, just updated the comment to be a bit more clear |
||
if not image_query_id.startswith(("chk_", "iq_")): | ||
raise ValueError(f"Invalid image query id {image_query_id}") | ||
api_label = convert_display_label_to_internal(image_query_id, label) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like showing this as the default use case.