def generate_new_anchors(self, new_anchors_conf):
        """
        Create new anchors according to given configuration.
        Args:
            new_anchors_conf: A dictionary containing the following keys:
                - anchors_no
                and one of the following:
                    - relative_labels
                    - from_xml
                    - adjusted_frame

        Returns:
            None
        """
        anchor_no = new_anchors_conf.get('anchor_no')
        if not anchor_no:
            raise ValueError(f'No "anchor_no" found in new_anchors_conf')
        labels_frame = self.get_adjusted_labels(new_anchors_conf)
        relative_dims = np.array(
            list(
                zip(
                    labels_frame['Relative Width'],
                    labels_frame['Relative Height'],
                )))
        centroids, _ = k_means(relative_dims, anchor_no, frame=labels_frame)
        self.anchors = (
            generate_anchors(self.image_width, self.image_height, centroids) /
            self.input_shape[0])
        default_logger.info('Changed default anchors to generated ones')
 def create_new_dataset(self, new_dataset_conf):
     """
     Build new dataset and respective TFRecord(s).
     Args:
         new_dataset_conf: A dictionary containing the following keys:
             one of the following:
                 - relative_labels
                 - from_xml
                 - adjusted_frame
                 - coordinate_labels(optional)
             and:
                 - sequences
                 - workers(optional, defaults to 32)
                 - batch_size(optional, defaults to 64)
                 - new_size(optional, defaults to None)
     Returns:
         None
     """
     default_logger.info(f'Generating new dataset ...')
     test_size = new_dataset_conf.get('test_size')
     labels_frame = self.generate_new_frame(new_dataset_conf)
     save_tfr(
         labels_frame,
         os.path.join('..', 'Data', 'TFRecords'),
         new_dataset_conf['dataset_name'],
         test_size,
         self,
     )
Example #3
0
 def create_new_dataset(self, new_dataset_conf):
     """
     Create a new TFRecord dataset.
     Args:
         new_dataset_conf: A dictionary containing the following keys:
             - dataset_name(required) str representing a name for the dataset
             - test_size(optional) ex: 0.1
             - augmentation(optional) True or False
             - sequences(required if augmentation is True)
             - aug_workers(optional if augmentation is True) defaults to 32.
             - aug_batch_size(optional if augmentation is True) defaults to 64.
             And one of the following is required:
                 - relative_labels: Path to csv file with the following columns:
                 ['Image', 'Object Name', 'Object Index', 'bx', 'by', 'bw', 'bh']
                 - coordinate_labels: Path to csv file with the following columns:
                 ['Image Path', 'Object Name', 'Image Width', 'Image Height',
                 'X_min', 'Y_min', 'X_max', 'Y_max', 'Relative Width', 'Relative Height',
                 'Object ID']
                 - from_xml: True or False to parse from XML Labels folder.
     """
     default_logger.info(f'Generating new dataset ...')
     test_size = new_dataset_conf.get('test_size')
     labels_frame = self.generate_new_frame(new_dataset_conf)
     save_tfr(
         labels_frame,
         os.path.join('..', 'Data', 'TFRecords'),
         new_dataset_conf['dataset_name'],
         test_size,
         self,
     )
Example #4
0
    def relative_to_coordinates(self, out_file=None):
        """
        Convert relative coordinates in self.mapping
        to coordinates.
        Args:
            out_file: path to new converted csv.

        Returns:
            pandas DataFrame with the new coordinates.
        """
        items_to_save = []
        for index, data in self.mapping.iterrows():
            image_name, object_name, object_index, bx, by, bw, bh = data
            x1, y1, x2, y2 = ratios_to_coordinates(bx, by, bw, bh,
                                                   self.image_width,
                                                   self.image_height)
            items_to_save.append([
                image_name,
                x1,
                y1,
                x2,
                y2,
                object_name,
                object_index,
                bx,
                by,
                bw,
                bh,
            ])
        new_data = pd.DataFrame(
            items_to_save,
            columns=[
                'image',
                'x1',
                'y1',
                'x2',
                'y2',
                'object_type',
                'object_id',
                'bx',
                'by',
                'bw',
                'bh',
            ],
        )
        new_data[['x1', 'y1', 'x2', 'y2']] = new_data[['x1', 'y1', 'x2',
                                                       'y2']].astype('int64')
        if out_file:
            new_data.to_csv(out_file, index=False)
        default_logger.info(
            f'Converted labels in {self.labels_file} to coordinates')
        return new_data
