def __call__(self,im_staffonly,alpha,n,p=0.5,random_seed=0):
        from gamera.core import RGBPixel,FloatPoint
        from gamera.toolkits.musicstaves.stafffinder import StafflineSkeleton
        seed(random_seed)
        im_full=self
        im_staffless=im_full.xor_image(im_staffonly)
        def_staffonly=im_staffonly.image_copy()
        [first_x, last_x, stafflines, thickness]=find_stafflines_int(im_staffonly)
        bnp=binomial(n,p)
        # walk along all pixels of all stafflines
        for y in stafflines:
            for x in range(first_x, last_x):
                # now do the russian roulette
                if random()<alpha:
                    w=bnp.random()
                    if w>0:
                        def_staffonly.draw_filled_rect(FloatPoint(x-w/2,y-thickness),FloatPoint(x+w/2+w%2-1,y+thickness),RGBPixel(0,0,0))
        def_full=im_staffless.or_image(def_staffonly)

        # construct skeletons (in fact they didn't change)
        staffline_skel=[]
        for y in stafflines:
            skel=StafflineSkeleton()
            skel.left_x=first_x
            # all stafflines are completely straight
            skel.y_list=(last_x-first_x+1)*[y]
            staffline_skel.append(skel)

        return [def_full,def_staffonly,staffline_skel]
    def __call__(self, im_staffonly, ampx, period=1):
        from gamera.toolkits.musicstaves.stafffinder import StafflineSkeleton
        from gamera.plugins.deformation import wave
        im_full = self
        [first_x, last_x, stafflines,
         thickness] = find_stafflines_int(im_staffonly)

        periodwidth = int((last_x - first_x) * 2 / period)
        amp = int(ampx * periodwidth)

        # the wave-plugin does the main work
        wave_staffonly = im_staffonly.wave(amp, periodwidth, 0, 0, first_x)
        wave_full = im_full.wave(amp, periodwidth, 0, 0, first_x)

        # now we put together the new staffline skeletons
        staffline_skel = []
        for i in stafflines:
            skel = StafflineSkeleton()
            skel.left_x = first_x
            skel.y_list = []
            staffline_skel.append(skel)

        xfactor = 2 * pi / periodwidth
        yfactor = -amp * 0.5
        yoffset = amp * 0.5
        for x in range(last_x - first_x + 1):
            y_shift = int(yoffset + yfactor * sin(x * xfactor))
            # y_shift applies to all stafflines at the same x
            for i in range(len(stafflines)):
                staffline_skel[i].y_list.append(stafflines[i] + y_shift)

        return [wave_full, wave_staffonly, staffline_skel]
    def __call__(self, im_staffonly, ampx, period=1):
        from gamera.toolkits.musicstaves.stafffinder import StafflineSkeleton
        from gamera.plugins.deformation import wave
        im_full=self
        [first_x, last_x, stafflines, thickness] =find_stafflines_int(im_staffonly)

        periodwidth=int((last_x-first_x)*2/period)
        amp=int(ampx*periodwidth)

        # the wave-plugin does the main work
        wave_staffonly=im_staffonly.wave(amp,periodwidth,0,0,first_x)
        wave_full=im_full.wave(amp,periodwidth,0,0,first_x)

        # now we put together the new staffline skeletons
        staffline_skel=[]
        for i in stafflines:
            skel=StafflineSkeleton()
            skel.left_x=first_x
            skel.y_list=[]
            staffline_skel.append(skel)

        xfactor=2*pi/periodwidth
        yfactor=-amp*0.5
        yoffset=amp*0.5
        for x in range(last_x-first_x+1):
            y_shift=int(yoffset+yfactor*sin(x*xfactor))
            # y_shift applies to all stafflines at the same x
            for i in range(len(stafflines)):
                staffline_skel[i].y_list.append(stafflines[i]+y_shift)

        return [wave_full,wave_staffonly,staffline_skel]
    def __call__(self,im_staffonly, ratio):
        from gamera.core import Image,RGBPixel,Point
        from gamera.toolkits.musicstaves.stafffinder import StafflineSkeleton
        im_full=self
        im_staffless=im_full.xor_image(im_staffonly)
        [first_x, last_x, stafflines, thickness]=find_stafflines_int(im_staffonly)

        # ratio=staffline_height/staffspace_height
        thickness=(stafflines[1]-stafflines[0])/(1/ratio+1)
        im_newlines=Image(im_full.ul,im_full.size)

        # we just draw the lines ourselves
        for y in stafflines:
            im_newlines.draw_line(Point(first_x,y),Point(last_x,y),RGBPixel(255,255,255),thickness)

        # new full: lines OR symbols
        def_full=im_staffless.or_image(im_newlines)
        # new staffonly: lines AND NOT symbols
        def_staffonly=im_staffless.image_copy()
        def_staffonly.invert()
        def_staffonly.and_image(im_newlines,True)
        # staffless image doesn't change
        def_staffless=im_staffless.image_copy()

        # construct skeletons
        staffline_skel=[]
        for y in stafflines:
            skel=StafflineSkeleton()
            skel.left_x=first_x
            # all stafflines are completely straight
            skel.y_list=(last_x-first_x+1)*[y]
            staffline_skel.append(skel)

        return [def_full,def_staffonly,staffline_skel]
    def __call__(self, im_staffonly, alpha, n, p=0.5, random_seed=0):
        from gamera.core import RGBPixel, FloatPoint
        from gamera.toolkits.musicstaves.stafffinder import StafflineSkeleton
        seed(random_seed)
        im_full = self
        im_staffless = im_full.xor_image(im_staffonly)
        def_staffonly = im_staffonly.image_copy()
        [first_x, last_x, stafflines,
         thickness] = find_stafflines_int(im_staffonly)
        bnp = binomial(n, p)
        # walk along all pixels of all stafflines
        for y in stafflines:
            for x in range(first_x, last_x):
                # now do the russian roulette
                if random() < alpha:
                    w = bnp.random()
                    if w > 0:
                        def_staffonly.draw_filled_rect(
                            FloatPoint(x - w / 2, y - thickness),
                            FloatPoint(x + w / 2 + w % 2 - 1, y + thickness),
                            RGBPixel(0, 0, 0))
        def_full = im_staffless.or_image(def_staffonly)

        # construct skeletons (in fact they didn't change)
        staffline_skel = []
        for y in stafflines:
            skel = StafflineSkeleton()
            skel.left_x = first_x
            # all stafflines are completely straight
            skel.y_list = (last_x - first_x + 1) * [y]
            staffline_skel.append(skel)

        return [def_full, def_staffonly, staffline_skel]
    def __call__(self, im_staffonly, angle):
        from gamera.core import RGBPixel
        from gamera.toolkits.musicstaves.stafffinder import StafflineSkeleton
        im_full = self
        im_staffless = im_full.xor_image(im_staffonly)
        # let someone else do the work for us
        # we use -angle because rotate() uses the wrong direction
        rot_staffonly = im_staffonly.rotate(-angle, RGBPixel(0, 0, 0))
        rot_full = im_full.rotate(-angle, RGBPixel(0, 0, 0))

        def apply_transformation(mat, pt):
            return [
                pt[0] * mat[0] + pt[1] * mat[1] + mat[2],
                pt[0] * mat[3] + pt[1] * mat[4] + mat[5]
            ]

        # find offset by which the image is moved while rotating it
        m = [
            cos(angle * pi / 180),
            sin(angle * pi / 180), 0, -sin(angle * pi / 180),
            cos(angle * pi / 180), 0
        ]
        pts_orig = [[0, 0], [im_full.width - 1, 0], [0, im_full.height - 1],
                    [im_full.width - 1, im_full.height - 1]]
        pts = [apply_transformation(m, p) for p in pts_orig]
        m[2] = -min([p[0] for p in pts])
        m[5] = -min([p[1] for p in pts])

        # find the stafflines in the original image
        [first_x, last_x, stafflines,
         thickness] = find_stafflines_int(im_staffonly)

        # rotate/move them and wrap them inside a list of StafflineSkeleton objects
        staffline_skel = []
        for y in stafflines:
            startpt = apply_transformation(m, [first_x, y])
            endpt = apply_transformation(m, [last_x, y])
            # swap point, if the rotation was so hard, that what was left is now right (ie. angle>90 or <-90)
            if startpt[0] > endpt[0]:
                tempswap = startpt
                startpt = endpt
                endpt = tempswap
            o = StafflineSkeleton()
            o.left_x = startpt[0]
            o.y_list = [startpt[1]]
            ty = startpt[1]
            dy = (endpt[1] - startpt[1]) / (endpt[0] - startpt[0])
            for n in range(int(round(endpt[0] - startpt[0]))):
                ty = ty + dy
                o.y_list.append(ty)
            staffline_skel.append(o)

        return [rot_full, rot_staffonly, staffline_skel]
    def __call__(self, im_staffonly, min, max, c=0.5, random_seed=0):
        from gamera.core import Image, RGBPixel
        from gamera.toolkits.musicstaves.stafffinder import StafflineSkeleton
        seed(random_seed)
        im_full = self
        im_staffless = im_full.xor_image(im_staffonly)
        [first_x, last_x, stafflines,
         thickness] = find_stafflines_int(im_staffonly)
        def_staffonly = Image(im_full.ul, im_full.size)
        # state machine for the thickness
        states = states_mh(max - min + 1, c)

        # draw the deformed stafflines in a blank image
        last_thickness = 0
        even_shift = 0
        for l in range(len(stafflines)):
            y = stafflines[l]
            for x in range(first_x, last_x + 1):
                thickness = states.next() + min
                y_st = y - thickness / 2
                if thickness % 2 == 0:
                    if thickness != last_thickness:
                        if random() < 0.5:
                            even_shift = 1
                        else:
                            even_shift = 0
                    y_st = y_st + even_shift
                if thickness > 0:
                    def_staffonly.draw_line((x, y_st),
                                            (x, y_st + thickness - 1),
                                            RGBPixel(255, 255, 255))
                last_thickness = thickness

        # stafflines = stafflines AND NOT symbols
        im_staffless_invert = im_staffless.image_copy()
        im_staffless_invert.invert()
        def_staffonly.and_image(im_staffless_invert, True)
        # full = symbols OR stafflines
        def_full = im_staffless.image_copy()
        def_full.or_image(def_staffonly, True)

        # construct skeletons (in fact they didn't change)
        staffline_skel = []
        for y in stafflines:
            skel = StafflineSkeleton()
            skel.left_x = first_x
            # all stafflines are completely straight
            skel.y_list = (last_x - first_x + 1) * [y]
            staffline_skel.append(skel)

        return [def_full, def_staffonly, staffline_skel]
 def __call__(self, im_staffonly, p, n, k=2, connectivity=2, random_seed=0):
     from gamera.toolkits.musicstaves.stafffinder import StafflineSkeleton
     # the C++ plugin only returns the images
     [staffdef, nostaffdef] = _staffdeformation.white_speckles_parallel_noskel(self, im_staffonly, p, n, k, connectivity, random_seed)
     # create staffline skeletons
     [first_x, last_x, stafflines, thickness] =find_stafflines_int(im_staffonly)
     staffline_skel=[]
     for y in stafflines:
         skel=StafflineSkeleton()
         skel.left_x=first_x
         # all stafflines are completely straight
         skel.y_list=(last_x-first_x+1)*[y]
         staffline_skel.append(skel)
     return [staffdef, nostaffdef, staffline_skel]
    def __call__(self):
        from gamera.toolkits.musicstaves.stafffinder import StafflineSkeleton
        [first_x, last_x, stafflines, thickness]=find_stafflines_int(self)

        # construct skeletons (in fact they didn't change)
        staffline_skel=[]
        for y in stafflines:
            skel=StafflineSkeleton()
            skel.left_x=first_x
            # all stafflines are completely straight
            skel.y_list=(last_x-first_x+1)*[y]
            staffline_skel.append(skel)

        return staffline_skel
    def __call__(self):
        from gamera.toolkits.musicstaves.stafffinder import StafflineSkeleton
        [first_x, last_x, stafflines, thickness] = find_stafflines_int(self)

        # construct skeletons (in fact they didn't change)
        staffline_skel = []
        for y in stafflines:
            skel = StafflineSkeleton()
            skel.left_x = first_x
            # all stafflines are completely straight
            skel.y_list = (last_x - first_x + 1) * [y]
            staffline_skel.append(skel)

        return staffline_skel
    def __call__(self,im_staffonly,min,max,c=0.5,random_seed=0):
        from gamera.core import Image,RGBPixel
        from gamera.toolkits.musicstaves.stafffinder import StafflineSkeleton
        seed(random_seed)
        im_full=self
        im_staffless=im_full.xor_image(im_staffonly)
        [first_x, last_x, stafflines, thickness]=find_stafflines_int(im_staffonly)
        def_staffonly=Image(im_full.ul,im_full.size)
        # state machine for the thickness
        states=states_mh(max-min+1,c)

        # draw the deformed stafflines in a blank image
        last_thickness=0
        even_shift=0
        for l in range(len(stafflines)):
            y=stafflines[l]
            for x in range(first_x,last_x+1):
                thickness=states.next()+min
                y_st=y-thickness/2
                if thickness%2==0:
                    if thickness!=last_thickness:
                        if random()<0.5:
                            even_shift=1
                        else:
                            even_shift=0
                    y_st=y_st+even_shift
                if thickness>0:
                    def_staffonly.draw_line((x,y_st),(x,y_st+thickness-1),RGBPixel(255,255,255))
                last_thickness=thickness

        # stafflines = stafflines AND NOT symbols
        im_staffless_invert=im_staffless.image_copy()
        im_staffless_invert.invert()
        def_staffonly.and_image(im_staffless_invert,True)
        # full = symbols OR stafflines
        def_full=im_staffless.image_copy()
        def_full.or_image(def_staffonly,True)

        # construct skeletons (in fact they didn't change)
        staffline_skel=[]
        for y in stafflines:
            skel=StafflineSkeleton()
            skel.left_x=first_x
            # all stafflines are completely straight
            skel.y_list=(last_x-first_x+1)*[y]
            staffline_skel.append(skel)

        return [def_full,def_staffonly,staffline_skel]
    def __call__(self, im_staffonly, angle):
        from gamera.core import RGBPixel
        from gamera.toolkits.musicstaves.stafffinder import StafflineSkeleton
        im_full=self
        im_staffless=im_full.xor_image(im_staffonly)
        # let someone else do the work for us
        # we use -angle because rotate() uses the wrong direction
        rot_staffonly=im_staffonly.rotate(-angle,RGBPixel(0,0,0))
        rot_full=im_full.rotate(-angle,RGBPixel(0,0,0))

        def apply_transformation(mat,pt):
            return [pt[0]*mat[0]+pt[1]*mat[1]+mat[2],pt[0]*mat[3]+pt[1]*mat[4]+mat[5]]

        # find offset by which the image is moved while rotating it
        m=[ cos(angle*pi/180),sin(angle*pi/180),0,
           -sin(angle*pi/180),cos(angle*pi/180),0]
        pts_orig=[[0,0],
                  [im_full.width-1,0],
                  [0,im_full.height-1],
                  [im_full.width-1,im_full.height-1]]
        pts=[apply_transformation(m,p) for p in pts_orig]
        m[2]=-min([p[0] for p in pts])
        m[5]=-min([p[1] for p in pts])

        # find the stafflines in the original image
        [first_x, last_x, stafflines, thickness]=find_stafflines_int(im_staffonly)

        # rotate/move them and wrap them inside a list of StafflineSkeleton objects
        staffline_skel=[]
        for y in stafflines:
            startpt=apply_transformation(m,[first_x,y])
            endpt=apply_transformation(m,[last_x,y])
            # swap point, if the rotation was so hard, that what was left is now right (ie. angle>90 or <-90)
            if startpt[0]>endpt[0]:
                tempswap=startpt
                startpt=endpt
                endpt=tempswap
            o=StafflineSkeleton()
            o.left_x=startpt[0]
            o.y_list=[startpt[1]]
            ty=startpt[1]
            dy=(endpt[1]-startpt[1])/(endpt[0]-startpt[0])
            for n in range(int(round(endpt[0]-startpt[0]))):
                ty=ty+dy
                o.y_list.append(ty)
            staffline_skel.append(o)

        return [rot_full,rot_staffonly,staffline_skel]
