コード例 #1
0
def __verify_submission():
    file_path = os.path.join(paths.DATA_FOLDER_PATH, "out", "predictions.txt")
    colour_mapping = mapping.get_colour_mapping()

    with open(file_path, "r") as fp:
        for line in fp:

            # parse file
            file_id, data = parse_submission_line(line)
            confidences = [d["confidence"] for d in data]
            indices = np.argsort(confidences)

            # draw data on image
            colour_mask_1 = np.zeros((3024, 4032)).astype(np.uint8)
            for index in indices:
                polygon = np.array(data[index]["polygon"])
                polygon = polygon.reshape((-1, 1, 2))

                colour_mask_1 = cv2.fillPoly(
                    colour_mask_1, [polygon],
                    color=colour_mapping[data[index]["class_name"]])

            # load original prediciton
            prediction_file = os.path.join(paths.DATA_FOLDER_PATH,
                                           "predictions", file_id + ".npy")
            prediction = np.load(prediction_file)
            class_id_mask = np.argmax(prediction, axis=-1)
            colour_mask_2 = class_id_mask_to_colour_mask(class_id_mask)

            plt.subplot(121)
            plt.imshow(colour_mask_1)
            plt.subplot(122)
            plt.imshow(colour_mask_2)
            plt.show()
コード例 #2
0
def colour_mask_to_class_id_mask(colour_mask, data_folder_path=None):
    """
    :param colour_mask:
    :return:
    """
    colour_mapping = mapping.get_colour_mapping(data_folder_path)
    class_id_mask = np.zeros(colour_mask.shape[:2]).astype(np.uint8)

    for i, k in enumerate(sorted(colour_mapping.keys())):
        class_id_mask[colour_mask == colour_mapping[k]] = i

    return class_id_mask
コード例 #3
0
def class_id_mask_to_colour_mask(class_id_mask):
    """
    :param class_id_mask:
    :return:
    """
    colour_mapping = mapping.get_colour_mapping()

    colour_mask = np.zeros(class_id_mask.shape[:2]).astype(np.uint8)

    for i, k in enumerate(sorted(colour_mapping.keys())):
        colour_mask[class_id_mask == i] = colour_mapping[k]

    return colour_mask
コード例 #4
0
def create_submission_file(prediction_file_names, output_file_path):
    """
    Creates a submission file in accordance with the rules of the competition based on the given files
    :param prediction_file_names: paths to numpy files or arrays with size (h, w, class_count)
    :param output_file_path: path to the  file where the results will be stored
    :return: Nothing
    """

    colour_mapping = mapping.get_colour_mapping()
    class_names = sorted(colour_mapping.keys())

    out_lines = []

    for i, file in enumerate(prediction_file_names):
        print("file {} of {}\n".format(i + 1, len(prediction_file_names)))

        prediction = np.load(file)

        # start output row
        line = "{};".format(os.path.split(file)[1][:-4])

        class_id_mask = np.argmax(prediction, axis=-1)

        class_ids = np.unique(class_id_mask).tolist()

        # remove background class
        if 0 in class_ids:
            class_ids.remove(0)

        pbar = tqdm(class_ids, ncols=50)
        for class_id in pbar:
            mask = ((class_id_mask == class_id) * 255).astype(np.uint8)

            # apply opening to mask to remove small coral-areas
            # TODO: the size needs to be set to something sensible!
            kernel_size = (31, 31)
            kernel = np.ones(kernel_size, np.uint8)
            mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)

            kernel = np.ones(kernel_size, np.uint8)
            mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)

            # if there are less than x pixel, skip the class
            if np.sum(mask / 255) < 100:
                continue

            line += "{} ".format(class_names[class_id])
            # find connected components
            ret, labels = cv2.connectedComponents(mask)
            for j, label in enumerate(
                    range(1, ret)
            ):  # label 0 is for background of the connected components
                cc_mask = ((labels == label) * 255).astype(np.uint8)

                non_nulls = np.where(cc_mask)

                cv2.floodFill(cc_mask, None,
                              (non_nulls[1][0], non_nulls[0][0]), 255)

                polygon = _get_contour(cc_mask)

                cc_mask = cc_mask / 255
                confidence = (prediction[:, :, class_id] *
                              cc_mask).sum() / cc_mask.sum()
                # print("confidence: {:.2f}".format(confidence))
                if j > 0:
                    line += ","
                line += "{:.2f}:".format(confidence)
                line += _create_polygon_string(polygon)

            line += ";"
        line = line[:-1]  # remove last semicolon
        line += "\n"
        out_lines.append(line)

    with open(output_file_path, "w") as fp:
        fp.writelines(out_lines)
