@@ -48,12 +48,18 @@ def fixture_detector(gl: Groundlight) -> Detector:
4848 return gl .create_detector (name = name , query = query , pipeline_config = pipeline_config )
4949
5050
51- @pytest .fixture (name = "image_query " )
52- def fixture_image_query (gl : Groundlight , detector : Detector ) -> ImageQuery :
51+ @pytest .fixture (name = "image_query_yes " )
52+ def fixture_image_query_yes (gl : Groundlight , detector : Detector ) -> ImageQuery :
5353 iq = gl .submit_image_query (detector = detector .id , image = "test/assets/dog.jpeg" )
5454 return iq
5555
5656
57+ @pytest .fixture (name = "image_query_no" )
58+ def fixture_image_query_no (gl : Groundlight , detector : Detector ) -> ImageQuery :
59+ iq = gl .submit_image_query (detector = detector .id , image = "test/assets/cat.jpeg" )
60+ return iq
61+
62+
5763def test_create_detector (gl : Groundlight ):
5864 name = f"Test { datetime .utcnow ()} " # Need a unique name
5965 query = "Is there a dog?"
@@ -232,88 +238,89 @@ def test_list_image_queries(gl: Groundlight):
232238 assert is_valid_display_result (image_query .result )
233239
234240
235- def test_get_image_query (gl : Groundlight , image_query : ImageQuery ):
236- _image_query = gl .get_image_query (id = image_query .id )
241+ def test_get_image_query (gl : Groundlight , image_query_yes : ImageQuery ):
242+ _image_query = gl .get_image_query (id = image_query_yes .id )
237243 assert str (_image_query )
238244 assert isinstance (_image_query , ImageQuery )
239245 assert is_valid_display_result (_image_query .result )
240246
241247
242- def test_get_image_query_label_yes (gl : Groundlight , image_query : ImageQuery ):
243- gl .add_label (image_query , Label .YES )
244- retrieved_iq = gl .get_image_query (id = image_query .id )
248+ def test_get_image_query_label_yes (gl : Groundlight , image_query_yes : ImageQuery ):
249+ gl .add_label (image_query_yes , Label .YES )
250+ retrieved_iq = gl .get_image_query (id = image_query_yes .id )
245251 assert retrieved_iq .result .label == Label .YES
246252
247253
248- def test_get_image_query_label_no (gl : Groundlight , image_query : ImageQuery ):
249- gl .add_label (image_query , Label .NO )
250- retrieved_iq = gl .get_image_query (id = image_query .id )
254+ def test_get_image_query_label_no (gl : Groundlight , image_query_no : ImageQuery ):
255+ gl .add_label (image_query_no , Label .NO )
256+ retrieved_iq = gl .get_image_query (id = image_query_no .id )
251257 assert retrieved_iq .result .label == Label .NO
252258
253259
254- def test_add_label_to_object (gl : Groundlight , image_query : ImageQuery ):
255- assert isinstance (image_query , ImageQuery )
256- gl .add_label (image_query , Label .YES )
260+ def test_add_label_to_object (gl : Groundlight , image_query_yes : ImageQuery ):
261+ assert isinstance (image_query_yes , ImageQuery )
262+ gl .add_label (image_query_yes , Label .YES )
257263
258264
259- def test_add_label_by_id (gl : Groundlight , image_query : ImageQuery ):
260- iqid = image_query .id
265+ def test_add_label_by_id (gl : Groundlight , image_query_no : ImageQuery ):
266+ iqid = image_query_no .id
261267 # TODO: Fully deprecate chk_ prefix
262268 assert iqid .startswith (("chk_" , "iq_" ))
263269 gl .add_label (iqid , Label .NO )
264270
265271
266- def test_add_label_names (gl : Groundlight , image_query : ImageQuery ):
267- iqid = image_query .id
272+ def test_add_label_names (gl : Groundlight , image_query_yes : ImageQuery , image_query_no : ImageQuery ):
273+ iqid_yes = image_query_yes .id
274+ iqid_no = image_query_no .id
268275
269276 # Valid labels
270- gl .add_label (iqid , Label .YES )
271- gl .add_label (iqid , Label .YES .value )
272- gl .add_label (iqid , "YES" )
273- gl .add_label (iqid , "yes" )
274- gl .add_label (iqid , "yEs" )
275- gl .add_label (iqid , Label .NO )
276- gl .add_label (iqid , Label .NO .value )
277- gl .add_label (iqid , "NO" )
278- gl .add_label (iqid , "no" )
277+ gl .add_label (iqid_yes , Label .YES )
278+ gl .add_label (iqid_yes , Label .YES .value )
279+ gl .add_label (iqid_yes , "YES" )
280+ gl .add_label (iqid_yes , "yes" )
281+ gl .add_label (iqid_yes , "yEs" )
282+ gl .add_label (iqid_no , Label .NO )
283+ gl .add_label (iqid_no , Label .NO .value )
284+ gl .add_label (iqid_no , "NO" )
285+ gl .add_label (iqid_no , "no" )
279286
280287 # Invalid labels
281288 with pytest .raises (ValueError ):
282- gl .add_label (iqid , "PASS" )
289+ gl .add_label (iqid_yes , "PASS" )
283290 with pytest .raises (ValueError ):
284- gl .add_label (iqid , "FAIL" )
291+ gl .add_label (iqid_yes , "FAIL" )
285292 with pytest .raises (ValueError ):
286- gl .add_label (iqid , DeprecatedLabel .PASS )
293+ gl .add_label (iqid_yes , DeprecatedLabel .PASS )
287294 with pytest .raises (ValueError ):
288- gl .add_label (iqid , DeprecatedLabel .FAIL )
295+ gl .add_label (iqid_yes , DeprecatedLabel .FAIL )
289296 with pytest .raises (ValueError ):
290- gl .add_label (iqid , "sorta" )
297+ gl .add_label (iqid_yes , "sorta" )
291298 with pytest .raises (ValueError ):
292- gl .add_label (iqid , "YES " )
299+ gl .add_label (iqid_yes , "YES " )
293300 with pytest .raises (ValueError ):
294- gl .add_label (iqid , " YES" )
301+ gl .add_label (iqid_yes , " YES" )
295302 with pytest .raises (ValueError ):
296- gl .add_label (iqid , "0" )
303+ gl .add_label (iqid_yes , "0" )
297304 with pytest .raises (ValueError ):
298- gl .add_label (iqid , "1" )
305+ gl .add_label (iqid_yes , "1" )
299306
300307 # We technically don't allow these in the type signature, but users might do it anyway
301308 with pytest .raises (ValueError ):
302- gl .add_label (iqid , 0 ) # type: ignore
309+ gl .add_label (iqid_yes , 0 ) # type: ignore
303310 with pytest .raises (ValueError ):
304- gl .add_label (iqid , 1 ) # type: ignore
311+ gl .add_label (iqid_yes , 1 ) # type: ignore
305312 with pytest .raises (ValueError ):
306- gl .add_label (iqid , None ) # type: ignore
313+ gl .add_label (iqid_yes , None ) # type: ignore
307314 with pytest .raises (ValueError ):
308- gl .add_label (iqid , True ) # type: ignore
315+ gl .add_label (iqid_yes , True ) # type: ignore
309316 with pytest .raises (ValueError ):
310- gl .add_label (iqid , False ) # type: ignore
317+ gl .add_label (iqid_yes , False ) # type: ignore
311318 with pytest .raises (ValueError ):
312- gl .add_label (iqid , b"YES" ) # type: ignore
319+ gl .add_label (iqid_yes , b"YES" ) # type: ignore
313320
314321 # We may want to support something like this in the future, but not yet
315322 with pytest .raises (ValueError ):
316- gl .add_label (iqid , Label .UNSURE )
323+ gl .add_label (iqid_yes , Label .UNSURE )
317324
318325
319326def test_label_conversion_produces_strings ():
@@ -337,3 +344,60 @@ def test_submit_numpy_image(gl: Groundlight, detector: Detector):
337344 assert str (_image_query )
338345 assert isinstance (_image_query , ImageQuery )
339346 assert is_valid_display_result (_image_query .result )
347+
348+
349+ @pytest .mark .skipif (MISSING_PIL , reason = "Needs pillow" ) # type: ignore
350+ def test_detector_improvement (gl : Groundlight ):
351+ # test that we get confidence improvement after sending images in
352+ # Pass two of each type of image in
353+ import random
354+ import time
355+
356+ from PIL import Image , ImageEnhance
357+
358+ random .seed (2741 )
359+
360+ name = f"Test { datetime .utcnow ()} " # Need a unique name
361+ query = "Is there a dog?"
362+ detector = gl .create_detector (name = name , query = query )
363+
364+ def submit_noisy_image (image , label = None ):
365+ sharpness = ImageEnhance .Sharpness (image )
366+ noisy_image = sharpness .enhance (random .uniform (0.75 , 1.25 ))
367+ color = ImageEnhance .Color (noisy_image )
368+ noisy_image = color .enhance (random .uniform (0.75 , 1 ))
369+ contrast = ImageEnhance .Contrast (noisy_image )
370+ noisy_image = contrast .enhance (random .uniform (0.75 , 1 ))
371+ brightness = ImageEnhance .Brightness (noisy_image )
372+ noisy_image = brightness .enhance (random .uniform (0.75 , 1 ))
373+ img_query = gl .submit_image_query (detector = detector .id , image = noisy_image , wait = 0 )
374+ if label is not None :
375+ gl .add_label (img_query , label )
376+ return img_query
377+
378+ dog = Image .open ("test/assets/dog.jpeg" )
379+ cat = Image .open ("test/assets/cat.jpeg" )
380+
381+ submit_noisy_image (dog , "YES" )
382+ submit_noisy_image (dog , "YES" )
383+ submit_noisy_image (cat , "NO" )
384+ submit_noisy_image (cat , "NO" )
385+
386+ # wait to give enough time to train
387+ wait_period = 30 # seconds
388+ num_wait_periods = 4 # 2 minutes total
389+ for _ in range (num_wait_periods ):
390+ time .sleep (wait_period )
391+ new_dog_query = submit_noisy_image (dog )
392+ new_cat_query = submit_noisy_image (cat )
393+ if new_cat_query .result .confidence < 0.6 or new_cat_query .result .label == "YES" :
394+ # If the new query is not confident enough, we'll try again
395+ continue
396+ elif new_dog_query .result .confidence < 0.6 or new_dog_query .result .label == "NO" :
397+ # If the new query is not confident enough, we'll try again
398+ continue
399+ else :
400+ assert True
401+ return
402+
403+ assert False , "The detector performance has not improved after two minutes"
0 commit comments