Example #5
0
    def create_models(self):
        """
        Create training and inference yolo models.

        Returns:
            training, inference models
        """
        input_initial = self.apply_func(Input, shape=self.input_shape)
        cfg_out = self.read_dark_net_cfg()
        cfg_parser = configparser.ConfigParser()
        cfg_parser.read_file(cfg_out)
        self.output_indices = []
        self.previous_layer = input_initial
        for section in cfg_parser.sections():
            self.create_section(section, cfg_parser)
        if len(self.output_indices) == 0:
            self.output_indices.append(len(self.model_layers) - 1)
        self.output_layers.extend(
            [self.model_layers[i] for i in self.output_indices])
        if '4' in self.model_configuration:
            self.output_layers.reverse()
        self.training_model = Model(inputs=input_initial,
                                    outputs=self.output_layers)
        output_0, output_1, output_2 = self.output_layers
        boxes_0 = self.apply_func(
            Lambda,
            output_0,
            lambda item: get_boxes(item, self.anchors[self.masks[0]], self.
                                   classes),
        )
        boxes_1 = self.apply_func(
            Lambda,
            output_1,
            lambda item: get_boxes(item, self.anchors[self.masks[1]], self.
                                   classes),
        )
        boxes_2 = self.apply_func(
            Lambda,
            output_2,
            lambda item: get_boxes(item, self.anchors[self.masks[2]], self.
                                   classes),
        )
        outputs = self.apply_func(
            Lambda,
            (boxes_0[:3], boxes_1[:3], boxes_2[:3]),
            lambda item: self.get_nms(item),
        )
        self.inference_model = Model(input_initial,
                                     outputs,
                                     name='inference_model')
        default_logger.info('Training and inference models created')
        return self.training_model, self.inference_model
def parse_voc_folder(folder_path, voc_conf):
    """
    Parse a folder containing voc xml annotation files.
    Args:
        folder_path: Folder containing voc xml annotation files.
        voc_conf: Path to voc json configuration file.

    Returns:
        pandas DataFrame with the annotations.
    """
    assert os.path.exists(folder_path)
    cache_path = os.path.join('..', 'Output', 'Data', 'parsed_from_xml.csv')
    if os.path.exists(cache_path):
        frame = pd.read_csv(cache_path)
        print(
            f'Labels retrieved from cache:'
            f'\n{frame["Object Name"].value_counts()}'
        )
        return frame
    image_data = []
    frame_columns = [
        'Image Path',
        'Object Name',
        'Image Width',
        'Image Height',
        'X_min',
        'Y_min',
        'X_max',
        'Y_max',
    ]
    xml_files = [
        file_name
        for file_name in os.listdir(folder_path)
        if file_name.endswith('.xml')
    ]
    for file_name in xml_files:
        annotation_path = os.path.join(folder_path, file_name)
        image_labels = parse_voc_file(annotation_path, voc_conf)
        image_data.extend(image_labels)
    frame = pd.DataFrame(image_data, columns=frame_columns)
    classes = frame['Object Name'].drop_duplicates()
    default_logger.info(f'Read {len(xml_files)} xml files')
    default_logger.info(
        f'Received {len(frame)} labels containing ' f'{len(classes)} classes'
    )
    if frame.empty:
        raise ValueError(
            f'No labels were found in {os.path.abspath(folder_path)}'
        )
    frame = adjust_frame(frame, 'parsed_from_xml.csv')
    return frame
    def clear_outputs():
        """
        Clear Output folder.

        Returns:
            None
        """
        for file_name in os.listdir(os.path.join('..', 'Output')):
            if not file_name.startswith('.'):
                full_path = (Path(os.path.join(
                    '..', 'Output', file_name)).absolute().resolve())
                if os.path.isdir(full_path):
                    shutil.rmtree(full_path)
                else:
                    os.remove(full_path)
                default_logger.info(f'Deleted old output: {full_path}')
Example #8
0
    def detect_video(self,
                     video,
                     trained_weights,
                     codec='mp4v',
                     display=False):
        """
        Perform detection on a video, stream(optional) and save results.
        Args:
            video: Path to video file.
            trained_weights: .tf or .weights file
            codec: str ex: mp4v
            display: If True, detections will be displayed during
                the detection operation.

        Returns:
            None
        """
        self.create_models()
        self.load_weights(trained_weights)
        vid = cv2.VideoCapture(video)
        length = int(vid.get(cv2.CAP_PROP_FRAME_COUNT))
        width = int(vid.get(cv2.CAP_PROP_FRAME_WIDTH))
        height = int(vid.get(cv2.CAP_PROP_FRAME_HEIGHT))
        fps = int(vid.get(cv2.CAP_PROP_FPS))
        current = 1
        codec = cv2.VideoWriter_fourcc(*codec)
        out = os.path.join('..', 'Output', 'Detections', 'predicted_vid.mp4')
        writer = cv2.VideoWriter(out, codec, fps, (width, height))
        while vid.isOpened():
            _, frame = vid.read()
            detections, adjusted = self.detect_image(frame, f'frame_{current}')
            self.draw_on_image(adjusted, detections)
            writer.write(adjusted)
            completed = f'{(current / length) * 100}% completed'
            print(
                f'\rframe {current}/{length}\tdetections: '
                f'{len(detections)}\tcompleted: {completed}',
                end='',
            )
            if display:
                cv2.imshow(f'frame {current}', adjusted)
            current += 1
            if cv2.waitKey(1) == ord('q'):
                default_logger.info(
                    f'Video detection stopped by user {current}/{length} '
                    f'frames completed')
                break
Example #9
0
    def predict_photos(self,
                       photos,
                       trained_weights,
                       batch_size=32,
                       workers=16):
        """
        Predict a list of image paths and save results to output folder.
        Args:
            photos: A list of image paths.
            trained_weights: .weights or .tf file
            batch_size: Prediction batch size.
            workers: Parallel predictions.

        Returns:
            None
        """
        self.create_models()
        self.load_weights(trained_weights)
        to_predict = photos.copy()
        with ThreadPoolExecutor(max_workers=workers) as executor:
            predicted = 1
            done = []
            total_photos = len(photos)
            while to_predict:
                current_batch = [
                    to_predict.pop() for _ in range(batch_size) if to_predict
                ]
                future_predictions = {
                    executor.submit(self.predict_on_image, image): image
                    for image in current_batch
                }
                for future_prediction in as_completed(future_predictions):
                    future_prediction.result()
                    completed = f'{predicted}/{total_photos}'
                    current_image = future_predictions[future_prediction]
                    percent = (predicted / total_photos) * 100
                    print(
                        f'\rpredicting {os.path.basename(current_image)} '
                        f'{completed}\t{percent}% completed',
                        end='',
                    )
                    predicted += 1
                    done.append(current_image)
            for item in done:
                default_logger.info(f'Saved prediction: {item}')
Example #10
0
def save_fig(title, save_figures=True):
    """
    Save generated figures to Output folder.
    Args:
        title: Figure title also the image to save file name.
        save_figures: If True, figure will be saved

    Returns:
        None
    """
    if save_figures:
        saving_path = str(
            Path(os.path.join('..', 'Output', 'Plots',
                              f'{title}.png')).absolute().resolve())
        if os.path.exists(saving_path):
            return
        plt.savefig(saving_path)
        default_logger.info(f'Saved figure {saving_path}')
        plt.close()
def save_tfr(data, output_folder, dataset_name, test_size=None, trainer=None):
    """
    Transform and save dataset into TFRecord format.
    Args:
        data: pandas DataFrame with adjusted labels.
        output_folder: Path to folder where TFRecord(s) will be saved.
        dataset_name: str name of the dataset.
        test_size: relative test subset size.
        trainer: Main.Trainer object

    Returns:
        None
    """
    data['Object Name'] = data['Object Name'].apply(
        lambda x: x.encode('utf-8')
    )
    data['Object ID'] = data['Object ID'].astype(int)
    data[data.dtypes[data.dtypes == 'int64'].index] = data[
        data.dtypes[data.dtypes == 'int64'].index
    ].apply(abs)
    data.to_csv(
        os.path.join('..', 'Data', 'TFRecords', 'full_data.csv'), index=False
    )
    groups = np.array(data.groupby('Image Path'))
    np.random.shuffle(groups)
    if test_size:
        assert (
            0 < test_size < 1
        ), f'test_size must be 0 < test_size < 1 and {test_size} is given'
        separation_index = int((1 - test_size) * len(groups))
        training_set = groups[:separation_index]
        test_set = groups[separation_index:]
        training_frame = pd.concat([item[1] for item in training_set])
        test_frame = pd.concat([item[1] for item in test_set])
        training_frame.to_csv(
            os.path.join('..', 'Data', 'TFRecords', 'training_data.csv'),
            index=False,
        )
        test_frame.to_csv(
            os.path.join('..', 'Data', 'TFRecords', 'test_data.csv'),
            index=False,
        )
        training_path = str(
            Path(os.path.join(output_folder, f'{dataset_name}_train.tfrecord'))
            .absolute()
            .resolve()
        )
        test_path = str(
            Path(os.path.join(output_folder, f'{dataset_name}_test.tfrecord'))
            .absolute()
            .resolve()
        )
        write_tf_record(training_path, training_set, data, trainer)
        default_logger.info(f'Saved training TFRecord: {training_path}')
        write_tf_record(test_path, test_set, data, trainer)
        default_logger.info(f'Saved validation TFRecord: {test_path}')
        return
    tf_record_path = os.path.join(output_folder, f'{dataset_name}.tfrecord')
    write_tf_record(tf_record_path, groups, data, trainer)
    default_logger.info(f'Saved TFRecord {tf_record_path}')
