--- a
+++ b/utils.py
@@ -0,0 +1,192 @@
+import os
+import io
+import base64
+
+import numpy as np
+import pandas as pd
+import cv2
+
+import matplotlib.pyplot as plt
+
+import torch
+import torch.nn as nn
+from albumentations import Normalize
+
+import time
+from IPython.display import clear_output
+from IPython.display import HTML
+
+from loss_metric import dice_coef_metric_per_classes, jaccard_coef_metric_per_classes
+
+def get_one_slice_data(img_name: str,
+                       mask_name: str,
+                       root_imgs_path: str = "images/",
+                       root_masks_path: str = "masks/",) -> np.ndarray:
+
+    img_path = os.path.join('images/', img_name)
+    mask_path = os.path.join('masks/', mask_name)
+    one_slice_img = cv2.imread(img_path)#[:,:,0] uncomment for grayscale
+    one_slice_mask = cv2.imread(mask_path)
+    one_slice_mask[one_slice_mask < 240] = 0  # remove artifacts
+    one_slice_mask[one_slice_mask >= 240] = 255
+
+    return one_slice_img, one_slice_mask
+
+
+def get_id_predictions(net: nn.Module,
+                       ct_scan_id_df: pd.DataFrame,
+                       root_imgs_dir: str,
+                       treshold: float = 0.3) -> list:
+
+    """
+    Factory for getting predictions and storing them and images in lists as uint8 images.
+    Params:
+        net: model for prediction.
+        ct_scan_id_df: df with unique patient id.
+        root_imgs_dir: root path for images.
+        treshold: threshold for probabilities.
+    """
+    sigmoid = lambda x: 1 / (1 + np.exp(-x))
+    images = []
+    predictions = []
+    net.eval()
+    device = "cuda" if torch.cuda.is_available() else "cpu"
+    print("device:", device)
+    with torch.no_grad():
+        for idx in range(len(ct_scan_id_df)):
+            img_name = ct_scan_id_df.loc[idx, "ImageId"]
+            path = os.path.join(root_imgs_dir, img_name)
+
+            img_ = cv2.imread(path)
+    
+            img = Normalize().apply(img_)
+            tensor = torch.FloatTensor(img).permute(2, 0, 1).unsqueeze(0)
+            prediction = net.forward(tensor.to(device))
+            prediction = prediction.cpu().detach().numpy()
+            prediction = prediction.squeeze(0).transpose(1, 2, 0)
+            prediction = sigmoid(prediction)
+            prediction = (prediction >= treshold).astype(np.float32)
+
+            predictions.append((prediction * 255).astype("uint8"))
+            images.append(img_)
+
+    return images, predictions
+
+
+# Save image in original resolution
+# helpful link - https://stackoverflow.com/questions/34768717/matplotlib-unable-to-save-image-in-same-resolution-as-original-image
+
+def get_overlaid_masks_on_image(
+                one_slice_image: np.ndarray,
+                one_slice_mask: np.ndarray, 
+                w: float = 512,
+                h: float = 512, 
+                dpi: float = 100,
+                write: bool = False,
+                path_to_save: str = '/content/',
+                name_to_save: str = 'img_name'):
+    """overlap masks on image and save this as a new image."""
+
+    path_to_save_ = os.path.join(path_to_save, name_to_save)
+    lung, heart, trachea = [one_slice_mask[:, :, i] for i in range(3)]
+    figsize = (w / dpi), (h / dpi)
+    fig = plt.figure(figsize=(figsize))
+    fig.add_axes([0, 0, 1, 1])
+
+    # image
+    plt.imshow(one_slice_image, cmap="bone")
+
+    # overlaying segmentation masks
+    plt.imshow(np.ma.masked_where(lung == False, lung),
+            cmap='cool', alpha=0.3)
+    plt.imshow(np.ma.masked_where(heart == False, heart),
+            cmap='autumn', alpha=0.3)
+    plt.imshow(np.ma.masked_where(trachea == False, trachea),
+               cmap='autumn_r', alpha=0.3) 
+
+    plt.axis('off')
+    fig.savefig(f"{path_to_save_}.png",bbox_inches='tight', 
+                pad_inches=0.0, dpi=dpi,  format="png")
+    if write:
+        plt.close()
+    else:
+        plt.show()
+        
+        
+def get_overlaid_masks_on_full_ctscan(ct_scan_id_df: pd.DataFrame, path_to_save: str):
+    """
+    Creating images with overlaid masks on each slice of CT scan.
+    Params:
+         ct_scan_id_df: df with unique patient id.
+         path_to_save: path to save images.
+    """
+    num_slice = len(ct_scan_id_df)
+    for slice_ in range(num_slice):
+        img_name = ct_scan_id_df.loc[slice_, "ImageId"]
+        mask_name = ct_scan_id_df.loc[slice_, "MaskId"]
+        one_slice_img, one_slice_mask = get_one_slice_data(img_name, mask_name)
+        get_overlaid_masks_on_image(one_slice_img,
+                                one_slice_mask,
+                                write=True, 
+                                path_to_save=path_to_save,
+                                name_to_save=str(slice_)
+                                )
+
+def create_video(path_to_imgs: str, video_name: str, framerate: int):
+    """
+    Create video from images.
+    Params:
+        path_to_imgs: path to dir with images.
+        video_name: name for saving video.
+        framerate: num frames per sec in video.
+    """
+    img_names = sorted(os.listdir(path_to_imgs), key=lambda x: int(x[:-4]))  # img_name must be numbers
+    img_path = os.path.join(path_to_imgs, img_names[0])
+    frame_width, frame_height, _ = cv2.imread(img_path).shape
+    fourc = cv2.VideoWriter_fourcc(*'MP4V')
+    video = cv2.VideoWriter(video_name + ".mp4", 
+                            fourc, 
+                            framerate, 
+                            (frame_width, frame_height))
+
+    for img_name in img_names:
+        img_path = os.path.join(path_to_imgs, img_name)
+        image = cv2.imread(img_path)
+        video.write(image)
+            
+    cv2.destroyAllWindows()
+    video.release()
+
+    
+def compute_scores_per_classes(model,
+                               dataloader,
+                               classes):
+    """
+    Compute Dice and Jaccard coefficients for each class.
+    Params:
+        model: neural net for make predictions.
+        dataloader: dataset object to load data from.
+        classes: list with classes.
+        Returns: dictionaries with dice and jaccard coefficients for each class for each slice.
+    """
+    device = 'cuda' if torch.cuda.is_available() else 'cpu'
+    dice_scores_per_classes = {key: list() for key in classes}
+    iou_scores_per_classes = {key: list() for key in classes}
+
+    with torch.no_grad():
+        for i, (imgs, targets) in enumerate(dataloader):
+            imgs, targets = imgs.to(device), targets.to(device)
+            logits = model(imgs)
+            logits = logits.detach().cpu().numpy()
+            targets = targets.detach().cpu().numpy()
+            
+            dice_scores = dice_coef_metric_per_classes(logits, targets)
+            iou_scores = jaccard_coef_metric_per_classes(logits, targets)
+
+            for key in dice_scores.keys():
+                dice_scores_per_classes[key].extend(dice_scores[key])
+
+            for key in iou_scores.keys():
+                iou_scores_per_classes[key].extend(iou_scores[key])
+
+    return dice_scores_per_classes, iou_scores_per_classes