Esempio n. 1
0
    def OnOpenClassifier(self, event):
        # ---------------------------------------------------------------------------
        from gamera.args import Args, Class
        from gamera.gui import gui
        from gamera import knn

        dialog = Args([Class("Classifier", knn._kNNBase)], name="Select an existing classifier...")
        results = dialog.show(
            self, gui.main_win.shell.locals, docstring="""Choose an already created classifier for optimization."""
        )

        if results == None:
            return

        self.classifier = results[0]

        if isinstance(self.classifier, knn.kNNInteractive):
            gui_util.message(
                "Given interactive classifier will be converted"
                " to noninteractive classifier for optimization (internal)"
            )
            self.classifier = self.CopyClassifier(self.classifier)

        self.SetClassifiers()
        self.UpdatePanels()
        self.EnableControls(True)
Esempio n. 2
0
    def OnChangeFeatures(self, event):
    #---------------------------------------------------------------------------
        from gamera import plugin
        from gamera.core import ONEBIT
        from gamera.core import ImageBase
        from gamera.args import Args, Check

        if self.classifier is None:
            gui.message("No classifier loaded")
            return

        allFeatures = [x[0] for x in plugin.methods_flat_category("Features", ONEBIT)]
        allFeatures.sort()

        existingFeatures = [x[0] for x in ImageBase.get_feature_functions(self.classifier.features)[0]]

        featureControls = []
        for f in allFeatures:
            featureControls.append(Check('', f, default=(f in existingFeatures)))

        dialog = Args(featureControls, name = "Feature selection",
            title="Select the features you want to use for optimization")

        result = dialog.show(self)

        if result is None:
            gui_util.message("No change applied")
            return

        selectedFeatures = [name for check, name in zip(result, allFeatures) if check]
        self.classifier.change_feature_set(selectedFeatures)

        self.UpdatePanels()
Esempio n. 3
0
    def OnOpenClassifier(self, event):
    #---------------------------------------------------------------------------
        from gamera.args import Args, Class
        from gamera.gui import gui
        from gamera import knn

        dialog = Args(
            [Class("Classifier", knn._kNNBase)],
             name="Select an existing classifier...")
        results = dialog.show(
            self, gui.main_win.shell.locals,
            docstring="""Choose an already created classifier for optimization.""")

        if results is None:
            return

        self.classifier = results[0]

        if isinstance(self.classifier, knn.kNNInteractive):
            gui_util.message("Given interactive classifier will be converted"\
                " to noninteractive classifier for optimization (internal)")
            self.classifier = self.CopyClassifier(self.classifier)

        self.SetClassifiers()
        self.UpdatePanels()
        self.EnableControls(True)
Esempio n. 4
0
    def OnChangeFeatures(self, event):
        # ---------------------------------------------------------------------------
        from gamera import plugin
        from gamera.core import ONEBIT
        from gamera.core import ImageBase
        from gamera.args import Args, Check

        if self.classifier == None:
            gui.message("No classifier loaded")
            return

        allFeatures = [x[0] for x in plugin.methods_flat_category("Features", ONEBIT)]
        allFeatures.sort()

        existingFeatures = [x[0] for x in ImageBase.get_feature_functions(self.classifier.features)[0]]

        featureControls = []
        for f in allFeatures:
            featureControls.append(Check("", f, default=(f in existingFeatures)))

        dialog = Args(
            featureControls, name="Feature selection", title="Select the features you want to use for optimization"
        )

        result = dialog.show(self)

        if result == None:
            gui_util.message("No change applied")
            return

        selectedFeatures = [name for check, name in zip(result, allFeatures) if check]
        self.classifier.change_feature_set(selectedFeatures)

        self.UpdatePanels()
Esempio n. 5
0
    def cb_select_steps(self, all, custom, start_step, end_step):
        if all:
            start_step = 0
            end_step = len(self.process.steps) - 1
            self.start_step = start_step
            self.end_step = end_step
            self.saves = None
            return None
        elif custom:
            if start_step > end_step:
                end_step, start_step = start_step, end_step
            self.start_step = start_step
            self.end_step = end_step
            saves = Set()
            for step in range(start_step, end_step + 1):
                deps = find_method_dependencies(
                    getattr(self.process, self.process.steps[step]))
                saves.extend(deps[0])
                saves.extend(deps[1])
            args = []
            self.saveables = []
            for step in saves:
                if hasattr(self.process, "save_" + step):
                    args.append(Check("", step, 1))
                    self.saveables.append(step)

            self.dlg_select_saves = Args(
                args,
                # caption=self.caption,
                title="Select which members to save.",
                function="cb_select_saves")
            return self.dlg_select_saves
