def parse_params(): # class YOLO defines the default value, so suppress any default HERE parser = arg_params_yolo() parser.add_argument('-i', '--path_image', nargs='*', type=str, required=False, help='Images to be processed (sequence of paths)') parser.add_argument('-v', '--path_video', nargs='*', type=str, required=False, help='Video to be processed (sequence of paths)') arg_params = vars(parser.parse_args()) for k_name in ('path_image', 'path_video'): # if there is only single path still make it as a list if k_name in arg_params and not isinstance(arg_params[k_name], (list, tuple)): arg_params[k_name] = [arg_params[k_name]] # Update paths for k in (k for k in arg_params if 'path' in k): if k in ('path_image', 'path_video'): arg_params[k] = [update_path(path_) for path_ in arg_params[k]] elif arg_params[k]: arg_params[k] = update_path(arg_params[k]) assert os.path.exists( arg_params[k]), 'missing (%s): %s' % (k, arg_params[k]) logging.debug('PARAMETERS: \n %s', repr(arg_params)) return arg_params
def parse_arguments(): parser = argparse.ArgumentParser(description='Darknet To Keras Converter.') parser.add_argument('--config_path', type=str, required=True, help='Path to Darknet cfg file.') parser.add_argument('--weights_path', type=str, required=True, help='Path to Darknet weights file.') parser.add_argument('--output_path', type=str, required=True, help='Path to output Keras model file.') parser.add_argument('-p', '--plot_model', action='store_true', help='Plot generated Keras model and save as image.') parser.add_argument( '-w', '--weights_only', action='store_true', help='Save as Keras weights file instead of model file.') arg_params = vars(parser.parse_args()) for k in (k for k in arg_params if 'path' in k and 'output' not in k): arg_params[k] = update_path(arg_params[k]) assert os.path.exists( arg_params[k]), 'missing (%s): %s' % (k, arg_params[k]) logging.debug('PARAMETERS: \n %s', repr(arg_params)) return arg_params
def __init__(self, weights_path, anchors_path, classes_path, model_image_size=(None, None), score=0.3, iou=0.45, nb_gpu=1, gpu_frac=None, **kwargs): """ :param str weights_path: path to loaded model weights, e.g. 'model_data/tiny-yolo.h5' :param str anchors_path: path to loaded model anchors, e.g. 'model_data/tiny-yolo_anchors.csv' :param str classes_path: path to loaded trained classes, e.g. 'model_data/coco_classes.txt' :param float score: confidence score :param float iou: :param tuple(int,int) model_image_size: e.g. for tiny (416, 416) :param int nb_gpu: :param float gpu_frac: fraction of GPU memory to reserve, None for automatic :param kwargs: """ self.__dict__.update(kwargs) # and update with user overrides self.weights_path = update_path(weights_path) self.anchors_path = update_path(anchors_path) self.classes_path = update_path(classes_path) self.score = score self.iou = iou self.model_image_size = model_image_size self.nb_gpu = nb_gpu if not self.nb_gpu: # disable all GPUs os.environ["CUDA_VISIBLE_DEVICES"] = "-1" self.class_names = get_class_names(self.classes_path) self.anchors = get_anchors(self.anchors_path) self._open_session(gpu_frac) self.boxes, self.scores, self.classes = self._create_model() self._generate_class_colors()
def parse_arguments(): parser = argparse.ArgumentParser(description='Annotation Converter (COCO).') parser.add_argument('--path_annot', type=str, required=True, help='Path to annotation for COCO dataset.') parser.add_argument('--path_images', type=str, required=True, help='Path to images of COCO dataset.') parser.add_argument('--path_output', type=str, required=False, default='.', help='Path to output folder.') arg_params = vars(parser.parse_args()) for k in (k for k in arg_params if 'path' in k): arg_params[k] = update_path(arg_params[k]) assert os.path.exists(arg_params[k]), 'missing (%s): %s' % (k, arg_params[k]) logging.info('PARAMETERS: \n%s', '\n'.join(['"%s": \t\t %r' % (k, arg_params[k]) for k in arg_params])) return arg_params
def parse_arguments(): parser = argparse.ArgumentParser(description='Annotation Converter (VOC).') parser.add_argument('--path_dataset', type=str, required=True, help='Path to VOC dataset.') parser.add_argument('--sets', type=str, required=False, nargs='+', help='List of sets in dataset.', default=('2007,train', '2007,val', '2007,test')) parser.add_argument('--path_output', type=str, required=False, default='.', help='Path to output folder.') parser.add_argument('--classes', type=str, required=False, default=None, nargs='*', help='Use only following classes.') arg_params = vars(parser.parse_args()) for k in (k for k in arg_params if 'path' in k): arg_params[k] = update_path(arg_params[k]) assert os.path.exists(arg_params[k]), 'missing (%s): %s' % (k, arg_params[k]) logging.info('PARAMETERS: \n%s', '\n'.join(['"%s": \t\t %r' % (k, arg_params[k]) for k in arg_params])) return arg_params
def predict_image(yolo, path_image, path_output=None): path_image = update_path(path_image) if not path_image: logging.debug('no image given') elif not os.path.isfile(path_image): logging.warning('missing image: %s', path_image) image = Image.open(path_image) image_pred, pred_items = yolo.detect_image(image) if path_output is None or not os.path.isdir(path_output): image_pred.show() else: name = os.path.splitext(os.path.basename(path_image))[0] path_out_img = os.path.join(path_output, name + VISUAL_EXT + '.jpg') path_out_csv = os.path.join(path_output, name + '.csv') logging.info('exporting image: "%s" and detection: "%s"', path_out_img, path_out_csv) image_pred.save(path_out_img) pd.DataFrame(pred_items).to_csv(path_out_csv)
def parse_params(): # class YOLO defines the default value, so suppress any default HERE parser = arg_params_yolo() parser.add_argument('--images', default=False, action='store_true', help='Image detection mode.') parser.add_argument('--videos', default=False, action='store_true', help='Video detection mode.') parser.add_argument('--stream', default=False, action='store_true', help='Detection from stream.') arg_params = vars(parser.parse_args()) for k in (k for k in arg_params if 'path' in k): arg_params[k] = update_path(arg_params[k]) assert os.path.exists( arg_params[k]), 'missing (%s): %s' % (k, arg_params[k]) logging.debug('PARAMETERS: \n %s', repr(arg_params)) return arg_params
def parse_params(): # class YOLO defines the default value, so suppress any default HERE parser = arg_params_yolo() parser.add_argument('-d', '--path_dataset', type=str, required=True, help='path to the train source - dataset,' ' with single taining instance per line') parser.add_argument( '--path_config', type=str, required=False, help='path to the train configuration, using YAML format') arg_params = vars(parser.parse_args()) for k in (k for k in arg_params if 'path' in k): if not arg_params[k]: continue arg_params[k] = update_path(arg_params[k]) assert os.path.exists( arg_params[k]), 'missing (%s): %s' % (k, arg_params[k]) logging.debug('PARAMETERS: \n %s', repr(arg_params)) return arg_params
def create_model_bottleneck(input_shape, anchors, num_classes, freeze_body=2, weights_path=None, nb_gpu=1): """create the training model""" # K.clear_session() # get a new session image_input = Input(shape=(None, None, 3)) h, w = input_shape num_anchors = len(anchors) y_true = [ Input(shape=(h // { 0: 32, 1: 16, 2: 8 }[l], w // { 0: 32, 1: 16, 2: 8 }[l], num_anchors // 3, num_classes + 5)) for l in range(3) ] _LOSS_ARGUMENTS = { 'anchors': anchors, 'num_classes': num_classes, 'ignore_thresh': 0.5 } if not nb_gpu: # disable all GPUs os.environ["CUDA_VISIBLE_DEVICES"] = "-1" model_body = yolo_body_full(image_input, num_anchors // 3, num_classes) logging.info('Create YOLOv3 model with %i anchors and %i classes.', num_anchors, num_classes) if weights_path is not None: weights_path = update_path(weights_path) if os.path.isfile(weights_path): logging.warning('missing weights: %s', weights_path) else: logging.info('Load weights %s.', weights_path) model_body.load_weights(weights_path, by_name=True, skip_mismatch=True) if freeze_body in [1, 2]: # Freeze darknet53 body or freeze all but 3 output layers. num = (185, len(model_body.layers) - 3)[freeze_body - 1] for i in range(num): model_body.layers[i].trainable = False logging.info('Freeze the first %i layers of total %i layers.', num, len(model_body.layers)) # get output of second last layers and create bottleneck model of it out1 = model_body.layers[246].output out2 = model_body.layers[247].output out3 = model_body.layers[248].output model_bottleneck = Model([model_body.input, *y_true], [out1, out2, out3]) # create last layer model of last layers from yolo model in0 = Input(shape=model_bottleneck.output[0].shape[1:].as_list()) in1 = Input(shape=model_bottleneck.output[1].shape[1:].as_list()) in2 = Input(shape=model_bottleneck.output[2].shape[1:].as_list()) last_out0 = model_body.layers[249](in0) last_out1 = model_body.layers[250](in1) last_out2 = model_body.layers[251](in2) model_last = Model(inputs=[in0, in1, in2], outputs=[last_out0, last_out1, last_out2]) fn_loss = Lambda(yolo_loss, output_shape=(1, ), name='yolo_loss', arguments=_LOSS_ARGUMENTS) model_loss_last = fn_loss([*model_last.output, *y_true]) last_layer_model = Model([in0, in1, in2, *y_true], model_loss_last) fn_loss = Lambda(yolo_loss, output_shape=(1, ), name='yolo_loss', arguments=_LOSS_ARGUMENTS) model_loss = fn_loss([*model_body.output, *y_true]) model = Model([model_body.input, *y_true], model_loss) if nb_gpu >= 2: model = multi_gpu_model(model, gpus=nb_gpu) model_bottleneck = multi_gpu_model(model_bottleneck, gpus=nb_gpu) return model, model_bottleneck, last_layer_model
class YOLO(object): """YOLO detector with tiny alternative Example ------- >>> # prepare EMPTY model since download and convert existing is a bit complicated >>> anchors = get_anchors(YOLO.get_defaults('anchors_path')) >>> classes = get_class_names(YOLO.get_defaults('classes_path')) >>> yolo_empty = yolo_body_tiny(Input(shape=(None, None, 3)), len(anchors) // 2, len(classes)) >>> path_model = os.path.join(update_path('model_data'), 'yolo_empty.h5') >>> yolo_empty.save(path_model) >>> # use the empty one, so no reasonable detections are expected >>> from yolo3.utils import image_open >>> yolo = YOLO(weights_path=path_model, ... anchors_path=YOLO.get_defaults('anchors_path'), ... classes_path=YOLO.get_defaults('classes_path'), ... model_image_size=YOLO.get_defaults('model_image_size')) >>> img = image_open(os.path.join(update_path('model_data'), 'bike-car-dog.jpg')) >>> yolo.detect_image(img) # doctest: +ELLIPSIS (<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=520x518 at ...>, [...]) """ _DEFAULT_PARAMS = { "weights_path": os.path.join(update_path('model_data'), 'tiny-yolo.h5'), "anchors_path": os.path.join(update_path('model_data'), 'tiny-yolo_anchors.csv'), "classes_path": os.path.join(update_path('model_data'), 'coco_classes.txt'), "score": 0.3, "iou": 0.45, "model_image_size": (416, 416), "nb_gpu": 1, } @classmethod def get_defaults(cls, name): if name not in cls._DEFAULT_PARAMS: logging.warning('Unrecognized attribute name "%s"', name) return cls._DEFAULT_PARAMS.get(name) def __init__(self, weights_path, anchors_path, classes_path, model_image_size=(None, None), score=0.3, iou=0.45, nb_gpu=1, gpu_frac=None, **kwargs): """ :param str weights_path: path to loaded model weights, e.g. 'model_data/tiny-yolo.h5' :param str anchors_path: path to loaded model anchors, e.g. 'model_data/tiny-yolo_anchors.csv' :param str classes_path: path to loaded trained classes, e.g. 'model_data/coco_classes.txt' :param float score: confidence score :param float iou: :param tuple(int,int) model_image_size: e.g. for tiny (416, 416) :param int nb_gpu: :param float gpu_frac: fraction of GPU memory to reserve, None for automatic :param kwargs: """ self.__dict__.update(kwargs) # and update with user overrides self.weights_path = update_path(weights_path) self.anchors_path = update_path(anchors_path) self.classes_path = update_path(classes_path) self.score = score self.iou = iou self.model_image_size = model_image_size self.nb_gpu = nb_gpu if not self.nb_gpu: # disable all GPUs os.environ["CUDA_VISIBLE_DEVICES"] = "-1" self.class_names = get_class_names(self.classes_path) self.anchors = get_anchors(self.anchors_path) self._open_session(gpu_frac) self.boxes, self.scores, self.classes = self._create_model() self._generate_class_colors() def _open_session(self, gpu_frac): if K.backend() == 'tensorflow': import tensorflow.compat.v1 as tf tf.disable_v2_behavior() from keras.backend.tensorflow_backend import set_session if tf.__version__[0] == '2': logging.debug( "Tensorflow version 2.X detected, enabling compatibility with version 1.X." ) import tensorflow.compat.v1 as tf tf.disable_v2_behavior() from tensorflow.python.keras.backend import set_session config = tf.ConfigProto(allow_soft_placement=True, log_device_placement=False) if self.nb_gpu > 0: logging.debug("Creating GPU model") config.gpu_options.force_gpu_compatible = True if gpu_frac is not None: config.gpu_options.per_process_gpu_memory_fraction = gpu_frac # Don't pre-allocate memory; allocate as-needed config.gpu_options.allow_growth = True else: logging.debug("Creating CPU model") config = tf.ConfigProto(device_count={'GPU': 0}) sess = tf.Session(config=config) set_session(sess) if tf.__version__[0] == '2': self.sess = tf.keras.backend.get_session() else: self.sess = K.get_session() def _create_model(self): # weights_path = update_path(self.weights_path) logging.debug('loading model from "%s"', self.weights_path) assert self.weights_path.endswith( '.h5'), 'Keras model or weights must be a .h5 file.' # Load model, or construct model and load weights. num_anchors = len(self.anchors) num_classes = len(self.class_names) try: self.yolo_model = load_model(self.weights_path, compile=False) except Exception: is_tiny_version = (num_anchors == 6) # default setting logging.exception('Loading weights from "%s"', self.weights_path) if is_tiny_version: self.yolo_model = yolo_body_tiny(Input(shape=(None, None, 3)), num_anchors // 2, num_classes) else: self.yolo_model = yolo_body_full(Input(shape=(None, None, 3)), num_anchors // 3, num_classes) # make sure model, anchors and classes match self.yolo_model.load_weights(self.weights_path, by_name=True, skip_mismatch=True) else: out_shape = self.yolo_model.layers[-1].output_shape[-1] ration_anchors = num_anchors / len( self.yolo_model.output) * (num_classes + 5) assert out_shape == ration_anchors, \ 'Mismatch between model and given anchor %r and class %r sizes' \ % (ration_anchors, out_shape) logging.info('loaded model, anchors (%i), and classes (%i) from %s', num_anchors, num_classes, self.weights_path) # Generate output tensor targets for filtered bounding boxes. self.input_image_shape = K.placeholder(shape=(2, )) if self.nb_gpu >= 2: self.yolo_model = multi_gpu_model(self.yolo_model, gpus=self.nb_gpu) boxes, scores, classes = yolo_eval(self.yolo_model.output, self.anchors, len(self.class_names), self.input_image_shape, score_threshold=self.score, iou_threshold=self.iou) return boxes, scores, classes def _generate_class_colors(self): """Generate colors for drawing bounding boxes.""" hsv_tuples = [(x / len(self.class_names), 1., 1.) for x in range(len(self.class_names))] self.colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples)) def _fn_colorr(x): return (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)) self.colors = list(map(_fn_colorr, self.colors)) np.random.seed(10101) # Fixed seed for consistent colors across runs. # Shuffle colors to decorrelate adjacent classes. np.random.shuffle(self.colors) np.random.seed(None) # Reset seed to default. def detect_image(self, image): start = time.time() if isinstance(self.model_image_size, (list, tuple, set)) and all(self.model_image_size): assert self.model_image_size[ 0] % 32 == 0, 'Multiples of 32 required' assert self.model_image_size[ 1] % 32 == 0, 'Multiples of 32 required' boxed_image = letterbox_image( image, tuple(reversed(self.model_image_size))) else: new_image_size = (image.width - (image.width % 32), image.height - (image.height % 32)) boxed_image = letterbox_image(image, new_image_size) image_data = np.array(boxed_image, dtype='float32') logging.debug('image shape: %s', repr(image_data.shape)) if image_data.max() > 1.5: image_data /= 255. image_data = np.expand_dims(image_data, 0) # Add batch dimension. out_boxes, out_scores, out_classes = self.sess.run( [self.boxes, self.scores, self.classes], feed_dict={ self.yolo_model.input: image_data, self.input_image_shape: [image.size[1], image.size[0]], K.learning_phase(): 0 }) end = time.time() logging.debug('Found %i boxes in %f sec.', len(out_boxes), (end - start)) thickness = (image.size[0] + image.size[1]) // 300 predicts = [] for i, c in reversed(list(enumerate(out_classes))): draw_bounding_box(image, self.class_names[c], out_boxes[i], out_scores[i], self.colors[c], thickness) pred = dict( zip(PREDICT_FIELDS, (int(c), self.class_names[c], float( out_scores[i]), *[int(x) for x in out_boxes[i]]))) predicts.append(pred) return image, predicts def detect_only(self, frame2): image = Image.fromarray(frame2) start = time.time() if isinstance(self.model_image_size, (list, tuple, set)) and all(self.model_image_size): assert self.model_image_size[ 0] % 32 == 0, 'Multiples of 32 required' assert self.model_image_size[ 1] % 32 == 0, 'Multiples of 32 required' boxed_image = letterbox_image( image, tuple(reversed(self.model_image_size))) else: new_image_size = (image.width - (image.width % 32), image.height - (image.height % 32)) boxed_image = letterbox_image(image, new_image_size) image_data = np.array(boxed_image, dtype='float32') logging.debug('image shape: %s', repr(image_data.shape)) if image_data.max() > 1.5: image_data /= 255. image_data = np.expand_dims(image_data, 0) # Add batch dimension. out_boxes, out_scores, out_classes = self.sess.run( [self.boxes, self.scores, self.classes], feed_dict={ self.yolo_model.input: image_data, self.input_image_shape: [image.size[1], image.size[0]], K.learning_phase(): 0 }) end = time.time() logging.debug('Found %i boxes in %f sec.', len(out_boxes), (end - start)) return out_boxes, out_scores, out_classes def _close_session(self): if self.sess is not None: self.sess.close() def __del__(self): self._close_session()
def predict_video(yolo, path_video, path_output=None, show_stream=False): try: path_video = int(path_video) except Exception: # not using web cam path_video = update_path(path_video) else: # using the (infinite) stream add option to terminate show_stream = True # Create a video capture object to read videos try: video = cv2.VideoCapture(path_video) except Exception: logging.warning('missing: %s', path_video) return if path_output is not None and os.path.isdir(path_output): video_fps = video.get(cv2.CAP_PROP_FPS) video_size = (int(video.get(cv2.CAP_PROP_FRAME_WIDTH)), int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))) name = os.path.splitext(os.path.basename(path_video))[0] \ if isinstance(path_video, str) else str(path_video) path_out = os.path.join(path_output, name + VISUAL_EXT + '.avi') logging.info('export video: %s', path_out) out_vid = cv2.VideoWriter(path_out, VIDEO_FORMAT, video_fps, video_size) frame_preds = [] path_json = os.path.join(path_output, name + '.json') else: out_vid, frame_preds, path_json = None, None, None while video.isOpened(): success, frame = video.read() if not success: logging.warning('video read status: %r', success) break image = Image.fromarray(frame) t_start = time.time() image_pred, pred_items = yolo.detect_image(image) frame = np.asarray(image_pred) fps = 'FPS: %f' % (1. / (time.time() - t_start)) cv2.putText(frame, text=fps, org=(3, 15), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=0.50, color=(255, 0, 0), thickness=2) if out_vid: out_vid.write(frame) frame_preds.append(pred_items) with open(path_json, 'w') as fp: json.dump(frame_preds, fp) if show_stream: cv2.imshow('YOLOv3', frame) if cv2.waitKey(1) & 0xFF == ord('q'): cv2.destroyWindow('YOLOv3') break if out_vid: out_vid.release() logging.info('exported predictions: %s', path_json)
def _main(config_path, weights_path, output_path, weights_only, plot_model): assert os.path.isfile(config_path), 'missing "%s"' % config_path assert os.path.isfile(weights_path), 'missing "%s"' % weights_path assert config_path.endswith('.cfg'), \ '"%s" is not a .cfg file' % os.path.basename(config_path) assert weights_path.endswith('.weights'), \ '"%s" is not a .weights file' % os.path.basename(config_path) output_dir = update_path(os.path.dirname(output_path)) assert os.path.isdir(output_dir), 'missing "%s"' % output_dir output_path = os.path.join(output_dir, os.path.basename(output_path)) assert output_path.endswith('.h5'), \ 'output path "%s" is not a .h5 file' % os.path.basename(output_path) # Load weights and config. logging.info('Loading weights: %s', weights_path) weights_file = open(weights_path, 'rb') major, minor, revision = np.ndarray(shape=(3, ), dtype='int32', buffer=weights_file.read(12)) if (major * 10 + minor) >= 2 and major < 1000 and minor < 1000: seen = np.ndarray(shape=(1, ), dtype='int64', buffer=weights_file.read(8)) else: seen = np.ndarray(shape=(1, ), dtype='int32', buffer=weights_file.read(4)) logging.info('Weights Header: %i.%i.%i %s', major, minor, revision, repr(seen.tolist())) logging.info('Parsing Darknet config: %s', config_path) unique_config_file = unique_config_sections(config_path) cfg_parser = configparser.ConfigParser() cfg_parser.read_file(unique_config_file) logging.info('Creating Keras model.') input_layer = Input(shape=(None, None, 3)) prev_layer = input_layer all_layers = [] weight_decay = float(cfg_parser['net_0']['decay'] ) if 'net_0' in cfg_parser.sections() else 5e-4 count = 0 out_index = [] for section in tqdm.tqdm(cfg_parser.sections()): logging.info('Parsing section "%s"', section) (all_layers, cfg_parser, section, prev_layer, weights_file, count, weight_decay, out_index) = parse_section(all_layers, cfg_parser, section, prev_layer, weights_file, count, weight_decay, out_index) # Create and save model. if len(out_index) == 0: out_index.append(len(all_layers) - 1) model = Model(inputs=input_layer, outputs=[all_layers[i] for i in out_index]) logging.info(model.summary(line_length=120)) if weights_only: model.save_weights(output_path) logging.info('Saved Keras weights to "%s"', output_path) else: model.save(output_path) logging.info('Saved Keras model to "%s"', output_path) # Check to see if all weights have been read. remaining_weights = len(weights_file.read()) / 4 weights_file.close() logging.info('Read %i of %i from Darknet weight.', count, count + remaining_weights) if remaining_weights > 0: logging.warning('there are %i unused weights', remaining_weights) if plot_model: path_img = '%s.png' % os.path.splitext(output_path)[0] plot(model, to_file=path_img, show_shapes=True) logging.info('Saved model plot to %s', path_img)