コード例 #5
0
def evaluate(image_file_paths,
             gt_file_paths,
             model_path,
             nn_input_size,
             window_sizes=None,
             step_sizes=None,
             device=None,
             data_folder_path=None,
             log_file_path=None):
    """
    Evaluate model performance
    :param image_file_paths: paths to images that will be predicted
    :param gt_file_paths: paths to ground truth masks
    :param model: model used for prediction
    :param nn_input_size: size that the images will be scaled to before feeding them into the nn
    :param num_classes: number of classes
    :param window_sizes: list of sizes that determine how the image will be cut (for sliding window). Image will be cut
    into squares
    :param step_sizes: list of step sizes for sliding window
    :param device: PyTorch device (cpu or gpu)
    :return: list of prediction masks if res_fcn is None, else Nothing
    """

    model = load_model(model_path)
    colour_mapping = mapping.get_colour_mapping(
        data_folder_path=data_folder_path)
    classes_map = {x: y for x, y in enumerate(sorted(colour_mapping.keys()))}

    with torch.no_grad():
        predictions = predict.predict(image_file_paths=image_file_paths,
                                      model=model,
                                      nn_input_size=nn_input_size,
                                      window_sizes=window_sizes,
                                      step_sizes=step_sizes,
                                      device=device)

    # Calculates IoU looping over images and masks

    intersection_per_substrate = defaultdict(int)
    union_per_substrate = defaultdict(int)

    for gt_file_path, prediction in zip(gt_file_paths, predictions):
        pred_class_id_mask = np.argmax(prediction, axis=-1)
        gt_colour_mask = cv2.imread(gt_file_path)[:, :, 0]
        gt_class_id_mask = colour_mask_to_class_id_mask(gt_colour_mask)

        for substrate_idx, substrate_name in classes_map.items():
            intersection, union = calculate_agreement(
                gt_class_id_mask,
                pred_class_id_mask,
                substrate_idx=substrate_idx)
            if union:
                intersection_per_substrate[substrate_name] += intersection
                union_per_substrate[substrate_name] += union

        iou_per_substrate, iou_average = _calculate_iou(
            intersection_per_substrate, union_per_substrate, classes_map)

        # Gradually append accuracy stats

        if log_file_path:
            with open(log_file_path, 'w') as f:
                f.write(json.dumps(iou_per_substrate, indent=4))
                f.write('\n')
                f.write(f"avg: {iou_average}")

    return iou_per_substrate, iou_average