Esempio n. 6
0
    def __init__(self, shell, locals, process):
        self.shell = shell
        self.parent = None
        self.locals = locals
        self.process = process
        self.caption = self.process.__name__ + " process"
        self.init_args = ()

        self.dlg_select_steps = Args(
            [
                Radio("Perform the entire process", "All"),
                Radio("Perform part of the process", "Custom"),
                Choice("   Starting step", self.process.steps),
                Choice("   Ending step", self.process.steps, -1)
            ],
            # caption=self.caption,
            function='cb_select_steps',
            title=
            ('Select which steps of the %s process you would like to perform.'
             % self.process.__name__))

        if hasattr(self, 'dlg_init'):
            self.show(self.dlg_init)
        else:
            self.show(self.dlg_select_steps)
class lyric_height_estimation(PluginFunction):
    """
    Returns the estimation of average lyric height.

    *baseline*
        local minimum vertex map of lyric baseline.

    *staffspace*
        staffspace height.

    *scalar_cc_strip*
        it should be the same as the parameter used in baseline_detection.

    *scalar_height*
        scala_valid_height*staffspace: maximum height of potential lyric.
    """
    return_type = Real("output")
    self_type = ImageType([ONEBIT])
    args = Args([ImageType([ONEBIT], "baseline"),
                 Real("staffspace"),
                 Real("scalar_cc_strip", default=1.0),
                 Real("scalar_height", default=3.0)])

    def __call__(self, baseline, staffspace,
                 scalar_cc_strip=1.0, scalar_height=3.0):
        return _lyricline.lyric_height_estimation(self, baseline, staffspace,
                                             scalar_cc_strip, scalar_height)
    __call__ = staticmethod(__call__)
class staffheight_estimation(PluginFunction):
    """
    Returns the staffline height of music score.

    *staff_win*
        width of each vertical strip. Local projection is done within each strip.

    *staffspace_threshold1*, *staffspace_threshold2*
        staffspace_threshold1 is the minimum height of staffline height. If the estimation is underneath this threshold,
        chose the next peak whose staffspace is over staffspace_threshold2
    """
    return_type = Int("output")
    self_type = ImageType([ONEBIT])
    args = Args([
        Int("staff_win", default=30),
        Int("staffspace_threshold1", default=5),
        Int("staffspace_threshold2", default=10)
    ])

    def __call__(self,
                 staff_win=30,
                 staffspace_threshold1=5,
                 staffspace_threshold2=10):
        return _staff_removal.staffheight_estimation(self, staff_win,
                                                     staffspace_threshold1,
                                                     staffspace_threshold2)

    __call__ = staticmethod(__call__)
Esempio n. 9
0
class EditMnnCnn(EditingAlgorithm):
    """**edit_mnn_cnn** (kNNInteractive *classifier*, int *k* = 0, bool *protectRare*, int *rareThreshold*, bool *randomize*)

Combined execution of Wilson's Modified Nearest Neighbour and Hart's
Condensed Nearest Neighbour. Combining the algorithms in this order is 
recommended, because first bad samples are removed to improve the classifiers 
accuracy, and then the remaining samples are condensed to speed up the classifier

For documentation of the parameters see the independent algorithms"""
    name = "MNN, then CNN"
    args = Args([
        Int("Internal k", default=0),
        Check("Protect rare classes", default=True),
        Int("Rare class threshold", default=3),
        Check("Randomize", default=True)
    ])

    def __call__(self,
                 classifier,
                 k=0,
                 protectRare=True,
                 rareThreshold=3,
                 randomize=True):
        return edit_cnn(edit_mnn(classifier, k, protectRare, rareThreshold),
                        randomize)
class count_black_under_line_points(PluginFunction):
    """
    Returns the number of pixels beneath a given line that are the given colour.
    The arguments x0, y0 are the start coordinates of the line and the arguments
    x1, y1 are the end coordinates of the line.
    """
    self_type = ImageType([ONEBIT])
    return_type = Int("num_black_pixels")
    args = Args([Real("x0"), Real("y0"), Real("x1"), Real("y1")])
    doc_examples = [(ONEBIT, )]
class count_black_under_line(PluginFunction):
    """
    Returns the number of pixels beneath a given line that are black.
    The arguments 'slope' and 'y_intercept' correspond to the m and b in the
    equation of a line, y = m * x + b, respectively. I don't know if this works
    properly because I've had it count white pixels as black ones...
    """
    self_type = ImageType([ONEBIT])
    return_type = Int("num_black_pixels")
    args = Args([Real("slope"), Real("y_intercept")])
    doc_examples = [(ONEBIT, )]
