Etiquetador de imágenes



In [35]:
from __future__ import print_function
#Importss
%matplotlib inline
import numpy as np

Añadimos capacidad de mostrar notificaciones


In [36]:
%%javascript

// Añadimos script que nos permite mostrar notificaciones bootstrap-growl
$('head').append('<script src="../../rsc/bootstrap-growl/jquery.bootstrap-growl.min.js">');

$('head').append('<link rel="stylesheet" href="Style.css">');


Creamos el Widget Etiquetador de imágenes


In [37]:
import ipywidgets as widgets
from traitlets import Unicode, validate, Bool, CBool, Any, List, observe
from ipywidgets import Color
from skimage import io
from skimage.transform import resize

class ImageLabelerWidget(widgets.DOMWidget):
    _view_name = Unicode('ImageLabelerView').tag(sync=True)
    _view_module = Unicode('ImageLabeler').tag(sync=True)
    # Atributos sincronos entre js y python
    image = Unicode('blank.png').tag(sync=True)
    rect = List().tag(sync=True)
    rect_to_add = List().tag(sync=True)
    text_to_add = List().tag(sync=True)
    
    removed_rect = List().tag(sync=True)
    # Intento de carga de imágen incorrecto notificación
    error_loading = Unicode('').tag(sync=True)
    # Imagen guardada correctamente notificación
    saved_successfuly = Unicode('').tag(sync=True)
    # Texto a incluir en el rectangulo
    text = Unicode('').tag(sync=True)
    
    Phytolith_type = Any().tag(sync=True)
    
    def __init__(self, *pargs, **kwargs):
        super(widgets.DOMWidget, self).__init__(*pargs, **kwargs)
        self._rectList = []
        self._removed_rectList = []
        # Para patron observer
        self._rectList_observers = []
        self._removed_rectList_observers = []
    
    '''Método que se encarga de recibir la notificación
    de la adicción de unas nuevas coordenadas a la imagen'''
    @observe('rect')
    def new_rect(self, change):
        rec = change["new"]
        self._rectList.append(rec)
        # Callback a observers
        for callback in self._rectList_observers:
            callback(self._rectList)
            
    '''Método que se encarga de recibir la notificación
    de la eliminación de unas nuevas coordenadas a la imagen'''
    @observe('removed_rect')
    def old_rect(self, change):
        removed_rec = change["new"]
        #print("removed_rect", removed_rec)
        #Cambiamos indices
        self._removed_rectList.append(removed_rec)
        # Callback a observers
        for callback in self._removed_rectList_observers:
            callback(self._removed_rectList)
    
    '''Método para enlazar al observer con
    el atributo rectList'''
    def rectList_bind_to(self, callback):
        self._rectList_observers.append(callback)
    
    '''Método para enlazar al observer con
    el atributo removed_rect'''
    def removed_rect_bind_to(self, callback):
        self._removed_rectList_observers.append(callback)
    
    def get_rectList(self):
        return self._rectList
    
    def set_rectList(self, value):
        self._rectList = value

In [38]:
import csv, json
from datetime import datetime

''' Rescalamos las coordenadas para que coincidan 
las coordenadas de la vista con las de la imagen original'''
def transform_view_coords_to_image_coords(coords, svg_h, svg_w, real_image_h, real_image_w):
    # TODO Cuidado con si es menor de  svg_size x svg_size
#     print("\t\t>>>Coords entrantes a conversión: ", coords)
    # Rescalado de la imagen en la vista
    if real_image_h > real_image_w:
        coef = real_image_h / svg_h
        svg_image_h = svg_h
        svg_image_w = round(real_image_w / coef)
    else:
        coef = real_image_w / svg_w
        svg_image_w = svg_w
        svg_image_h = round(real_image_h / coef)
        
    # Coeficientes de rescalado
    coef_h = real_image_h / svg_image_h
    coef_w = real_image_w / svg_image_w
    
    # Calculamos padding en función de
    # los valores de ancho y alto de la imagen
    # en la vista
    svg_padding_h = (svg_h - svg_image_h) / 2
    svg_padding_w = (svg_w - svg_image_w) / 2
    
    #TODO comprobar que las coordenadas no se salen de la imagen
    
    new_coords = []
    # Por cada rectangulo generamos una nueva imagen
    # siempre que se encuentre dentro de la imagen
    for rect_x, rect_y, rect_height, rect_width in coords:
        rect_x = round((rect_x - svg_padding_h) * coef_h)
        rect_y = round((rect_y - svg_padding_w) * coef_w)
        rect_height = round(rect_height * coef_h)
        rect_width = round(rect_width * coef_w)
        rect_x2 = rect_x+rect_height
        rect_y2 = rect_y+rect_width
        