Example #13
0
    def find_staves(self, with_trimming=True, with_deletion=True, \
                        with_staff_fixing=True, \
                        enable_strong_staff_pixels=False):
        """Method for finding the staff lines.

Signature:

  ``find_staves(with_trimming=True, with_deletion=True, with_staff_fixing=True, enable_strong_staff_pixels=False)``

with

- *with_trimming*:
  Trims staff sets where white space or ornamentations are found.
        
- *with_deletion*:
  If true, the image will be processed once and will create an image comprised of only found staves and then the code is run again. More accurate for images with a lot of lyrics or ornamentation.
        
- *with_staff_fixing*:
  Uses the slopes of staff sets to fix staff lines that differ wildly from the slope at specific intervals.
        
- *enable_strong_staff_pixels*:
  Experimental method that reduces the weights of vertical runs that are the exact width of staffline_height and are exactly staffspace_height away from the closest black pixel.

This method fills the *self.linelist* attribute for further
processing.
"""

        # Get the skeleton list
        skeleton_list = self.image.get_stable_path_staff_skeletons( \
            with_trimming=with_trimming, \
            with_staff_fixing=with_staff_fixing, \
            enable_strong_staff_pixels=enable_strong_staff_pixels, \
            staffline_height=self.staffline_height, \
            staffspace_height=self.staffspace_height)

        # copy over to stafflist
        self.linelist = []

        print len(skeleton_list)
        for g in skeleton_list:
            newstaff = []
            for s in g:
                skel = StafflineSkeleton()
                skel.left_x = s[0]
                skel.y_list = s[1]
                newstaff.append(skel)
            self.linelist.append(newstaff)
 def __call__(self, im_staffonly, p, n, k=2, connectivity=2, random_seed=0):
     from gamera.toolkits.musicstaves.stafffinder import StafflineSkeleton
     # the C++ plugin only returns the images
     [staffdef,
      nostaffdef] = _staffdeformation.white_speckles_parallel_noskel(
          self, im_staffonly, p, n, k, connectivity, random_seed)
     # create staffline skeletons
     [first_x, last_x, stafflines,
      thickness] = find_stafflines_int(im_staffonly)
     staffline_skel = []
     for y in stafflines:
         skel = StafflineSkeleton()
         skel.left_x = first_x
         # all stafflines are completely straight
         skel.y_list = (last_x - first_x + 1) * [y]
         staffline_skel.append(skel)
     return [staffdef, nostaffdef, staffline_skel]
 def __call__(self, im_staffonly, eta, a0, a, b0, b, k=2, random_seed=0):
     from gamera.toolkits.musicstaves.stafffinder import StafflineSkeleton
     # the C++ plugin only returns a single image
     # random_seed should guarantee compatible results
     # additionally we subtract staffless image from nostaffdef
     # so that not too many symbol pixels are redefined as staff pixels
     staffdef = _staffdeformation.degrade_kanungo_single_image(self, eta, a0, a, b0, b, k, random_seed)
     nostaffdef = _staffdeformation.degrade_kanungo_single_image(im_staffonly, eta, a0, a, b0, b, k, random_seed)
     im_nostaff = self.subtract_images(im_staffonly)
     nostaffdef = nostaffdef.subtract_images(im_nostaff)
     # create staffline skeletons
     [first_x, last_x, stafflines, thickness] =find_stafflines_int(im_staffonly)
     staffline_skel=[]
     for y in stafflines:
         skel=StafflineSkeleton()
         skel.left_x=first_x
         # all stafflines are completely straight
         skel.y_list=(last_x-first_x+1)*[y]
         staffline_skel.append(skel)
     return [staffdef, nostaffdef, staffline_skel]
    def __call__(self, im_staffonly, maxdiff, c=0.5, random_seed=0):
        from gamera.core import Image, RGBPixel
        from gamera.toolkits.musicstaves.stafffinder import StafflineSkeleton
        seed(random_seed)
        im_full = self
        im_staffless = im_full.xor_image(im_staffonly)
        [first_x, last_x, stafflines,
         thickness] = find_stafflines_int(im_staffonly)
        def_staffonly = Image(im_full.ul, im_full.size)
        states = states_mh(2 * maxdiff + 1, c)

        m = (thickness - 1) / 2
        if (thickness and 1) == 1:
            p = m
        else:
            p = m + 1

        staffline_skel = []
        for l in range(len(stafflines)):
            y = stafflines[l] - maxdiff
            skel = StafflineSkeleton()
            skel.left_x = first_x
            skel.y_list = (last_x - first_x + 1) * [y]
            for x in range(first_x, last_x + 1):
                y_l = y + states.next()
                skel.y_list[x - first_x] = y_l
                def_staffonly.draw_line((x, y_l - m), (x, y_l + p),
                                        RGBPixel(255, 255, 255))
            staffline_skel.append(skel)

        im_staffless_invert = im_staffless.image_copy()
        im_staffless_invert.invert()
        def_staffonly.and_image(im_staffless_invert, True)
        def_staffless = im_staffless.image_copy()
        def_full = im_staffless.image_copy()
        def_full.or_image(def_staffonly, True)

        return [def_full, def_staffonly, staffline_skel]
 def __call__(self, im_staffonly, eta, a0, a, b0, b, k=2, random_seed=0):
     from gamera.toolkits.musicstaves.stafffinder import StafflineSkeleton
     # the C++ plugin only returns a single image
     # random_seed should guarantee compatible results
     # additionally we subtract staffless image from nostaffdef
     # so that not too many symbol pixels are redefined as staff pixels
     staffdef = _staffdeformation.degrade_kanungo_single_image(
         self, eta, a0, a, b0, b, k, random_seed)
     nostaffdef = _staffdeformation.degrade_kanungo_single_image(
         im_staffonly, eta, a0, a, b0, b, k, random_seed)
     im_nostaff = self.subtract_images(im_staffonly)
     nostaffdef = nostaffdef.subtract_images(im_nostaff)
     # create staffline skeletons
     [first_x, last_x, stafflines,
      thickness] = find_stafflines_int(im_staffonly)
     staffline_skel = []
     for y in stafflines:
         skel = StafflineSkeleton()
         skel.left_x = first_x
         # all stafflines are completely straight
         skel.y_list = (last_x - first_x + 1) * [y]
         staffline_skel.append(skel)
     return [staffdef, nostaffdef, staffline_skel]
    def __call__(self,im_staffonly,maxdiff,c=0.5,random_seed=0):
        from gamera.core import Image,RGBPixel
        from gamera.toolkits.musicstaves.stafffinder import StafflineSkeleton
        seed(random_seed)
        im_full=self
        im_staffless=im_full.xor_image(im_staffonly)
        [first_x, last_x, stafflines, thickness]=find_stafflines_int(im_staffonly)
        def_staffonly=Image(im_full.ul,im_full.size)
        states=states_mh(2*maxdiff+1,c)

        m=(thickness-1)/2
        if (thickness and 1)==1:
            p=m
        else:
            p=m+1
        
        staffline_skel=[]
        for l in range(len(stafflines)):
            y=stafflines[l]-maxdiff
            skel=StafflineSkeleton()
            skel.left_x=first_x
            skel.y_list=(last_x-first_x+1)*[y]
            for x in range(first_x,last_x+1):
                y_l=y+states.next()
                skel.y_list[x-first_x]=y_l
                def_staffonly.draw_line((x,y_l-m),(x,y_l+p),RGBPixel(255,255,255))
            staffline_skel.append(skel)

        im_staffless_invert=im_staffless.image_copy()
        im_staffless_invert.invert()
        def_staffonly.and_image(im_staffless_invert,True)
        def_staffless=im_staffless.image_copy()
        def_full=im_staffless.image_copy()
        def_full.or_image(def_staffonly,True)

        return [def_full,def_staffonly,staffline_skel]
    def __call__(self, im_staffonly, ratio):
        from gamera.core import Image, RGBPixel, Point
        from gamera.toolkits.musicstaves.stafffinder import StafflineSkeleton
        im_full = self
        im_staffless = im_full.xor_image(im_staffonly)
        [first_x, last_x, stafflines,
         thickness] = find_stafflines_int(im_staffonly)

        # ratio=staffline_height/staffspace_height
        thickness = (stafflines[1] - stafflines[0]) / (1 / ratio + 1)
        im_newlines = Image(im_full.ul, im_full.size)

        # we just draw the lines ourselves
        for y in stafflines:
            im_newlines.draw_line(Point(first_x, y), Point(last_x, y),
                                  RGBPixel(255, 255, 255), thickness)

        # new full: lines OR symbols
        def_full = im_staffless.or_image(im_newlines)
        # new staffonly: lines AND NOT symbols
        def_staffonly = im_staffless.image_copy()
        def_staffonly.invert()
        def_staffonly.and_image(im_newlines, True)
        # staffless image doesn't change
        def_staffless = im_staffless.image_copy()

        # construct skeletons
        staffline_skel = []
        for y in stafflines:
            skel = StafflineSkeleton()
            skel.left_x = first_x
            # all stafflines are completely straight
            skel.y_list = (last_x - first_x + 1) * [y]
            staffline_skel.append(skel)

        return [def_full, def_staffonly, staffline_skel]
    def find_staves(self, num_lines=0, window=2, blackness = 60, \
                    tolerance = 25, align_edges=True, join_interrupted=True, debug = 0):
        """Method for finding the staff lines.

Signature:

  ``find_staves(num_lines=0, window=1, blackness=60, tolerance=25, align_edges=True, join_interrupted=True, debug=0)``

with

- *num_lines*:

  Number of lines per staff. If unknown, set to 0.

- *window* and *blackness*:

  Parameters for the extraction of long horizontal runs. Only pixels are
  extracted that have an average blackness greater than *blackness* within
  a window of the width *window* \* *staff_space_height*.

- *tolerance*:

  The tolerance that is used while connecting line segments that
  belong to the same staff. They may have a vertical distance of
  *staff_line_height* + *staff_space_height* with a deviation of
  *tolerance* in percent.

- *align_edges*:

  When `True`, staff line edges in a staff are aligned up to the
  left most and right most found staff line within this staff.

- *debug*:

  0 = Be quiet.
  1 = Show Progress messages.
  2 = print images with prefix 'dalitzdebug' to current directory

This method fills the *self.linelist* attribute for further
processing.
"""

        #--------------------------------------------------------
        #
        # Step 1: Get staff skeleton list
        #

        if debug > 0:
            print
            print "Getting staff skeletons..."

        # Get the skeleton list
        skeleton_list = self.image.get_staff_skeleton_list( \
            self.staffline_height, \
            window * self.staffspace_height, \
            blackness)

        too_short_skeletons = [ line for line in skeleton_list \
                                if len(line[1]) < self.staffspace_height * 2 ]


        if len(too_short_skeletons) > 0:
            
            if debug > 0:
                print "   %i skeletons are too short. Removing." \
                      % (len(too_short_skeletons))

            # Remove very short segments
            for s in too_short_skeletons:
                skeleton_list.remove(s)

        # Create graph
        segments = []
        for line in skeleton_list:
            n = StaffSegment()
            n.row_start = line[1][0]
            n.col_start = line[0]
            n.row_end = line[1][-1]
            n.col_end = n.col_start + len(line[1]) - 1
            n.skeleton = line		
            segments.append(n)

        #--------------------------------------------------------
        #
        # Step 2: Create vertical connections
        #
        #  A connection is done between two segments that
        #  overlap horizontally if the vertical distance
        #  is staff_line_height + staff_space_height
        #  (+- tolerance) percent
        #

        if debug > 0:
            print "Creating vertical connections..."
 
        connections = []
	
        tol = float(tolerance) / 100.0
        min_dist = (self.staffline_height + self.staffspace_height) \
                   * (1.0 - tol)
        max_dist = (self.staffline_height + self.staffspace_height) \
                   * (1.0 + tol)

        for seg1 in segments:
            for seg2 in segments:

                # No self-connections, segments must overlap
                if seg1 != seg2 and seg1.overlaps(seg2):

                    # Calculate vertical distance in
                    # the middle of the overlapping parts
                    mid = (max(seg1.col_start, seg2.col_start) + \
                           min(seg1.col_end, seg2.col_end)) / 2
                    row1 = seg1.skeleton[1][mid - seg1.col_start]
                    row2 = seg2.skeleton[1][mid - seg2.col_start]
                    dist = row2 - row1

                    if dist >= min_dist and dist <= max_dist:

                        # seg2 belongs to a staff
                        # line below seg1
                        if seg1.down_links.count(seg2) == 0:
                            seg1.down_links.append(seg2)
                        if seg2.up_links.count(seg1) == 0:
                            seg2.up_links.append(seg1)

                        # Add connection for debugging
                        conn = StaffConn()
                        conn.col = mid
                        conn.row_start = min(row1, row2)
                        conn.row_end = max(row1, row2)
                        connections.append(conn)

                    elif dist <= -min_dist and dist >= -max_dist:

                        # seg2 belongs to a staff
                        # line on top of seg1
                        if (seg2.down_links.count(seg1) == 0):
                            seg2.down_links.append(seg1)
                        if (seg1.up_links.count(seg2) == 0):
                            seg1.up_links.append(seg2)

        if debug > 0:
            print "   %i connections created." % (len(connections))
                            
        #--------------------------------------------------------
        #
        # Step 3a: Remove Segments without links
        #
    
        tmp = []
        for seg in segments:
            if len(seg.down_links) > 0 or len(seg.up_links) > 0:
                tmp.append(seg)

        segments = tmp
        del tmp

        #--------------------------------------------------------
        #
        # Step 3b: Label CC's and line numbers
        #
        #  Do a breadth-first search on the segments
        #  and give a unique label to every segment that
        #  belongs to a certain group of connected segments.
        #  Increase the line number with every downward
        #  connection and decrease it with every upward
        #  connection.

        if debug > 0:
            print "Grouping segments to staffs..."

        label = -1
        groups = []

        for segment in segments:

            # Segment already labeled: Process next one
            if segment.label != None: continue

            seg = segment
            label = label + 1 # Increase label
            group = StaffGroup()

            # Label this segment
            seg.label = label
            seg.line = 0
            group.extend(seg)

            # Label neighbors
            neighbors = []
            for n in seg.up_links:
                if n.label == None:
                    n.label = label
                    # Up-link: Decrease line number
                    n.line = seg.line - 1
                    group.extend(n)
                    neighbors.append(n)

                elif n.label != label:
                    raise RuntimeError, "Labelling error!"

            for n in seg.down_links:
                if n.label == None:
                    n.label = label
                    # Down-link: Increase line number
                    n.line = seg.line + 1
                    group.extend(n)
                    neighbors.append(n)

                elif n.label != label:
                    raise RuntimeError, "Labelling error!"

            # Process neighbors
            while len(neighbors) > 0:

                new_neighbors = []

                for seg in neighbors:
                    for n in seg.up_links:
                        if n.label == None:
                            n.label = label
                            # Up-Link: Decrease line number
                            n.line = seg.line - 1
                            group.extend(n)
                            new_neighbors.append(n)

                        elif n.label != label:
                            raise RuntimeError, "Labelling error!"

                    for n in seg.down_links:
                        if n.label == None:
                            n.label = label
                            # Down-Link: Increase line numver
                            n.line = seg.line + 1
                            group.extend(n)
                            new_neighbors.append(n)

                        elif n.label != label:
                            raise RuntimeError, "Labelling error!"

                neighbors = new_neighbors

            groups.append(group)

        if debug > 0:
            print "   Found %i staffs." % (len(groups))

        #--------------------------------------------------------
        #
        # Step 4: Melt overlapping segments of a staff line
        #
        #  If two segments of the same staff line overlap,
        #  they have to be melted to one, so that the later
        #  interpolation can assume non-overlapping segments.
        #
        #  In the overlapped parts, the decision of which
        #  part to use is made by laying a least square fit over
        #  the non-overlapping parts. The overlapping part, that
        #  fits better to the resulting straight line is used
        #  to substitute the overlapping range.

        if debug > 0:
            print "Melting overlapping line segments..."

        melt_skeletons = []

        melt_count = 0
        for g in groups:
            for l in range(g.min_line, g.max_line + 1):

                melted = True
                while (melted):
                    melted = False

                    for seg1 in g.segments:
                        if seg1.line != l: continue

                        for seg2 in g.segments:
                            if seg2.line != l or seg1 == seg2: continue

                            if seg1.overlaps(seg2):
                                melt_skeletons.append(seg1.melt(seg2))
                                g.segments.remove(seg2)
                                melted = True
                                melt_count = melt_count + 1

                                # Jump out of the
                                # inner 'for'
                                break

                        # Jump out of the outer 'for'
                        if melted: break

        if debug > 0 and melt_count > 0:
            print "   %d segments melted." % (melt_count)

        #--------------------------------------------------------
        #
        # Step 5a: Removal of staffs with too few lines
        #
        #  when the number of lines is not given, it is
        #  estimated as the most frequent num_lines among the wide groups
        #

        # Get maximum staff line width of all staffs
        max_group_width = 0
        for g in groups:
            width = g.col_end - g.col_start + 1
            if width > max_group_width: max_group_width = width

        # estimate num_lines
        if num_lines > 0:
            estimated_num_lines = num_lines
        else:
            num_hist = {}
            for g in groups:
                if g.col_end - g.col_start + 1 > max_group_width / 2:
                    n = g.max_line - g.min_line + 1
                    if num_hist.has_key(n):
                        num_hist[n] += 1
                    else:
                        num_hist[n] = 1
            max_count = 0
            for (n,c) in num_hist.iteritems():
                if c > max_count:
                    estimated_num_lines = n
                    max_count = c
            print "num_lines estimated as ", estimated_num_lines

        # remove staffs with fewer lines
        if debug > 0:
            print "Removing staffs with fewer lines than", estimated_num_lines, "..."
        rem_groups = [g for g in groups \
                      if g.max_line - g.min_line + 1 < estimated_num_lines]
        for g in rem_groups: groups.remove(g)
        if debug > 0 and len(rem_groups) > 0:
            print "   %i removed, %i staffs left." \
                  % (len(rem_groups), len(groups))

        #--------------------------------------------------------
        #
        # Step 5b: Remove additional lines above and below staffs
        #
        #  If the number of staff lines in a staff is known,
        #  the top or bottom line (the narrower one) is removed
        #  until the correct number of lines is reached.
        #
        #  If it is not known, every line is removed, that is
        #  much narrower than the maximum line length in this
        #  staff.
        #

        if debug > 0:
            print "Removing additional staff lines..."

        lines_removed = 0

        # In every staff group
        for g in groups:

            lengths = []
            max_length = 0

            # Calculate range of every staff line
            for line in range(g.min_line, g.max_line + 1):

                length_sum = 0
                for s in [seg for seg in g.segments if seg.line == line]:
                    length_sum = length_sum + s.col_end - s.col_start + 1
                lengths.append((line, length_sum))

                if length_sum > max_length: max_length = length_sum

            # If the real number of staff lines is given...
            if num_lines > 0:

                # While there are to much lines...
                while g.max_line - g.min_line + 1 > num_lines:

                    if lengths[0][1] < lengths[-1][1]: # Upper line shortest
                        g.remove_line(lengths[0][0])
                        lengths.pop(0) # Remove first line info

                    else: # Bottom line shortest
                        g.remove_line(lengths[-1][0])
                        lengths.pop() # Remove last line info

                    lines_removed = lines_removed + 1

            else: # Real number of lines not given

                # Simply remove lines that are too short
                for line, length in lengths:
                    if (length < max_length * 0.8):
                        g.remove_line(line)
                        lines_removed = lines_removed + 1

            # TODO: Check if groups have been seperated!

        if debug > 0 and lines_removed > 0:
            print "   Removed %d lines." % (lines_removed)

        #--------------------------------------------------------
        #
        # Step 6a: Remove groups that overlap with wider groups
        #

        if debug > 0:
            print "Removing embedded staffs..."

        # sort groups line for line from left to right
        def _cmp_y(g,h):
            if g.row_start < h.row_start: return -1
            else: return 1
        groups.sort(_cmp_y)
        ngroups = len(groups)
        breakdist = 2 * self.staffspace_height
        for i in range(ngroups):
            # find items in same line
            candidates = []
            for j in range(i+1,ngroups):
                if groups[i].row_end - breakdist < groups[j].row_start:
                    break
                candidates.append([j,groups[j]])
            # pick the leftmost as next in order
            if len(candidates) > 0:
                minj = i; min_col_start = groups[i].col_start
                for c in candidates:
                    if c[1].col_start < min_col_start:
                        minj = c[0]; min_col_start = c[1].col_start
                if minj > i:
                    g = groups[i]
                    groups[i] = groups[minj]
                    groups[minj] = g

        #print "groups sorted:"
        #for g in groups:
        #    print "rows = ", g.row_start, "-", g.row_end, "col_start =",g.col_start, "lines =", g.max_line - g.min_line + 1

        # when consecutive groups overlap, keep only the widest
        rem_groups = []
        i = 0; j = 0
        while i < len(groups) and j < len(groups) - 1:
            j += 1
            g = groups[i]; h = groups[j]
            if g.col_end >= h.col_start and \
               ((h.row_start < g.row_start and g.row_start < h.row_end) or \
                (h.row_start < g.row_end and g.row_end < h.row_end)):
                if (g.col_end - g.col_start) < (h.col_end - h.col_start):
                    rem_groups.append(g)
                    i = j
                else:
                    rem_groups.append(h)
            else:
                i += 1
        for g in rem_groups:
            if g in groups:
                groups.remove(g)
        if debug > 0 and len(rem_groups) > 0:
            print "   %i removed, %i staffs left." \
                  % (len(rem_groups), len(groups))

        #--------------------------------------------------------
        #
        # Step 6b: Join groups belonging to the same staff system
        #          (only executed when join_interrupted is set)
        #

        if join_interrupted:
            if debug > 0:
                print "Join interrupted staves..."
            # check whether consecutive groups follow each other
            # and how they could be linked
            # condition: distance < 2*staffspace_height
            rem_groups = []
            for i, g1 in enumerate(groups):

                if g1 in rem_groups:
                    continue

                for j in range(i + 1, len(groups)):
                    g2 = groups[j]

                    # join only if vertically overlapping
                    if max(g1.row_start, g2.row_start) > min(g1.row_end, g2.row_end):
                        break

                    # join groups with the same line count only
                    if g2.max_line - g2.min_line != g1.max_line - g1.min_line:
                        break

                    if g2.col_start <= g1.col_end:
                        break

                    if g2.col_start - g1.col_end >= 2*self.staffspace_height:
                        break

                    # now do the join thing
                    g1.join(g2)
                    rem_groups.append(g2)

            for g in rem_groups: groups.remove(g)

            if debug > 0:
                print "   %i group(s) joined." % len(rem_groups)


        #--------------------------------------------------------
        #
        # Step 7: Removal of narrow staffs
        #

        if debug > 0:
            print "Removing invalid staffs..."
        rem_groups = [g for g in groups \
                      if g.col_end - g.col_start + 1 < max_group_width / 2]

        for g in rem_groups: groups.remove(g)
        if debug > 0 and len(rem_groups) > 0:
            print "   %i removed, %i staffs left." \
                  % (len(rem_groups), len(groups))


        #--------------------------------------------------------
        #
        # Step 8: Interpolate broken staff lines
        #
        #  If there is more than one segment left for a staff
        #  line: Order and connect them.
        #

        if debug > 0:
            print "Connecting broken lines..."

        conn_skels = []
        conn_count = 0
        
        for g in groups:
            for line in range(g.min_line, g.max_line + 1):

                # Count segments in this line
                line_segs = []
                for s in g.segments:
                    if s.line == line: line_segs.append(s)

                # If more than one segment per line: Connect them!
                if len(line_segs) > 1:

                    conn_count = conn_count + len(line_segs)

                    # Sort segments by start column
                    line_segs.sort(lambda x, y: int(x.col_start - y.col_start))

                    s1 = line_segs.pop(0) # Leftmost segment

                    for s in line_segs:
                        conn_skel = s1.connect(s)
                        if conn_skel: conn_skels.append(conn_skel)
                        g.segments.remove(s)

        if debug > 0 and conn_count > 0:
            print "   %i connected" % (conn_count)

        #--------------------------------------------------------
        #
        #  Visualization
        #

        if (debug > 1):

            rgb = Image(self.image, RGB)

            print
            print "Drawing group backgrounds..."
            for g in groups:
                color = RGBPixel(150 + (31 * g.label) % 106, \
                                 150 + (111 * (g.label + 1)) % 106, \
                                 150 + (201 * (g.label + 2)) % 106)

                rgb.draw_filled_rect((g.col_start, g.row_start), \
                                     (g.col_end, g.row_end), \
                                     color)

            print "Drawing original image..."
            rgb.highlight(self.image, RGBPixel(0, 0, 0))

            print "Highlighting staff line candidates..."
            staff_skeleton = self.image.skeleton_list_to_image(skeleton_list)
            rgb.highlight(staff_skeleton, RGBPixel(255, 150, 0)) # orange
            staff_skeleton_short = self.image.skeleton_list_to_image(\
                too_short_skeletons)
            rgb.highlight(staff_skeleton_short, RGBPixel(255, 0, 150)) # orange

            black_runs = self.image.extract_filled_horizontal_black_runs(\
                windowwidth = self.staffspace_height * window, \
                blackness = blackness)

            black_runs = black_runs.to_rgb()

            black_runs.highlight(staff_skeleton, RGBPixel(0, 255, 0))
            black_runs.save_PNG("dalitzdebug_blackruns.png")

            print "Highlighting group segments..."
            group_skeletons = []
            melted_skeletons = []
            conn_skeletons = []

            for g in groups:
                for seg in g.segments:
                    group_skeletons.append(seg.skeleton)

            group_image = self.image.skeleton_list_to_image(group_skeletons)
            rgb.highlight(group_image, RGBPixel(0, 255, 0)) # green

            print "Highlighting melted sections..."
            melt_image = self.image.skeleton_list_to_image(melt_skeletons)
            rgb.highlight(melt_image, RGBPixel(0, 255, 255)) # cyan

            print "Highlighting connections..."
            conn_image = self.image.skeleton_list_to_image(conn_skels)
            rgb.highlight(conn_image, RGBPixel(255, 255, 0)) # yellow

            print "Drawing segment markers..."
            for g in groups:
                for seg in g.segments:

                    color = RGBPixel(100 + ((71 * seg.line) % 156), 0, 0)

                    rgb.draw_marker((seg.col_start, seg.row_start), \
                                    self.staffline_height * 2, \
                                    3, color)

                    rgb.draw_marker((seg.col_end, seg.row_end), \
                                    self.staffline_height * 2, \
                                    3, color)

            print "Drawing links..."

            # All connections

            for c in connections:
                    rgb.draw_line((c.col, c.row_start), (c.col, c.row_end), \
                                  RGBPixel(0, 0, 255)) # blue

            # Connections of group segments

            for g in groups:
                for seg in g.segments:
                    for link in seg.down_links:
                        mid = (max(seg.col_start, link.col_start) + \
                               min(seg.col_end, link.col_end)) / 2
                        row1 = seg.skeleton[1][mid - seg.col_start]
                        row2 = link.skeleton[1][mid - link.col_start]
                        rgb.draw_line((mid, row1), (mid, row2), \
                                      RGBPixel(255, 0, 200)) # pink

            print "Writing file..."
            rgb.save_PNG("dalitzdebug_out.png")

        #--------------------------------------------------------
        #
        # Copy over the results into self.linelist
        #

        self.linelist = []

        for g in groups:
            newstaff = []
            #for line in range(g.min_line, g.max_line + 1):
            for s in g.segments:
                skel = StafflineSkeleton()
                skel.left_x = s.skeleton[0]
                skel.y_list = s.skeleton[1]
                newstaff.append(skel)
            # sort by y-position
            newstaff.sort(lambda s1,s2: int(s1.y_list[0] - s2.y_list[0]))    
            self.linelist.append(newstaff)

        #--------------------------------------------------------
        #
        # Adjust edge points to the left/right most point with each staff
        #

        if align_edges:
            if debug > 0:
                print "Align edge points"
            for staff in self.linelist:
                # find left/right most edge point
                lefti = 0; left = self.image.ncols
                righti = 0; right = 0
                for i,skel in enumerate(staff):
                    if skel.left_x < left:
                        lefti = i; left = skel.left_x
                    if skel.left_x + len(skel.y_list) - 1 > right:
                        righti = i; right = skel.left_x + len(skel.y_list) - 1
                leftref = staff[lefti].y_list
                rightref = staff[righti].y_list
                # extrapolate left edge points
                for skel in staff:
                    if skel.left_x > left:
                        if skel.left_x - left < len(leftref):
                            dy = skel.y_list[0] - leftref[skel.left_x - left]
                        else:
                            dy = self.staffspace_height
                        x = skel.left_x - 1
                        while (x >= left):
                            if x-left < len(leftref):
                                skel.y_list.insert(0, leftref[x-left] + dy)
                            else:
                                skel.y_list.insert(0, skel.y_list[0])
                            x -= 1
                        skel.left_x = left
                # extrapolate right edge points
                for skel in staff:
                    if skel.left_x + len(skel.y_list) - 1 < right:
                        dy = skel.y_list[-1] - rightref[len(skel.y_list)]
                        x = skel.left_x + len(skel.y_list)
                        while (x <= right):
                            skel.y_list.append(rightref[x-left] + dy)
                            x += 1

        if debug > 0:
            print "Ready."
            print
    def __call__(self,
                 im_staffonly,
                 n_gap,
                 p_gap,
                 n_shift,
                 random_seed=0,
                 add_noshift=False):
        from gamera.core import RGBPixel, Point, FloatPoint, Rect
        from gamera.toolkits.musicstaves.stafffinder import StafflineSkeleton
        seed(random_seed)
        bnp_gap = binomial(n_gap, p_gap)
        bnp_shift = binomial(n_shift, 0.5)
        im_full = self.image_copy()
        im_staffless = im_full.xor_image(im_staffonly)
        im_staffonly = im_staffonly.image_copy()
        il_shifts = [im_staffless, im_staffonly, im_full]
        il_breaks = il_shifts
        if add_noshift:
            ns_full = im_full.image_copy()
            ns_staffonly = im_staffonly.image_copy()
            il_breaks = [
                im_staffless, im_staffonly, im_full, ns_full, ns_staffonly
            ]

        #find stafflines
        [first_x, last_x, stafflines,
         thickness] = find_stafflines_int(im_staffonly)

        #find breaks (where are the symbols?)
        stafflinedist = stafflines[1] - stafflines[0]
        staffline_skel = []
        for sl in range(len(stafflines)):
            if sl == 0 or stafflines[sl] - stafflines[sl -
                                                      1] > 3 * stafflinedist:
                #first staffline of a system
                first_line = stafflines[sl]
                lines_per_system = 0

            lines_per_system = lines_per_system + 1

            if sl == len(stafflines) - 1 or stafflines[
                    sl + 1] - stafflines[sl] > 3 * stafflinedist:
                #last staffline of a system
                last_line = stafflines[sl]
                # a hoizontal projection of the symbols helps us to find the x positions of the symbols.
                syst = im_staffless.subimage(
                    Point(0, max((0, first_line - 3 * stafflinedist))),
                    Point(
                        im_staffless.width,
                        min((last_line + 3 * stafflinedist,
                             im_full.lr_y - 1))))
                symbolcols = syst.projection_cols()

                # collect the breaks, i.e. the mid of the white spaces between two symbols
                breaks = []
                whiterun = False
                for col in range(len(symbolcols)):
                    if (not whiterun) and symbolcols[col] == 0:
                        whiterun = True
                        runbegin = col
                    else:
                        if whiterun and symbolcols[col] > 0:
                            whiterun = False
                            br = [(runbegin + col) / 2, col - runbegin]
                            if br[0] >= first_x:
                                breaks.append(br)

                # replace the first break (before the first symbol) with a break at the beginning of the stafflines
                breaks[0] = [first_x, 1]
                # and append a break after the last symbol
                if whiterun:
                    breaks.append([(last_x + runbegin) / 2, last_x - runbegin])
                else:
                    breaks.append([last_x, 1])

                # draw white lines at the breaks
                new_breaks = []
                for br in breaks:
                    w = bnp_gap.random()
                    # draw line if it fits only
                    if w < br[1]:
                        for im in il_breaks:
                            im.draw_line(
                                FloatPoint(br[0], first_line - stafflinedist),
                                FloatPoint(br[0], last_line + stafflinedist),
                                RGBPixel(0, 0, 0), w)
                        new_breaks.append(br)
                breaks = new_breaks
                skeleton = []

                # do the vertical shift by making a temporary copy (orig_type), white them out (draw_filled_rect) and "or" them again in with a new y-position
                for t in range(len(breaks) - 1):
                    vertical_shift = bnp_shift.random() - n_shift / 2
                    typerect = Rect(
                        Point(breaks[t][0],
                              max((first_line - 3 * stafflinedist, 0))),
                        Point(
                            breaks[t + 1][0],
                            min((last_line + 3 * stafflinedist,
                                 im_full.lr_y))))
                    moveto = Rect(
                        Point(typerect.ul_x, typerect.ul_y + vertical_shift),
                        typerect.size)
                    if moveto.ul_y < 0:
                        typerect.ul_y = typerect.ul_y - moveto.ul_y
                        moveto.ul_y = 0
                    if moveto.lr_y > im_full.lr_y:
                        typerect.lr_y = typerect.lr_y - (moveto.lr_y -
                                                         im_full.lr_y)
                        moveto.lr_y = im_full.lr_y

                    for im in il_shifts:
                        orig_type = im.subimage(typerect)
                        orig_type = orig_type.image_copy()
                        im.draw_filled_rect(typerect, RGBPixel(0, 0, 0))
                        im.subimage(moveto).or_image(orig_type, True)

                    # collect data for later construction of the skeletons
                    for line in range(lines_per_system):
                        if len(skeleton) <= line:
                            skeleton.append([])
                        st_line = stafflines[sl - lines_per_system + 1 + line]
                        y = st_line + vertical_shift
                        skeleton[line].append(Point(typerect.ul_x, y))
                        skeleton[line].append(Point(typerect.ur_x, y))

                # now construct the skeletons
                for sk in skeleton:
                    x = sk[0].x
                    y = sk[0].y
                    left_x = x
                    y_list = []
                    i = 1
                    while i < len(sk):
                        y_list.append(y)
                        if x >= sk[i].x:
                            y = sk[i].y
                            i = i + 1
                        x = x + 1
                    o = StafflineSkeleton()
                    o.left_x = left_x
                    o.y_list = y_list
                    staffline_skel.append(o)

        if add_noshift:
            return [
                im_full, im_staffonly, staffline_skel, ns_full, ns_staffonly
            ]
        else:
            return [im_full, im_staffonly, staffline_skel]