class mask_fill(PluginFunction):
    """
    fills masked region with color
  """
    return_type = ImageType([GREYSCALE, ONEBIT], "output")
    self_type = ImageType([GREYSCALE, ONEBIT])
    args = Args([ImageType([ONEBIT], "mask"), Int("color")])

    def __call__(self, mask, color):
        return _background_estimation.mask_fill(self, mask, color)

    __call__ = staticmethod(__call__)
Esempio n. 13
0
class edge_detection(PluginFunction):
    """
    Detects and combines edges from two images in different levels of smoothness.

    *image2*
        the image of same subject as current image, but in lower level of smoothness.
        (the result of "paper_estimation" with sign=0)

    *threshold1_scale*
        scale for canny edge detector on current image. See Edge->canny_edge_image for details.

    *threshold1_gradient*
        gradient for canny edge detector on current image. See Edge->canny_edge_image for details.

    *threshold2_scale*
        scale for canny edge detector on image2. See Edge->canny_edge_image for details.

    *threshold2_gradient*
        gradient for canny edge detector on image2. See Edge->canny_edge_image for details.

    *transfer_para*
        edge tranfer parameter.
        Ther higher it is, the more edges in image2 will be combined into final edge map.
    """
    category = "Border Removal"
    author = "Yue Phyllis Ouyang and John Ashley Burgoyne"
    url = "http://ddmal.music.mcgill.ca/"
    return_type = ImageType([ONEBIT], "output")
    self_type = ImageType([GREYSCALE])
    args = Args([
        ImageType([GREYSCALE], "image2"),
        Real("threshold1_scale", default=0.8),
        Real("threshold1_gradient", default=6.0),
        Real("threshold2_scale", default=0.8),
        Real("threshold2_gradient", default=6.0),
        Real("tranfer_parameter", default=0.25)
    ])

    def __call__(self,
                 image2,
                 threshold1_scale,
                 threshold1_gradient,
                 threshold2_scale,
                 threshold2_gradient,
                 scale_length=0.25):
        return _border_removal.edge_detection(self, image2, threshold1_scale,
                                              threshold1_gradient,
                                              threshold2_scale,
                                              threshold2_gradient,
                                              scale_length)

    __call__ = staticmethod(__call__)
Esempio n. 14
0
class border_removal(PluginFunction):
    """
    Returns the mask of music score region.

    Gathers paper_estimation, edge_detection and boundary_reconstruct functions.
    """
    category = "Border Removal"
    author = "Yue Phyllis Ouyang and John Ashley Burgoyne"
    url = "http://ddmal.music.mcgill.ca/"
    return_type = ImageType([ONEBIT], "output")
    self_type = ImageType([GREYSCALE])
    args = Args([
        Int("win_dil", default=3),
        Int("win_avg", default=5),
        Int("win_med", default=5),
        Real("threshold1_scale", default=0.8),
        Real("threshold1_gradient", default=6.0),
        Real("threshold2_scale", default=0.8),
        Real("threshold2_gradient", default=6.0),
        Real("transfer_parameter", default=0.25),
        Int("terminate_time1", default=15),
        Int("terminate_time2", default=23),
        Int("terminate_time3", default=75),
        Int("interval2", default=45),
        Int("interval3", default=15)
    ])

    def __call__(self,
                 win_dil=3,
                 win_avg=5,
                 win_med=5,
                 threshold1_scale=0.8,
                 threshold1_gradient=6.0,
                 threshold2_scale=0.8,
                 threshold2_gradient=6.0,
                 transfer_parameter=0.25,
                 terminate_time1=15,
                 terminate_time2=23,
                 terminate_time3=75,
                 interval2=45,
                 interval3=15):
        return _border_removal.border_removal(
            self, win_dil, win_avg, win_med, threshold1_scale,
            threshold1_gradient, threshold2_scale, threshold2_gradient,
            transfer_parameter, terminate_time1, terminate_time2,
            terminate_time3, interval2, interval3)

    __call__ = staticmethod(__call__)
class equalise_histogram_mask(PluginFunction):
    """
     Normalises the histogram of the given image to match an input
     histogram within mask region
  """
    return_type = ImageType([GREYSCALE], "output")
    self_type = ImageType([GREYSCALE])
    args = Args(
        [ImageType([ONEBIT], "mask"),
         FloatVector("reference_histogram")])

    def __call__(self, mask, reference_histogram):
        return _background_estimation.equalise_histogram_mask(
            self, mask, reference_histogram)

    __call__ = staticmethod(__call__)
class background_estimation(PluginFunction):
    """
    background estimation

    *med_size*
        the kernel for median filter.
        the default value works best for image with size 1000*1000 to 2000*2000
    """
    return_type = ImageType([GREYSCALE], "output")
    self_type = ImageType([GREYSCALE])
    args = Args([Int("med_size", default=17)])

    def __call__(self, med_size=17):
        return _background_estimation.background_estimation(self, med_size)

    __call__ = staticmethod(__call__)
class lyric_line_detection(PluginFunction):
    """
    Returns the mask of lyric lines.

    Gathers baseline_detection, lyric_height_estimation and lyric_line_fit functions.

    Note: no post-processing to extract precise posistion of each lyric or deal with overlapping situation is applied.
    """
    return_type = ImageType([ONEBIT], "output")
    self_type = ImageType([ONEBIT])
    args = Args([Real("staffspace"),
                 Int("threshold_noise", default=15),
                 Real("scalar_cc_strip", default=1.0),
                 Real("seg_angle_degree", default=30.0),
                 Real("scalar_seg_dist", default=3.5),
                 Int("min_group", default=4),
                 Real("merge_angle_degree", default=5.0),
                 Real("scalar_merge_dist", default=5.0),
                 Real("valid_angle_degree", default=20.0),
                 Real("scalar_valid_height", default=1.0),
                 Int("valid_min_group", default=8),
                 Real("scalar_height", default=3.0),
                 Real("fit_angle_degree", default=2.5),
                 Real("scalar_search_height", default=1.2),
                 Real("scalar_fit_up", default=1.2),
                 Real("scalar_fit_down", default=0.3)])

    def __call__(self, staffspace,
                 threshold_noise=15, scalar_cc_strip=1.0,
                 seg_angle_degree=30.0, scalar_seg_dist=3.5, min_group=4,
                 merge_angle_degree=5.0, scalar_merge_dist=5.0,
                 valid_angle_degree=20.0, scalar_valid_height=1.0, valid_min_group=8,
                 scalar_height=3.0,
                 fit_angle_degree=2.5, scalar_search_height=1.2,
                 scalar_fit_up=1.2, scalar_fit_down=0.3):
        return _lyricline.lyric_line_detection(self, staffspace,
                                             threshold_noise, scalar_cc_strip,
                                             seg_angle_degree, scalar_seg_dist, min_group,
                                             merge_angle_degree, scalar_merge_dist,
                                             valid_angle_degree, scalar_valid_height, valid_min_group,
                                             scalar_height,
                                             fit_angle_degree, scalar_search_height,
                                            scalar_fit_up, scalar_fit_down)
    __call__ = staticmethod(__call__)
Esempio n. 18
0
class boundary_reconstruct(PluginFunction):
    """
    Reconstructs boundary of music score based on edge map from edg_detection.

    *terminate_time1*
        maximum numbers of iterations in 1st round.

    *terminate_time2*
        maximum numbers of iterations in 2nd round.

    *terminate_time3*
        maximum numbers of iterations in 3rd round.

    *interval2*
        interval for edge adding in 2nd round.

    *interval3*
        interval for edge adding in 3rd round.
    """
    category = "Border Removal"
    author = "Yue Phyllis Ouyang and John Ashley Burgoyne"
    url = "http://ddmal.music.mcgill.ca/"
    return_type = ImageType([ONEBIT], "output")
    self_type = ImageType([ONEBIT])
    args = Args([
        Int("terminate_time1", default=15),
        Int("terminate_time2", default=23),
        Int("terminate_time3", default=75),
        Int("interval2", default=45),
        Int("interval3", default=15)
    ])

    def __call__(self,
                 terminate_time1=15,
                 terminate_time2=23,
                 terminate_time3=75,
                 interval2=45,
                 interval3=15):
        return _border_removal.boundary_reconstruct(self, terminate_time1,
                                                    terminate_time2,
                                                    terminate_time3, interval2,
                                                    interval3)

    __call__ = staticmethod(__call__)
class gatos_threshold_mask(PluginFunction):
    """
    Thresholds an image according to Gatos et al.'s method. See:

    Gatos, Basilios, Ioannis Pratikakis, and Stavros
    J. Perantonis. 2004. An adaptive binarization technique for low
    quality historical documents. *Lecture Notes in Computer
    Science* 3163: 102-113.

    This version adds masking process. Only regions within the mask are binarized, the rest is filled with white color

    *background*
      Estimated background of the image.

    *binarization*
      A preliminary binarization of the image.

    *mask*
      Mask image that defines the process region

    Use the default settings for the other parameters unless you know
    what you are doing.
    """
    return_type = ImageType([ONEBIT], "output")
    self_type = ImageType([GREYSCALE])
    args = Args([
        ImageType([GREYSCALE], "background"),
        ImageType([ONEBIT], "binarization"),
        ImageType([ONEBIT], "mask"),
        Real("q", default=0.6),
        Real("p1", default=0.5),
        Real("p2", default=0.8)
    ])

    def __call__(self, background, binarization, mask, q=0.6, p1=0.5, p2=0.8):
        return _background_estimation.gatos_threshold_mask(self, \
                                            background, \
                                            binarization, \
                                            mask, \
                                            q, \
                                            p1, \
                                            p2)

    __call__ = staticmethod(__call__)
Esempio n. 20
0
class med_filter(PluginFunction):
    """
    Returns the regional intermediate value of an image as a FLOAT.

    *region_size*
      The size of the region within which to calculate the intermediate pixel value.
    """
    return_type = ImageType([FLOAT], "output")
    self_type = ImageType([GREYSCALE, GREY16, FLOAT])
    args = Args([Int("region size", default=5)])
    doc_examples = [(GREYSCALE, ), (GREY16, ), (FLOAT, )]
    category = "Border Removal"
    author = "Yue Phyllis Ouyang and John Ashley Burgoyne"
    url = "http://ddmal.music.mcgill.ca/"

    def __call__(self, region_size=5):
        return _border_removal.med_filter(self, region_size)

    __call__ = staticmethod(__call__)
class directional_med_filter_bw(PluginFunction):
    """
    Returns the regional intermediate value of an image as a FLOAT.
    The shape of window is not necessarily a square.
    This function currently only works on binary image.

    *region_width*, *region_height*
      The size of the region within which to calculate the intermediate pixel value.
    """
    return_type = ImageType([ONEBIT], "output")
    self_type = ImageType([ONEBIT])
    args = Args(
        [Int("region_width", default=5),
         Int("region_height", default=5)])

    def __call__(self, region_width=5, region_height=5):
        return _staff_removal.directional_med_filter_bw(
            self, region_width, region_height)

    __call__ = staticmethod(__call__)
class lyric_line_fit(PluginFunction):
    """
    Returns the mask of lyric lines.
    The lines are estimated by linear least square fitting from local minimum vertex map of lyric baseline.

    *baseline*
        local minimum vertex map of lyric baseline.

    *lyric_height*
        estimation of average lyric height.

    *fit_angle_degree*
        tolerance on angle between lyric line and horizon(in degree).

    *scalar_search_height*
        scala_search_height*lyric_height: tolerance on distance (in y-axis) between local minimum vertices that belong to a single lyric line.

    *scalar_fit_up*
        scalar_fit_up*lyric_height: height of lyric portion above baseline.

    *scalar_fit_down*
        scalar_fit_down*lyric_height: height of lyric portion beneath baseline.

    Note: no post-processing to extract precise posistion of each lyric or deal with overlapping situation is applied.
    """
    return_type = ImageType([ONEBIT], "output")
    self_type = ImageType([ONEBIT])
    args = Args([ImageType([ONEBIT], "baseline"),
                 Real("lyric_height"),
                 Real("fit_angle_degree", default=2.5),
                 Real("scalar_search_height", default=1.2),
                 Real("scalar_fit_up", default=1.2),
                 Real("scalar_fit_down", default=0.3)])

    def __call__(self, baseline, lyric_height,
                 fit_angle_degree=2.5, scalar_search_height=1.2,
                 scalar_fit_up=1.2, scalar_fit_down=0.3):
        return _lyricline.lyric_line_fit(self, baseline, lyric_height,
                                            fit_angle_degree, scalar_search_height,
                                            scalar_fit_up, scalar_fit_down)
    __call__ = staticmethod(__call__)
class histogram_mask(PluginFunction):
    """
    Compute the histogram of the pixel values within the mask.
    Returns a Python array of doubles, with each value being a
    percentage.

    If the GUI is being used, the histogram is displayed.

    .. image:: images/histogram.png
    """
    self_type = ImageType([GREYSCALE, GREY16])
    return_type = FloatVector()
    args = Args([ImageType([ONEBIT], "mask")])
    doc_examples = [(GREYSCALE)]

    def __call__(image, mask):
        hist = _background_estimation.histogram_mask(image, mask)
        if has_gui.has_gui == has_gui.WX_GUI:
            has_gui.gui.ShowHistogram(hist, mark=image.otsu_find_threshold())
        return hist

    __call__ = staticmethod(__call__)
