diff --git a/modulesettings.json b/modulesettings.json index a36af18..1861d26 100644 --- a/modulesettings.json +++ b/modulesettings.json @@ -25,7 +25,7 @@ "RuntimeLocation": "Local", // Can be Local, Shared or System "PostStartPauseSecs": 1, // Generally 1 if using GPU, 0 for CPU "Queue": "objectdetection_queue", // We make all Object detectors use the same queue. - "Parallelism": 16 // Should probably be TPU count * 2; I don't see harm in overprovisioning threads + "Parallelism": 2 // Should probably be TPU count * 2; I don't see harm in overprovisioning threads }, "ModelRequirements" : [{ diff --git a/more_examples.txt b/more_examples.txt index 59dda93..716e8a8 100644 --- a/more_examples.txt +++ b/more_examples.txt @@ -15,3 +15,4 @@ python3 examples/classify_image.py \ --model test_data/mobilenet_v2_1.0_224_inat_bird_quant_edgetpu.tflite \ --labels test_data/inat_bird_labels.txt \ --input test_data/parrot.jpg + diff --git a/objectdetection_coral_adapter.py b/objectdetection_coral_adapter.py index 380ba50..c1bc0de 100644 --- a/objectdetection_coral_adapter.py +++ b/objectdetection_coral_adapter.py @@ -179,13 +179,14 @@ def _do_detection(self, img: any, score_threshold: float): result = do_detect(opts, img, score_threshold) if not result['success']: + error = result["error"] if "error" in result else "Unable to perform detection" return { "success" : False, - "error" : result["error"] if "error" in result else "Unable to perform detection", + "error" : error, "inferenceMs" : result['inferenceMs'], "processMs" : int((time.perf_counter() - start_process_time) * 1000), "predictions" : [], - "message" : '', + "message" : error, "count" : 0 } diff --git a/objectdetection_coral_singletpu.py b/objectdetection_coral_singletpu.py index 762e11f..c07d04d 100644 --- a/objectdetection_coral_singletpu.py +++ b/objectdetection_coral_singletpu.py @@ -270,58 +270,64 @@ def do_detect(options: Options, img: Image, score_threshold: float = 0.5): std = 128 # args.input_std top_k = 1 - # Once in a while refresh the interpreter - (device, error) = periodic_check(options) + # The periodic (re)check/refresh of the interpreter, input setup, invoke and + # output extraction must all happen under the same lock: periodic_check can + # tear down and rebuild the shared `interpreter` (reset_detector/init_detect), + # and doing that concurrently with another thread's invoke()/get_objects() on + # the old interpreter corrupts its internal tensor buffers ("There is at least + # 1 reference to internal data in the interpreter..." from TF-Lite). + with inference_lock: + # Once in a while refresh the interpreter + (device, error) = periodic_check(options) - if not interpreter: - return { - "success" : False, - "error" : error, - "count" : 0, - "predictions" : [], - "inferenceMs" : 0 - } + if not interpreter: + return { + "success" : False, + "error" : error, + "count" : 0, + "predictions" : [], + "inferenceMs" : 0 + } - w,h = img.size - # print("Debug: Input(height, width): ", h, w) + w,h = img.size + # print("Debug: Input(height, width): ", h, w) - _, scale = common.set_resized_input( - interpreter, img.size, lambda size: img.resize(size, Image.Resampling.LANCZOS)) + _, scale = common.set_resized_input( + interpreter, img.size, lambda size: img.resize(size, Image.Resampling.LANCZOS)) - """ - size = common.input_size(interpreter) - resize_im = img.convert('RGB').resize(size, Image.ANTIALIAS) - - # numpy_image = np.array(img) - # input_im = cv2.cvtColor(numpy_image, cv2.COLOR_BGR2RGB) - # resize_im = cv2.resize(input_im, size) - - # Image data must go through two transforms before running inference: - # 1. normalization: f = (input - mean) / std - # 2. quantization: q = f / scale + zero_point - # The following code combines the two steps as such: - # q = (input - mean) / (std * scale) + zero_point - # However, if std * scale equals 1, and mean - zero_point equals 0, the input - # does not need any preprocessing (but in practice, even if the results are - # very close to 1 and 0, it is probably okay to skip preprocessing for better - # efficiency; we use 1e-5 below instead of absolute zero). - - params = common.input_details(interpreter, 'quantization_parameters') - scale = params['scales'] - zero_point = params['zero_points'] - - if abs(scale * std - 1) < 1e-5 and abs(mean - zero_point) < 1e-5: - # Input data does not require preprocessing. - common.set_input(interpreter, resize_im) - else: - # Input data requires preprocessing - normalized_input = (np.asarray(resize_im) - mean) / (std * scale) + zero_point - np.clip(normalized_input, 0, 255, out=normalized_input) - common.set_input(interpreter, normalized_input.astype(np.uint8)) - """ + """ + size = common.input_size(interpreter) + resize_im = img.convert('RGB').resize(size, Image.ANTIALIAS) + + # numpy_image = np.array(img) + # input_im = cv2.cvtColor(numpy_image, cv2.COLOR_BGR2RGB) + # resize_im = cv2.resize(input_im, size) + + # Image data must go through two transforms before running inference: + # 1. normalization: f = (input - mean) / std + # 2. quantization: q = f / scale + zero_point + # The following code combines the two steps as such: + # q = (input - mean) / (std * scale) + zero_point + # However, if std * scale equals 1, and mean - zero_point equals 0, the input + # does not need any preprocessing (but in practice, even if the results are + # very close to 1 and 0, it is probably okay to skip preprocessing for better + # efficiency; we use 1e-5 below instead of absolute zero). + + params = common.input_details(interpreter, 'quantization_parameters') + scale = params['scales'] + zero_point = params['zero_points'] + + if abs(scale * std - 1) < 1e-5 and abs(mean - zero_point) < 1e-5: + # Input data does not require preprocessing. + common.set_input(interpreter, resize_im) + else: + # Input data requires preprocessing + normalized_input = (np.asarray(resize_im) - mean) / (std * scale) + zero_point + np.clip(normalized_input, 0, 255, out=normalized_input) + common.set_input(interpreter, normalized_input.astype(np.uint8)) + """ - # Run inference - with inference_lock: + # Run inference start_inference_time = time.perf_counter() try: interpreter.invoke()