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 *.pyc
data/* data/*
!data/demo.jpg !data/demo.jpg
!data/pynovisao.png
!data/demo/.gitignore
...@@ -18,6 +18,27 @@ Pacote de Visão Computacional do Inovisão. ...@@ -18,6 +18,27 @@ Pacote de Visão Computacional do Inovisão.
$ python main.py $ 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 ## Dependências - Aplicação base - Python
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
Nome: Pynovisao.py Nome: Pynovisao.py
Autor: Alessandro dos Santos Ferreira ( santosferreira.alessandro@gmail.com ) 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 from collections import OrderedDict
...@@ -24,50 +24,70 @@ class Act(object): ...@@ -24,50 +24,70 @@ class Act(object):
segmenter = None segmenter = None
classes = None classes = None
dataset = None
__image = None __image = None
__const_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.tk = tk
self.segmenter = Slic.Slic() self.segmenter = Slic.Slic()
self.classes = [] self.dataset = args["dataset"]
self.add_class(dialog = False, color='green') self.__init_classes(args["classes"], args["colors"])
self.add_class(dialog = False, color='yellow')
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): def onclick(event):
if event.xdata != None and event.ydata != None and int(event.ydata) != 0: if event.xdata != None and event.ydata != None and int(event.ydata) != 0:
x = int(event.xdata) x = int(event.xdata)
y = int(event.ydata) y = int(event.ydata)
self.tk.write_log("Coordinates: x = %d y = %d ", x, y) 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]) segment, size_segment, idx_segment, run_time = self.segmenter.get_segment(x, y)
imagename = self.tk.utils.ask_image_name() 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: if imagename:
self.__image = f.open_image(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.write_log("Opening %s...", self.__image_name)
self.tk.add_image(self.__image, name, onclick) self.tk.add_image(self.__image, self.__image_name, onclick)
self.__const_image = self.__image 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): def close_image(self):
...@@ -77,31 +97,37 @@ class Act(object): ...@@ -77,31 +97,37 @@ class Act(object):
if self.tk.close_image(): if self.tk.close_image():
self.tk.write_log("Closing image...") self.tk.write_log("Closing image...")
self.__const_image = None self.__const_image = None
self.__segments = None
def add_class(self, dialog = True, name = None, color = None):
def add_class(self, dialog = True, color = None):
n_classes = len(self.classes) n_classes = len(self.classes)
if n_classes >= self.tk.MAX_CLASSES: if n_classes >= self.tk.MAX_CLASSES:
raise IException("You have reached the limite of %d classes" % self.tk.MAX_CLASSES) raise IException("You have reached the limite of %d classes" % self.tk.MAX_CLASSES)
def edit_class(index): def edit_class(index):
self.edit_class(index) self.edit_class(index)
def update_current_class(index):
self.update_current_class(index)
def process_config(): def process_config():
new_class = self.tk.get_config_and_destroy() new_class = self.tk.get_config_and_destroy()
new_class["name"].value = '_'.join(new_class["name"].value.split())
self.classes.append( new_class ) self.classes.append( new_class )
self.tk.write_log("New class: %s", new_class["name"].value) 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: if color is None:
color = X11Colors.Colors.random_color() color = X11Colors.Colors.random_color()
class_config = OrderedDict() 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["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) class_config["args"] = Config(label=None, value=n_classes, c_type=int, hidden=True)
if dialog == False: if dialog == False:
...@@ -109,23 +135,37 @@ class Act(object): ...@@ -109,23 +135,37 @@ class Act(object):
return return
title = "Add a new classe" 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 edit_class(self, index):
def process_update(index): def process_update(index):
updated_class = self.tk.get_config_and_destroy() updated_class = self.tk.get_config_and_destroy()
updated_class["name"].value = '_'.join(updated_class["name"].value.split())
self.classes[index] = updated_class self.classes[index] = updated_class
self.tk.write_log("Class updated: %s", updated_class["name"].value) 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] current_config = self.classes[index]
title = "Edit class %s" % current_config["name"].value title = "Edit class %s" % current_config["name"].value
self.tk.dialogue_config(title, current_config, lambda *_ : process_update(index)) 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): def run_segmentation(self):
if self.__const_image is None: if self.__const_image is None:
...@@ -134,8 +174,8 @@ class Act(object): ...@@ -134,8 +174,8 @@ class Act(object):
self.tk.write_log("Running %s,,,", self.segmenter.get_name()) self.tk.write_log("Running %s,,,", self.segmenter.get_name())
self.tk.append_log("\nConfig: %s", str(self.segmenter.get_summary_config())) 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.__image, run_time = self.segmenter.run(self.__const_image)
self.tk.append_log("\nTime elapsed: %0.3f seconds", run_time) self.tk.append_log("Time elapsed: %0.3f seconds", run_time)
self.tk.refresh_image(self.__image) self.tk.refresh_image(self.__image)
...@@ -150,10 +190,11 @@ class Act(object): ...@@ -150,10 +190,11 @@ class Act(object):
new_config = self.tk.get_config_and_destroy() new_config = self.tk.get_config_and_destroy()
self.segmenter.set_config(new_config) 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) self.tk.dialogue_config(title, current_config, process_config)
def func_not_available(self): def func_not_available(self):
self.tk.write_log("This functionality is not available right now.") self.tk.write_log("This functionality is not available right now.")
...@@ -127,7 +127,7 @@ class TkInterface(Interface): ...@@ -127,7 +127,7 @@ class TkInterface(Interface):
side = Tk.LEFT if left == True else Tk.RIGHT side = Tk.LEFT if left == True else Tk.RIGHT
self.__grid.pack(side=side, fill=Tk.Y, expand=False) 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: if self.__grid is None:
self.__grid = TkCustomFrame.CustomGrid(self.__root) self.__grid = TkCustomFrame.CustomGrid(self.__root)
self.clear_panel_classes() self.clear_panel_classes()
...@@ -140,15 +140,16 @@ class TkInterface(Interface): ...@@ -140,15 +140,16 @@ class TkInterface(Interface):
length = len(classes) length = len(classes)
for i in range(1, length+1): 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_radio_button(classes[i-1]["name"].value[:12], i, i, 1, 12, selected=True if i == selected+1 else False,
self.__grid.add_cell_button_color("", i, 2, 2, bg=classes[i-1]["color"].value,
command=classes[i-1]["callback"].value, command_args=classes[i-1]["args"].value ) 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) 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.clear_panel_classes()
self.add_panel_classes(classes) self.add_panel_classes(classes, selected)
def clear_panel_classes(self): def clear_panel_classes(self):
for i in range(1, self.MAX_CLASSES+1): for i in range(1, self.MAX_CLASSES+1):
......
...@@ -42,11 +42,11 @@ class CustomGrid(Tk.Frame): ...@@ -42,11 +42,11 @@ class CustomGrid(Tk.Frame):
command=lambda *_: command(command_args)) command=lambda *_: command(command_args))
bt.grid(row=row, column=column) 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, radio = Tk.Radiobutton(self, text=text, variable=self.v, value=value,
width=width, height=height, bg=bg, fg=fg, padx=4, pady=4, 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') activebackground='#404040', highlightbackground='white')
radio.grid(row=row, column=column) radio.grid(row=row, column=column)
......
...@@ -13,8 +13,10 @@ import tkColorChooser ...@@ -13,8 +13,10 @@ import tkColorChooser
class Utils(object): class Utils(object):
dialog_dir = '../data/' image_dir = '../data/'
dialog_file = 'demo.jpg' image_file = 'demo.jpg'
default_directory = '../data/demo'
@staticmethod @staticmethod
def ask_image_name(title = 'Open a image'): def ask_image_name(title = 'Open a image'):
...@@ -25,17 +27,32 @@ class Utils(object): ...@@ -25,17 +27,32 @@ class Utils(object):
('JPEG', ("*.JPG", "*.jpg", "*.JPEG", "*.jpeg")), ('JPEG', ("*.JPG", "*.jpg", "*.JPEG", "*.jpeg")),
('PNG', ("*.PNG", "*.png")), ('PNG', ("*.PNG", "*.png")),
('TIF', ("*.TIF", "*.tif"))] ('TIF', ("*.TIF", "*.tif"))]
options['initialdir'] = Utils.dialog_dir options['initialdir'] = Utils.image_dir
options['initialfile'] = Utils.dialog_file options['initialfile'] = Utils.image_file
#options['parent'] = self.__root #options['parent'] = self.__root
options['title'] = title options['title'] = title
filename = tkFileDialog.askopenfilename(**file_opt) filename = tkFileDialog.askopenfilename(**file_opt)
if filename: if filename:
Utils.dialog_dir, Utils.dialog_file = os.path.split(filename) Utils.image_dir, Utils.image_file = os.path.split(filename)
return 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 @staticmethod
def ask_color_choose(title = 'Choose a color', default_color = 'white'): def ask_color_choose(title = 'Choose a color', default_color = 'white'):
......
...@@ -5,28 +5,40 @@ ...@@ -5,28 +5,40 @@
Nome: pynovisao.py Nome: pynovisao.py
Autor: Alessandro dos Santos Ferreira ( santosferreira.alessandro@gmail.com ) 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 Pynovisao import Act
from interface import * 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__": if __name__ == "__main__":
tk = TkInterface.TkInterface("Pynovisao") tk = TkInterface.TkInterface("Pynovisao")
act = Act(tk) act = Act(tk, get_args())
tk.add_menu("File") tk.add_menu("File")
tk.add_command("Open a image", act.open_image, 'O') tk.add_command("Open a image", act.open_image, 'O')
tk.add_separator() 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_separator()
tk.add_command("Add Class", act.add_class, 'A') tk.add_check_button("Show log", tk.toogle_log)
tk.add_command("Set dataset path", act.func_not_available, 'd')
tk.add_separator() tk.add_separator()
tk.add_command("Rotate image", act.rotate_image, 't')
tk.add_command("Close image", act.close_image, 'W') tk.add_command("Close image", act.close_image, 'W')
tk.add_separator() tk.add_separator()
tk.add_command("Quit", tk.quit, 'Q') tk.add_command("Quit", tk.quit, 'Q')
......
...@@ -28,6 +28,14 @@ class Segmentation(object): ...@@ -28,6 +28,14 @@ class Segmentation(object):
def get_summary_config(self): def get_summary_config(self):
pass 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 @abstractmethod
def run(self, image): def run(self, image):
pass pass
...@@ -8,55 +8,159 @@ ...@@ -8,55 +8,159 @@
Descricão: TODO. Descricão: TODO.
""" """
import cv2
import numpy as np import numpy as np
from skimage.segmentation import slic from skimage.segmentation import slic
from skimage.segmentation import mark_boundaries 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.Config import Config
from util.Utils import TimeUtils from util.Utils import TimeUtils
from util.X11Colors import Colors
from Segmentation import Segmentation from Segmentation import Segmentation
class Slic(Segmentation): class Slic(Segmentation):
segments = None n_segments = None
sigma = None sigma = None
compactness = None compactness = None
border_color = None
border_outline = None
def __init__(self, n_segments = 100, sigma = 5.0, compactness = 10.0): __segments = None
self.segments = Config("Segments", n_segments, int)
__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.sigma = Config("Sigma", sigma, float)
self.compactness = Config("Compactness", compactness, 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): def get_config(self):
return { slic_config = OrderedDict()
"segments" : self.segments,
"sigma" : self.sigma,