Esempio n. 24
0
class paper_estimation(PluginFunction):
    """
    Returns the estimation of background paper by removing foreground pen strokes.

    Note: the default parameter works on image with standard area 114000 (rows*cols)

    Main process: filling hols -> mean filter -> median filter

    *sign*
        An extra smoothing process(dilation+erosion) is applied at the begining, when sign=1.
        An extra edge-preserving process(filling holes) is applied at the end, when sign=0.

    *dil_win*
        region size for dilation+erosion.

    *avg_win*
        region size for mean filter.

    *med_win*
        region size for median filter.
    """
    category = "Border Removal"
    author = "Yue Phyllis Ouyang and John Ashley Burgoyne"
    url = "http://ddmal.music.mcgill.ca/"
    return_type = ImageType([GREYSCALE], "output")
    self_type = ImageType([GREYSCALE])
    args = Args([
        Int("sign", default=1),
        Int("dil_win", default=3),
        Int("avg_win", default=5),
        Int("med_win", default=5)
    ])

    def __call__(self, sign=1, win_dil=3, win_avg=5, win_med=5):
        return _border_removal.paper_estimation(self, sign, win_dil, win_avg,
                                                win_med)

    __call__ = staticmethod(__call__)
class wiener2_filter(PluginFunction):
    """
    Adaptive directional filtering

    *region_width*, *region_height*
      The size of the region within which to calculate the intermediate pixel value.

    *noise_variancee*
      noise variance. If negative, estimated automatically.
    """
    return_type = ImageType([GREYSCALE, GREY16, FLOAT], "output")
    self_type = ImageType([GREYSCALE, GREY16, FLOAT])
    args = Args([
        Int("region_width", default=5),
        Int("region_height", default=5),
        Real("noise_variance", default=-1.0)
    ])

    def __call__(self, region_width=5, region_height=5, noise_variance=-1.0):
        return _background_estimation.wiener2_filter(self, region_width,
                                                     region_height,
                                                     noise_variance)

    __call__ = staticmethod(__call__)
class segment_by_colour(PluginFunction):
    """
    Same as extract_lyrics, only the lyrics and neumes are highlighted by the specified colours (neums: red, text: black).

    Parameters:

      minimum_y_threshold: the minimum value that may be considered a local peak
      in the horizontal projection.

      num_searches: the number of searches to do around each local peak

      negative_bound: how far below the local peak to start the line search (this
      value is positive! so the value of negative_bound=10 will start searching 10
      pixels below the peak-point (or -10 pixels. To make this even more
      confusing, the negative direction is actually upward when talking about
      images, but you already knew this from reading the Gamera documentation).

      positive_bound: how far above the local peak to start the line search

      thickness_above: the number of parallel lines to add above the original 
      intercept line; this simulates "thickness" above the line; use 0 for just no 
      extra lines

      thickness_below: the number of parallel lines to add below the original 
      intercept line; this simulates "thickness" above the line; use 0 for just no 
      extra lines
    """
    pure_python = 1
    return_type = ImageType([RGB], "output")
    self_type = ImageType([ONEBIT])
    args = Args([
        Int("minimum_y_threshold", default=10),
        Int("num_searches", default=4),
        Int("negative_bound", default=10),
        Int("postive_bound", default=10),
        Int("thickness_above", default=0),
        Int("thickness_below", default=0)
    ])

    def __call__(self,
                 minimum_y_threshold=10,
                 num_searches=4,
                 negative_bound=10,
                 postive_bound=10,
                 thickness_above=0,
                 thickness_below=0):
        from gamera.core import RGBPixel

        # Do analysis.
        result = lyric_extractor_helper.extract_lyric_ccs(
            self, minimum_y_threshold, num_searches, negative_bound,
            postive_bound, thickness_above, thickness_below)

        # Check color input.
        neumeColour = RGBPixel(0, 255, 0)
        lyricColour = RGBPixel(255, 0, 0)

        # Prepare output image.
        returnImage = self.to_rgb()

        # Do highlighting.
        for cc in set(result[0]) - set(result[1]):
            returnImage.highlight(cc, lyricColour)
        for cc in set(result[1]):
            returnImage.highlight(cc, neumeColour)
        return returnImage

    __call__ = staticmethod(__call__)
