Commit 1afdf6cd authored by Gilberto Astolfi's avatar Gilberto Astolfi
Browse files

Merge branch 'feature-cnn'

parents c711d7f5 537743f7
File mode changed from 100644 to 100755
......@@ -10,3 +10,4 @@ javabridge
python-weka-wrapper
cycler
cython
h5py
......@@ -2,17 +2,36 @@ from .classifier import Classifier
try:
from .weka_classifiers import WekaClassifiers
except:
except Exception as e:
WekaClassifiers = None
print e.message
try:
from .cnn_caffe import CNNCaffe
except:
except Exception as e:
CNNCaffe = None
print e.message
__all__ = ["cnn_caffe",
"classifier",
"weka_classifiers"]
try:
from .cnn_keras import CNNKeras
except Exception as e:
CNNKeras = None
print e.message
try:
from .segnet_keras import SEGNETKeras
except Exception as e:
SEGNETKeras = None
print e.message
__all__ = ["classifier",
"cnn_caffe",
"cnn_keras",
"segnet_keras",
"weka_classifiers"
]
from collections import OrderedDict
......@@ -22,6 +41,10 @@ from util.config import Config
_classifier_list = OrderedDict( [
["cnn_caffe", Config("Invalid" if CNNCaffe is None else CNNCaffe.__name__,
WekaClassifiers is None and CNNCaffe is not None, bool, meta=CNNCaffe, hidden=CNNCaffe is None)],
["cnn_keras", Config("Invalid" if CNNKeras is None else CNNKeras.__name__,
CNNKeras is not None, bool, meta=CNNKeras, hidden=CNNKeras is None)],
["segnet_keras", Config("Invalid" if SEGNETKeras is None else SEGNETKeras.__name__,
SEGNETKeras is not None, bool, meta=SEGNETKeras, hidden=SEGNETKeras is None)],
["weka_classifiers", Config("Invalid" if WekaClassifiers is None else WekaClassifiers.__name__,
WekaClassifiers is not None, bool, meta=WekaClassifiers, hidden=WekaClassifiers is None)]
] )
......@@ -31,4 +54,6 @@ def get_classifier_config():
def set_classifier_config(configs):
_classifier_list["cnn_caffe"] = Config.nvl_config(configs["cnn_caffe"], _classifier_list["cnn_caffe"])
_classifier_list["weka_classifiers"] = Config.nvl_config(configs["weka_classifiers"], _classifier_list["weka_classifiers"])
_classifier_list["cnn_keras"] = Config.nvl_config(configs["cnn_keras"], _classifier_list["cnn_keras"])
_classifier_list["segnet_keras"] = Config.nvl_config(configs["segnet_keras"], _classifier_list["segnet_keras"])
_classifier_list["weka_classifiers"] = Config.nvl_config(configs["weka_classifiers"], _classifier_list["weka_classifiers"])
\ No newline at end of file
......@@ -78,14 +78,19 @@ class Classifier(object):
"""Return if classifier must be trained.
"""
return False
def must_extract_features(self):
"""Return if classifier must be extracted features.
"""
return True
def train(self, dataset, training_data, force = False):
"""Perform the training of classifier.
"""
pass
@abstractmethod
def classify(self, dataset, test_dir = None, test_data = None):
def classify(self, dataset, test_dir = None, test_data = None, image = None):
"""Perform the classification.
Implement this method to extend this class with a new classifier algorithm.
"""
......
......@@ -116,7 +116,7 @@ class CNNCaffe(Classifier):
return summary
def classify(self, dataset, test_dir, test_data):
def classify(self, dataset, test_dir, test_data, image):
"""Perform the classification.
Parameters
......
This diff is collapsed.
This diff is collapsed.
......@@ -136,7 +136,7 @@ class WekaClassifiers(Classifier):
self.classifier.build_classifier(self.data)
def classify(self, dataset, test_dir, test_data):
def classify(self, dataset, test_dir, test_data, image):
"""Perform the classification.
Parameters
......
......@@ -46,6 +46,28 @@ class Utils(object):
return filename
@staticmethod
def ask_weight_name(title = 'Open a weight'):
"""Shows a default Tkinter dialogue to open a image.
Parameters
----------
title : string, optional, default = 'Open a image'
Dialogue title.
"""
file_opt = options = {}
options['defaultextension'] = '.jpg'
options['filetypes'] = [('All supported files', ("*.h5")),
('H5 weight', ("*.h5", "*.H5"))]
options['initialdir'] = Utils.image_dir
options['title'] = title
filename = tkFileDialog.askopenfilename(**file_opt)
if filename:
Utils.image_dir, Utils.image_file = os.path.split(filename)
return filename
@staticmethod
def ask_directory(title = 'Choose a directory', default_dir = None):
......
......@@ -70,23 +70,32 @@ if __name__ == "__main__":
tk.add_command("Configure", act.config_segmenter, 'g')
tk.add_separator()
tk.add_command("Execute", act.run_segmenter, 'S')
tk.add_separator()
tk.add_command("Assign using labeled image", act.assign_using_labeled_image, 'l')
tk.add_command("Execute folder", act.run_segmenter_folder)
tk.add_menu("Feature Extraction")
tk.add_command("Select extractors", act.select_extractors, 'e')
tk.add_separator()
tk.add_command("Execute", act.run_extractors, 'F')
tk.add_menu("Classification")
tk.add_menu("Training")
tk.add_command("Choose classifier", act.select_classifier)
tk.add_command("Configure", act.configure_classifier)
tk.add_separator()
tk.add_command("Execute", act.run_training, 'T')
tk.add_menu("Classification")
tk.add_command("Load h5 weight (only for CNNs)", act.open_weight)
tk.add_command("Execute", act.run_classifier, 'C')
tk.add_command("Execute folder", act.run_classifier_folder)
tk.add_menu("Experimenter")
tk.add_check_button("Ground Truth", act.toggle_ground_truth, default_state = False)
tk.add_separator()
tk.add_command("Cross Validation", act.cross_validation, 'X')
tk.add_command("Experimenter All", act.experimenter_all, 'p')
tk.add_separator()
tk.add_command("Execute", act.run_classifier, 'C')
tk.add_menu("Help")
tk.add_command("About Pynovisao", act.about, 'b')
......
This diff is collapsed.
......@@ -66,6 +66,7 @@ class Segmenter(object):
Implement this method to extend this class with a new segmenter algorithm.
"""
pass
@abstractmethod
def run(self, image):
......
......@@ -14,6 +14,8 @@ import os
import shutil
from skimage.util import img_as_float
from scipy import ndimage
from PIL import Image
import numpy as np
class File(object):
"""Set of utilities to handle files."""
......@@ -86,6 +88,33 @@ class File(object):
#return img_as_float(image)
return image
@staticmethod
def open_image_lut(filepath):
"""Open a image.
Parameters
----------
filepath : string
Filepath of a file.
Returns
-------
image : 3-channel color image.
Return the image opened.
Raises
------
IOError
Error opening the image.
"""
image = Image.open(filepath)
if image is None:
raise IOError('Image not opened')
return np.array(image)
@staticmethod
def save_image(image, directory, filename, ext = '.tif'):
"""Save a image.
......
......@@ -10,6 +10,8 @@
import cv2
import random
import time
from sklearn.metrics import confusion_matrix
import numpy as np
class ColorUtils(object):
"""Set of utilities to manipulate colors."""
......@@ -192,3 +194,178 @@ class TimeUtils(object):
Returns the time as a floating point number expressed in seconds since the epoch, in UTC.
"""
return time.time()
class MetricUtils(object):
"""Calculate segmentation metrics."""
@staticmethod
def confusion_matrix(eval_segm, gt_segm):
return confusion_matrix(gt_segm.flatten(), eval_segm.flatten())
@staticmethod
def pixel_accuracy(eval_segm, gt_segm):
"""sum_i(n_ii) / sum_i(t_i)"""
MetricUtils.check_size(eval_segm, gt_segm)
cl, n_cl = MetricUtils.extract_classes(gt_segm)
eval_mask, gt_mask = MetricUtils.extract_both_masks(eval_segm, gt_segm, cl, n_cl)
sum_n_ii = 0
sum_t_i = 0
for i, c in enumerate(cl):
curr_eval_mask = eval_mask[i, :, :]
curr_gt_mask = gt_mask[i, :, :]
sum_n_ii += np.sum(np.logical_and(curr_eval_mask, curr_gt_mask))
sum_t_i += np.sum(curr_gt_mask)
if (sum_t_i == 0):
pixel_accuracy_ = 0
else:
pixel_accuracy_ = sum_n_ii / sum_t_i
return pixel_accuracy_
@staticmethod
def mean_accuracy(eval_segm, gt_segm):
"""(1/n_cl) sum_i(n_ii/t_i)"""
MetricUtils.check_size(eval_segm, gt_segm)
cl, n_cl = MetricUtils.extract_classes(gt_segm)
eval_mask, gt_mask = MetricUtils.extract_both_masks(eval_segm, gt_segm, cl, n_cl)
accuracy = list([0]) * n_cl
for i, c in enumerate(cl):
curr_eval_mask = eval_mask[i, :, :]
curr_gt_mask = gt_mask[i, :, :]
n_ii = np.sum(np.logical_and(curr_eval_mask, curr_gt_mask))
t_i = np.sum(curr_gt_mask)
if (t_i != 0):
accuracy[i] = n_ii / t_i
mean_accuracy_ = np.mean(accuracy)
return mean_accuracy_, accuracy
@staticmethod
def mean_IU(eval_segm, gt_segm):
"""(1/n_cl) * sum_i(n_ii / (t_i + sum_j(n_ji) - n_ii))"""
MetricUtils.check_size(eval_segm, gt_segm)
cl, n_cl = MetricUtils.union_classes(eval_segm, gt_segm)
_, n_cl_gt = MetricUtils.extract_classes(gt_segm)
eval_mask, gt_mask = MetricUtils.extract_both_masks(eval_segm, gt_segm, cl, n_cl)
IU = list([0]) * n_cl
for i, c in enumerate(cl):
curr_eval_mask = eval_mask[i, :, :]
curr_gt_mask = gt_mask[i, :, :]
if (np.sum(curr_eval_mask) == 0) or (np.sum(curr_gt_mask) == 0):
continue
n_ii = np.sum(np.logical_and(curr_eval_mask, curr_gt_mask))
t_i = np.sum(curr_gt_mask)
n_ij = np.sum(curr_eval_mask)
IU[i] = n_ii / (t_i + n_ij - n_ii)
mean_IU_ = np.sum(IU) / n_cl_gt
return mean_IU_, IU
@staticmethod
def frequency_weighted_IU(eval_segm, gt_segm):
"""sum_k(t_k)^(-1) * sum_i((t_i*n_ii)/(t_i + sum_j(n_ji) - n_ii))"""
MetricUtils.check_size(eval_segm, gt_segm)
cl, n_cl = MetricUtils.union_classes(eval_segm, gt_segm)
eval_mask, gt_mask = MetricUtils.extract_both_masks(eval_segm, gt_segm, cl, n_cl)
frequency_weighted_IU_ = list([0]) * n_cl
for i, c in enumerate(cl):
curr_eval_mask = eval_mask[i, :, :]
curr_gt_mask = gt_mask[i, :, :]
if (np.sum(curr_eval_mask) == 0) or (np.sum(curr_gt_mask) == 0):
continue
n_ii = np.sum(np.logical_and(curr_eval_mask, curr_gt_mask))
t_i = np.sum(curr_gt_mask)
n_ij = np.sum(curr_eval_mask)
frequency_weighted_IU_[i] = (t_i * n_ii) / (t_i + n_ij - n_ii)
sum_k_t_k = MetricUtils.get_pixel_area(eval_segm)
frequency_weighted_IU_ = np.sum(frequency_weighted_IU_) / sum_k_t_k
return frequency_weighted_IU_
@staticmethod
def get_pixel_area(segm):
"""Auxiliary functions used during evaluation."""
return segm.shape[0] * segm.shape[1]
@staticmethod
def extract_both_masks(eval_segm, gt_segm, cl, n_cl):
eval_mask = MetricUtils.extract_masks(eval_segm, cl, n_cl)
gt_mask = MetricUtils.extract_masks(gt_segm, cl, n_cl)
return eval_mask, gt_mask
@staticmethod
def extract_classes(segm):
cl = np.unique(segm)
n_cl = len(cl)
return cl, n_cl
@staticmethod
def union_classes(eval_segm, gt_segm):
eval_cl, _ = MetricUtils.extract_classes(eval_segm)
gt_cl, _ = MetricUtils.extract_classes(gt_segm)
cl = np.union1d(eval_cl, gt_cl)
n_cl = len(cl)
return cl, n_cl
@staticmethod
def extract_masks(segm, cl, n_cl):
h, w = MetricUtils.segm_size(segm)
masks = np.zeros((n_cl, h, w))
for i, c in enumerate(cl):
masks[i, :, :] = segm == c
return masks
@staticmethod
def segm_size(segm):
try:
height = segm.shape[0]
width = segm.shape[1]
except IndexError:
raise
return height, width
@staticmethod
def check_size(eval_segm, gt_segm):
h_e, w_e = MetricUtils.segm_size(eval_segm)
h_g, w_g = MetricUtils.segm_size(gt_segm)
if (h_e != h_g) or (w_e != w_g):
raise EvalSegErr("DiffDim: Different dimensions of matrices!")
......@@ -206,6 +206,7 @@ class X11Colors(object):
idx = X11Colors.search_color(name)
return _x11_colors.values()[idx]
@staticmethod
def get_color_bgr(name):
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment