pynovisao.py 18 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
            self.segmenter.reset()

110 111 112 113 114 115
        
    def restore_image(self):
        if self._const_image is not None:
            self.tk.write_log("Restoring image...")
            self.tk.refresh_image(self._const_image)
            
116
            self.segmenter.reset()
117 118 119
        
    def close_image(self):
        
120
        if self._const_image is None:
121 122 123 124
            raise IException("Image not found")
        
        if self.tk.close_image():
            self.tk.write_log("Closing image...")
125
            self._const_image = None
126
            self._image = None
127 128

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

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

167 168 169
    def edit_class(self, index):
        def process_update(index):
            updated_class = self.tk.get_config_and_destroy()
170
            updated_class["name"].value = '_'.join(updated_class["name"].value.split())
171 172 173
            
            self.classes[index] = updated_class
            self.tk.write_log("Class updated: %s", updated_class["name"].value)
174
            self.tk.refresh_panel_classes(self.classes, self._current_class)
175 176 177 178 179 180
        
        current_config = self.classes[index]
            
        title = "Edit class %s" % current_config["name"].value
        self.tk.dialogue_config(title, current_config, lambda *_ : process_update(index))
            
181
    def update_current_class(self, index):
182
        self._current_class = index
183 184 185 186 187 188 189 190
        
    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")
191

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

207 208
            
    def select_segmenter(self):
209
        
210 211
        title = "Choosing a segmenter"
        self.tk.write_log(title)
212

213
        current_config = segmentation.get_segmenter_config()
214
        
215 216
        def process_config():
            new_config = self.tk.get_config_and_destroy()
217

218 219 220
            self.segmenter = [new_config[segmenter].meta for segmenter in new_config
                                if new_config[segmenter].value == True ][0]()

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

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

    def config_segmenter(self):
227 228 229 230 231 232 233 234 235 236
        
        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)
237
            self.tk.append_log("\nConfig updated:\n%s", str(self.segmenter.get_summary_config()))
238
            self.segmenter.reset()
239 240

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

        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)
254 255


256 257 258 259 260 261 262 263 264 265 266 267
    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 ]
268 269 270
                                
            if len(self.extractors) == 0:
                raise IException("Please select at least one extractor")
271 272 273 274 275 276 277

            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)
278 279 280 281
        
    def run_extractors(self):
        
        self.tk.write_log("Running extractors on all images in %s", self.dataset)
282

283 284 285
        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 ]))
286
        
287
        output_file, run_time = fextractor.extract_all(self.dataset, "training")
288 289
        self.tk.append_log("\nOutput file saved in %s", output_file)
        self.tk.append_log("Time elapsed: %0.3f seconds", run_time)
290 291
        
        if self.classifier: self.classifier.reset()
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
        
    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()))
328 329
            
            if self.classifier: self.classifier.reset()
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345

        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()

346
        
347 348 349 350 351 352 353 354 355
        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():
356
            self.tk.append_log("Creating training data... (%0.3f seconds)", (TimeUtils.get_time() - start_time))
357 358
            
            fextractor = FeatureExtractor(self.extractors)
359
            output_file, run_time = fextractor.extract_all(self.dataset, "training", overwrite = False)
360 361 362
        
            self.tk.append_log("Training classifier... (%0.3f seconds)", (TimeUtils.get_time() - start_time))
            
363
            self.classifier.train(self.dataset, "training")
364 365 366
        
        self._image = self._const_image

367
        
368
        #  New and optimized classification
369 370
        tmp = ".tmp"
        f.remove_dir(f.make_path(self.dataset, tmp))
371

372 373 374 375 376
        self.tk.append_log("Generating test images... (%0.3f seconds)", (TimeUtils.get_time() - start_time))
        
        len_segments = {}
        for idx_segment in list_segments:
            segment, size_segment, idx_segment = self.segmenter.get_segment(self, idx_segment=idx_segment)[:-1]
377
            
378 379
            filepath = f.save_class_image(segment, self.dataset, tmp, self._image_name, idx_segment)
            len_segments[idx_segment] = size_segment
380
            
381 382
        if self.classifier.must_train():
            self.tk.append_log("Running extractors on test images... (%0.3f seconds)", (TimeUtils.get_time() - start_time))
383
            
384 385 386 387 388 389 390 391 392
            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))
        
393

394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
        popup_info = "%s\n" % str(self.classifier.get_summary_config())
        
        len_total = sum([len_segments[idx] for idx in len_segments])
        popup_info += "%-16s%-16s%0.2f%%\n" % ("Total", str(len_total), (len_total*100.0)/len_total)
        
        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)
              
            len_classes = sum([len_segments[idx] for idx in idx_segment])
            popup_info += "%-16s%-16s%0.2f%%\n" % (cl["name"].value, str(len_classes), (len_classes*100.0)/len_total)

        self.tk.refresh_image(self._image)
        self.tk.popup(popup_info)

        
411 412 413 414 415
        end_time = TimeUtils.get_time()
            
        self.tk.append_log("\nClassification finished")
        self.tk.append_log("Time elapsed: %0.3f seconds", (end_time - start_time))

416

417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
    def cross_validation(self):
        if self.classifier is None:
            raise IException("You must install python-weka-wrapper")
        
        if self.classifier.must_train():
            self.tk.write_log("Creating training data...")
            
            fextractor = FeatureExtractor(self.extractors)
            output_file, run_time = fextractor.extract_all(self.dataset, "training", overwrite = False)
            self.classifier.train(self.dataset, "training")
        
        self.tk.write_log("Running Cross Validation on %s...", self.classifier.get_name())
        self.tk.append_log("\n%s", str(self.classifier.get_summary_config()))
        
        popup_info = self.classifier.cross_validate()
        self.tk.append_log("Cross Validation finished")
        self.tk.popup(popup_info)
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
        
    def experimenter_all(self):
        if self.classifier is None:
            raise IException("You must install python-weka-wrapper")
        
        if self.tk.ask_ok_cancel("Experimenter All", "This may take several minutes to complete. Are you sure?"):
            if self.classifier.must_train():
                self.tk.write_log("Creating training data...")
                
                fextractor = FeatureExtractor(self.extractors)
                output_file, run_time = fextractor.extract_all(self.dataset, "training", overwrite = False)
                self.classifier.train(self.dataset, "training")
                
            self.tk.write_log("Running Experimenter All on %s...", self.classifier.get_name())
            
            popup_info = self.classifier.experimenter()
            self.tk.append_log("\nExperimenter All finished")
            self.tk.popup(popup_info)
452 453


454 455
    def func_not_available(self):
        self.tk.write_log("This functionality is not available right now.")
456