pynovisao.py 16.5 KB
Newer Older
1 2 3 4
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
"""
5 6 7 8
    This file must contain the implementation code for all actions of pynovisao.
    
    Name: pynovisao.py
    Author: Alessandro dos Santos Ferreira ( santosferreira.alessandro@gmail.com )
9 10 11 12
"""

from collections import OrderedDict

13 14
import interface
from interface.interface import InterfaceException as IException
15

16
import segmentation
17
import extraction
18
from extraction import FeatureExtractor
19
import classification
20

21 22 23
import util
from util.config import Config
from util.file_utils import File as f
24
from util.utils import TimeUtils
25 26

class Act(object):
27 28

    def __init__(self, tk, args):
29
        self.tk = tk
30 31 32
        
        self.segmenter = [segmentation._segmenter_list[segmenter].meta for segmenter in segmentation._segmenter_list
                            if segmentation._segmenter_list[segmenter].value == True ][0]()
33 34 35
        
        self.extractors = [extraction._extractor_list[extractor].meta for extractor in extraction._extractor_list
                            if extraction._extractor_list[extractor].value == True ]
36 37 38 39 40 41
        
        try:
            self.classifier = [classification._classifier_list[classifier].meta for classifier in classification._classifier_list
                                if classification._classifier_list[classifier].value == True ][0]()
        except:
            self.classifier = None
42

43 44 45
        self._image = None
        self._const_image = None
        self._image_name = None
46
                    
47 48
        self._init_dataset(args["dataset"])
        self._init_classes(args["classes"], args["colors"])
49 50
        
        self._dataset_generator = True
51

52
    
53
    def _init_dataset(self, directory):
54 55 56 57 58
        if(directory[-1] == '/'):
            directory = directory[:-1]
            
        self.dataset = directory
        f.create_dir(self.dataset)
59
    
60
    def _init_classes(self, classes = None, colors = None):
61 62 63 64 65 66 67 68 69 70 71 72
        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')
            
73
        self._current_class = 0
74
        
75

76
    def open_image(self, imagename = None):
77 78
        
        def onclick(event):
79
            if event.xdata != None and event.ydata != None and int(event.ydata) != 0 and self._dataset_generator == True:
80 81
                x = int(event.xdata)
                y = int(event.ydata)
82 83 84 85 86 87 88
                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)
                    
89
                    self._image, run_time = self.segmenter.paint_segment(self._image, self.classes[self._current_class]["color"].value, x, y)
90
                    self.tk.append_log("Painting segment: %0.3f seconds", run_time)
91
                    self.tk.refresh_image(self._image)
92
                    
93
                    filepath = f.save_class_image(segment, self.dataset, self.classes[self._current_class]["name"].value, self._image_name, idx_segment)
94 95 96 97 98
                    if filepath:
                        self.tk.append_log("\nSegment saved in %s", filepath)
        
        if imagename is None:
            imagename = self.tk.utils.ask_image_name()
99 100

        if imagename:
101 102
            self._image = f.open_image(imagename)
            self._image_name = f.get_filename(imagename)
103

104 105 106
            self.tk.write_log("Opening %s...", self._image_name)
            self.tk.add_image(self._image, self._image_name, onclick)
            self._const_image = self._image
107 108 109 110 111 112 113 114 115
            
            self.segmenter.clean()
        
    def restore_image(self):
        if self._const_image is not None:
            self.tk.write_log("Restoring image...")
            self.tk.refresh_image(self._const_image)
            
            self.segmenter.clean()
116 117 118
        
    def close_image(self):
        
119
        if self._const_image is None:
120 121 122 123
            raise IException("Image not found")
        
        if self.tk.close_image():
            self.tk.write_log("Closing image...")
124
            self._const_image = None
125
            self._image = None
126 127

    def add_class(self, dialog = True, name = None, color = None):
128 129 130
        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)
131
                
132 133
        def edit_class(index):
            self.edit_class(index)
134 135 136
            
        def update_current_class(index):
            self.update_current_class(index)
137 138 139
        
        def process_config():
            new_class = self.tk.get_config_and_destroy()
140
            new_class["name"].value = '_'.join(new_class["name"].value.split())
141 142 143

            self.classes.append( new_class )
            self.tk.write_log("New class: %s", new_class["name"].value)
144
            self.tk.refresh_panel_classes(self.classes, self._current_class)
145
            
146 147
        if name is None:
            name = "Class_%02d" % (n_classes+1)
148
        if color is None:
149
            color = util.X11Colors.random_color()
150 151
            
        class_config = OrderedDict()
152
        class_config["name"] = Config(label="Name", value=name, c_type=str)
153
        class_config["color"] = Config(label="Color (X11 Colors)", value=color, c_type='color')
154 155
        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)
156 157 158 159 160 161 162
        class_config["args"] = Config(label=None, value=n_classes, c_type=int, hidden=True)
        
        if dialog == False:
            self.classes.append( class_config )
            return 

        title = "Add a new classe"