コード例 #6
0
    def __init__(self,
                 data_train,
                 data_valid,
                 image_base_dir,
                 instructions,
                 models_folder_path=None,
                 data_folder_path=None,
                 checkpoint_file_path=None):
        """

        :param data_train:
        :param data_valid:
        :param image_base_dir: A directory with all CLEF images (for training and validating)
        :param instructions (dict): A dictionary containing instructions to train. It has
        the following keys:
            epochs
            model_name
            nn_input_shape
            state_dict_file_path (default = None)
            crops
            images_per_batch
            batch_size
            backbone (default = resnet)
            deeplab_output_stride (default = 16)
            learning_rate (default = 1e-05)
            multi_gpu (default = False)
            class_stats_file_path
            use_lr_scheduler (default = True)
        """

        self.image_base_dir = image_base_dir
        self.data_valid = data_valid
        self.instructions = instructions

        # specify model save dir
        self.model_name = instructions[STR.MODEL_NAME]
        # now = time.localtime()
        # start_time = "{}-{}-{}T{}:{}:{}".format(now.tm_year, now.tm_mon, now.tm_mday, now.tm_hour, now.tm_min,
        #                                         now.tm_sec)
        models_folder_path = models_folder_path or paths.MODELS_FOLDER_PATH
        data_folder_path = data_folder_path or paths.DATA_FOLDER_PATH
        experiment_folder_path = os.path.join(models_folder_path,
                                              self.model_name)

        if os.path.exists(experiment_folder_path):
            Warning(
                "Experiment folder exists already. Files might be overwritten")
        os.makedirs(experiment_folder_path, exist_ok=True)

        # define saver and save instructions
        self.saver = Saver(folder_path=experiment_folder_path,
                           instructions=instructions)
        self.saver.save_instructions()

        # define Tensorboard Summary
        self.writer = SummaryWriter(log_dir=experiment_folder_path)

        nn_input_size = instructions[STR.NN_INPUT_SIZE]
        state_dict_file_path = instructions.get(STR.STATE_DICT_FILE_PATH, None)

        self.colour_mapping = mapping.get_colour_mapping(
            data_folder_path=data_folder_path)

        # define transformers for training
        crops_per_image = instructions.get(STR.CROPS_PER_IMAGE, 10)

        apply_random_cropping = (STR.CROPS_PER_IMAGE in instructions.keys()) and \
                                (STR.IMAGES_PER_BATCH in instructions.keys())

        print("{}applying random cropping".format(
            "" if apply_random_cropping else "_NOT_ "))

        crop = RandomCrop(min_size=instructions.get(STR.CROP_SIZE_MIN, 400),
                          max_size=instructions.get(STR.CROP_SIZE_MAX, 1000),
                          crop_count=crops_per_image)

        t = [Normalize()]
        if apply_random_cropping:
            t.append(crop)
        t += [
            Resize(nn_input_size),
            Flip(p_vertical=0.2, p_horizontal=0.5),
            ToTensor()
        ]

        transformations_train = transforms.Compose(t)

        # define transformers for validation
        transformations_valid = transforms.Compose(
            [Normalize(), crop,
             Resize(nn_input_size),
             ToTensor()])

        # set up data loaders
        dataset_train = DictArrayDataSet(image_base_dir=image_base_dir,
                                         data=data_train,
                                         data_folder_path=data_folder_path,
                                         num_classes=len(
                                             self.colour_mapping.keys()),
                                         transformation=transformations_train)
        # define batch sizes
        self.batch_size = instructions[STR.BATCH_SIZE]

        if apply_random_cropping:
            self.data_loader_train = DataLoader(
                dataset=dataset_train,
                batch_size=instructions[STR.IMAGES_PER_BATCH],
                shuffle=True,
                collate_fn=custom_collate)
        else:
            self.data_loader_train = DataLoader(dataset=dataset_train,
                                                batch_size=self.batch_size,
                                                shuffle=True,
                                                collate_fn=custom_collate)

        dataset_valid = DictArrayDataSet(image_base_dir=image_base_dir,
                                         data=data_valid,
                                         data_folder_path=data_folder_path,
                                         num_classes=len(
                                             self.colour_mapping.keys()),
                                         transformation=transformations_valid)

        self.data_loader_valid = DataLoader(dataset=dataset_valid,
                                            batch_size=self.batch_size,
                                            shuffle=False,
                                            collate_fn=custom_collate)

        self.num_classes = dataset_train.num_classes()

        # define model
        print("Building model")
        self.model = DeepLab(num_classes=self.num_classes,
                             backbone=instructions.get(STR.BACKBONE, "resnet"),
                             output_stride=instructions.get(
                                 STR.DEEPLAB_OUTPUT_STRIDE, 16))

        # load weights
        if checkpoint_file_path is not None:
            print("loading state_dict from:")
            print(checkpoint_file_path)
            load_state_dict(self.model, checkpoint_file_path)

        learning_rate = instructions.get(STR.LEARNING_RATE, 1e-5)
        train_params = [{
            'params': self.model.get_1x_lr_params(),
            'lr': learning_rate
        }, {
            'params': self.model.get_10x_lr_params(),
            'lr': learning_rate
        }]

        # choose gpu or cpu
        self.device = torch.device(
            "cuda:0" if torch.cuda.is_available() else "cpu")
        if instructions.get(STR.MULTI_GPU, False):
            if torch.cuda.device_count() > 1:
                print("Using ", torch.cuda.device_count(), " GPUs!")
                self.model = nn.DataParallel(self.model)

        print(f"Using {self.device}")
        self.model.to(self.device)

        # Define Optimizer
        self.optimizer = torch.optim.SGD(train_params,
                                         momentum=0.9,
                                         weight_decay=5e-4,
                                         nesterov=False)

        # calculate class weights
        if instructions.get(STR.CLASS_STATS_FILE_PATH, None):

            class_weights = calculate_class_weights(
                instructions[STR.CLASS_STATS_FILE_PATH],
                self.colour_mapping,
                modifier=instructions.get(STR.LOSS_WEIGHT_MODIFIER, 1.01))

            class_weights = torch.from_numpy(class_weights.astype(np.float32))
        else:
            class_weights = None
        self.criterion = SegmentationLosses(
            weight=class_weights, cuda=self.device.type != "cpu").build_loss()

        # Define Evaluator
        self.evaluator = Evaluator(self.num_classes)

        # Define lr scheduler
        self.scheduler = None
        if instructions.get(STR.USE_LR_SCHEDULER, True):
            self.scheduler = LR_Scheduler(mode="cos",
                                          base_lr=learning_rate,
                                          num_epochs=instructions[STR.EPOCHS],
                                          iters_per_epoch=len(
                                              self.data_loader_train))

        # print information before training start
        print("-" * 60)
        print("instructions")
        pprint(instructions)
        model_parameters = sum([p.nelement() for p in self.model.parameters()])
        print("Model parameters: {:.2E}".format(model_parameters))

        self.best_prediction = 0.0
