示例#1
0
文件: generate.py 项目: zoginni/helit
def stitch_connect(glyph_layout, soft=True, half=False, pair_base=0):
    """Converts a glyph layout to a linegraph layout. This stitches together the glyphs when it has sufficient information to do so."""
    ret = []

    # First copy over the actual glyphs...
    for pair in glyph_layout:
        if pair is not None:
            hg, glyph = pair
            ret.append((hg, glyph.lg))

    # Now loop through and identify all pairs that can be stitched together, and stitch them...
    pair_code = 0
    for i in xrange(len(glyph_layout) - 1):
        # Can't stitch spaces...
        if glyph_layout[i] is not None and glyph_layout[i + 1] is not None:
            l_hg, l_glyph = glyph_layout[i]
            r_hg, r_glyph = glyph_layout[i + 1]

            matches = costs.match_links(l_glyph, r_glyph)

            # Iterate and do each pairing in turn...
            for ml, mr in matches:
                # Calculate the homographies to put the two line graphs into position...
                lc_hg = numpy.dot(
                    l_hg, numpy.dot(l_glyph.transform,
                                    la.inv(ml[0].transform)))
                rc_hg = numpy.dot(
                    r_hg, numpy.dot(r_glyph.transform,
                                    la.inv(mr[0].transform)))

                # Copy the links, applying the homographies...
                lc = LineGraph()
                lc.from_many(lc_hg, ml[0].lg)

                rc = LineGraph()
                rc.from_many(rc_hg, mr[0].lg)

                # Extract the merge points...
                blend = [(ml[3], 0.0, mr[4]), (ml[4], 1.0, mr[3])]

                # Do the blending...
                lc.blend(rc, blend, soft)

                # Record via tagging that the two parts are the same entity...
                pair = 'duplicate:%i,%i' % (pair_base, pair_code)
                lc.add_tag(0, 0.5, pair)
                rc.add_tag(0, 0.5, pair)
                pair_code += 1

                # Store the pair of line graphs in the return, with identity homographies...
                ret.append((numpy.eye(3), lc))
                if not half: ret.append((numpy.eye(3), rc))

    return ret
示例#2
0
def gen_bias(lg, hg):
    """Helper function - creates and returns a bias object for use when creating Glyphs. Basically weights each line with the amount of ink on it, so when a writter uses every other line it strongly biases towards letters being assigned to the lines they wrote on."""
    bias = defaultdict(float)

    # Transform the line graph to line space...
    ls_lg = LineGraph()
    ls_lg.from_many(lg)

    ihg = la.inv(hg)
    ls_lg.transform(ihg, True)

    # Add weight from all of the line segments...
    for ei in xrange(ls_lg.edge_count):
        edge = ls_lg.get_edge(ei)

        vf = ls_lg.get_vertex(edge[0])
        vt = ls_lg.get_vertex(edge[1])

        dx = vt[0] - vf[0]
        dy = vt[1] - vf[1]

        mass = (vf[5] + vt[5]) * numpy.sqrt(dx * dx + dy * dy)
        line = int(numpy.floor(0.5 * (vt[1] + vf[1])))

        bias[line] += mass

    # Normalise and return...
    maximum = max(bias.values())

    for key in bias.keys():
        bias[key] /= maximum

    return bias
示例#3
0
文件: generate.py 项目: zoginni/helit
def combine_seperate(lg_layout):
    """Given a line graph layout (List of (homography, line graph) pairs) this merges them all together into a single LineGraph. This version doesn't do anything clever."""
    args = []
    for hg, lg in lg_layout:
        args.append(hg)
        args.append(lg)

    ret = LineGraph()
    ret.from_many(*args)
    return ret
示例#4
0
print 'Found %i line graphs' % len(lg_fn)

# Load and process each in turn, to create a big database of feature/class...
train = []

for fn_num, fn in enumerate(lg_fn):
    print 'Processing %s: (%i of %i)' % (fn, fn_num + 1, len(lg_fn))

    # Load line graph...
    print '    Loading...'
    f = open(fn, 'r')
    data = ply2.read(f)
    f.close()

    lg = LineGraph()
    lg.from_dict(data)
    lg.segment()

    # Extract features...
    print '    Extracting features...'
    fv = lg.features(**feat_param)

    # Extract labels, to match the features...
    segs = lg.get_segs()

    def seg_to_label(seg):
        tags = lg.get_tags(seg)
        if len(tags) == 0: return 0  # Its a ligament

        for tag in tags:
示例#5
0
    def __init__(self, lg, seg, hg, extra=0.4, bias=None):
        """Given a segmented LineGraph and segment number this extracts it, transforms it into the standard coordinate system and stores the homography used to get there. (hg transforms from line space, where there is a line for each y=integer, to the space of the original pixels.) Also records its position on its assigned line and line number so it can be ordered suitably. Does not store connectivity information - that is done later. extra is used for infering the line position, and is extra falloff to have either side of a line voting for it - a smoothing term basically. bias is an optional dictionary indexed by line number that gives a weight to assign to being assigned to that line - used to utilise the fact that data collection asks the writter to use every-other line, which helps avoid misassigned dropped j's for instance."""
        if lg == None: return

        # Extract the line graph...
        self.lg = LineGraph()
        self.adjacent = self.lg.from_segment(lg, seg)
        self.seg = seg

        # Tranform it to line space...
        ihg = la.inv(hg)
        self.lg.transform(ihg, True)

        # Check if which line its on is tagged - exists as an override for annoying glyphs...
        line = None
        for tag in self.lg.get_tags():
            if tag[0] == 'line':
                # We have a tag of line - its position specifies the line the glyph is on...
                point = self.lg.get_point(tag[1], tag[2])
                line = int(numpy.floor(point[1]))
                break

        # Record which line it is on and its position along the line...
        # (Works by assuming that the line is the one below the space where most of the mass of the glyph is. Takes it range to be that within the space, so crazy tails are cut off.)
        min_x, max_x, min_y, max_y = self.lg.get_bounds()
        self.source = (0.5 * (min_x + max_x), 0.5 * (min_y + max_y))

        if line == None:
            best_mass = 0.0
            self.left_x = min_x
            self.right_x = max_x
            line = 0

            start = int(numpy.trunc(min_y))
            for pl in xrange(start, int(numpy.ceil(max_y))):
                mass = 0.0
                low_y = float(pl) - extra
                high_y = float(pl + 1) + extra

                left_x = None
                right_x = None

                for es in self.lg.within(min_x, max_x, low_y, high_y):
                    for ei in xrange(*es.indices(self.lg.edge_count)):
                        edge = self.lg.get_edge(ei)
                        vf = self.lg.get_vertex(edge[0])
                        vt = self.lg.get_vertex(edge[1])

                        if vf[1] > low_y and vf[1] < high_y and vt[
                                1] > low_y and vt[1] < high_y:
                            dx = vt[0] - vf[0]
                            dy = vt[1] - vf[1]
                            mass += (vf[5] + vt[5]) * numpy.sqrt(dx * dx +
                                                                 dy * dy)

                            if left_x == None: left_x = min(vf[0], vt[0])
                            else: left_x = min(vf[0], vt[0], left_x)

                            if right_x == None: right_x = max(vf[0], vt[0])
                            else: right_x = max(vf[0], vt[0], right_x)

                mass *= 1.0 / (1.0 + pl - start
                               )  # Bias to choosing higher, for tails.

                if bias != None:
                    mass *= bias[pl]

                if mass > best_mass:
                    best_mass = mass
                    self.left_x = left_x
                    self.right_x = right_x
                    line = pl

        # Transform it so it is positioned to be sitting on line 1 of y, store the total homography that we have applied...
        self.offset_x = -min_x
        self.offset_y = -line

        hg = numpy.eye(3, dtype=numpy.float32)
        hg[0, 2] = self.offset_x
        hg[1, 2] = self.offset_y

        self.left_x += self.offset_x
        self.right_x += self.offset_x

        self.lg.transform(hg)

        self.transform = numpy.dot(hg, ihg)

        # Set as empty its before and after glyphs - None if there is no adjacency, or a tuple if there is: (glyph, list of connecting (link glyph, shared vertex in this, shared vertex in glyph, vertex in link glyph on this side, vertex in link glyph on glyph side), empty if none.)...
        self.left = None
        self.right = None

        # Extract the character this glyph represents...
        tags = self.lg.get_tags()
        codes = [
            t[0] for t in tags if len(filter(lambda c: c != '_', t[0])) == 1
        ]
        self.key = codes[0] if len(codes) != 0 else None

        self.code = -id(self)

        # Cache stuff...
        self.mass = None
        self.center = None
        self.feat = None
        self.v_offset = None
示例#6
0
    def add(self, fn):
        """Given the filename for a LineGraph file this loads it in and splits it into Glyphs, which it dumps into the db. Does nothing if the file is already loaded. returns the number of glyphs added."""

        if fn in self.fnl: return 0
        self.fnl.append(fn)

        # Load the LineGraph from the given filename, and get the homography...
        f = open(fn, 'r')
        data = ply2.read(f)
        f.close()

        lg = LineGraph()
        lg.from_dict(data)
        lg.segment()

        hg = data['element']['homography']['v'].reshape((3, 3))

        texture = os.path.normpath(
            os.path.join(os.path.dirname(fn), data['meta']['image']))

        # Create a line bias object to be used in the next step...
        bias = gen_bias(lg, hg)

        # First pass - create each glyph object...
        glyphs = []
        for s in xrange(lg.segments):
            g = Glyph(lg, s, hg, bias=bias)
            glyphs.append(g if '_' not in
                          map(lambda t: t[0], g.lg.get_tags()) else None)

        # Second pass - fill in the connectivity information supported by adjacency...
        link_glyphs = []
        for seg, glyph in enumerate(glyphs):
            if glyph == None: continue
            if glyph.key == None: continue

            # Brute force search to find a left partner...
            if glyph.left == None:
                best = None
                for g in glyphs:
                    # Check it satisfies the conditions...
                    if g == None: continue
                    if g.key == None: continue
                    if id(g) == id(glyph): continue
                    if g.offset_y != glyph.offset_y: continue
                    if (g.right_x - g.offset_x) > (glyph.right_x -
                                                   glyph.offset_x):
                        continue

                    # Check its better than the current best...
                    if best == None or (best.right_x - best.offset_x) < (
                            g.right_x - g.offset_x):
                        best = g

                if best != None:
                    glyph.left = (best, [])

            # Brute force search to find a right partner...
            if glyph.right == None:
                best = None
                for g in glyphs:
                    # Check it satisfies the conditions...
                    if g == None: continue
                    if g.key == None: continue
                    if id(g) == id(glyph): continue
                    if g.offset_y != glyph.offset_y: continue
                    if (g.left_x - g.offset_x) < (glyph.left_x -
                                                  glyph.offset_x):
                        continue

                    # Check its better than the current best...
                    if best == None or (best.left_x - best.offset_x) > (
                            g.left_x - g.offset_x):
                        best = g

                if best != None:
                    glyph.right = (best, [])

            # Now we have the best matches find common glyphs to link them, and record them...
            for other, out in [
                    g for g in [glyph.left, glyph.right] if g != None
            ]:
                shared_seg = set([a[1] for a in glyph.adjacent]) & set(
                    [a[1] for a in other.adjacent])

                for seg in shared_seg:
                    g = glyphs[seg]
                    if g == None: continue

                    # We have a linking glyph - extract the information...
                    glyph_vert = [a[0] for a in glyph.adjacent if a[1] == seg]
                    other_vert = [a[0] for a in other.adjacent if a[1] == seg]

                    link_glyph = [
                        a[0] for a in g.adjacent if a[1] == glyph.seg
                    ]
                    link_other = [
                        a[0] for a in g.adjacent if a[1] == other.seg
                    ]

                    # Check if we have a multi-link scenario - if so choose links...
                    if len(glyph_vert) > 1 or len(other_vert) > 1:
                        gv_y = map(lambda v: glyph.lg.get_vertex(v)[1],
                                   glyph_vert)
                        ov_y = map(lambda v: other.lg.get_vertex(v)[1],
                                   other_vert)

                        if (max(gv_y) - min(ov_y)) > (max(ov_y) - min(gv_y)):
                            glyph_vert = glyph_vert[numpy.argmax(gv_y)]
                            other_vert = other_vert[numpy.argmin(ov_y)]
                        else:
                            glyph_vert = glyph_vert[numpy.argmin(gv_y)]
                            other_vert = other_vert[numpy.argmax(ov_y)]

                        lg_y = map(lambda v: g.lg.get_vertex(v)[1], link_glyph)
                        lo_y = map(lambda v: g.lg.get_vertex(v)[1], link_other)

                        if (max(lg_y) - min(lo_y)) > (max(lo_y) - min(lg_y)):
                            link_glyph = link_glyph[numpy.argmax(lg_y)]
                            link_other = link_other[numpy.argmin(lo_y)]
                        else:
                            link_glyph = link_glyph[numpy.argmin(lg_y)]
                            link_other = link_other[numpy.argmax(lo_y)]

                    else:
                        # Simple scenario...
                        glyph_vert = glyph_vert[0]
                        other_vert = other_vert[0]

                        link_glyph = link_glyph[0]
                        link_other = link_other[0]

                    # Recreate the link as a simple path - its the only way to be safe!..
                    try:
                        g = g.clone()
                        nlg = LineGraph()
                        link_glyph, link_other = nlg.from_path(
                            g.lg, link_glyph, link_other)
                        g.lg = nlg
                        link_glyphs.append(g)
                    except:
                        continue  # Line is broken in the centre - don't use it.

                    # Add the tuple to the storage...
                    out.append(
                        (g, glyph_vert, other_vert, link_glyph, link_other))

        # Third pass - enforce consistancy...
        for glyph in glyphs:
            if glyph == None: continue

            if glyph.left != None:
                if glyph.left[0].right == None or id(
                        glyph.left[0].right[0]) != id(glyph):
                    # Inconsistancy - break it...
                    glyph.left[0].right = None
                    glyph.left = None

            if glyph.right != None:
                if glyph.right[0].left == None or id(
                        glyph.right[0].left[0]) != id(glyph):
                    # Inconsistancy - break it...
                    glyph.right[0].left = None
                    glyph.right = None

        # Forth pass - add filename tags and add to db (Count and return the glyph count)...
        count = 0

        for glyph in (glyphs + link_glyphs):
            if glyph == None: continue

            glyph.lg.add_tag(0, 0.1, 'file:%s' % fn)
            glyph.lg.add_tag(0, 0.2, 'texture:%s' % texture)

            glyph.lg.add_tag(
                0, 0.3,
                'link:left:%s' % ('n' if glyph.left == None else
                                  ('g' if len(glyph.left[1]) == 0 else 'l')))
            glyph.lg.add_tag(
                0, 0.4,
                'link:right:%s' % ('n' if glyph.right == None else
                                   ('g' if len(glyph.right[1]) == 0 else 'l')))

            glyph.lg.add_tag(0, 0.5, 'source:x:%f' % glyph.source[0])
            glyph.lg.add_tag(0, 0.6, 'source:y:%f' % glyph.source[1])

            if glyph.key != None:
                count += 1
                if glyph.key in self.db: self.db[glyph.key].append(glyph)
                else: self.db[glyph.key] = [glyph]

            glyph.code = len(self.by_code)
            glyph.lg.add_tag(0, 0.5, 'code:%i' % glyph.code)
            self.by_code.append(glyph)

        return count