163 164 165
        self.tk.dialogue_config(title, class_config, process_config)        
      

166 167 168
    def edit_class(self, index):
        def process_update(index):
            updated_class = self.tk.get_config_and_destroy()
169
            updated_class["name"].value = '_'.join(updated_class["name"].value.split())
170 171 172
            
            self.classes[index] = updated_class
            self.tk.write_log("Class updated: %s", updated_class["name"].value)
173
            self.tk.refresh_panel_classes(self.classes, self._current_class)
174 175 176 177 178 179
        
        current_config = self.classes[index]
            
        title = "Edit class %s" % current_config["name"].value
        self.tk.dialogue_config(title, current_config, lambda *_ : process_update(index))
            
180
    def update_current_class(self, index):
181
        self._current_class = index
182 183 184 185 186 187 188 189
        
    def get_class_by_name(self, name):
        name = name.strip()
        
        for cl in self.classes:
            if cl["name"].value == name:
                return cl
        raise Exception("Class not found")
190

191
        
192 193 194
    def set_dataset_path(self):
        directory = self.tk.utils.ask_directory(default_dir = self.dataset)
        if directory:
195
            self._init_dataset(directory)
196 197
            self.tk.write_log("Image dataset defined: %s", self.dataset)
            
198
            self._init_classes()
199
            self.tk.refresh_panel_classes(self.classes)
200 201 202
            
    def toggle_dataset_generator(self):
        self._dataset_generator = not self._dataset_generator
203

204 205
            
    def select_segmenter(self):
206
        
207 208
        title = "Choosing a segmenter"
        self.tk.write_log(title)
209

210
        current_config = segmentation.get_segmenter_config()
211
        
212 213
        def process_config():
            new_config = self.tk.get_config_and_destroy()
214

215 216 217
            self.segmenter = [new_config[segmenter].meta for segmenter in new_config
                                if new_config[segmenter].value == True ][0]()

218
            self.tk.append_log("\nSegmenter: %s\n%s", str(self.segmenter.get_name()), str(self.segmenter.get_summary_config()))
219 220 221 222 223
            segmentation.set_segmenter_config(new_config)

        self.tk.dialogue_choose_one(title, current_config, process_config)

    def config_segmenter(self):
224 225 226 227 228 229 230 231 232 233
        
        title = "Configuring %s" % self.segmenter.get_name()
        self.tk.write_log(title)

        current_config = self.segmenter.get_config()
        
        def process_config():
            new_config = self.tk.get_config_and_destroy()

            self.segmenter.set_config(new_config)
234
            self.tk.append_log("\nConfig updated:\n%s", str(self.segmenter.get_summary_config()))
235
            self.segmenter.clean()
236 237

        self.tk.dialogue_config(title, current_config, process_config)
238 239 240 241 242 243
        
    def run_segmenter(self):
        
        if self._const_image is None:
            raise IException("Image not found")
        
244
        self.tk.write_log("Running %s...", self.segmenter.get_name())
245 246 247 248 249 250

        self.tk.append_log("\nConfig: %s", str(self.segmenter.get_summary_config()))
        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)
251 252


253 254 255 256 257 258 259 260 261 262 263 264
    def select_extractors(self):
        
        title = "Selecting extractors"
        self.tk.write_log(title)

        current_config = extraction.get_extractor_config()
        
        def process_config():
            new_config = self.tk.get_config_and_destroy()

            self.extractors = [new_config[extractor].meta for extractor in new_config
                                if new_config[extractor].value == True ]
265 266 267
                                
            if len(self.extractors) == 0:
                raise IException("Please select at least one extractor")
268 269 270 271 272 273 274

            self.tk.append_log("\nConfig updated:\n%s", 
                                '\n'.join(["%s: %s" % (new_config[extractor].label, "on" if new_config[extractor].value==True else "off")
                                            for extractor in new_config]))
            extraction.set_extractor_config(new_config)

        self.tk.dialogue_select(title, current_config, process_config)
275 276 277 278
        
    def run_extractors(self):
        
        self.tk.write_log("Running extractors on all images in %s", self.dataset)
279

280 281 282
        fextractor = FeatureExtractor(self.extractors)
        self.tk.append_log("%s", '\n'.join([extraction._extractor_list[extractor].label for extractor in extraction._extractor_list
                                                if extraction._extractor_list[extractor].value == True ]))
283
        
284
        output_file, run_time = fextractor.extract_all(self.dataset, "training")
285 286 287
        self.tk.append_log("\nOutput file saved in %s", output_file)
        self.tk.append_log("Time elapsed: %0.3f seconds", run_time)