#         print("\t\t>>>Coords pre salientes conversión: ", coords)
#         print(rect_x, rect_x2, rect_y, rect_y2)
        # Comprobamos que las coordenadas estan dentro de la imagen
        if rect_x > 0 and rect_x2 < real_image_h \
        and rect_y2 < real_image_w and rect_y > 0:
            new_coords.append([rect_x, rect_x2, rect_y, rect_y2])  
#         else:
#             print(rect_x, ">", 0," and", rect_x2, "<", real_image_h, "and", rect_y2, "<", real_image_w, "and", rect_y, ">", 0)
#             print("NO CONVERTIDA: ",[rect_x, rect_x2, rect_y, rect_y2])
    return new_coords

In [39]:
''' Rescalamos las coordenadas para que coincidan 
las coordenadas de la vista con las de la imagen original'''
def transform_image_coords_to_view_coords(coords, svg_h, svg_w, real_image_h, real_image_w):
    # Rescalado de la imagen en la vista
    if real_image_h > real_image_w:
        coef = real_image_h / svg_h
        svg_image_h = svg_h
        svg_image_w = round(real_image_w / coef)
    else:
        coef = real_image_w / svg_w
        svg_image_w = svg_w
        svg_image_h = round(real_image_h / coef)
        
    # Coeficientes de rescalado
    coef_h = real_image_h / svg_image_h
    coef_w = real_image_w / svg_image_w
    
    # Calculamos padding en función de
    # los valores de ancho y alto de la imagen
    # en la vista
    svg_padding_h = (svg_h - svg_image_h) / 2
    svg_padding_w = (svg_w - svg_image_w) / 2
    
    new_coords = []
    # Por cada rectangulo generamos una nueva imagen
    # siempre que se encuentre dentro de la imagen
    for rect_x, rect_y, rect_height, rect_width in coords:
        rect_x = round((rect_x / coef_h) + svg_padding_h)
        rect_y = round((rect_y / coef_w) + svg_padding_w)
        rect_height = round(rect_height / coef_h)
        rect_width = round(rect_width / coef_w)

        new_coords.append([rect_x, rect_y, rect_height, rect_width])
    
    
    return new_coords

In [40]:
def additional_text():
    # Timestamp for jpg files
    time = datetime.today()
    year = time.year
    month = time.month
    day = time.day
    hour = time.hour
    minute = time.minute
    
    return str(year) + '_' + str(month) + '_' + str(day) \
+ '_' + str(hour) + '_' + str(minute)

'''Función que genera las imágenes a partir de las coordenadas'''
def save_coords_as_images(coords, image, dest_path, image_name):
    # Por cada conjunto de rectangulos generamos una nueva imagen
    for rect_x, rect_x2, rect_y, rect_y2 in coords:
        name = image_name + str(rect_x) + \
        'X'+ str(rect_x2) + 'X2'+str(rect_y) \
        + 'Y' + str(rect_y2) + 'Y2' + ".jpg"
        
        img = image[rect_x:rect_x2,rect_y:rect_y2]
        io.imsave(dest_path+name, img, quality=100)

'''Función que genera un fichero CSV a partir de las coordenadas'''
def save_coords_as_csv(coords, dest_path, image_name):
    with open(dest_path+image_name+'.csv', 'w', newline='') as csvfile:
        coords_writer = csv.writer(csvfile, delimiter=',',
                                   quotechar='|', quoting = csv.QUOTE_MINIMAL)
        # Por cada conjunto de rectangulos generamos una nueva imagen
        for rect_x, rect_x2, rect_y, rect_y2 in coords:
            coords_writer.writerow([rect_x,rect_x2,rect_y,rect_y2])

'''Función que genera un fichero JSON a partir de las coordenadas'''
def save_coords_as_json(coords, dest_path, image_name):
    with open(dest_path + image_name+'.json', 'w') as jsonfile:
        coords_dict = dict()
        coords_dict[image_name+'.jpg'] = coords
        
        json.dump(coords_dict, jsonfile)

'''Función que genera todos los ficheros anteriores'''
def save_as_all_formats(coords, image, dest_path, image_name):
    save_coords_as_images(coords, image, dest_path, image_name)
    save_coords_as_csv(coords, dest_path, image_name)
    save_coords_as_json(coords, dest_path, image_name)

In [41]:
%%javascript

//Añadimos estilo a las notificaciones:

var error_type ='danger';
var success_type ='success';
var info_type = 'info';

var notify_style = {
  ele: 'body', // which element to append to
  offset: {from: 'bottom', amount: 20}, // 'top', or 'bottom'
  align: 'left', // ('left', 'right', or 'center')
  width: 'auto',//250, // (integer, or 'auto')
  delay: 4000, // Time while the message will be displayed. It's not equivalent to the *demo* timeOut!
  allow_dismiss: true, // If true then will display a cross to close the popup.
  stackup_spacing: 10, // spacing between consecutively stacked growls.
}

function change_style(style, type){
    style['type'] = type;
}

function sucess_image_notify(image_name){
    change_style(notify_style, success_type);
    if (image_name != "blank.png"){
        var notify_msg = "La imagen " + image_name + " ha sido cargada correctamente!";
        $.bootstrapGrowl(notify_msg, notify_style);
    }
}

function error_image_notify(file_name){
    change_style(notify_style, error_type);
    if(file_name != "blank.png"){
        var notify_msg = "El fichero " + file_name + " no " +
            "tiene formato de imagen. Ejemplo: jpeg, jpg, tif, tiff, png ";
        $.bootstrapGrowl(notify_msg, notify_style);
    }
}

function success_saving_labels_notify(file_name){
    change_style(notify_style, success_type);
    if(file_name != "blank.png"){
        var notify_msg = " Las etiquetas para la imagen " + file_name + " han " +
            "sido guardadas correctamente ";
        $.bootstrapGrowl(notify_msg, notify_style);
    }
}

// JS Widget

require.undef('ImageLabeler');

var startX, startY;
var count = 0;
var rect = null;
var rectsList = [];
var model;
var myThis;

function getMousePos(evt) {
    // Obtenemos posición absoluta del elemento svg
    var svgRef = document.getElementById("svg");
    var htmlPos = svgRef.getBoundingClientRect();
    return {
        x: evt.clientX - htmlPos.left,
        y: evt.clientY - htmlPos.top
    };
}

function updateRect(eve, rect){
    var pos = getMousePos(eve);
    var endX = pos.x;
    var endY = pos.y;
    if (endX - startX > 0){
        var x = startX
    }else{
        var x = endX;
    }

    if (endY - startY > 0){
        var y = startY
    }else{
        var y = endY;
    }
    var width = Math.abs(endX - startX);
    var height = Math.abs(endY - startY);
    
    rect.setAttributeNS(null, 'x', x);
    rect.setAttributeNS(null, 'y', y);
    rect.setAttributeNS(null, 'height', height);
    rect.setAttributeNS(null, 'width', width);
}

function setStyleRect(rect){
    rect.setAttributeNS(null, 'fill', 'transparent');
    rect.setAttributeNS(null, 'stroke', 'green');
    rect.setAttributeNS(null, 'linewidth', '10px');
}

function mouseMove(eve) {
    if(rect){
        updateRect(eve, rect);
      }
}

function mouseClick(eve) {
    if (rect !== null) {
        //Cada vez que finalizamos el rectangulo
        // , le añadimos a la lista
        //console.log(rect.x['baseVal'].value,rect.y['baseVal'].value,rect.width['baseVal'].value,rect.height['baseVal'].value);
        rectsList = [];
        model.set('rect',[rect.y['baseVal'].value, rect.x['baseVal'].value, rect.height['baseVal'].value, rect.width['baseVal'].value]);
        var svg = document.getElementById('svg');
        var x= rect.x['baseVal'].value;
        var y= rect.y['baseVal'].value;
        
        var text = model.get('text');
        // Añadimos la etiqueta de texto que deseemos al rectangulo
        svg.innerHTML += "<text class='text' id='" + (count-1) + "txt" +"' x='"+ x +"' y='"+ y +"' font-family='Verdana' font-size='14px'>"+ text +"</text>"
        
        var close = document.createElementNS("http://www.w3.org/2000/svg", "text");
        close.setAttribute("id","closeParent"+(count-1));
        close.setAttribute("class","close");
        close.setAttribute("x",x);
        close.setAttribute("y",y+14);
        close.setAttribute("font-family","Verdana");
        close.setAttribute("font-size","15px");
        
        close.innerHTML += "<tspan id='close" + (count-1) + "' >x</tspan>";
        svg.appendChild(close);
        
        myThis.touch();
        
        rect = null;
        //console.log("finsihed.");     
    } else {
        var text = $(event.target);
        if (text.html() != "x"){
            var pos = getMousePos(eve);

            startX= pos.x;
            startY = pos.y;

            //console.log("begun.");
        
            if(!rect){
                rect = document.createElementNS("http://www.w3.org/2000/svg", 'rect');
                rect.id = 'rect' + count;
                rect.setAttribute('class', 'rect');
                
                count = count + 1;

                updateRect(eve, rect);

                setStyleRect(rect);
                document.getElementById('svg').appendChild(rect);
            }
        }else{
            var closeId = text.attr("id");
            //console.log(closeId);
            var id = closeId[closeId.length - 1];
            var closeParentId = "closeParent" + id;
            var rectId = "rect" + id;
            var textId = id + "txt";
            
            //Obtenemos coordenadas del rectangulo
            var removed_rect = document.getElementById(rectId);
            
            //console.log('Eliminamos rect.')
            // Transmitimos al kernel, python, el
            // rectangulo a eliminar
            model.set('removed_rect',[removed_rect.y['baseVal'].value, removed_rect.x['baseVal'].value, removed_rect.height['baseVal'].value, removed_rect.width['baseVal'].value]);
            myThis.touch();
            // Eliminamos todo lo relativo al rectangulo
            document.getElementById(closeParentId).remove();
            document.getElementById(rectId).remove();
            document.getElementById(textId).remove();
        }
    }
}

// Funciones para la carga y cambio de imagen
function loadImage(svg, src){
    $( ".rect" ).remove();
    $( ".text" ).remove();
    $( ".close" ).remove();
    svg.innerHTML = '<image id="image" xlink:href="'+ src +'" x="0" y="0" height="500px" width="700px"/>'
}

function changeImage(svg, el, image){
    //console.log("Cambio de imagen");
    //console.log(image);
    $( ".rect" ).remove();
    $( ".text" ).remove();
    $( ".close" ).remove();
    svg.innerHTML = '<image id="image" xlink:href="' + image + '" x="0" y="0" height="500px" width="700px"/>';
    
    el = svg;
}