コード例 #7
0
def create_annotation_masks(data_folder_path, image_folder="images",
                            mask_folder="masks",
                            annotations_file="annotations_train.csv",
                            create_new_mapping=False):
    """
    Create mask images acting as annotations from the annotations data file. Mask files will be stored in the
    path.MASK_FOLDER_PATH folder

    :return: Nothing
    """

    # parse the annotations file to get the data
    data_folder_path = (data_folder_path if data_folder_path else paths.DATA_FOLDER_PATH)
    image_folder_path = os.path.join(data_folder_path, image_folder)
    mask_folder_path = os.path.join(data_folder_path, mask_folder)

    os.makedirs(data_folder_path, exist_ok=True)
    os.makedirs(image_folder_path, exist_ok=True)
    os.makedirs(mask_folder_path, exist_ok=True)

    csv_file_path = os.path.join(data_folder_path, annotations_file)
    data = parse_csv_data_file(csv_file_path)

    # create a list containing all classes
    classes = list(sorted(set([annotation["class"] for img_name in data.keys() for annotation in data[img_name]])))

    # create a colour for each class, 0 is background
    colours = np.linspace(0, 255, len(classes) + 1).astype(int).tolist()
    if create_new_mapping:
        class_mapping = {c: i + 1 for i, c in enumerate(classes)}

        colour_mapping = {"background": 0}
        colour_mapping.update({c: colours[class_mapping[c]] for c in classes})

        with open(os.path.join(data_folder_path, "colour_mapping.json"), "w") as fp:
            json.dump(colour_mapping, fp, indent=4)
    else:
        colour_mapping = mapping.get_colour_mapping(data_folder_path)

    print("Creating masks")

    for i, image_name in enumerate(data.keys()):
        # create mask based on the size of the corresponding image
        print(image_folder_path)
        print(image_name)
        image_path = os.path.join(image_folder_path, image_name)
        # CLEF files has some images which are JPG in caps
        try: 
            image_height, image_width = cv2.imread(image_path).shape[:2]
        except AttributeError:
            image_path_head, ext = image_path.split('.')
            # Tries with the reverse capitalisation
            image_path = image_path_head + ('.JPG' if ext.islower() else '.jpg')
            image_height, image_width = cv2.imread(image_path).shape[:2]
        
        mask = np.zeros((image_height, image_width), dtype=np.uint8)

        # go through each annotation entry and fill the corresponding polygon. Color corresponds to class
        for annotation in data[image_name]:
            colour = colour_mapping[annotation["class"]]

            points = annotation["coordinates"]
            cv2.fillPoly(mask, [np.array(points)], color=colour)

        # save the mask
        name, _ = os.path.splitext(image_name)
        out_name = name + "_mask.png"
        print(f"Saving {os.path.join(mask_folder_path, out_name)}")
        cv2.imwrite(os.path.join(mask_folder_path, out_name), mask)
コード例 #8
0

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--data_folder_path',
        default=None,
        type=str,
        help='Path to the data directory, where to save the outputs.')
    data_folder_path = parser.parse_args().data_folder_path

    data_folder_path = (data_folder_path
                        if data_folder_path else paths.DATA_FOLDER_PATH)
    mask_folder_path = os.path.join(data_folder_path, 'masks')

    colour_mapping = mapping.get_colour_mapping(data_folder_path)

    files_train, files_valid = calculate_split(mask_folder_path,
                                               colour_mapping)

    data_train = [{
        "image_name": os.path.join("images", name[:-9] + ".JPG"),
        "mask_name": os.path.join("masks", name)
    } for name in files_train]

    data_val = [{
        "image_name": os.path.join("images", name[:-9] + ".JPG"),
        "mask_name": os.path.join("masks", name)
    } for name in files_valid]

    data = data_train + data_val