示例#7
0
文件: chunk_db.py 项目: zoginni/helit
    def add(self, fn):
        """Given a filename for a linegraph file this loads it, extracts all chunks and stores them in the db."""
        if fn in self.fnl: return 0
        self.fnl.append(fn)
        self.kdtree = None

        # Load the LineGraph from the given filename...
        data = ply2.read(fn)

        lg = LineGraph()
        lg.from_dict(data)

        texture = os.path.normpath(
            os.path.join(os.path.dirname(fn), data['meta']['image']))

        # Calculate the radius scaler and distance for this line graph, by calculating the median radius...
        rads = map(lambda i: lg.get_vertex(i)[5], xrange(lg.vertex_count))
        rads.sort()
        median_radius = rads[len(rads) // 2]
        radius_mult = 1.0 / median_radius

        dist = self.dist * median_radius

        # Chop it up into chains, extract chunks and store them in the database...
        ret = 0

        for raw_chain in lg.chains():
            for chain in filter(lambda c: len(c) > 1,
                                [raw_chain, raw_chain[::-1]]):
                head = 0
                tail = 0
                length = 0.0

                while True:
                    # Move tail so its long enough, or has reached the end...
                    while length < dist and tail + 1 < len(chain):
                        tail += 1
                        v1 = lg.get_vertex(chain[tail - 1])
                        v2 = lg.get_vertex(chain[tail])
                        length += numpy.sqrt((v1[0] - v2[0])**2 +
                                             (v1[1] - v2[1])**2)

                    # Create the chunk...
                    chunk = LineGraph()
                    chunk.from_vertices(lg, chain[head:tail + 1])

                    # Tag it...
                    chunk.add_tag(0, 0.1, 'file:%s' % fn)
                    chunk.add_tag(0, 0.2, 'texture:%s' % texture)

                    # Store it...
                    self.chunks.append((chunk, median_radius))
                    ret += 1

                    # If tail is at the end exit the loop...
                    if tail + 1 >= len(chain): break

                    # Move head along for the next chunk...
                    to_move = dist * self.factor
                    while to_move > 0.0 and head + 2 < len(chain):
                        head += 1
                        v1 = lg.get_vertex(chain[head - 1])
                        v2 = lg.get_vertex(chain[head])
                        offset = numpy.sqrt((v1[0] - v2[0])**2 +
                                            (v1[1] - v2[1])**2)
                        length -= offset
                        to_move -= offset

        return ret
示例#8
0
文件: chunk_db.py 项目: zoginni/helit
    def convert(self,
                lg,
                choices=1,
                adv_match=False,
                textures=TextureCache(),
                memory=0):
        """Given a line graph this chops it into chunks, matches each chunk to the database of chunks and returns a new line graph with these chunks instead of the original. Output will involve heavy overlap requiring clever blending. choices is the number of options it select from the db - it grabs this many closest to the requirements and then randomly selects from them. If adv_match is True then instead of random selection from the choices it does a more advanced match, and select the best match in terms of colour distance from already-rendered chunks. This option is reasonably expensive. memory is how many recently use chunks to remember, to avoid repetition."""
        if memory > (choices - 1):
            memory = choices - 1

        # If we have no data just return the input...
        if self.empty(): return lg

        # Check if the indexing structure is valid - if not create it...
        if self.kdtree == None:
            data = numpy.array(map(lambda p: self.feature_vect(p[0], p[1]),
                                   self.chunks),
                               dtype=numpy.float)
            self.kdtree = scipy.spatial.cKDTree(data, 4)

        # Calculate the radius scaler and distance for this line graph, by calculating the median radius...
        rads = map(lambda i: lg.get_vertex(i)[5], xrange(lg.vertex_count))
        rads.sort()
        median_radius = rads[len(rads) // 2]
        radius_mult = 1.0 / median_radius

        dist = self.dist * median_radius

        # Create the list into which we dump all the chunks that will make up the return...
        chunks = []
        temp = LineGraph()

        # List of recently used chunks, to avoid obvious patterns...
        recent = []

        # If advanced match we need a Composite of the image thus far, to compare against...
        if adv_match:
            canvas = Composite()
            min_x, max_x, min_y, max_y = lg.get_bounds()
            canvas.set_size(int(max_x + 8), int(max_y + 8))

        # Iterate the line graph, choping it into chunks and matching a chunk to each chop...
        for chain in lg.chains():
            head = 0
            tail = 0
            length = 0.0

            while True:
                # Move tail so its long enough, or has reached the end...
                while length < dist and tail + 1 < len(chain):
                    tail += 1
                    v1 = lg.get_vertex(chain[tail - 1])
                    v2 = lg.get_vertex(chain[tail])
                    length += numpy.sqrt((v1[0] - v2[0])**2 +
                                         (v1[1] - v2[1])**2)

                # Extract a feature vector for this chunk...
                temp.from_vertices(lg, chain[head:tail + 1])
                fv = self.feature_vect(temp, median_radius)

                # Select a chunk from the database...
                if choices == 1:
                    selected = self.kdtree.query(fv)[1]
                    orig_chunk = self.chunks[selected]
                else:
                    options = list(self.kdtree.query(fv, choices)[1])
                    options = filter(lambda v: v not in recent, options)
                    if not adv_match:
                        selected = random.choice(options)
                        orig_chunk = self.chunks[selected]
                    else:
                        cost = 1e64 * numpy.ones(len(options))

                        for i, option in enumerate(options):
                            fn = filter(lambda t: t[0].startswith('texture:'),
                                        self.chunks[option][0].get_tags())
                            if len(fn) != 0:
                                fn = fn[0][0][len('texture:'):]
                                tex = textures[fn]

                                chunk = LineGraph()
                                chunk.from_many(self.chunks[option][0])
                                chunk.morph_to(lg, chain[head:tail + 1])

                                part = canvas.draw_line_graph(chunk)
                                cost[i] = canvas.cost_texture_nearest(
                                    tex, part)

                        selected = options[numpy.argmin(cost)]
                        orig_chunk = self.chunks[selected]

                # Update recent list...
                recent.append(selected)
                if len(recent) > memory:
                    recent.pop(0)

                # Distort it to match the source line graph...
                chunk = LineGraph()
                chunk.from_many(orig_chunk[0])
                chunk.morph_to(lg, chain[head:tail + 1])

                # Record it for output...
                chunks.append(chunk)

                # If advanced matching is on write it out to canvas, so future choices will take it into account...
                if adv_match:
                    fn = filter(lambda t: t[0].startswith('texture:'),
                                chunk.get_tags())
                    if len(fn) != 0:
                        fn = fn[0][0][len('texture:'):]
                        tex = textures[fn]

                        part = canvas.draw_line_graph(chunk)
                        canvas.paint_texture_nearest(tex, part)

                # If tail is at the end exit the loop...
                if tail + 1 >= len(chain): break

                # Move head along for the next chunk...
                to_move = dist * self.factor
                while to_move > 0.0 and head + 2 < len(chain):
                    head += 1
                    v1 = lg.get_vertex(chain[head - 1])
                    v2 = lg.get_vertex(chain[head])
                    offset = numpy.sqrt((v1[0] - v2[0])**2 +
                                        (v1[1] - v2[1])**2)
                    length -= offset
                    to_move -= offset

        # Return the final line graph...
        ret = LineGraph()
        ret.from_many(*chunks)
        return ret
示例#9
0
文件: generate.py 项目: zoginni/helit
def render(lg,
           border=8,
           textures=TextureCache(),
           cleverness=0,
           radius_growth=3.0,
           stretch_weight=0.5,
           edge_weight=0.5,
           smooth_weight=2.0,
           alpha_weight=1.0,
           unary_mult=1.0,
           overlap_weight=0.0,
           use_linear=True):
    """Given a line_graph this will render it, returning a numpy array that represents an image (As the first element in a tuple - second element is how many graph cut problems it solved.). It will transform the entire linegraph to obtain a suitable border. The cleverness parameter indicates how it merges the many bits - 0 means last layer (stupid), 1 means averaging; 2 selecting a border using max flow; 3 using graph cuts to take into account weight as well."""

    # Setup the compositor...
    comp = Composite()
    min_x, max_x, min_y, max_y = lg.get_bounds()

    do_transform = False
    offset_x = 0.0
    offset_y = 0.0

    if min_x < border:
        do_transform = True
        offset_x = border - min_x

    if min_y < border:
        do_transform = True
        offset_y = border - min_y

    if do_transform:
        hg = numpy.eye(3, dtype=numpy.float32)
        hg[0, 2] = offset_x
        hg[1, 2] = offset_y

        lg.transform(hg)

        max_x += offset_x
        max_y += offset_y

    comp.set_size(int(max_x + border), int(max_y + border))

    # Break the lg into segments, as each can have its own image - draw & paint each in turn...
    lg.segment()
    duplicate_sets = dict()

    for s in xrange(lg.segments):

        slg = LineGraph()
        slg.from_segment(lg, s)
        part = comp.draw_line_graph(slg, radius_growth, stretch_weight)

        done = False
        fn = filter(lambda t: t[0].startswith('texture:'), slg.get_tags())
        if len(fn) != 0: fn = fn[0][0][len('texture:'):]
        else: fn = None

        for pair in filter(lambda t: t[0].startswith('duplicate:'),
                           slg.get_tags()):
            key = pair[0][len('duplicate:'):]
            if key in duplicate_sets: duplicate_sets[key].append(part)
            else: duplicate_sets[key] = [part]

        tex = textures[fn]

        if tex is not None:
            if use_linear:
                comp.paint_texture_linear(tex, part)
            else:
                comp.paint_texture_nearest(tex, part)
            done = True

        if not done:
            comp.paint_test_pattern(part)

    # Bias towards pixels that are opaque...
    comp.inc_weight_alpha(alpha_weight)

    # Arrange for duplicate pairs to have complete overlap, by adding transparent pixels, so graph cuts doesn't create a feather effect...
    if overlap_weight > 1e-6:
        for values in duplicate_sets.itervalues():
            for i, part1 in enumerate(values):
                for part2 in values[i:]:
                    comp.draw_pair(part1, part2, overlap_weight)

    # If requested use maxflow to find optimal cuts, to avoid any real blending...
    count = 0
    if cleverness == 2:
        count = comp.maxflow_select(edge_weight, smooth_weight, maxflow)
    elif cleverness == 3:
        count = comp.graphcut_select(edge_weight, smooth_weight, unary_mult,
                                     maxflow)

    if cleverness == 0:
        render = comp.render_last()
    else:
        render = comp.render_average()

    # Return the rendered image (If cleverness==0 this will actually do some averaging, otherwise it will just create an image)...
    return render, count