// Definición del Widget en javasript
define('ImageLabeler', ["jupyter-js-widgets"], function(widgets) {
    var svg;
    var ImageLabelerView = widgets.DOMWidgetView.extend({
        // Renderizar vista
        render: function() {
            model = this.model;
            myThis = this;
            // Creamos el SVG
            svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
            svg.id = 'svg';
            svg.setAttribute('height', '500px');
            svg.setAttribute('width', '700px');
            
            // Asignamos listeners al SVG
            svg.addEventListener("click", mouseClick, false);
            svg.addEventListener("mousemove", mouseMove, false);
            
            // Imágen
            var src = "../rsc/img/family.jpg";
            loadImage(svg, src);
            // Listener para cambios de imagen
            this.image_changed();
            this.model.on('change:image', this.image_changed, this);
            
            // Listener para dibujar rectangulos dados
            // desde Python
            this.model.on('change:rect_to_add', this.add_rect, this);
            
            // Listener para intento de cambio de imagen erroneo
            this.model.on('change:error_loading', this.error_loading_image, this);
            
            // Listener para notificar guardado de las imágenes
            this.model.on('change:saved_successfuly', this.success_saving_labels, this);
            
            // Asignamos a la vista el elemento SVG
            this.el = svg;
        },
        // Función que se lanza con el cambio de imagen
        image_changed: function() {
            var image = this.model.get('image');
            changeImage(svg, this.el, image);
            console.log(image)
            var image_name = image.split(/[/]+/);
            image_name = image_name[image_name.length - 1];
            
            sucess_image_notify(image_name);
        },
        // Función que se lanza cuando se intenta cargar un
        // fichero distinto a una imagen
        error_loading_image: function() {
            var file_name = this.model.get('error_loading');
            var file_name = file_name.split(/[/]+/);
            file_name = file_name[file_name.length - 1];
            error_image_notify(file_name);
        },
        // Función que se lanza cuando se guardan
        // las etiquetas de la imagen
        success_saving_labels: function() {
            //console.log('File')
            var file_name = this.model.get('image');
            var file_name = file_name.split(/[/]+/);
            file_name = file_name[file_name.length - 1];
            success_saving_labels_notify(file_name);
        },        
        // Función par dibujar rectangulos
        // con coordenadas provistas por Python
        add_rect: function() {
            
            var rect_coords = this.model.get('rect_to_add');
            //console.log("add rects", rect_coords);
            
            var text_for_coords = this.model.get('text_to_add');
            //console.log("text rects", text_for_coords[0]);
            
            for (var i = 0; i < rect_coords.length; i++) { 
                
                var new_rect_coords = rect_coords[i];
                
                var x = new_rect_coords[1];
                var y = new_rect_coords[0];
                var height = new_rect_coords[2];
                var width = new_rect_coords[3];
                //console.log('Add', x,' ', y, ' ',height, ' ', width);

                var new_rect = document.createElementNS("http://www.w3.org/2000/svg", 'rect');
                new_rect.id = 'rect' + count;
                new_rect.setAttribute('class', 'rect');

                count = count + 1;

                new_rect.setAttributeNS(null, 'x', x);
                new_rect.setAttributeNS(null, 'y', y);
                new_rect.setAttributeNS(null, 'height', height);
                new_rect.setAttributeNS(null, 'width', width);

                setStyleRect(new_rect);

                document.getElementById('svg').appendChild(new_rect);
                
                
                
                var text = text_for_coords[i];
                
                // Añadimos la etiqueta de texto que deseemos al rectangulo
                svg.innerHTML += "<text class='text' id='" + (count-1) + "txt" +"' x='"+ x +"' y='"+ y +"' font-family='Verdana' font-size='14px'>"+ text +"</text>"

                var close = document.createElementNS("http://www.w3.org/2000/svg", "text");
                close.setAttribute("id","closeParent"+(count-1));
                close.setAttribute("class","close");
                close.setAttribute("x",x);
                close.setAttribute("y",y+14);
                close.setAttribute("font-family","Verdana");
                close.setAttribute("font-size","15px");

                close.innerHTML += "<tspan id='close" + (count-1) + "' >x</tspan>";
                svg.appendChild(close);
            }
            
            myThis.touch();
        },
    });

    return {
        ImageLabelerView: ImageLabelerView
    };
});



In [42]:
# Inicializamos Widget etiquetador de imágenes
image_labeler = ImageLabelerWidget()

Creamos directorios


In [43]:
# Clases para la gestión de directorios
import Directory_Manager
# Creamos directorios
dir_list = ['Rondel','Bulliform',
            'Bilobate','Trichomas',
            'Saddle', 'Spherical',
           'Cyperaceae']

dir_manager = Directory_Manager.Directory_Manager(dir_list, current_dir= dir_list[0])

Añadimos gestión de tipo de fitolito junto a coordenadas de cada rectángulo


In [44]:
# Añadimos un diccionario que nos permita 
# añadir las coordenadas segun el tipo de fitolito
# para guardar así la imagenes en el 
# directorio que corresponda
coords_dict = dict()

''' Función que indexa las coordenadas al tipo de
fitolito que estamos eligiendo, de manera que añadimos
al diccionario cada una de las coordenadas que dibujamos
correspondiendo a la carpeta en la que sera guardada'''
def update_coords_list(coords_list):
    
    # Coordenadas a añadir
    coords_to_add = coords_list[len(coords_list)-1]
    
    # Leemos el directorio seleccionado actualmente
    current_dir_path = dir_manager.get_current_dir_path()
    current_dir = dir_manager.get_current_dir()
    
    # Si existia ya, añadimos coords a la lista
    if current_dir_path in coords_dict:
        coords_dict[current_dir_path].append(coords_to_add)
    # Sino creamos lista y añadimos coords
    else:
        coords_dict[current_dir_path] = []
        coords_dict[current_dir_path].append(coords_to_add)

# Patrón observe sobre la lista de coordenadas
image_labeler.rectList_bind_to(update_coords_list)