class extract_lyrics(PluginFunction):
    """
    Takes in a binarised image and attempts to remove lyrics by processing horizontal
    projections (see find_blackest_lines).  Whatever lines are found from find_blackest_lines
    are superimposed onto the connected components of the image.  Those CCs are then removed
    from the binarised image.

    Parameters:

      minimum_y_threshold: the minimum value that may be considered a local peak
      in the horizontal projection.

      num_searches: the number of searches to do around each local peak

      negative_bound: how far below the local peak to start the line search (this
      value is positive! so the value of negative_bound=10 will start searching 10
      pixels below the peak-point (or -10 pixels. To make this even more
      confusing, the negative direction is actually upward when talking about
      images, but you already knew this from reading the Gamera documentation).

      positive_bound: how far above the local peak to start the line search

      thickness_above: the number of parallel lines to add above the original 
      intercept line; this simulates "thickness" above the line; use 0 for just no 
      extra lines

      thickness_below: the number of parallel lines to add below the original 
      intercept line; this simulates "thickness" above the line; use 0 for just no 
      extra lines
    """
    pure_python = 1
    return_type = ImageType([ONEBIT], "output")
    self_type = ImageType([ONEBIT])
    args = Args([
        Int("minimum_y_threshold", default=10),
        Int("num_searches", default=4),
        Int("negative_bound", default=10),
        Int("postive_bound", default=10),
        Int("thickness_above", default=0),
        Int("thickness_below", default=0)
    ])

    def __call__(self,
                 minimum_y_threshold=10,
                 num_searches=4,
                 negative_bound=10,
                 postive_bound=10,
                 thickness_above=0,
                 thickness_below=0):
        result = lyric_extractor_helper.extract_lyric_ccs(
            self,
            minimum_y_threshold=10,
            num_searches=4,
            negative_bound=10,
            postive_bound=10,
            thickness_above=0,
            thickness_below=0)
        for cc in set(result[0]) - set(result[1]):
            cc.fill_white()
        return self

    __call__ = staticmethod(__call__)
class find_blackest_lines(PluginFunction):
    """
    Operates on a binarised image and a list of its horizontal projections. Finds
    local peaks in the horizontal projections of the image. This means it adds up
    the number of times black is seen in a row and stores the value for each row
    in an array. It then finds local peaks in this array. It then draws a bunch of
    lines on the image, all pivoting around the centres of the horizontal
    projections. It discards all but the lines that cross the most black pixels
    and returns the start and end points of these lines, e.g.
    [ [(x00,y00),(x01,y01)], [(x10,y10),(x11,y11)], ... [(xn0,yn0),(xn1,yn1)] ].

    Parameters:

      minimum_y_threshold: the minimum value that may be considered a local peak
      in the horizontal projection.

      num_searches: the number of searches to do around each local peak

      negative_bound: how far below the local peak to start the line search (this
      value is positive! so the value of negative_bound=10 will start searching 10
      pixels below the peak-point (or -10 pixels. To make this even more
      confusing, the negative direction is actually upward when talking about
      images, but you already knew this from reading the Gamera documentation).

      positive_bound: how far above the local peak to start the line search

      delta: see the delta parameter for the peakdet function above

      thickness_above: the number of parallel lines to add above the original 
      intercept line; this simulates "thickness" above the line; use 0 for just no 
      extra lines

      thickness_below: the number of parallel lines to add below the original 
      intercept line; this simulates "thickness" above the line; use 0 for just no 
      extra lines
    """
    self_type = ImageType([ONEBIT])
    args = Args([
        Class('horizontal_projections', list),
        Int("minimum_y_threshold"),
        Int("num_searches"),
        Int("negative_bound"),
        Int("positive_bound"),
        Int("delta"),
        Int("thickness_above"),
        Int("thickness_below")
    ])
    return_type = Float("area")
    pure_python = True

    @staticmethod
    def __call__(self,
                 horizontal_projections,
                 minimum_y_threshold,
                 num_searches,
                 negative_bound,
                 positive_bound,
                 delta=10,
                 thickness_above=0,
                 thickness_below=0):
        return lyric_extractor_helper._find_blackest_lines(
            self, horizontal_projections, minimum_y_threshold, num_searches,
            negative_bound, positive_bound, delta, thickness_above,
            thickness_below)
Esempio n. 29
0
    def register(name, callable, args=Args(), doc=""):
        """Register a new editing Algorithm: The parameters are the same as in
*AlgoData.__init__*, so see its doc for an explanation of the parameters."""
        AlgoRegistry.registerData(AlgoData(name, callable, args, doc))
