class PixelClassificationSerializer(AppletSerializer):
    """Encapsulate the serialization scheme for pixel classification
    workflow parameters and datasets.

    """
    def __init__(self, operator, projectFileGroupName):
        self._serialClassifierSlot =  SerialClassifierSlot(operator.Classifier,
                                                           operator.classifier_cache,
                                                           name="ClassifierForests")
        slots = [SerialListSlot(operator.LabelNames,
                                transform=str),
                 SerialListSlot(operator.LabelColors, transform=lambda x: tuple(x.flat)),
                 SerialListSlot(operator.PmapColors, transform=lambda x: tuple(x.flat)),
                 SerialBlockSlot(operator.LabelImages,
                                 operator.LabelInputs,
                                 operator.NonzeroLabelBlocks,
                                 name='LabelSets',
                                 subname='labels{:03d}',
                                 selfdepends=False,
                                 shrink_to_bb=True),
                 SerialPickledSlot(operator.ClassifierFactory),
                 self._serialClassifierSlot ]

        super(PixelClassificationSerializer, self).__init__(projectFileGroupName, slots, operator)
    
    def _deserializeFromHdf5(self, topGroup, groupVersion, hdf5File, projectFilePath):
        """
        Override from AppletSerializer.
        Implement any additional deserialization that wasn't already accomplished by our list of serializable slots.
        """
        # If this is an old project file that didn't save the label names to the project,
        #   create some default names.
        if not self.operator.LabelNames.ready() or len(self.operator.LabelNames.value) == 0:
            # How many labels are there?
            # We have to count them.  
            # This is slow, but okay for this special backwards-compatibilty scenario.

            # For each image
            all_labels = set()
            for image_index, group in enumerate(topGroup['LabelSets'].values()):
                # For each label block
                for block in group.values():
                    data = block[:]
                    all_labels.update( numpy.unique(data) )

            max_label = max(all_labels)
            
            label_names = []
            for i in range(max_label):
                label_names.append( "Label {}".format( i+1 ) )
            
            self.operator.LabelNames.setValue( label_names )
            # Make some default colors, too
            default_colors = [(255,0,0),
                              (0,255,0),
                              (0,0,255),
                              (255,255,0),
                              (255,0,255),
                              (0,255,255),
                              (128,128,128),
                              (255, 105, 180),
                              (255, 165, 0),
                              (240, 230, 140) ]
            colors = []
            for i, _ in enumerate(label_names):
                colors.append( default_colors[i] )
            self.operator.LabelColors.setValue( colors )
            self.operator.PmapColors.setValue( colors )
            
            # Now RE-deserialize the classifier, so it isn't marked dirty
            self._serialClassifierSlot.deserialize(topGroup)