Gestión en la eliminación de rectángulos


In [45]:
''' Función que se encarga de eliminar del diccionario
anterior las coordenadas del rectangulo que ha sido
eliminado por el usuario'''
def remove_coords_list(coords_list):
    # Coordenadas a eliminar
    # TODO Tener en cuenta transformación a realizar
    coords_to_delete = coords_list[len(coords_list)-1]
    
    # Comprobamos en que clave del 
    # diccionario se encuentran las 
    # coordenadas y las eliminamos
    for k in coords_dict.keys():
        #print("To remove", coords_to_delete)
        if coords_to_delete in coords_dict[k]:
            coords_dict[k].remove(coords_to_delete)
# Patrón observe sobre la lista de coordenadas
image_labeler.removed_rect_bind_to(remove_coords_list)

Añadimos más widgets

En esta celda añadimos el file upload


In [46]:
from IPython.display import display
import fileupload
import PIL.Image
import io as io2
from skimage.color import rgb2gray

''' Función que se encarga de aplicar las operaciones 
necesarias para convertir los datos obtenidos del FileUpload
en una imagen'''
def image_converter(image):
        image = io2.BytesIO(image)
        image = PIL.Image.open(image)
        return np.array(image)

Inicializamos el resto de Widgets


In [47]:
from ipywidgets import HBox, VBox, Label, Layout
import re
import os.path
from time import sleep


#Inicializamos Widget de File Upload
upload_widget = fileupload.FileUploadWidget()
upload_widget.description = '(50% width, 80px height) button'

# Creamos el patron para el nombre 
# de una imagen
pattern = re.compile("^.*\.(jpeg|jpg|tif|tiff|png)$", re.IGNORECASE)

# TODO Cuando se cambia de imagen
# hay que asegurarse de que las
# distintas variables se inicializan
# a los valores por defecto

# Callback para el cambio de imagen
def _cb(change):
    # Limpiamos variables
    coords_dict.clear()
    
    change = change['owner']
    # Control de que el fichero es una imagen
    # y no otro tipo de fichero
    if pattern.match(change.filename):
        
        image = image_converter(change.data)
        
        image_name = change.filename.split(".")[0]
        
        image_path = dir_manager.get_default_dir() + image_name + ".jpg" 
                
        # Guardamos imagen
        io.imsave(image_path, image, quality=100)
        
        # Y la cargamos
        image = io.imread(image_path)
        
        # Sincronizamos cambio
        image_labeler.image = image_path
        
        # Comprobar si la imagen había 
        # sido previamente cargada.
        # Si es así, cargamos etiquetas previas
        name = change.filename.split(".")[0]
        
        json_path = dir_manager.get_default_dir() +\
        name + '.json'
        
        if os.path.exists(json_path):
            with open(json_path) as jsonfile:
                
                old_coords_dict = json.load(jsonfile)[name + ".jpg"]
                
                image_h, image_w, _ = image.shape
                
                # Añadir al diccionario actual todas las coordenadas
                # existentes para no eliminarlas al volver
                # a guardar imagen
                coords = []
                text_to_add_with_coords = []
                for k, _ in old_coords_dict.items():
                    
                    #Añadimos los rectangulos
                    for rect_x, rect_x2, rect_y, rect_y2 in old_coords_dict[k]:
                        
                        coord = [[rect_x, rect_y, rect_x2 - rect_x, rect_y2 - rect_y]]
                        coord = transform_image_coords_to_view_coords(coord, 500, 700, image_h, image_w)[0]
                        # Añadimos coordenadas a la variable en ejecucuión que las maneja
                        if dir_manager.get_possible_dir(k) in coords_dict:
                            coords_dict[dir_manager.get_possible_dir(k)].append(coord)
                        else:
                            coords_dict[dir_manager.get_possible_dir(k)] = [coord]
                        
                        image_labeler.text = k
                        #print("New rect", coord)
                        text_to_add_with_coords.append(k)
                        coords.append(coord)
                        #image_labeler.rect_to_add = coord
                        #sleep(0.05)
                
                image_labeler.text_to_add = text_to_add_with_coords
                image_labeler.rect_to_add = coords
                # Reasignamos valor por defecto a texto
                image_labeler.text = 'Rondel'
                
    else:
        # Sino, lanzamos error
        image_labeler.error_loading = change.filename

