|
import os |
|
import argparse |
|
import cv2 |
|
import numpy as np |
|
|
|
from PIL import Image |
|
from pathlib import Path |
|
from glob import glob |
|
from tqdm import tqdm |
|
|
|
import torch |
|
import torchvision.transforms as tf |
|
|
|
from segmentation_models_pytorch import Linknet |
|
import myutils |
|
|
|
|
|
ROOT_DIR = './' |
|
|
|
|
|
DEFAULT_OUT = os.path.join(ROOT_DIR, 'output', 'segs') |
|
|
|
|
|
|
|
|
|
|
|
def norm_imagenet(img_pil, dims): |
|
""" |
|
Normalizes and resizes input image |
|
:param img_pil: PIL Image |
|
:param dims: Model's expected input dimensions |
|
:return: Normalized Image as a Tensor |
|
""" |
|
|
|
|
|
mean = torch.tensor([0.485, 0.456, 0.406]) |
|
std = torch.tensor([0.229, 0.224, 0.225]) |
|
|
|
|
|
transform_norm = tf.Compose([ |
|
tf.Resize([dims[0], dims[1]]), |
|
tf.ToTensor(), |
|
tf.Normalize(mean, std) |
|
]) |
|
|
|
img_norm = transform_norm(img_pil) |
|
return img_norm |
|
|
|
|
|
def predict_one(path, model, mask_outdir, overlay_outdir, device): |
|
""" |
|
Predicts a single image from path |
|
:param path: Path to image |
|
:param model: Loaded Torch Model |
|
:param mask_outdir: Filepath to mask out directory |
|
:param overlay_outdir: Filepath to overlay out directory |
|
:return: None |
|
""" |
|
img_pil = myutils.load_image_in_PIL(path) |
|
|
|
|
|
prediction = predict_pil(model, img_pil, model_dims=(416, 416), device=device) |
|
|
|
basename = str(Path(os.path.basename(path)).stem) |
|
mask_savepth = os.path.join(mask_outdir, basename + '.png') |
|
|
|
prediction.save(mask_savepth) |
|
|
|
over_savepth = os.path.join(overlay_outdir, basename + '.png') |
|
img_np = cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR) |
|
overlay_np = myutils.add_overlay(img_np, np.array(prediction)) |
|
cv2.imwrite(over_savepth, overlay_np) |
|
|
|
|
|
|
|
|
|
|
|
def predict_pil(model, img_pil, model_dims, device): |
|
""" |
|
Predicts a single PIL Image |
|
:param model: Loaded PyTorch model |
|
:param img_pil: PIL image |
|
:param model_dims: Model input dimensions |
|
:return: Segmentation prediction as PIL Image |
|
""" |
|
|
|
img_np = np.array(img_pil) |
|
img_tensor_norm = norm_imagenet(img_pil, model_dims) |
|
|
|
|
|
pred_resize = tf.Compose([tf.Resize([img_np.shape[0], img_np.shape[1]])]) |
|
|
|
|
|
input_data = img_tensor_norm.unsqueeze(0) |
|
|
|
try: |
|
|
|
prediction = model.predict(input_data.to(device)) |
|
except: |
|
print("Did not convert input image to cuda.") |
|
prediction = model.predict(input_data) |
|
|
|
prediction = pred_resize(prediction) |
|
prediction = myutils.postprocessing_pred(prediction.squeeze().cpu().round().numpy().astype(np.uint8)) |
|
prediction = Image.fromarray(prediction).convert('P') |
|
prediction.putpalette(myutils.color_palette) |
|
return prediction |
|
|
|
|
|
def test_waterseg(model_path, test_path, test_name, out_path, device): |
|
""" |
|
Tests either a single or an entire folder of images |
|
:param args: Command line args |
|
:return: None |
|
""" |
|
model = torch.load(model_path) |
|
print('############################################') |
|
print('############################################') |
|
print('############################################') |
|
test_path = test_path |
|
out_path = os.path.join(out_path, test_name) |
|
|
|
mask_out = os.path.join(out_path, 'mask') |
|
overlay_out = os.path.join(out_path, 'overlay') |
|
if not os.path.exists(mask_out): |
|
os.makedirs(mask_out) |
|
if not os.path.exists(overlay_out): |
|
os.makedirs(overlay_out) |
|
|
|
if os.path.isfile(test_path): |
|
predict_one(test_path, model, mask_out, overlay_out, device) |
|
elif os.path.isdir(test_path): |
|
paths = glob(os.path.join(test_path, '*.jpg')) + glob(os.path.join(test_path, '*.png')) |
|
for path in tqdm(paths): |
|
predict_one(path, model, mask_out, overlay_out, device) |
|
else: |
|
print("Error: Unknown path type:", test_path) |
|
|
|
|
|
if __name__ == '__main__': |
|
|
|
parser = argparse.ArgumentParser(description='V-FloodNet: Water Image Segmentation') |
|
|
|
parser.add_argument('--model-path', |
|
default='./records/link_efficientb4_model.pth', |
|
type=str, |
|
metavar='PATH', |
|
help='Path to the model') |
|
|
|
parser.add_argument('--test-path', |
|
type=str, |
|
metavar='PATH', |
|
required=True, |
|
help='Can point to folder or an individual jpg/png image') |
|
parser.add_argument('--test-name', |
|
type=str, |
|
required=True, |
|
help='Test name') |
|
parser.add_argument('--out-path', |
|
default=DEFAULT_OUT, |
|
type=str, |
|
metavar='PATH', |
|
help='(OPTIONAL) Path to output folder, defaults to project root/output') |
|
args = parser.parse_args() |
|
|
|
|
|
device = torch.device('cpu') |
|
if torch.cuda.is_available(): |
|
device = torch.device('cuda') |
|
|
|
test_waterseg(args.model_path, args.test_path, args.test_name, args.out_path, device) |
|
|
|
print(myutils.gct(), 'Test image segmentation done.') |
|
|