288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
        
    def select_classifier(self):
        if self.classifier is None:
            raise IException("You must install python-weka-wrapper")
        
        title = "Choosing a classifier"
        self.tk.write_log(title)

        current_config = classification.get_classifier_config()
        
        def process_config():
            new_config = self.tk.get_config_and_destroy()

            self.classifier = [new_config[classifier].meta for classifier in new_config
                                if new_config[classifier].value == True ][0]()

            self.tk.append_log("\nClassifier: %s\n%s", str(self.classifier.get_name()), str(self.classifier.get_summary_config()))
            classification.set_classifier_config(new_config)

        self.tk.dialogue_choose_one(title, current_config, process_config)
        
    def configure_classifier(self):
        if self.classifier is None:
            raise IException("You must install python-weka-wrapper")
        
        title = "Configuring %s" % self.classifier.get_name()
        self.tk.write_log(title)

        current_config = self.classifier.get_config()
        
        def process_config():
            new_config = self.tk.get_config_and_destroy()

            self.classifier.set_config(new_config)
            self.tk.append_log("\nConfig updated:\n%s", str(self.classifier.get_summary_config()))

        self.tk.dialogue_config(title, current_config, process_config)
    
    
    def run_classifier(self):
        if self.classifier is None:
            raise IException("You must install python-weka-wrapper")
        
        if self._const_image is None:
            raise IException("Image not found")
        
        self.tk.write_log("Running %s...", self.classifier.get_name())
        self.tk.append_log("\n%s", str(self.classifier.get_summary_config()))
        
        start_time = TimeUtils.get_time()

        list_segments = self.segmenter.get_list_segments()
        if len(list_segments) == 0:
            self.tk.append_log("Running %s... (%0.3f seconds)", self.segmenter.get_name(), (TimeUtils.get_time() - start_time))
            
            self._image, _ = self.segmenter.run(self._const_image)
            self.tk.refresh_image(self._image)        
            list_segments = self.segmenter.get_list_segments()
        
        if self.classifier.must_train():
348
            self.tk.append_log("Creating training data... (%0.3f seconds)", (TimeUtils.get_time() - start_time))
349 350
            
            fextractor = FeatureExtractor(self.extractors)
351
            output_file, run_time = fextractor.extract_all(self.dataset, "training", overwrite = False)
352 353 354
        
            self.tk.append_log("Training classifier... (%0.3f seconds)", (TimeUtils.get_time() - start_time))
            
355
            self.classifier.train(self.dataset, "training")
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
        
        self._image = self._const_image

        #  Original classification from python-superpixel, deprecated, use only for debug purposes
        if hasattr(self, 'python_superpixel'):
            for idx_segment in list_segments:
                segment, size_segment = self.segmenter.get_segment(self, idx_segment=idx_segment)[0:2]
                
                filepath = f.save_image(segment, self.dataset, "test")
                
                if self.classifier.must_train():
                    output_file, _ = fextractor.extract_one_file(self.dataset, filepath, "test")
                
                labels = self.classifier.classify(self.dataset, "test")
                cl = self.get_class_by_name(labels[0])
                
                self._image, _ = self.segmenter.paint_segment(self._image, cl["color"].value, idx_segment=[idx_segment], border=False)
                self.tk.refresh_image(self._image)
        #  New and optimized classification
        else:
            tmp = ".tmp"
            f.remove_dir(f.make_path(self.dataset, tmp))

            self.tk.append_log("Generating test images... (%0.3f seconds)", (TimeUtils.get_time() - start_time))
            
            for idx_segment in list_segments:
                segment, size_segment, idx_segment = self.segmenter.get_segment(self, idx_segment=idx_segment)[:-1]
                filepath = f.save_class_image(segment, self.dataset, tmp, self._image_name, idx_segment)
                
            if self.classifier.must_train():
                self.tk.append_log("Running extractors on test images... (%0.3f seconds)", (TimeUtils.get_time() - start_time))
                
                output_file, _ = fextractor.extract_all(self.dataset, "test", dirs=[tmp])
                    
            self.tk.append_log("Running classifier on test data... (%0.3f seconds)", (TimeUtils.get_time() - start_time))
            
            labels = self.classifier.classify(self.dataset, "test")
            f.remove_dir(f.make_path(self.dataset, tmp))
            
            self.tk.append_log("Painting segments... (%0.3f seconds)", (TimeUtils.get_time() - start_time))
            
            for cl in self.classes:
                idx_segment = [ list_segments[idx] for idx in range(0, len(labels)) if cl["name"].value == labels[idx]]
                if len(idx_segment) > 0:
                    self._image, _ = self.segmenter.paint_segment(self._image, cl["color"].value, idx_segment=idx_segment, border=False)

            self.tk.refresh_image(self._image)
            
        end_time = TimeUtils.get_time()
            
        self.tk.append_log("\nClassification finished")
        self.tk.append_log("Time elapsed: %0.3f seconds", (end_time - start_time))

409

410 411
    def func_not_available(self):
        self.tk.write_log("This functionality is not available right now.")
412