upload_widget.observe(_cb, names='data')

In [48]:
# Selector de fitolito

btns_selector =widgets.ToggleButtons(
    options= dir_list,
    #description='Tipo de fitolito',
    disabled=False,
    # 'success', 'info', 'warning', 'danger' or ''
    button_style='',
    tooltip='Description',
    #icon='check'
)

'''Función que se encarga de hacer los cambios 
necesarios al cambiar el fitolito que se etiqueta'''
def on_phytolith_change(change):
    new_dir = change['new']
    dir_manager.change_dir(new_dir)  
    image_labeler.text = new_dir

# listener del cambio de fitolito
btns_selector.observe(on_phytolith_change,names='value')


#Asignamos al texto del etiquetador por defecto al primer elemento de los botones
image_labeler.text = dir_list[0]

In [49]:
import warnings
# Añadimos el botón que se encarga de guardar
#las imágenes en el directorio correspondiente

save_btn = widgets.Button(
    description='Guardar imágenes',
    disabled=False,
    button_style='',
    tooltip='Guardar imágenes',
    icon='check'
)

#Ignoramos warnings de calidad de imagen

warnings.filterwarnings('ignore')
'''Función que se encarga de llamar a la función 
de guardar las etiquetas como imágenes'''
def on_save_btn_click(ch):
    #Obtenemos el nombre de la imagen
    image_path = image_labeler.image
    image_name = os.path.split(image_path)[1]
    image_name = image_name.split(".")[0]
    # Cargamos imágen
    image = io.imread(image_path)
    
    # Eliminamos fichero json e imagen previa si
    # había sido previamente etiquetada
    json_path = dir_manager.get_default_dir() +\
    image_name + ".json"
    
    if os.path.exists(image_path):
        #print(image_path)
        os.remove(image_path)
        
    if os.path.exists(json_path):  
        #print(json_path)
        os.remove(json_path)
    
    image_name =additional_text() + image_name
    image_path = dir_manager.get_default_dir() + image_name + ".jpg"
    
    #print(dir_manager.get_default_dir() + image_name)
    #Guardamos imagen con fingerprint
    io.imsave(image_path, image, quality=100)
    
    image_h, image_w, _ = image.shape
    
    coords_dict_copy = dict()
    
    for path, coords_set in coords_dict.items():
        # Transformamos las coordenadas
        #print("\t>>k ->",path, " :  ", coords_set)
        coords_set = transform_view_coords_to_image_coords(coords_set, 500, 700, image_h, image_w)
        #print("\t>>Despues de conversión, k ->",path, " :  ", coords_set)
        # Guardamos la imagenes en sus correspondientes directorios
        #save_as_all_formats(coords_set, image, path, image_name)
        save_coords_as_images(coords_set, image, path, image_name)
        
        # Realizamos una copía para la posterior realización 
        # del fichero json con las claves siendo el tipo de fitolito 
        # y no el path
        key = os.path.split(os.path.split(path)[0])[1]
        coords_dict_copy[key] = coords_set
    #print(">JSON to save before", coords_dict)
    #print(">JSON to save", coords_dict_copy)
    save_coords_as_json(coords_dict_copy, dir_manager.get_default_dir(), image_name)
    
    # Notificamos al usuario que las etiquetas 
    #han sido guardadas correctamente
    if image_labeler.saved_successfuly == 'false':
        image_labeler.saved_successfuly = 'true'
    else:
        image_labeler.saved_successfuly = 'false'
    # Eliminamos los rectangulos que añadimos para
    # evitar redundacia de coordenadas
    image_labeler.set_rectList([]) 
    
save_btn.on_click(on_save_btn_click)

Dando formato al Widget


In [50]:
# Formato de los widgets
right_size_widget = VBox([upload_widget, btns_selector, save_btn])

right_size_widget.width = '10%'
right_size_widget.margin = '5% 5% 5% 5%'
btns_selector.padding = '10%'
btns_selector.add_class("btns_selector")
upload_widget.padding = '10%'
upload_widget.add_class('btn')
image_labeler.margin = '5% 10% 0% 0%'
upload_widget.margin = '0 0 7% 12%'
upload_widget.width = '50'
save_btn.margin = '10%'

Widget


In [51]:
w = HBox([image_labeler,right_size_widget])
w.margin = '5%'
w.add_class('widget')
w