|
| 1 | +"""Main script to run the object detection routine.""" |
| 2 | +import argparse |
| 3 | +import os |
| 4 | +import sys |
| 5 | +import shutil |
| 6 | +import time |
| 7 | +import datetime |
| 8 | + |
| 9 | +import numpy as np |
| 10 | +import cv2 |
| 11 | +from tflite_support.task import core |
| 12 | +from tflite_support.task import processor |
| 13 | +from tflite_support.task import vision |
| 14 | +import utils |
| 15 | + |
| 16 | +import constant |
| 17 | + |
| 18 | +# Define Image and Video Directory |
| 19 | +PARENT_DIR = r'/home/pi/examples/lite/examples/object_detection/raspberry_pi' |
| 20 | +IMAGE_DIR = r'/home/pi/examples/lite/examples/object_detection/raspberry_pi/image' |
| 21 | +VIDEO_DIR = r'/home/pi/examples/lite/examples/object_detection/raspberry_pi/video' |
| 22 | + |
| 23 | + |
| 24 | +def run( |
| 25 | + is_multiple: bool, |
| 26 | + all_images: bool, |
| 27 | + true_name: str, |
| 28 | + model: str, |
| 29 | + camera_id: int, |
| 30 | + width: int, |
| 31 | + height: int, |
| 32 | + num_threads: int, |
| 33 | + enable_edgetpu: bool, |
| 34 | + detection_threshold: float, |
| 35 | + vid_filename: str): |
| 36 | + """Continuously run inference on images acquired from the camera. |
| 37 | + Args: |
| 38 | + is_multiple: True/False whether there is multiple type of object. |
| 39 | + all_images: True/False whether to collect all images. |
| 40 | + true_name: true label for detected object name. |
| 41 | + model: Name of the TFLite object detection model. |
| 42 | + camera_id: The camera id to be passed to OpenCV. |
| 43 | + width: The width of the frame captured from the camera. |
| 44 | + height: The height of the frame captured from the camera. |
| 45 | + num_threads: The number of CPU threads to run the model. |
| 46 | + enable_edgetpu: True/False whether the model is a EdgeTPU model. |
| 47 | + detection_threshold: Minimum score to be detected by model. |
| 48 | + vid_filename: filename of output video. |
| 49 | + """ |
| 50 | + |
| 51 | + # Variables to calculate FPS |
| 52 | + counter, fps = 0, 0 |
| 53 | + start_time = time.time() |
| 54 | + |
| 55 | + # Variables to collect detection |
| 56 | + detection_counter = 1 |
| 57 | + frame_counter = 1 |
| 58 | + old_cmr_time = 0 |
| 59 | + cmr_time = 0 |
| 60 | + |
| 61 | + # Define flags |
| 62 | + print_message = False |
| 63 | + |
| 64 | + # Start capturing video input from the camera |
| 65 | + cap = cv2.VideoCapture(camera_id) |
| 66 | + cap.set(cv2.CAP_PROP_FRAME_WIDTH, width) |
| 67 | + cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height) |
| 68 | + |
| 69 | + # Define the codec and create VideoWriter Object |
| 70 | + fourcc = cv2.VideoWriter_fourcc(*'mp4v') |
| 71 | + out = cv2.VideoWriter(vid_filename, fourcc, 2.5, (width, height)) |
| 72 | + |
| 73 | + # Visualization parameters |
| 74 | + size_ratio = np.sqrt(width**2 + height**2)/np.sqrt(640**2 + 480**2) |
| 75 | + x_position = 5 # pixels |
| 76 | + y_position = 5 # pixels |
| 77 | + font_size = 2*size_ratio |
| 78 | + font_thickness = 3*int(size_ratio) |
| 79 | + fps_avg_frame_count = 10 |
| 80 | + |
| 81 | + # Initialize the object detection model |
| 82 | + base_options = core.BaseOptions( |
| 83 | + file_name=model, use_coral=enable_edgetpu, num_threads=num_threads) |
| 84 | + detection_options = processor.DetectionOptions( |
| 85 | + max_results=3, score_threshold=detection_threshold) |
| 86 | + options = vision.ObjectDetectorOptions( |
| 87 | + base_options=base_options, detection_options=detection_options) |
| 88 | + detector = vision.ObjectDetector.create_from_options(options) |
| 89 | + |
| 90 | + # Continuously capture images from the camera and run inference |
| 91 | + print("DETECTION STARTED!") |
| 92 | + print("") |
| 93 | + print(f"Detection Session: {detection_counter}") |
| 94 | + |
| 95 | + # If there is multiple type of object |
| 96 | + if is_multiple: |
| 97 | + # prompt user to input its true name/label |
| 98 | + true_name = input("True label/name of the object: ") |
| 99 | + |
| 100 | + while cap.isOpened(): |
| 101 | + success, image = cap.read() |
| 102 | + if not success: |
| 103 | + sys.exit( |
| 104 | + 'ERROR: Unable to read from webcam. Please verify your webcam settings.' |
| 105 | + ) |
| 106 | + |
| 107 | + counter += 1 |
| 108 | + image = cv2.flip(image, 1) |
| 109 | + |
| 110 | + # Convert the image from BGR to RGB as required by the TFLite model. |
| 111 | + rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) |
| 112 | + |
| 113 | + # Create a TensorImage object from the RGB image. |
| 114 | + input_tensor = vision.TensorImage.create_from_array(rgb_image) |
| 115 | + |
| 116 | + # Run object detection estimation using the model. |
| 117 | + detection_result = detector.detect(input_tensor) |
| 118 | + |
| 119 | + # If something detected on camera |
| 120 | + if (detection_result.detections and |
| 121 | + (time.time() - old_cmr_time) >= constant.LIMIT_CMR_TIME): |
| 122 | + # Record camera time |
| 123 | + cmr_time = time.time() |
| 124 | + # update old values |
| 125 | + old_cmr_time = cmr_time |
| 126 | + # Update flag |
| 127 | + print_message = True |
| 128 | + |
| 129 | + # Only print result once every detection session |
| 130 | + if ((time.time() > (old_cmr_time + constant.LIMIT_DET_TIME)) |
| 131 | + and (print_message)): |
| 132 | + # print messages |
| 133 | + print("Images of object has been collected.") |
| 134 | + print("") |
| 135 | + # Update counter |
| 136 | + detection_counter = detection_counter + 1 |
| 137 | + frame_counter = 1 |
| 138 | + print(f"Detection Session: {detection_counter}") |
| 139 | + |
| 140 | + # If there is multiple type of object |
| 141 | + if is_multiple: |
| 142 | + # prompt user to input its true name/label |
| 143 | + true_name = input("True label/name of the object: ") |
| 144 | + |
| 145 | + # Update flag |
| 146 | + print_message = False |
| 147 | + |
| 148 | + # Draw keypoints and edges on input image if detected |
| 149 | + if detection_result.detections: |
| 150 | + image = utils.visualize(image, detection_result, (width, height), |
| 151 | + utils.detect_color(detection_result)[2]) |
| 152 | + # If all images want to be collected |
| 153 | + if all_images: |
| 154 | + # Change directory and save image |
| 155 | + os.chdir(IMAGE_DIR) |
| 156 | + current_date = datetime.datetime.now() |
| 157 | + img_filename = ( |
| 158 | + current_date.strftime("%d%m%Y") + |
| 159 | + "_" + |
| 160 | + utils.categorize(detection_result)[2] + |
| 161 | + "_" + |
| 162 | + str(detection_counter) + |
| 163 | + "_" + |
| 164 | + str(frame_counter) + |
| 165 | + ".jpg") |
| 166 | + cv2.imwrite(img_filename, image) |
| 167 | + os.chdir(PARENT_DIR) |
| 168 | + # update counter |
| 169 | + frame_counter = frame_counter + 1 |
| 170 | + else: |
| 171 | + # Collect detection that is True Positive |
| 172 | + if utils.categorize(detection_result)[2] == true_name: |
| 173 | + # Change directory and save image |
| 174 | + os.chdir(IMAGE_DIR) |
| 175 | + current_date = datetime.datetime.now() |
| 176 | + img_filename = ( |
| 177 | + current_date.strftime("%d%m%Y") + |
| 178 | + "_" + |
| 179 | + true_name + |
| 180 | + "_" + |
| 181 | + str(detection_counter) + |
| 182 | + "_" + |
| 183 | + str(frame_counter) + |
| 184 | + ".jpg") |
| 185 | + cv2.imwrite(img_filename, image) |
| 186 | + os.chdir(PARENT_DIR) |
| 187 | + # update counter |
| 188 | + frame_counter = frame_counter + 1 |
| 189 | + else: |
| 190 | + image = utils.visualize(image, detection_result) |
| 191 | + |
| 192 | + # Calculate the FPS |
| 193 | + if counter % fps_avg_frame_count == 0: |
| 194 | + end_time = time.time() |
| 195 | + fps = fps_avg_frame_count / (end_time - start_time) |
| 196 | + start_time = time.time() |
| 197 | + |
| 198 | + # Show the FPS |
| 199 | + fps_text = f"FPS = {round(fps, 1)}" |
| 200 | + text_location = (x_position, y_position) |
| 201 | + utils.draw_text(img=image, text=fps_text, font_size=font_size, |
| 202 | + font_thickness=int(font_thickness), pos=text_location) |
| 203 | + |
| 204 | + # Write Image to Videos |
| 205 | + out.write(image) |
| 206 | + |
| 207 | + # Stop the program if the ESC key is pressed. |
| 208 | + if cv2.waitKey(1) == 27: |
| 209 | + break |
| 210 | + cv2.imshow('object_detector', image) |
| 211 | + |
| 212 | + # Release everything if job is finished |
| 213 | + cap.release() |
| 214 | + out.release() |
| 215 | + cv2.destroyAllWindows() |
| 216 | + print("") |
| 217 | + print("DETECTION STOPPED!") |
| 218 | + |
| 219 | + |
| 220 | +def move_video(src, dst): |
| 221 | + """Move video file to another directory. |
| 222 | + Args: |
| 223 | + src: source path for video files |
| 224 | + dst: source path for video files |
| 225 | + """ |
| 226 | + shutil.move(src, dst) |
| 227 | + |
| 228 | + |
| 229 | +def main(): |
| 230 | + """Main function to run detection""" |
| 231 | + parser = argparse.ArgumentParser( |
| 232 | + formatter_class=argparse.ArgumentDefaultsHelpFormatter) |
| 233 | + parser.add_argument( |
| 234 | + '--multipleObject', |
| 235 | + help='Whether to detect single or multiple types of objects.', |
| 236 | + required=False, |
| 237 | + default=False) |
| 238 | + parser.add_argument( |
| 239 | + '--collectAll', |
| 240 | + help='Whether to collect all images or only the True Positive one.', |
| 241 | + required=False, |
| 242 | + default=False) |
| 243 | + parser.add_argument( |
| 244 | + '--trueObject', |
| 245 | + help='Name of the object to be detected if it is a single type.', |
| 246 | + required=False, |
| 247 | + type=str, |
| 248 | + default='yellow_duck') |
| 249 | + parser.add_argument( |
| 250 | + '--model', |
| 251 | + help='Path of the object detection model.', |
| 252 | + required=False, |
| 253 | + default='./model/frogducky2.tflite') |
| 254 | + parser.add_argument( |
| 255 | + '--cameraId', |
| 256 | + help='Id of camera.', |
| 257 | + required=False, |
| 258 | + type=int, |
| 259 | + default=0) |
| 260 | + parser.add_argument( |
| 261 | + '--frameWidth', |
| 262 | + help='Width of frame to capture from camera.', |
| 263 | + required=False, |
| 264 | + type=int, |
| 265 | + default=640) |
| 266 | + parser.add_argument( |
| 267 | + '--frameHeight', |
| 268 | + help='Height of frame to capture from camera.', |
| 269 | + required=False, |
| 270 | + type=int, |
| 271 | + default=480) |
| 272 | + parser.add_argument( |
| 273 | + '--numThreads', |
| 274 | + help='Number of CPU threads to run the model.', |
| 275 | + required=False, |
| 276 | + type=int, |
| 277 | + default=4) |
| 278 | + parser.add_argument( |
| 279 | + '--enableEdgeTPU', |
| 280 | + help='Whether to run the model on EdgeTPU.', |
| 281 | + action='store_true', |
| 282 | + required=False, |
| 283 | + default=False) |
| 284 | + parser.add_argument( |
| 285 | + '--detectionThreshold', |
| 286 | + help='Threshold score to be detected by model.', |
| 287 | + required=False, |
| 288 | + type=float, |
| 289 | + default=0.55) |
| 290 | + parser.add_argument( |
| 291 | + '--videoFilename', |
| 292 | + help='Name of the output video file with .mp4 extension', |
| 293 | + required=False, |
| 294 | + default='Deteksi_Objek.mp4') |
| 295 | + args = parser.parse_args() |
| 296 | + |
| 297 | + run(bool(args.multipleObject), |
| 298 | + bool(args.collectAll), |
| 299 | + str(args.trueObject), |
| 300 | + args.model, |
| 301 | + int(args.cameraId), |
| 302 | + args.frameWidth, |
| 303 | + args.frameHeight, |
| 304 | + int(args.numThreads), |
| 305 | + bool(args.enableEdgeTPU), |
| 306 | + args.detectionThreshold, |
| 307 | + str(args.videoFilename)) |
| 308 | + move_video(PARENT_DIR + '/' + str(args.videoFilename), |
| 309 | + VIDEO_DIR + '/' + str(args.videoFilename)) |
| 310 | + |
| 311 | + |
| 312 | +if __name__ == '__main__': |
| 313 | + main() |
0 commit comments