Esempio n. 30
0
class EditCnn(EditingAlgorithm):
    """**edit_cnn** (kNNInteractive *classifier*, int *k* = 0, bool *randomize*)
    
Hart's *Condensed Nearest Neighbour (CNN)* editing.
This alorithm is specialized in removing superfluous glyphs - glyphs that do not
influence the recognition rate - from the classifier to improve its 
classification speed. Typically glyphs far from the classifiers decision 
boundaries are removed.

    *classifier*
        The classifier from which to create an edited copy
    *internalK*
        The k value used internally by the editing algorithm. 0 means, use the 
        same value as the given classifier (recommended)    
    *randomize*
        Because the processing order of the glyphs in the classifier impacts the
        result of this algorithm, the order will be randomized. If reproducable
        results are required, turn this option off.

Reference: P.E. Hart: 'The Condensed Nearest Neighbor rule'. *IEEE Transactions on Information Theory*, 14(3):515-516, 1968
"""
    name = "Hart's Condensed Nearest Neighbour (CNN)"
    args = Args(
        [Int("Internal k", default=0),
         Check("Randomize", default=True)])

    def __call__(self, classifier, k=0, randomize=True):
        # special case of empty classifier
        if (not classifier.get_glyphs()):
            return _copyClassifier(classifier)

        if k == 0:
            k = classifier.num_k

        progress = ProgressFactory("Generating edited CNN classifier...",
                                   len(classifier.get_glyphs()))

        # initialize Store (a) with a single element
        if randomize:
            elem = _randomSetElement(classifier.get_glyphs())
        else:
            elem = classifier.get_glyphs().__iter__().next()

        aGlyphs = [elem]
        a = kNNInteractive(aGlyphs, classifier.features,
                           classifier._perform_splits, k)
        progress.step()

        # initialize Grabbag (b) with all other
        b = classifier.get_glyphs().copy()
        b.remove(aGlyphs[0])

        # Classify each glyph in b with a as the classifier
        # If glyph is misclassified, add it to a, repeat until no elements are
        # added to a
        changed = True
        while changed == True:
            changed = False
            # copy needed because iteration through dict is not possible while
            # deleting items from it
            copyOfB = b.copy()
            for glyph in copyOfB:
                if glyph.get_main_id() != _getMainId(
                        a.guess_glyph_automatic(glyph)):
                    b.remove(glyph)
                    a.get_glyphs().add(glyph)
                    progress.step()
                    changed = True
        progress.kill()
        a.num_k = 1
        return a
Esempio n. 31
0
class EditMnn(EditingAlgorithm):
    """**edit_mnn** (kNNInteractive *classifier*, int *k* = 0, bool *protectRare*, int *rareThreshold*)
    
Wilson's *Modified Nearest Neighbour* (MNN, aka *Leave-one-out-editing*).
The algorithm removes 'bad' glyphs from the classifier, i.e. glyphs that
are outliers from their class in featurespace, usually because they have been 
manually misclassified or are not representative for their class

    *classifier*
        The classifier from which to create an edited copy
    *internalK*
        The k value used internally by the editing algorithm. If 0 is given for 
        this parameter, the original classifier's k is used (recommended).        
    *protect rare classes*
        The algorithm tends to completely delete the items of rare classes,
        removing this whole class from the classifier. If this is not 
        desired these rare classes can be explicitly protected from deletion.
        Note that enabling this option causes additional computing effort    
    *rare class threshold*
        In case *protect rare classes* is enabled, classes with less than this
        number of elements are considered to be rare

Reference: D. Wilson: 'Asymptotic Properties of NN Rules Using Edited Data'.
*IEEE Transactions on Systems, Man, and Cybernetics*, 2(3):408-421, 1972
"""
    name = "Wilson's Modified Nearest Neighbour (MNN)"
    args = Args([
        Int("Internal k", default=0),
        Check("Protect rare classes", default=True),
        Int("Rare class threshold", default=3)
    ])

    def __call__(self, classifier, k=0, protectRare=True, rareThreshold=3):

        editedClassifier = _copyClassifier(classifier, k)
        toBeRemoved = set()
        progress = ProgressFactory("Generating edited MNN classifier...",
                                   len(classifier.get_glyphs()))

        # classify each glyph with its leave-one-out classifier
        for i, glyph in enumerate(classifier.get_glyphs()):
            editedClassifier.get_glyphs().remove(glyph)
            detectedClass = _getMainId(\
                                editedClassifier.guess_glyph_automatic(glyph))
            # check if recognized class complies with the true class
            if glyph.get_main_id() != detectedClass:
                toBeRemoved.add(glyph)
            editedClassifier.get_glyphs().add(glyph)
            progress.step()

        rareClasses = self._getRareClasses(classifier.get_glyphs(),
                                           protectRare, rareThreshold)

        # remove 'bad' glyphs, if they are not in a rare class
        for glyph in toBeRemoved:
            if glyph.get_main_id() in rareClasses:
                continue
            editedClassifier.get_glyphs().remove(glyph)

        progress.kill()
        return editedClassifier

    def _getRareClasses(self, glyphs, protectRare, rareThreshold):
        """Produces a set containing the names of all rare classes"""
        result = set()

        if not protectRare:
            return result

        # histogram of classNames
        histo = {}
        for g in glyphs:
            count = histo.get(g.get_main_id(), 0)
            histo[g.get_main_id()] = count + 1

        for className in histo:
            if histo[className] < rareThreshold:
                result.add(className)
        return result
Esempio n. 32
0
 def __init__(self, name, callable, args=Args(), doc=""):
     self.name = name
     self.callable = callable
     self.args = args
     self.doc = doc