class PixelClassificationSerializer(AppletSerializer):
    """Encapsulate the serialization scheme for pixel classification
    workflow parameters and datasets.

    """
    def __init__(self, operator, projectFileGroupName):
        self._serialClassifierSlot =  SerialClassifierSlot(operator.Classifier,
                                                           operator.classifier_cache,
                                                           name="ClassifierForests")
        slots = [SerialListSlot(operator.LabelNames,
                                transform=str),
                 SerialListSlot(operator.LabelColors, transform=lambda x: tuple(x.flat)),
                 SerialListSlot(operator.PmapColors, transform=lambda x: tuple(x.flat)),
                 SerialPickledValueSlot(operator.Bookmarks),
                 SerialBlockSlot(operator.LabelImages,
                                 operator.LabelInputs,
                                 operator.NonzeroLabelBlocks,
                                 name='LabelSets',
                                 subname='labels{:03d}',
                                 selfdepends=False,
                                 shrink_to_bb=True),
                 SerialClassifierFactorySlot(operator.ClassifierFactory),
                 self._serialClassifierSlot ]

        super(PixelClassificationSerializer, self).__init__(projectFileGroupName, slots, operator)
        
    
    def _deserializeFromHdf5(self, topGroup, groupVersion, hdf5File, projectFilePath):
        """
        Override from AppletSerializer.
        Implement any additional deserialization that wasn't already accomplished by our list of serializable slots.
        """
        # If this is an old project file that didn't save the label names to the project,
        #   create some default names.
        if (not self.operator.LabelNames.ready() or len(self.operator.LabelNames.value) == 0)\
        and 'LabelSets' in topGroup:
            # How many labels are there?
            # We have to count them.  
            # This is slow, but okay for this special backwards-compatibilty scenario.

            # For each image
            all_labels = set()
            for image_index, group in enumerate(topGroup['LabelSets'].values()):
                # For each label block
                for block in group.values():
                    data = block[:]
                    all_labels.update( vigra.analysis.unique(data) )

            if all_labels:
                max_label = max(all_labels)
            else:
                max_label = 0
            
            label_names = []
            for i in range(max_label):
                label_names.append( "Label {}".format( i+1 ) )
            
            self.operator.LabelNames.setValue( label_names )
            # Make some default colors, too
            default_colors = [(255,0,0),
                              (0,255,0),
                              (0,0,255),
                              (255,255,0),
                              (255,0,255),
                              (0,255,255),
                              (128,128,128),
                              (255, 105, 180),
                              (255, 165, 0),
                              (240, 230, 140) ]
            colors = []
            for i, _ in enumerate(label_names):
                colors.append( default_colors[i] )
            self.operator.LabelColors.setValue( colors )
            self.operator.PmapColors.setValue( colors )
            
            # Now RE-deserialize the classifier, so it isn't marked dirty
            self._serialClassifierSlot.deserialize(topGroup)

        # SPECIAL CLEANUP for backwards compatibility:
        # Due to a bug, it was possible for a project to be saved with a classifier that was 
        #  trained with more label classes than the project file saved in the end.
        # That can cause a crash.  So here, we inspect the restored classifier and remove it if necessary.
        if not self.operator.classifier_cache._dirty:
            restored_classifier = self.operator.classifier_cache._value
            if hasattr(restored_classifier, 'known_classes'):
                num_classifier_classes = len(restored_classifier.known_classes)
                num_saved_label_classes = len(self.operator.LabelNames.value)
                if num_classifier_classes > num_saved_label_classes:
                    # Delete the classifier from the operator
                    logger.info( "Resetting classifier... will be forced to retrain" )
                    self.operator.classifier_cache.resetValue()
class PixelClassificationSerializer(AppletSerializer):
    """Encapsulate the serialization scheme for pixel classification
    workflow parameters and datasets.

    """
    def __init__(self, operator, projectFileGroupName):
        self._serialClassifierSlot =  SerialClassifierSlot(operator.Classifier,
                                                           operator.classifier_cache,
                                                           name="ClassifierForests",
                                                           subname="Forest{:04d}")
        slots = [SerialListSlot(operator.LabelNames,
                                transform=str),
                 SerialListSlot(operator.LabelColors, transform=lambda x: tuple(x.flat)),
                 SerialListSlot(operator.PmapColors, transform=lambda x: tuple(x.flat)),
                 SerialBlockSlot(operator.LabelImages,
                                 operator.LabelInputs,
                                 operator.NonzeroLabelBlocks,
                                 name='LabelSets',
                                 subname='labels{:03d}',
                                 selfdepends=False,
                                 shrink_to_bb=True),
                 self._serialClassifierSlot ]

        super(PixelClassificationSerializer, self).__init__(projectFileGroupName, slots, operator)
    
    def _deserializeFromHdf5(self, topGroup, groupVersion, hdf5File, projectFilePath):
        """
        Override from AppletSerializer.
        Implement any additional deserialization that wasn't already accomplished by our list of serializable slots.
        """
        # If this is an old project file that didn't save the label names to the project,
        #   create some default names.
        if not self.operator.LabelNames.ready():
            # How many labels are there?
            max_label = 0
            for op in self.operator.opLabelPipeline:
                max_label = max(max_label, op.opLabelArray.maxLabel.value)
            
            label_names = []
            for i in range(max_label):
                label_names.append( "Label {}".format( i+1 ) )
            
            self.operator.LabelNames.setValue( label_names )
            # Make some default colors, too
            default_colors = [(255,0,0),
                              (0,255,0),
                              (0,0,255),
                              (255,255,0),
                              (255,0,255),
                              (0,255,255),
                              (128,128,128),
                              (255, 105, 180),
                              (255, 165, 0),
                              (240, 230, 140) ]
            colors = []
            for i, _ in enumerate(label_names):
                colors.append( default_colors[i] )
            self.operator.LabelColors.setValue( colors )
            self.operator.PmapColors.setValue( colors )
            
            # Now RE-deserialize the classifier, so it isn't marked dirty
            self._serialClassifierSlot.deserialize(topGroup)