Commit ff317b60 authored by Alessandro dos Santos Ferreira's avatar Alessandro dos Santos Ferreira
Browse files

Pynovisao - Gerador de banco de imagens

parent 26e0252b
*.pyc
data/*
!data/demo.jpg
!data/pynovisao.png
!data/demo/.gitignore
......@@ -18,6 +18,27 @@ Pacote de Visão Computacional do Inovisão.
$ python main.py
```
- Uma imagem como a mostrada abaixo deve ser apresentada:
![pynovisao](data/pynovisao.png)
## Outras Opções
- Mostra todas as opções disponíveis
```
$ python slicParametros.py --help
```
- Executa o programa inicializando o banco de imagens em *../data/soja*
```
$ python slicParametros.py --dataset ../data/digits
```
- Executa o programa definindo as classes e suas respectivas cores (X11 color names)
```
$ python slicParametros.py --classes "Solo Soja Gramineas FolhasLargas" --colors "Orange SpringGreen RebeccaPurple Snow"
```
## Dependências - Aplicação base - Python
......
......@@ -5,7 +5,7 @@
Nome: Pynovisao.py
Autor: Alessandro dos Santos Ferreira ( santosferreira.alessandro@gmail.com )
Descricão: TODO
Descricão: Classe que contem a implementacao de todas as acoes disponiveis no programa.
"""
from collections import OrderedDict
......@@ -24,50 +24,70 @@ class Act(object):
segmenter = None
classes = None
dataset = None
__image = None
__const_image = None
__segments = None
__image_name = None
__current_class = None
def __init__(self, tk):
def __init__(self, tk, args):
self.tk = tk
self.segmenter = Slic.Slic()
self.classes = []
self.add_class(dialog = False, color='green')
self.add_class(dialog = False, color='yellow')
self.dataset = args["dataset"]
self.__init_classes(args["classes"], args["colors"])
def open_image(self):
def __init_classes(self, classes = None, colors = None):
self.classes = []
classes = sorted(f.list_dirs(self.dataset)) if classes is None else classes.split()
colors = [] if colors is None else colors.split()
if(len(classes) > 0):
for i in range(0, len(classes)):
self.add_class(dialog = False, name=classes[i], color=colors[i] if i < len(colors) else None)
else:
self.add_class(dialog = False, color='Green')
self.add_class(dialog = False, color='Yellow')
self.__current_class = 0
def open_image(self, imagename = None):
def onclick(event):
if event.xdata != None and event.ydata != None and int(event.ydata) != 0:
x = int(event.xdata)
y = int(event.ydata)
self.tk.write_log("Coordinates: x = %d y = %d ", x, y)
if self.__segments is not None:
self.tk.append_log("Segment = %d", segments[y, x])
imagename = self.tk.utils.ask_image_name()
self.tk.write_log("Coordinates: x = %d y = %d", x, y)
segment, size_segment, idx_segment, run_time = self.segmenter.get_segment(x, y)
if size_segment > 0:
self.tk.append_log("\nSegment = %d: %0.3f seconds", idx_segment, run_time)
self.__image, run_time = self.segmenter.paint_segment(self.__image, self.classes[self.__current_class]["color"].value, x, y)
self.tk.append_log("Painting segment: %0.3f seconds", run_time)
self.tk.refresh_image(self.__image)
filepath = f.save_image(segment, self.dataset, self.classes[self.__current_class]["name"].value, self.__image_name, idx_segment)
if filepath:
self.tk.append_log("\nSegment saved in %s", filepath)
if imagename is None:
imagename = self.tk.utils.ask_image_name()
if imagename:
self.__image = f.open_image(imagename)
name = f.get_filename(imagename)
self.__image_name = f.get_filename(imagename)
self.tk.write_log("Opening %s...", name)
self.tk.add_image(self.__image, name, onclick)
self.tk.write_log("Opening %s...", self.__image_name)
self.tk.add_image(self.__image, self.__image_name, onclick)
self.__const_image = self.__image
def rotate_image(self):
if self.__const_image is None:
raise IException("Image not found")
image = self.f.rotate_image(self.__const_image)
self.tk.write_log("Rotating image 90 degrees")
self.tk.refresh_image(self.__image)
self.__const_image = self.__image
def close_image(self):
......@@ -77,31 +97,37 @@ class Act(object):
if self.tk.close_image():
self.tk.write_log("Closing image...")
self.__const_image = None
self.__segments = None
def add_class(self, dialog = True, color = None):
def add_class(self, dialog = True, name = None, color = None):
n_classes = len(self.classes)
if n_classes >= self.tk.MAX_CLASSES:
raise IException("You have reached the limite of %d classes" % self.tk.MAX_CLASSES)
def edit_class(index):
self.edit_class(index)
def update_current_class(index):
self.update_current_class(index)
def process_config():
new_class = self.tk.get_config_and_destroy()
new_class["name"].value = '_'.join(new_class["name"].value.split())
self.classes.append( new_class )
self.tk.write_log("New class: %s", new_class["name"].value)
self.tk.refresh_panel_classes(self.classes)
self.tk.refresh_panel_classes(self.classes, self.__current_class)
if name is None:
name = "Class_%02d" % (n_classes+1)
if color is None:
color = X11Colors.Colors.random_color()
class_config = OrderedDict()
class_config["name"] = Config(label="Name", value="Class %02d" % (n_classes+1), c_type=str)
class_config["name"] = Config(label="Name", value=name, c_type=str)
class_config["color"] = Config(label="Color (X11 Colors)", value=color, c_type='color')
class_config["callback"] = Config(label=None, value=edit_class, c_type=None, hidden=True)
class_config["callback"] = Config(label=None, value=update_current_class, c_type=None, hidden=True)
class_config["callback_color"] = Config(label=None, value=edit_class, c_type=None, hidden=True)
class_config["args"] = Config(label=None, value=n_classes, c_type=int, hidden=True)
if dialog == False:
......@@ -109,23 +135,37 @@ class Act(object):
return
title = "Add a new classe"
self.tk.dialogue_config(title, class_config, process_config)
self.tk.dialogue_config(title, class_config, process_config)
def edit_class(self, index):
def process_update(index):
updated_class = self.tk.get_config_and_destroy()
updated_class["name"].value = '_'.join(updated_class["name"].value.split())
self.classes[index] = updated_class
self.tk.write_log("Class updated: %s", updated_class["name"].value)
self.tk.refresh_panel_classes(self.classes)
self.tk.refresh_panel_classes(self.classes, self.__current_class)
current_config = self.classes[index]
title = "Edit class %s" % current_config["name"].value
self.tk.dialogue_config(title, current_config, lambda *_ : process_update(index))
def update_current_class(self, index):
self.__current_class = index
def set_dataset_path(self):
directory = self.tk.utils.ask_directory(default_dir = self.dataset)
if directory:
self.dataset = directory
self.tk.write_log("Image dataset defined: %s", self.dataset)
self.__init_classes()
self.tk.refresh_panel_classes(self.classes)
def run_segmentation(self):
if self.__const_image is None:
......@@ -134,8 +174,8 @@ class Act(object):
self.tk.write_log("Running %s,,,", self.segmenter.get_name())
self.tk.append_log("\nConfig: %s", str(self.segmenter.get_summary_config()))
self.__segments, self.__image, run_time = self.segmenter.run(self.__const_image)
self.tk.append_log("\nTime elapsed: %0.3f seconds", run_time)
self.__image, run_time = self.segmenter.run(self.__const_image)
self.tk.append_log("Time elapsed: %0.3f seconds", run_time)
self.tk.refresh_image(self.__image)
......@@ -150,10 +190,11 @@ class Act(object):
new_config = self.tk.get_config_and_destroy()
self.segmenter.set_config(new_config)
self.tk.append_log("\nConfig updated: %s", str(self.segmenter.get_summary_config()))
self.tk.append_log("\nConfig updated:\n%s", str(self.segmenter.get_summary_config()))
self.tk.dialogue_config(title, current_config, process_config)
def func_not_available(self):
self.tk.write_log("This functionality is not available right now.")
......@@ -127,7 +127,7 @@ class TkInterface(Interface):
side = Tk.LEFT if left == True else Tk.RIGHT
self.__grid.pack(side=side, fill=Tk.Y, expand=False)
def add_panel_classes(self, classes = None):
def add_panel_classes(self, classes = None, selected = 0):
if self.__grid is None:
self.__grid = TkCustomFrame.CustomGrid(self.__root)
self.clear_panel_classes()
......@@ -140,15 +140,16 @@ class TkInterface(Interface):
length = len(classes)
for i in range(1, length+1):
self.__grid.add_cell_radio_button(classes[i-1]["name"].value[:12], i, i, 1, 12, selected=False if i > 1 else True)
self.__grid.add_cell_button_color("", i, 2, 2, bg=classes[i-1]["color"].value,
self.__grid.add_cell_radio_button(classes[i-1]["name"].value[:12], i, i, 1, 12, selected=True if i == selected+1 else False,
command=classes[i-1]["callback"].value, command_args=classes[i-1]["args"].value )
self.__grid.add_cell_button_color("", i, 2, 2, bg=classes[i-1]["color"].value,
command=classes[i-1]["callback_color"].value, command_args=classes[i-1]["args"].value )
self.__grid.pack(side=Tk.LEFT, fill=Tk.Y, expand=False)
def refresh_panel_classes(self, classes = None):
def refresh_panel_classes(self, classes = None, selected = 0):
self.clear_panel_classes()
self.add_panel_classes(classes)
self.add_panel_classes(classes, selected)
def clear_panel_classes(self):
for i in range(1, self.MAX_CLASSES+1):
......
......@@ -42,11 +42,11 @@ class CustomGrid(Tk.Frame):
command=lambda *_: command(command_args))
bt.grid(row=row, column=column)
def add_cell_radio_button(self, text, value, row, column, width=0, height=0, bg='white', fg="black", selected=False, command=None):
def add_cell_radio_button(self, text, value, row, column, width=0, height=0, bg='white', fg="black", selected=False, command=None, command_args=None):
radio = Tk.Radiobutton(self, text=text, variable=self.v, value=value,
width=width, height=height, bg=bg, fg=fg, padx=4, pady=4,
indicatoron=1, anchor=Tk.W,
indicatoron=1, anchor=Tk.W, command=lambda *_: command(command_args),
activebackground='#404040', highlightbackground='white')
radio.grid(row=row, column=column)
......
......@@ -13,8 +13,10 @@ import tkColorChooser
class Utils(object):
dialog_dir = '../data/'
dialog_file = 'demo.jpg'
image_dir = '../data/'
image_file = 'demo.jpg'
default_directory = '../data/demo'
@staticmethod
def ask_image_name(title = 'Open a image'):
......@@ -25,17 +27,32 @@ class Utils(object):
('JPEG', ("*.JPG", "*.jpg", "*.JPEG", "*.jpeg")),
('PNG', ("*.PNG", "*.png")),
('TIF', ("*.TIF", "*.tif"))]
options['initialdir'] = Utils.dialog_dir
options['initialfile'] = Utils.dialog_file
options['initialdir'] = Utils.image_dir
options['initialfile'] = Utils.image_file
#options['parent'] = self.__root
options['title'] = title
filename = tkFileDialog.askopenfilename(**file_opt)
if filename:
Utils.dialog_dir, Utils.dialog_file = os.path.split(filename)
Utils.image_dir, Utils.image_file = os.path.split(filename)
return filename
@staticmethod
def ask_directory(title = 'Choose a directory', default_dir = None):
dir_opt = options = {}
options['initialdir'] = Utils.default_directory if default_dir is None else default_dir
options['mustexist'] = False
#options['parent'] = root
options['title'] = title
directory = tkFileDialog.askdirectory(**dir_opt)
if directory:
Utils.default_directory = directory
return directory
@staticmethod
def ask_color_choose(title = 'Choose a color', default_color = 'white'):
......
......@@ -5,28 +5,40 @@
Nome: pynovisao.py
Autor: Alessandro dos Santos Ferreira ( santosferreira.alessandro@gmail.com )
Descricão: TODO
Descricão: Arquivo main do programa.
"""
import argparse
from Pynovisao import Act
from interface import *
def get_args():
ap = argparse.ArgumentParser()
ap.add_argument("-d", "--dataset", required=False, help="Dataset path", default="../data/demo", type=str)
ap.add_argument("-cl", "--classes", required=False, help="Classes names", default=None, type=str)
ap.add_argument("-co", "--colors", required=False, help="Classes colors (X11 color)", default=None, type=str)
return vars(ap.parse_args())
if __name__ == "__main__":
tk = TkInterface.TkInterface("Pynovisao")
act = Act(tk)
act = Act(tk, get_args())
tk.add_menu("File")
tk.add_command("Open a image", act.open_image, 'O')
tk.add_separator()
tk.add_check_button("Show Log", tk.toogle_log)
tk.add_command("Add new class", act.add_class, 'A')
tk.add_command("Set dataset path", act.set_dataset_path, 'd')
tk.add_separator()
tk.add_command("Add Class", act.add_class, 'A')
tk.add_command("Set dataset path", act.func_not_available, 'd')
tk.add_check_button("Show log", tk.toogle_log)
tk.add_separator()
tk.add_command("Rotate image", act.rotate_image, 't')
tk.add_command("Close image", act.close_image, 'W')
tk.add_separator()
tk.add_command("Quit", tk.quit, 'Q')
......
......@@ -28,6 +28,14 @@ class Segmentation(object):
def get_summary_config(self):
pass
@abstractmethod
def get_segment(self, px, py, idx_segment):
pass
@abstractmethod
def paint_segment(self, image, color, px, py, idx_segment, border, clear):
pass
@abstractmethod
def run(self, image):
pass
......@@ -8,55 +8,159 @@
Descricão: TODO.
"""
import cv2
import numpy as np
from skimage.segmentation import slic
from skimage.segmentation import mark_boundaries
from skimage.util import img_as_float
from skimage.util import img_as_float, img_as_ubyte
from collections import OrderedDict
from util.Config import Config
from util.Utils import TimeUtils
from util.X11Colors import Colors
from Segmentation import Segmentation
class Slic(Segmentation):
segments = None
n_segments = None
sigma = None
compactness = None
border_color = None
border_outline = None
def __init__(self, n_segments = 100, sigma = 5.0, compactness = 10.0):
self.segments = Config("Segments", n_segments, int)
__segments = None
__original_image = None
def __init__(self, n_segments = 100, sigma = 5.0, compactness = 10.0, border_color = 'Yellow', border_outline = 'No'):
self.n_segments = Config("Segments", n_segments, int)
self.sigma = Config("Sigma", sigma, float)
self.compactness = Config("Compactness", compactness, float)
self.border_color = Config("Border Color", border_color, 'color')
self.border_outline = Config("Border Outline", border_outline, str)
def get_config(self):
return {
"segments" : self.segments,
"sigma" : self.sigma,
"compactness" : self.compactness
}
slic_config = OrderedDict()
slic_config["n_segments"] = self.n_segments
slic_config["sigma"] = self.sigma
slic_config["compactness"] = self.compactness
slic_config["border_color"] = self.border_color
slic_config["border_outline"] = self.border_outline
return slic_config
def set_config(self, configs):
self.segments = Config.nvl_config(configs["segments"], self.segments)
self.n_segments = Config.nvl_config(configs["n_segments"], self.n_segments)
self.sigma = Config.nvl_config(configs["sigma"], self.sigma)
self.compactness = Config.nvl_config(configs["compactness"], self.compactness)
self.border_color = Config.nvl_config(configs["border_color"], self.border_color)
self.border_outline = Config.nvl_config(configs["border_outline"], self.border_outline)
self.border_outline.value = self.border_outline.value if self.border_outline.value == 'Yes' else 'No'
def get_summary_config(self):
return {
self.segments.label : self.segments.value,
self.sigma.label : self.sigma.value,
self.compactness.label : self.compactness.value
}
slic_config = OrderedDict()
slic_config[self.n_segments.label] = self.n_segments.value
slic_config[self.sigma.label] = self.sigma.value
slic_config[self.compactness.label] = self.compactness.value
slic_config[self.border_color.label] = self.border_color.value
slic_config[self.border_outline.label] = self.border_outline.value
summary = ''
for config in slic_config:
summary += "%s: %s\n" % (config, str(slic_config[config]))
return summary
def get_segment(self, px = 0, py = 0, idx_segment = None):
if self.__segments is None:
return None, 0, -1, 0
start_time = TimeUtils.get_time()
if idx_segment is None:
idx_segment = self.__segments[py, px]
mask_segment = np.zeros(self.__original_image.shape[:2], dtype="uint8")
mask_segment[self.__segments == idx_segment] = 255
size_segment = mask_segment[self.__segments == idx_segment].size
segment = self.__original_image.copy()
segment = cv2.bitwise_and(segment, segment, mask=mask_segment)
contours, _ = cv2.findContours(mask_segment,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)[-2:]
m = -1
max_contour = None
for cnt in contours:
if (len(cnt) > m):
m = len(cnt)
max_contour = cnt
x,y,w,h = cv2.boundingRect(max_contour)
segment = segment[y:y+h, x:x+w]
end_time = TimeUtils.get_time()
return segment, size_segment, idx_segment, (end_time - start_time)
def paint_segment(self, image, color, px = 0, py = 0, idx_segment = None, border = True, clear = False):
if self.__segments is None:
return image, 0
start_time = TimeUtils.get_time()
if idx_segment is None:
idx_segment = self.__segments[py, px]
height, width, channels = self.__original_image.shape
mask_segment = np.zeros(self.__original_image.shape[:2], dtype="uint8")
mask_segment[self.__segments == idx_segment] = 255
mask_inv = cv2.bitwise_not(mask_segment)
class_color = np.zeros((height,width,3), np.uint8)
class_color[:, :] = Colors.get_color(color)
if clear == False:
colored_image = cv2.addWeighted(self.__original_image, 0.7, class_color, 0.3, 0)
else:
colored_image = self.__original_image
colored_image = cv2.bitwise_and(colored_image, colored_image, mask=mask_segment)
new_image = cv2.bitwise_and(image, image, mask=mask_inv)
mask_segment[:] = 255
new_image = cv2.bitwise_or(new_image, colored_image, mask=mask_segment)
if border == True:
color = Colors.get_color_zero_one(self.border_color.get_cast_val())
outline_color = color if self.border_outline.value == 'Yes' else None
new_image = img_as_ubyte( mark_boundaries(img_as_float(new_image), self.__segments.astype(np.int8), color=color, outline_color=outline_color) )
end_time = TimeUtils.get_time()
return new_image, (end_time - start_time)
def run(self, image):
self.__original_image = image
color = Colors.get_color_zero_one(self.border_color.get_cast_val())
outline_color = color if self.border_outline.value == 'Yes' else None
start_time = TimeUtils.get_time()
segments = slic(img_as_float(image),
n_segments=self.segments.get_cast_val(),
self.__segments = slic(img_as_float(image),
n_segments=self.n_segments.get_cast_val(),
sigma=self.sigma.get_cast_val(),
compactness=self.compactness.get_cast_val())
end_time = TimeUtils.get_time()
return segments, mark_boundaries(image, segments.astype(np.int8)), (end_time - start_time)
return img_as_ubyte( mark_boundaries(image, self.__segments.astype(np.int8), color=color, outline_color=outline_color) ), (end_time - start_time)
......@@ -30,19 +30,32 @@ class File(object):
@staticmethod
def open_image(filepath):
try:
image = cv2.imread(filepath)
image = cv2.imread(filepath)
#image = np.flipud(image)
image = cv2.cvtColor(