Example #12
0
 def create_sequences(self, sequences):
     """
     Create sequences for imgaug.augmenters.Sequential().
     Args:
         sequences: A list of dictionaries with each dictionary
         containing the following:
         -sequence_group: str, one of self.augmentation_map keys including:
             ['meta', 'arithmetic', 'artistic', 'blend', 'gaussian_blur', 'color',
             'contrast', 'convolution', 'edges', 'flip', 'geometric', 'corrupt_like',
             'pi_like', 'pooling', 'segmentation', 'size']
         -no: augmentation number ('no' key in self.augmentation_map > chosen sequence_group)
             Example:
                 sequences = (
                 [[{'sequence_group': 'meta', 'no': 5},
                   {'sequence_group': 'arithmetic', 'no': 3}],
                  [{'sequence_group': 'arithmetic', 'no': 2}]]
                 )
     Returns:
         The list of augmentation sequences that will be applied over images.
     """
     total_target = (len(sequences) * len(self.image_paths)) + len(
         self.image_paths)
     default_logger.info(f'Total images(old + augmented): {total_target}')
     for group in sequences:
         some_ofs = [
             self.augmentation_map[item['sequence_group']][item['no'] - 1]
             for item in group[0]
         ]
         one_ofs = [
             self.augmentation_map[item['sequence_group']][item['no'] - 1]
             for item in group[1]
         ]
         some_of_aug = [item['augmentation'] for item in some_ofs]
         one_of_aug = [item['augmentation'] for item in one_ofs]
         some_of_seq = iaa.SomeOf((0, 5),
                                  [eval(item) for item in some_of_aug])
         one_of_seq = iaa.OneOf([eval(item) for item in one_of_aug])
         self.augmentation_sequences.append(
             iaa.Sequential([some_of_seq, one_of_seq], random_order=True))
     return self.augmentation_sequences
Example #13
0
def k_means(relative_sizes, k, distance_func=np.median, frame=None):
    """
    Calculate optimal anchor relative sizes.
    Args:
        relative_sizes: 2D array of relative box sizes.
        k: int, number of clusters.
        distance_func: function to calculate distance.
        frame: pandas DataFrame with the annotation data(for visualization purposes).

    Returns:
        Optimal relative sizes.
    """
    box_number = relative_sizes.shape[0]
    last_nearest = np.zeros((box_number,))
    centroids = relative_sizes[np.random.randint(0, box_number, k)]
    old_distances = np.zeros((relative_sizes.shape[0], k))
    iteration = 0
    while True:
        distances = 1 - iou(relative_sizes, centroids, k)
        print(
            f'Iteration: {iteration} Loss: '
            f'{np.sum(np.abs(distances - old_distances))}'
        )
        old_distances = distances.copy()
        iteration += 1
        current_nearest = np.argmin(distances, axis=1)
        if (last_nearest == current_nearest).all():
            default_logger.info(
                f'Generated {len(centroids)} anchors in '
                f'{iteration} iterations'
            )
            return centroids, frame
        for anchor in range(k):
            centroids[anchor] = distance_func(
                relative_sizes[current_nearest == anchor], axis=0
            )
        last_nearest = current_nearest
def read_tfr(
    tf_record_file,
    classes_file,
    feature_map,
    max_boxes,
    classes_delimiter='\n',
    new_size=None,
    get_features=False,
):
    """
    Read and load dataset from TFRecord file.
    Args:
        tf_record_file: Path to TFRecord file.
        classes_file: file containing classes.
        feature_map: A dictionary of feature names mapped to tf.io objects.
        max_boxes: Maximum number of boxes per image.
        classes_delimiter: delimiter in classes_file.
        new_size: w, h new image size
        get_features: If True, features will be returned.

    Returns:
        MapDataset object.
    """
    tf_record_file = str(Path(tf_record_file).absolute().resolve())
    text_init = tf.lookup.TextFileInitializer(
        classes_file, tf.string, 0, tf.int64, -1, delimiter=classes_delimiter
    )
    class_table = tf.lookup.StaticHashTable(text_init, -1)
    files = tf.data.Dataset.list_files(tf_record_file)
    dataset = files.flat_map(tf.data.TFRecordDataset)
    default_logger.info(f'Read TFRecord: {tf_record_file}')
    return dataset.map(
        lambda x: read_example(
            x, feature_map, class_table, max_boxes, new_size, get_features
        )
    )
    def train(
        self,
        epochs,
        batch_size,
        learning_rate,
        new_anchors_conf=None,
        new_dataset_conf=None,
        dataset_name=None,
        weights=None,
        evaluate=True,
        merge_evaluation=True,
        evaluation_workers=8,
        shuffle_buffer=512,
        min_overlaps=None,
        display_stats=True,
        plot_stats=True,
        save_figs=True,
        clear_outputs=False,
        n_epoch_eval=None,
    ):
        """
        Train on the dataset.
        Args:
            epochs: Number of training epochs.
            batch_size: Training batch size.
            learning_rate: non-negative value.
            new_anchors_conf: A dictionary containing anchor generation configuration.
            new_dataset_conf: A dictionary containing dataset generation configuration.
            dataset_name: Name of the dataset for model checkpoints.
            weights: .tf or .weights file
            evaluate: If False, the trained model will not be evaluated after training.
            merge_evaluation: If False, training and validation maps will
                be calculated separately.
            evaluation_workers: Parallel predictions.
            shuffle_buffer: Buffer size for shuffling datasets.
            min_overlaps: a float value between 0 and 1, or a dictionary
                containing each class in self.class_names mapped to its
                minimum overlap
            display_stats: If True and evaluate=True, evaluation statistics will be displayed.
            plot_stats: If True, Precision and recall curves as well as
                comparative bar charts will be plotted
            save_figs: If True and plot_stats=True, figures will be saved
            clear_outputs: If True, old outputs will be cleared
            n_epoch_eval: Conduct evaluation every n epoch.

        Returns:
            history object, pandas DataFrame with statistics, mAP score.
        """
        min_overlaps = min_overlaps or 0.5
        if clear_outputs:
            self.clear_outputs()
        activate_gpu()
        default_logger.info(f'Starting training ...')
        if new_anchors_conf:
            default_logger.info(f'Generating new anchors ...')
            self.generate_new_anchors(new_anchors_conf)
        self.create_models()
        if weights:
            self.load_weights(weights)
        if new_dataset_conf:
            self.create_new_dataset(new_dataset_conf)
        self.check_tf_records()
        training_dataset = self.initialize_dataset(self.train_tf_record,
                                                   batch_size, shuffle_buffer)
        valid_dataset = self.initialize_dataset(self.valid_tf_record,
                                                batch_size, shuffle_buffer)
        optimizer = tf.keras.optimizers.Adam(learning_rate)
        loss = [
            calculate_loss(self.anchors[mask], self.classes,
                           self.iou_threshold) for mask in self.masks
        ]
        self.training_model.compile(optimizer=optimizer, loss=loss)
        checkpoint_name = os.path.join(
            '..', 'Models', f'{dataset_name or "trained"}_model.tf')
        callbacks = self.create_callbacks(checkpoint_name)
        if n_epoch_eval:
            mid_train_eval = MidTrainingEvaluator(
                self.input_shape,
                self.classes_file,
                self.image_width,
                self.image_height,
                self.train_tf_record,
                self.valid_tf_record,
                self.anchors,
                self.masks,
                self.max_boxes,
                self.iou_threshold,
                self.score_threshold,
                n_epoch_eval,
                merge_evaluation,
                evaluation_workers,
                shuffle_buffer,
                min_overlaps,
                display_stats,
                plot_stats,
                save_figs,
                checkpoint_name,
            )
            callbacks.append(mid_train_eval)
        history = self.training_model.fit(
            training_dataset,
            epochs=epochs,
            callbacks=callbacks,
            validation_data=valid_dataset,
        )
        default_logger.info('Training complete')
        if evaluate:
            evaluations = self.evaluate(
                checkpoint_name,
                merge_evaluation,
                evaluation_workers,
                shuffle_buffer,
                min_overlaps,
                display_stats,
                plot_stats,
                save_figs,
            )
            return evaluations, history
        return history
    def evaluate(
        self,
        weights_file,
        merge,
        workers,
        shuffle_buffer,
        min_overlaps,
        display_stats=True,
        plot_stats=True,
        save_figs=True,
    ):
        """
        Evaluate on training and validation datasets.
        Args:
            weights_file: Path to trained .tf file.
            merge: If False, training and validation datasets will be evaluated separately.
            workers: Parallel predictions.
            shuffle_buffer: Buffer size for shuffling datasets.
            min_overlaps: a float value between 0 and 1, or a dictionary
                containing each class in self.class_names mapped to its
                minimum overlap
            display_stats: If True evaluation statistics will be printed.
            plot_stats: If True, evaluation statistics will be plotted including
                precision and recall curves and mAP
            save_figs: If True, resulting plots will be save to Output folder.

        Returns:
            stats, map_score.
        """
        default_logger.info('Starting evaluation ...')
        evaluator = Evaluator(
            self.input_shape,
            self.train_tf_record,
            self.valid_tf_record,
            self.classes_file,
            self.anchors,
            self.masks,
            self.max_boxes,
            self.iou_threshold,
            self.score_threshold,
        )
        predictions = evaluator.make_predictions(weights_file, merge, workers,
                                                 shuffle_buffer)
        if isinstance(predictions, tuple):
            training_predictions, valid_predictions = predictions
            if any([training_predictions.empty, valid_predictions.empty]):
                default_logger.info(
                    'Aborting evaluations, no detections found')
                return
            training_actual = pd.read_csv(
                os.path.join('..', 'Data', 'TFRecords', 'training_data.csv'))
            valid_actual = pd.read_csv(
                os.path.join('..', 'Data', 'TFRecords', 'test_data.csv'))
            training_stats, training_map = evaluator.calculate_map(
                training_predictions,
                training_actual,
                min_overlaps,
                display_stats,
                'Train',
                save_figs,
                plot_stats,
            )
            valid_stats, valid_map = evaluator.calculate_map(
                valid_predictions,
                valid_actual,
                min_overlaps,
                display_stats,
                'Valid',
                save_figs,
                plot_stats,
            )
            return training_stats, training_map, valid_stats, valid_map
        actual_data = pd.read_csv(
            os.path.join('..', 'Data', 'TFRecords', 'full_data.csv'))
        if predictions.empty:
            default_logger.info('Aborting evaluations, no detections found')
            return
        stats, map_score = evaluator.calculate_map(
            predictions,
            actual_data,
            min_overlaps,
            display_stats,
            save_figs=save_figs,
            plot_results=plot_stats,
        )
        return stats, map_score
Example #17
0
    def load_weights(self, weights_file):
        """
        Load DarkNet weights or checkpoint/pre-trained weights.
        Args:
            weights_file: .weights or .tf file path.

        Returns:
            None
        """
        assert weights_file.split('.')[-1] in [
            'tf',
            'weights',
        ], 'Invalid weights file'
        assert (
            self.classes == 80 if weights_file.endswith('.weights') else 1
        ), f'DarkNet model should contain 80 classes, {self.classes} is given.'
        if weights_file.endswith('.tf'):
            self.training_model.load_weights(weights_file)
            default_logger.info(f'Loaded weights: {weights_file} ... success')
            return
        with open(weights_file, 'rb') as weights_data:
            default_logger.info(f'Loading pre-trained weights ...')
            major, minor, revision, seen, _ = np.fromfile(weights_data,
                                                          dtype=np.int32,
                                                          count=5)
            all_layers = [
                layer for layer in self.training_model.layers
                if 'output' not in layer.name
            ]
            output_models = [
                layer for layer in self.training_model.layers
                if 'output' in layer.name
            ]
            output_layers = [item.layers for item in output_models]
            for output_item in output_layers:
                all_layers.extend(output_item)
            all_layers.sort(key=lambda layer: int(layer.name.split('_')[1]))
            for i, layer in enumerate(all_layers):
                current_read = weights_data.tell()
                total_size = os.fstat(weights_data.fileno()).st_size
                print(
                    f'\r{round(100 * (current_read / total_size))}%\t{current_read}/{total_size}',
                    end='',
                )
                if 'conv2d' not in layer.name:
                    continue
                next_layer = all_layers[i + 1]
                b_norm_layer = (next_layer if 'batch_normalization'
                                in next_layer.name else None)
                filters = layer.filters
                kernel_size = layer.kernel_size[0]
                input_dimension = layer.get_input_shape_at(-1)[-1]
                convolution_bias = (np.fromfile(
                    weights_data, dtype=np.float32, count=filters)
                                    if b_norm_layer is None else None)
                bn_weights = (np.fromfile(
                    weights_data, dtype=np.float32, count=4 * filters).reshape(
                        (4, filters))[[1, 0, 2, 3]] if
                              (b_norm_layer is not None) else None)
                convolution_shape = (
                    filters,
                    input_dimension,
                    kernel_size,
                    kernel_size,
                )
                convolution_weights = (np.fromfile(
                    weights_data,
                    dtype=np.float32,
                    count=np.product(convolution_shape),
                ).reshape(convolution_shape).transpose([2, 3, 1, 0]))
                if b_norm_layer is None:
                    try:
                        layer.set_weights(
                            [convolution_weights, convolution_bias])
                    except ValueError:
                        pass
                if b_norm_layer is not None:
                    layer.set_weights([convolution_weights])
                    b_norm_layer.set_weights(bn_weights)
            assert len(weights_data.read()) == 0, 'failed to read all data'
        default_logger.info(f'Loaded weights: {weights_file} ... success')
        print()
Example #18
0
    def create_models(self):
        """
        Create training and inference yolov3 models.
        Args:
                layer configuration.

        Returns:
            training, inference models
        """
        input_initial = self.apply_func(Input, shape=self.input_shape)
        x = self.convolution_block(input_initial, 32, 3, 1, True)
        x = self.convolution_block(x, 64, 3, 2, True)
        x = self.convolution_block(x, 32, 1, 1, True, 'append')
        x = self.convolution_block(x, 64, 3, 1, True, 'add')  #
        x = self.convolution_block(x, 128, 3, 2, True)
        x = self.convolution_block(x, 64, 1, 1, True, 'append')
        x = self.convolution_block(x, 128, 3, 1, True, 'add')  #
        x = self.convolution_block(x, 64, 1, 1, True, 'append')
        x = self.convolution_block(x, 128, 3, 1, True, 'add')  #
        x = self.convolution_block(x, 256, 3, 2, True)
        x = self.convolution_block(x, 128, 1, 1, True, 'append')
        x = self.convolution_block(x, 256, 3, 1, True, 'add')  #
        x = self.convolution_block(x, 128, 1, 1, True, 'append')
        x = self.convolution_block(x, 256, 3, 1, True, 'add')  #
        x = self.convolution_block(x, 128, 1, 1, True, 'append')
        x = self.convolution_block(x, 256, 3, 1, True, 'add')  #
        x = self.convolution_block(x, 128, 1, 1, True, 'append')
        x = self.convolution_block(x, 256, 3, 1, True, 'add')  #
        x = self.convolution_block(x, 128, 1, 1, True, 'append')
        x = self.convolution_block(x, 256, 3, 1, True, 'add')  #
        x = self.convolution_block(x, 128, 1, 1, True, 'append')
        x = self.convolution_block(x, 256, 3, 1, True, 'add')  #
        x = self.convolution_block(x, 128, 1, 1, True, 'append')
        x = self.convolution_block(x, 256, 3, 1, True, 'add')  #
        x = self.convolution_block(x, 128, 1, 1, True, 'append')
        x = self.convolution_block(x, 256, 3, 1, True, 'add')  #
        skip_36 = x
        x = self.convolution_block(x, 512, 3, 2, True)
        x = self.convolution_block(x, 256, 1, 1, True, 'append')
        x = self.convolution_block(x, 512, 3, 1, True, 'add')  #
        x = self.convolution_block(x, 256, 1, 1, True, 'append')
        x = self.convolution_block(x, 512, 3, 1, True, 'add')  #
        x = self.convolution_block(x, 256, 1, 1, True, 'append')
        x = self.convolution_block(x, 512, 3, 1, True, 'add')  #
        x = self.convolution_block(x, 256, 1, 1, True, 'append')
        x = self.convolution_block(x, 512, 3, 1, True, 'add')  #
        x = self.convolution_block(x, 256, 1, 1, True, 'append')
        x = self.convolution_block(x, 512, 3, 1, True, 'add')  #
        x = self.convolution_block(x, 256, 1, 1, True, 'append')
        x = self.convolution_block(x, 512, 3, 1, True, 'add')  #
        x = self.convolution_block(x, 256, 1, 1, True, 'append')
        x = self.convolution_block(x, 512, 3, 1, True, 'add')  #
        x = self.convolution_block(x, 256, 1, 1, True, 'append')
        x = self.convolution_block(x, 512, 3, 1, True, 'add')  #
        skip_61 = x
        x = self.convolution_block(x, 1024, 3, 2, True)
        x = self.convolution_block(x, 512, 1, 1, True, 'append')
        x = self.convolution_block(x, 1024, 3, 1, True, 'add')  #
        x = self.convolution_block(x, 512, 1, 1, True, 'append')
        x = self.convolution_block(x, 1024, 3, 1, True, 'add')  #
        x = self.convolution_block(x, 512, 1, 1, True, 'append')
        x = self.convolution_block(x, 1024, 3, 1, True, 'add')  #
        x = self.convolution_block(x, 512, 1, 1, True, 'append')
        x = self.convolution_block(x, 1024, 3, 1, True, 'add')  #
        x = self.convolution_block(x, 512, 1, 1, True)
        x = self.convolution_block(x, 1024, 3, 1, True)
        x = self.convolution_block(x, 512, 1, 1, True)
        x = self.convolution_block(x, 1024, 3, 1, True)
        x = self.convolution_block(x, 512, 1, 1, True)  #
        detection_0 = x
        output_0 = self.output(detection_0, 512)
        x = self.convolution_block(x, 256, 1, 1, True)  #
        x = self.apply_func(UpSampling2D, x, size=2)
        x = self.apply_func(Concatenate, [x, skip_61])
        x = self.convolution_block(x, 256, 1, 1, True)
        x = self.convolution_block(x, 512, 3, 1, True)
        x = self.convolution_block(x, 256, 1, 1, True)
        x = self.convolution_block(x, 512, 3, 1, True)
        x = self.convolution_block(x, 256, 1, 1, True)  #
        detection_1 = x
        output_1 = self.output(detection_1, 256)
        x = self.convolution_block(x, 128, 1, 1, True)  #
        x = self.apply_func(UpSampling2D, x, size=2)
        x = self.apply_func(Concatenate, [x, skip_36])
        x = self.convolution_block(x, 128, 1, 1, True)
        x = self.convolution_block(x, 256, 3, 1, True)
        x = self.convolution_block(x, 128, 1, 1, True)
        x = self.convolution_block(x, 256, 3, 1, True)
        x = self.convolution_block(x, 128, 1, 1, True)
        detection_2 = x
        output_2 = self.output(detection_2, 128)
        self.training_model = Model(
            input_initial,
            [output_0, output_1, output_2],
            name='training_model',
        )
        boxes_0 = self.apply_func(
            Lambda,
            output_0,
            lambda item: get_boxes(item, self.anchors[self.masks[0]], self.
                                   classes),
        )
        boxes_1 = self.apply_func(
            Lambda,
            output_1,
            lambda item: get_boxes(item, self.anchors[self.masks[1]], self.
                                   classes),
        )
        boxes_2 = self.apply_func(
            Lambda,
            output_2,
            lambda item: get_boxes(item, self.anchors[self.masks[2]], self.
                                   classes),
        )
        outputs = self.apply_func(
            Lambda,
            (boxes_0[:3], boxes_1[:3], boxes_2[:3]),
            lambda item: self.get_nms(item),
        )
        self.inference_model = Model(input_initial,
                                     outputs,
                                     name='inference_model')
        default_logger.info('Training and inference models created')
        return self.training_model, self.inference_model