Example #22
0
    def find_staves(self, num_lines=0, window=3, blackness = 60, \
                    tolerance = 25, align_edges=True, join_interrupted=True, debug = 0):
        """Method for finding the staff lines.

Signature:

  ``find_staves(num_lines=0, window=3, blackness=60, tolerance=25, align_edges=True, join_interrupted=True, debug=0)``

with

- *num_lines*:

  Number of lines per staff. If unknown, set to 0.

- *window* and *blackness*:

  Parameters for the extraction of long horizontal runs. Only pixels are
  extracted that have an average blackness greater than *blackness* within
  a window of the width *window* \* *staff_space_height*.

- *tolerance*:

  The tolerance that is used while connecting line segments that
  belong to the same staff. They may have a vertical distance of
  *staff_line_height* + *staff_space_height* with a deviation of
  *tolerance* in percent.

- *align_edges*:

  When `True`, staff line edges in a staff are aligned up to the
  left most and right most found staff line within this staff.

- *debug*:

  0 = Be quiet.
  1 = Show Progress messages.
  2 = print images with prefix 'dalitzdebug' to current directory

This method fills the *self.linelist* attribute for further
processing.
"""

        #--------------------------------------------------------
        #
        # Step 1: Get staff skeleton list
        #

        if debug > 0:
            print
            print "Getting staff skeletons..."

        # Get the skeleton list
        skeleton_list = self.image.get_staff_skeleton_list( \
            self.staffline_height, \
            window * self.staffspace_height, \
            blackness)

        too_short_skeletons = [ line for line in skeleton_list \
                                if len(line[1]) < self.staffspace_height * 2 ]

        if len(too_short_skeletons) > 0:

            if debug > 0:
                print "   %i skeletons are too short. Removing." \
                      % (len(too_short_skeletons))

            # Remove very short segments
            for s in too_short_skeletons:
                skeleton_list.remove(s)

        # Create graph
        segments = []
        for line in skeleton_list:
            n = StaffSegment()
            n.row_start = line[1][0]
            n.col_start = line[0]
            n.row_end = line[1][-1]
            n.col_end = n.col_start + len(line[1]) - 1
            n.skeleton = line
            segments.append(n)

        #--------------------------------------------------------
        #
        # Step 2: Create vertical connections
        #
        #  A connection is done between two segments that
        #  overlap horizontally if the vertical distance
        #  is staff_line_height + staff_space_height
        #  (+- tolerance) percent
        #

        if debug > 0:
            print "Creating vertical connections..."

        connections = []

        tol = float(tolerance) / 100.0
        min_dist = (self.staffline_height + self.staffspace_height) \
                   * (1.0 - tol)
        max_dist = (self.staffline_height + self.staffspace_height) \
                   * (1.0 + tol)

        for seg1 in segments:
            for seg2 in segments:

                # No self-connections, segments must overlap
                if seg1 != seg2 and seg1.overlaps(seg2):

                    # Calculate vertical distance in
                    # the middle of the overlapping parts
                    mid = (max(seg1.col_start, seg2.col_start) + \
                           min(seg1.col_end, seg2.col_end)) / 2
                    row1 = seg1.skeleton[1][mid - seg1.col_start]
                    row2 = seg2.skeleton[1][mid - seg2.col_start]
                    dist = row2 - row1

                    if dist >= min_dist and dist <= max_dist:

                        # seg2 belongs to a staff
                        # line below seg1
                        if seg1.down_links.count(seg2) == 0:
                            seg1.down_links.append(seg2)
                        if seg2.up_links.count(seg1) == 0:
                            seg2.up_links.append(seg1)

                        # Add connection for debugging
                        conn = StaffConn()
                        conn.col = mid
                        conn.row_start = min(row1, row2)
                        conn.row_end = max(row1, row2)
                        connections.append(conn)

                    elif dist <= -min_dist and dist >= -max_dist:

                        # seg2 belongs to a staff
                        # line on top of seg1
                        if (seg2.down_links.count(seg1) == 0):
                            seg2.down_links.append(seg1)
                        if (seg1.up_links.count(seg2) == 0):
                            seg1.up_links.append(seg2)

        if debug > 0:
            print "   %i connections created." % (len(connections))

        #--------------------------------------------------------
        #
        # Step 3a: Remove Segments without links
        #

        tmp = []
        for seg in segments:
            if len(seg.down_links) > 0 or len(seg.up_links) > 0:
                tmp.append(seg)

        segments = tmp
        del tmp

        #--------------------------------------------------------
        #
        # Step 3b: Label CC's and line numbers
        #
        #  Do a breadth-first search on the segments
        #  and give a unique label to every segment that
        #  belongs to a certain group of connected segments.
        #  Increase the line number with every downward
        #  connection and decrease it with every upward
        #  connection.

        if debug > 0:
            print "Grouping segments to staffs..."

        label = -1
        groups = []

        for segment in segments:

            # Segment already labeled: Process next one
            if segment.label != None: continue

            seg = segment
            label = label + 1  # Increase label
            group = StaffGroup()

            # Label this segment
            seg.label = label
            seg.line = 0
            group.extend(seg)

            # Label neighbors
            neighbors = []
            for n in seg.up_links:
                if n.label == None:
                    n.label = label
                    # Up-link: Decrease line number
                    n.line = seg.line - 1
                    group.extend(n)
                    neighbors.append(n)

                elif n.label != label:
                    raise RuntimeError, "Labelling error!"

            for n in seg.down_links:
                if n.label == None:
                    n.label = label
                    # Down-link: Increase line number
                    n.line = seg.line + 1
                    group.extend(n)
                    neighbors.append(n)

                elif n.label != label:
                    raise RuntimeError, "Labelling error!"

            # Process neighbors
            while len(neighbors) > 0:

                new_neighbors = []

                for seg in neighbors:
                    for n in seg.up_links:
                        if n.label == None:
                            n.label = label
                            # Up-Link: Decrease line number
                            n.line = seg.line - 1
                            group.extend(n)
                            new_neighbors.append(n)

                        elif n.label != label:
                            raise RuntimeError, "Labelling error!"

                    for n in seg.down_links:
                        if n.label == None:
                            n.label = label
                            # Down-Link: Increase line numver
                            n.line = seg.line + 1
                            group.extend(n)
                            new_neighbors.append(n)

                        elif n.label != label:
                            raise RuntimeError, "Labelling error!"

                neighbors = new_neighbors

            groups.append(group)

        if debug > 0:
            print "   Found %i staffs." % (len(groups))

        #--------------------------------------------------------
        #
        # Step 4: Melt overlapping segments of a staff line
        #
        #  If two segments of the same staff line overlap,
        #  they have to be melted to one, so that the later
        #  interpolation can assume non-overlapping segments.
        #
        #  In the overlapped parts, the decision of which
        #  part to use is made by laying a least square fit over
        #  the non-overlapping parts. The overlapping part, that
        #  fits better to the resulting straight line is used
        #  to substitute the overlapping range.

        if debug > 0:
            print "Melting overlapping line segments..."

        melt_skeletons = []

        melt_count = 0
        for g in groups:
            for l in range(g.min_line, g.max_line + 1):

                melted = True
                while (melted):
                    melted = False

                    for seg1 in g.segments:
                        if seg1.line != l: continue

                        for seg2 in g.segments:
                            if seg2.line != l or seg1 == seg2: continue

                            if seg1.overlaps(seg2):
                                melt_skeletons.append(seg1.melt(seg2))
                                g.segments.remove(seg2)
                                melted = True
                                melt_count = melt_count + 1

                                # Jump out of the
                                # inner 'for'
                                break

                        # Jump out of the outer 'for'
                        if melted: break

        if debug > 0 and melt_count > 0:
            print "   %d segments melted." % (melt_count)

        #--------------------------------------------------------
        #
        # Step 5a: Removal of staffs with too few lines
        #
        #  when the number of lines is not given, it is
        #  estimated as the most frequent num_lines among the wide groups
        #

        # Get maximum staff line width of all staffs
        max_group_width = 0
        for g in groups:
            width = g.col_end - g.col_start + 1
            if width > max_group_width: max_group_width = width

        # estimate num_lines
        if num_lines > 0:
            estimated_num_lines = num_lines
        else:
            num_hist = {}
            for g in groups:
                if g.col_end - g.col_start + 1 > max_group_width / 2:
                    n = g.max_line - g.min_line + 1
                    if num_hist.has_key(n):
                        num_hist[n] += 1
                    else:
                        num_hist[n] = 1
            max_count = 0
            estimated_num_lines = 0
            for (n, c) in num_hist.iteritems():
                if c > max_count:
                    estimated_num_lines = n
                    max_count = c
            print "num_lines estimated as ", estimated_num_lines

        # remove staffs with fewer lines
        if debug > 0:
            print "Removing staffs with fewer lines than", estimated_num_lines, "..."
        rem_groups = [g for g in groups \
                      if g.max_line - g.min_line + 1 < estimated_num_lines]
        for g in rem_groups:
            groups.remove(g)
        if debug > 0 and len(rem_groups) > 0:
            print "   %i removed, %i staffs left." \
                  % (len(rem_groups), len(groups))

        #--------------------------------------------------------
        #
        # Step 5b: Remove additional lines above and below staffs
        #
        #  If the number of staff lines in a staff is known,
        #  the top or bottom line (the narrower one) is removed
        #  until the correct number of lines is reached.
        #
        #  If it is not known, every line is removed, that is
        #  much narrower than the maximum line length in this
        #  staff.
        #

        if debug > 0:
            print "Removing additional staff lines..."

        lines_removed = 0

        # In every staff group
        for g in groups:

            lengths = []
            max_length = 0

            # Calculate range of every staff line
            for line in range(g.min_line, g.max_line + 1):

                length_sum = 0
                for s in [seg for seg in g.segments if seg.line == line]:
                    length_sum = length_sum + s.col_end - s.col_start + 1
                lengths.append((line, length_sum))

                if length_sum > max_length: max_length = length_sum

            # If the real number of staff lines is given...
            if num_lines > 0:

                # While there are to much lines...
                while g.max_line - g.min_line + 1 > num_lines:

                    if lengths[0][1] < lengths[-1][1]:  # Upper line shortest
                        g.remove_line(lengths[0][0])
                        lengths.pop(0)  # Remove first line info

                    else:  # Bottom line shortest
                        g.remove_line(lengths[-1][0])
                        lengths.pop()  # Remove last line info

                    lines_removed = lines_removed + 1

            else:  # Real number of lines not given

                # Simply remove lines that are too short
                for line, length in lengths:
                    if (length < max_length * 0.8):
                        g.remove_line(line)
                        lines_removed = lines_removed + 1

            # TODO: Check if groups have been seperated!

        if debug > 0 and lines_removed > 0:
            print "   Removed %d lines." % (lines_removed)

        #--------------------------------------------------------
        #
        # Step 6a: Remove groups that overlap with wider groups
        #

        if debug > 0:
            print "Removing embedded staffs..."

        # sort groups line for line from left to right
        def _cmp_y(g, h):
            if g.row_start < h.row_start: return -1
            else: return 1

        groups.sort(_cmp_y)
        ngroups = len(groups)
        breakdist = 2 * self.staffspace_height
        for i in range(ngroups):
            # find items in same line
            candidates = []
            for j in range(i + 1, ngroups):
                if groups[i].row_end - breakdist < groups[j].row_start:
                    break
                candidates.append([j, groups[j]])
            # pick the leftmost as next in order
            if len(candidates) > 0:
                minj = i
                min_col_start = groups[i].col_start
                for c in candidates:
                    if c[1].col_start < min_col_start:
                        minj = c[0]
                        min_col_start = c[1].col_start
                if minj > i:
                    g = groups[i]
                    groups[i] = groups[minj]
                    groups[minj] = g

        #print "groups sorted:"
        #for g in groups:
        #    print "rows = ", g.row_start, "-", g.row_end, "col_start =",g.col_start, "lines =", g.max_line - g.min_line + 1

        # when consecutive groups overlap, keep only the widest
        rem_groups = []
        i = 0
        j = 0
        while i < len(groups) and j < len(groups) - 1:
            j += 1
            g = groups[i]
            h = groups[j]
            if g.col_end >= h.col_start and \
               ((h.row_start < g.row_start and g.row_start < h.row_end) or \
                (h.row_start < g.row_end and g.row_end < h.row_end)):
                if (g.col_end - g.col_start) < (h.col_end - h.col_start):
                    rem_groups.append(g)
                    i = j
                else:
                    rem_groups.append(h)
            else:
                i += 1
        for g in rem_groups:
            if g in groups:
                groups.remove(g)
        if debug > 0 and len(rem_groups) > 0:
            print "   %i removed, %i staffs left." \
                  % (len(rem_groups), len(groups))

        #--------------------------------------------------------
        #
        # Step 6b: Join groups belonging to the same staff system
        #          (only executed when join_interrupted is set)
        #

        if join_interrupted:
            if debug > 0:
                print "Join interrupted staves..."
            # check whether consecutive groups follow each other
            # and how they could be linked
            # condition: distance < 2*staffspace_height
            rem_groups = []
            for i, g1 in enumerate(groups):

                if g1 in rem_groups:
                    continue

                for j in range(i + 1, len(groups)):
                    g2 = groups[j]

                    # join only if vertically overlapping
                    if max(g1.row_start, g2.row_start) > min(
                            g1.row_end, g2.row_end):
                        break

                    # join groups with the same line count only
                    if g2.max_line - g2.min_line != g1.max_line - g1.min_line:
                        break

                    if g2.col_start <= g1.col_end:
                        break

                    if g2.col_start - g1.col_end >= 2 * self.staffspace_height:
                        break

                    # now do the join thing
                    g1.join(g2)
                    rem_groups.append(g2)

            for g in rem_groups:
                groups.remove(g)

            if debug > 0:
                print "   %i group(s) joined." % len(rem_groups)

        #--------------------------------------------------------
        #
        # Step 7: Removal of narrow staffs
        #

        #if debug > 0:
        #    print "Removing invalid staffs..."
        #rem_groups = [g for g in groups \
        #              if g.col_end - g.col_start + 1 < max_group_width / 2]

        #for g in rem_groups: groups.remove(g)
        #if debug > 0 and len(rem_groups) > 0:
        #    print "   %i removed, %i staffs left." \
        #          % (len(rem_groups), len(groups))

        #--------------------------------------------------------
        #
        # Step 8: Interpolate broken staff lines
        #
        #  If there is more than one segment left for a staff
        #  line: Order and connect them.
        #

        if debug > 0:
            print "Connecting broken lines..."

        conn_skels = []
        conn_count = 0

        for g in groups:
            for line in range(g.min_line, g.max_line + 1):

                # Count segments in this line
                line_segs = []
                for s in g.segments:
                    if s.line == line: line_segs.append(s)

                # If more than one segment per line: Connect them!
                if len(line_segs) > 1:

                    conn_count = conn_count + len(line_segs)

                    # Sort segments by start column
                    line_segs.sort(lambda x, y: int(x.col_start - y.col_start))

                    s1 = line_segs.pop(0)  # Leftmost segment

                    for s in line_segs:
                        conn_skel = s1.connect(s)
                        if conn_skel: conn_skels.append(conn_skel)
                        g.segments.remove(s)

        if debug > 0 and conn_count > 0:
            print "   %i connected" % (conn_count)

        #--------------------------------------------------------
        #
        #  Visualization
        #

        if (debug > 1):

            rgb = Image(self.image, RGB)

            print
            print "Drawing group backgrounds..."
            for g in groups:
                color = RGBPixel(150 + (31 * g.label) % 106, \
                                 150 + (111 * (g.label + 1)) % 106, \
                                 150 + (201 * (g.label + 2)) % 106)

                rgb.draw_filled_rect((g.col_start, g.row_start), \
                                     (g.col_end, g.row_end), \
                                     color)

            print "Drawing original image..."
            rgb.highlight(self.image, RGBPixel(0, 0, 0))

            print "Highlighting staff line candidates..."
            staff_skeleton = self.image.skeleton_list_to_image(skeleton_list)
            rgb.highlight(staff_skeleton, RGBPixel(255, 150, 0))  # orange
            staff_skeleton_short = self.image.skeleton_list_to_image(\
                too_short_skeletons)
            rgb.highlight(staff_skeleton_short, RGBPixel(255, 0,
                                                         150))  # orange

            black_runs = self.image.extract_filled_horizontal_black_runs(\
                windowwidth = self.staffspace_height * window, \
                blackness = blackness)

            black_runs = black_runs.to_rgb()

            black_runs.highlight(staff_skeleton, RGBPixel(0, 255, 0))
            black_runs.save_PNG("dalitzdebug_blackruns.png")

            print "Highlighting group segments..."
            group_skeletons = []
            melted_skeletons = []
            conn_skeletons = []

            for g in groups:
                for seg in g.segments:
                    group_skeletons.append(seg.skeleton)

            group_image = self.image.skeleton_list_to_image(group_skeletons)
            rgb.highlight(group_image, RGBPixel(0, 255, 0))  # green

            print "Highlighting melted sections..."
            melt_image = self.image.skeleton_list_to_image(melt_skeletons)
            rgb.highlight(melt_image, RGBPixel(0, 255, 255))  # cyan

            print "Highlighting connections..."
            conn_image = self.image.skeleton_list_to_image(conn_skels)
            rgb.highlight(conn_image, RGBPixel(255, 255, 0))  # yellow

            print "Drawing segment markers..."
            for g in groups:
                for seg in g.segments:

                    color = RGBPixel(100 + ((71 * seg.line) % 156), 0, 0)

                    rgb.draw_marker((seg.col_start, seg.row_start), \
                                    self.staffline_height * 2, \
                                    3, color)

                    rgb.draw_marker((seg.col_end, seg.row_end), \
                                    self.staffline_height * 2, \
                                    3, color)

            print "Drawing links..."

            # All connections

            for c in connections:
                rgb.draw_line((c.col, c.row_start), (c.col, c.row_end), \
                              RGBPixel(0, 0, 255)) # blue

            # Connections of group segments

            for g in groups:
                for seg in g.segments:
                    for link in seg.down_links:
                        mid = (max(seg.col_start, link.col_start) + \
                               min(seg.col_end, link.col_end)) / 2
                        row1 = seg.skeleton[1][mid - seg.col_start]
                        row2 = link.skeleton[1][mid - link.col_start]
                        rgb.draw_line((mid, row1), (mid, row2), \
                                      RGBPixel(255, 0, 200)) # pink

            print "Writing file..."
            rgb.save_PNG("dalitzdebug_out.png")

        #--------------------------------------------------------
        #
        # Copy over the results into self.linelist
        #

        self.linelist = []

        for g in groups:
            newstaff = []
            #for line in range(g.min_line, g.max_line + 1):
            for s in g.segments:
                skel = StafflineSkeleton()
                skel.left_x = s.skeleton[0]
                skel.y_list = s.skeleton[1]
                newstaff.append(skel)
            # sort by y-position
            newstaff.sort(lambda s1, s2: int(s1.y_list[0] - s2.y_list[0]))
            self.linelist.append(newstaff)

        #--------------------------------------------------------
        #
        # Adjust edge points to the left/right most point with each staff
        #

        if align_edges:
            if debug > 0:
                print "Align edge points"
            for staff in self.linelist:
                # find left/right most edge point
                lefti = 0
                left = self.image.ncols
                righti = 0
                right = 0
                for i, skel in enumerate(staff):
                    if skel.left_x < left:
                        lefti = i
                        left = skel.left_x
                    if skel.left_x + len(skel.y_list) - 1 > right:
                        righti = i
                        right = skel.left_x + len(skel.y_list) - 1
                leftref = staff[lefti].y_list
                rightref = staff[righti].y_list
                # extrapolate left edge points
                for skel in staff:
                    if skel.left_x > left:
                        if skel.left_x - left < len(leftref):
                            dy = skel.y_list[0] - leftref[skel.left_x - left]
                        else:
                            dy = self.staffspace_height
                        x = skel.left_x - 1
                        while (x >= left):
                            if x - left < len(leftref):
                                skel.y_list.insert(0, leftref[x - left] + dy)
                            else:
                                skel.y_list.insert(0, skel.y_list[0])
                            x -= 1
                        skel.left_x = left
                # extrapolate right edge points
                for skel in staff:
                    if skel.left_x + len(skel.y_list) - 1 < right:
                        dy = skel.y_list[-1] - rightref[len(skel.y_list)]
                        x = skel.left_x + len(skel.y_list)
                        while (x <= right):
                            skel.y_list.append(rightref[x - left] + dy)
                            x += 1

        if debug > 0:
            print "Ready."
            print
    def __call__(self,im_staffonly, n_gap, p_gap, n_shift, random_seed=0, add_noshift=False ):
        from gamera.core import RGBPixel,Point,FloatPoint,Rect
        from gamera.toolkits.musicstaves.stafffinder import StafflineSkeleton
        seed(random_seed)
        bnp_gap=binomial(n_gap,p_gap)
        bnp_shift=binomial(n_shift,0.5)
        im_full=self.image_copy()
        im_staffless=im_full.xor_image(im_staffonly)
        im_staffonly=im_staffonly.image_copy()
        il_shifts=[im_staffless,im_staffonly,im_full]
        il_breaks=il_shifts
        if add_noshift:
            ns_full=im_full.image_copy()
            ns_staffonly=im_staffonly.image_copy()
            il_breaks=[im_staffless,im_staffonly,im_full,ns_full,ns_staffonly]
        
        #find stafflines
        [first_x, last_x, stafflines, thickness]=find_stafflines_int(im_staffonly)

        #find breaks (where are the symbols?)
        stafflinedist=stafflines[1]-stafflines[0]
        staffline_skel=[]
        for sl in range(len(stafflines)):
            if sl==0 or stafflines[sl]-stafflines[sl-1]>3*stafflinedist:
                #first staffline of a system
                first_line=stafflines[sl]
                lines_per_system=0

            lines_per_system=lines_per_system+1

            if sl==len(stafflines)-1 or stafflines[sl+1]-stafflines[sl]>3*stafflinedist:
                #last staffline of a system
                last_line=stafflines[sl]
                # a hoizontal projection of the symbols helps us to find the x positions of the symbols.
                syst=im_staffless.subimage(Point(0,max((0,first_line-3*stafflinedist))),Point(im_staffless.width,min((last_line+3*stafflinedist,im_full.lr_y-1))))
                symbolcols=syst.projection_cols()

                # collect the breaks, i.e. the mid of the white spaces between two symbols
                breaks=[]
                whiterun=False
                for col in range(len(symbolcols)):
                    if (not whiterun) and symbolcols[col]==0:
                        whiterun=True
                        runbegin=col
                    else:
                        if whiterun and symbolcols[col]>0:
                            whiterun=False
                            br=[(runbegin+col)/2,col-runbegin]
                            if br[0]>=first_x:
                                breaks.append(br)

                # replace the first break (before the first symbol) with a break at the beginning of the stafflines
                breaks[0]=[first_x,1]
                # and append a break after the last symbol
                if whiterun:
                    breaks.append([(last_x+runbegin)/2,last_x-runbegin])
                else:
                    breaks.append([last_x,1])

                # draw white lines at the breaks
                new_breaks=[]
                for br in breaks:
                    w=bnp_gap.random()
                    # draw line if it fits only
                    if w<br[1]:
                        for im in il_breaks:
                            im.draw_line(FloatPoint(br[0],first_line-stafflinedist),
                                         FloatPoint(br[0],last_line+stafflinedist),
                                         RGBPixel(0,0,0),
                                         w)
                        new_breaks.append(br)
                breaks=new_breaks
                skeleton=[]

                # do the vertical shift by making a temporary copy (orig_type), white them out (draw_filled_rect) and "or" them again in with a new y-position
                for t in range(len(breaks)-1):
                    vertical_shift=bnp_shift.random()-n_shift/2
                    typerect=Rect(Point(breaks[t][0],max((first_line-3*stafflinedist,0))),
                                  Point(breaks[t+1][0],min((last_line+3*stafflinedist,im_full.lr_y))))
                    moveto=Rect(Point(typerect.ul_x,
                                      typerect.ul_y+vertical_shift),
                                typerect.size)
                    if moveto.ul_y<0:
                        typerect.ul_y=typerect.ul_y-moveto.ul_y
                        moveto.ul_y=0
                    if moveto.lr_y>im_full.lr_y:
                        typerect.lr_y=typerect.lr_y-(moveto.lr_y-im_full.lr_y)
                        moveto.lr_y=im_full.lr_y

                    for im in il_shifts:
                        orig_type=im.subimage(typerect)
                        orig_type=orig_type.image_copy()
                        im.draw_filled_rect(typerect,RGBPixel(0,0,0))
                        im.subimage(moveto).or_image(orig_type,True)

                    # collect data for later construction of the skeletons
                    for line in range(lines_per_system):
                        if len(skeleton)<=line:
                            skeleton.append([])
                        st_line=stafflines[sl-lines_per_system+1+line]
                        y=st_line+vertical_shift
                        skeleton[line].append(Point(typerect.ul_x,y))
                        skeleton[line].append(Point(typerect.ur_x,y))

                # now construct the skeletons
                for sk in skeleton:
                    x=sk[0].x
                    y=sk[0].y
                    left_x=x
                    y_list=[]
                    i=1
                    while i<len(sk):
                        y_list.append(y)
                        if x>=sk[i].x:
                            y=sk[i].y
                            i=i+1
                        x=x+1
                    o=StafflineSkeleton()
                    o.left_x=left_x
                    o.y_list=y_list
                    staffline_skel.append(o)

        if add_noshift:
            return [im_full,im_staffonly,staffline_skel,ns_full,ns_staffonly]
        else:
            return [im_full,im_staffonly,staffline_skel]