def adjust_non_voc_csv(csv_file, image_path, image_width, image_height):
    """
    Read relative data and return adjusted frame accordingly.
    Args:
        csv_file: .csv file containing the following columns:
        [Image, Object Name, Object Index, bx, by, bw, bh]
        image_path: Path prefix to be added.
        image_width: image width.
        image_height: image height
    Returns:
        pandas DataFrame with the following columns:
        ['Image Path', 'Object Name', 'Image Width', 'Image Height', 'X_min',
       'Y_min', 'X_max', 'Y_max', 'Relative Width', 'Relative Height',
       'Object ID']
    """
    image_path = Path(image_path).absolute().resolve()
    coordinates = []
    old_frame = pd.read_csv(csv_file)
    new_frame = pd.DataFrame()
    new_frame['Image Path'] = old_frame['Image'].apply(
        lambda item: os.path.join(image_path, item)
    )
    new_frame['Object Name'] = old_frame['Object Name']
    new_frame['Image Width'] = image_width
    new_frame['Image Height'] = image_height
    new_frame['Relative Width'] = old_frame['bw']
    new_frame['Relative Height'] = old_frame['bh']
    new_frame['Object ID'] = old_frame['Object Index'] + 1
    for index, row in old_frame.iterrows():
        image, object_name, object_index, bx, by, bw, bh = row
        co = ratios_to_coordinates(bx, by, bw, bh, image_width, image_height)
        coordinates.append(co)
    (
        new_frame['X_min'],
        new_frame['Y_min'],
        new_frame['X_max'],
        new_frame['Y_max'],
    ) = np.array(coordinates).T
    new_frame[['X_min', 'Y_min', 'X_max', 'Y_max']] = new_frame[
        ['X_min', 'Y_min', 'X_max', 'Y_max']
    ].astype('int64')
    print(f'Parsed labels:\n{new_frame["Object Name"].value_counts()}')
    classes = new_frame['Object Name'].drop_duplicates()
    default_logger.info(
        f'Adjustment from existing received {len(new_frame)} labels containing '
        f'{len(classes)} classes'
    )
    default_logger.info(f'Added prefix to images: {image_path}')
    return new_frame[
        [
            'Image Path',
            'Object Name',
            'Image Width',
            'Image Height',
            'X_min',
            'Y_min',
            'X_max',
            'Y_max',
            'Relative Width',
            'Relative Height',
            'Object ID',
        ]
    ]
Example #20
0
    def augment_photos_folder(self, batch_size=64, new_size=None):
        """
        Augment photos in Data/Photos/
        Args:
            batch_size: Size of each augmentation batch.
            new_size: tuple, new image size.

        Returns:
            None
        """
        default_logger.info(
            f'Started augmentation with {self.workers} workers')
        default_logger.info(f'Total images to augment: {self.total_images}')
        default_logger.info(f'Session assigned id: {self.session_id}')
        with ThreadPoolExecutor(max_workers=self.workers) as executor:
            while self.image_paths_copy:
                current_batch, current_paths = self.load_batch(
                    new_size, batch_size)
                future_augmentations = {
                    executor.submit(self.augment_image, image, path): path
                    for image, path in zip(current_batch, current_paths)
                }
                for future_augmented in as_completed(future_augmentations):
                    future_augmented.result()
        default_logger.info(f'Augmentation completed')
        augmentation_frame = pd.DataFrame(self.augmentation_data,
                                          columns=self.mapping.columns)
        saving_path = os.path.join('..', 'Output', 'Data',
                                   f'augmented_data_plus_original.csv')
        combined = pd.concat([self.mapping, augmentation_frame])
        for item in ['bx', 'by', 'bw', 'bh']:
            combined = combined.drop(combined[combined[item] > 1].index)
        combined.to_csv(saving_path, index=False)
        default_logger.info(f'Saved old + augmented labels to {saving_path}')
        adjusted_combined = adjust_non_voc_csv(saving_path, self.image_folder,
                                               self.image_width,
                                               self.image_height)
        adjusted_saving_path = saving_path.replace('augmented', 'adjusted_aug')
        adjusted_combined.to_csv(adjusted_saving_path, index=False)
        default_logger.info(
            f'Saved old + augmented (adjusted) labels to {adjusted_saving_path}'
        )
        return adjusted_combined