Instruction:
Implement a Python 3 function for
removing horizontal breaks from an image

Response:
def hbreak(image, mask=None, iterations=1):
    '''Remove horizontal breaks

    1 1 1     1 1 1
    0 1 0 ->  0 0 0 (this case only)
    1 1 1     1 1 1
    '''
    global hbreak_table
    if mask is None:
        masked_image = image
    else:
        masked_image = image.astype(bool).copy()
        masked_image[~mask] = False
    result = table_lookup(masked_image, hbreak_table, False)
    if not mask is None:
        result[~mask] = image[~mask]
    return result


Instruction:
Here you have a function in Python 3, explain what it does
def vbreak(image, mask=None, iterations=1):
    '''Remove horizontal breaks

    1 1 1     1 1 1
    0 1 0 ->  0 0 0 (this case only)
    1 1 1     1 1 1
    '''
    global vbreak_table
    if mask is None:
        masked_image = image
    else:
        masked_image = image.astype(bool).copy()
        masked_image[~mask] = False
    result = table_lookup(masked_image, vbreak_table, False)
    if not mask is None:
        result[~mask] = image[~mask]
    return result

Response: Remove horizontal breaks from an image


Instruction:
How would you explain what the following Python 3 function does
def majority(image, mask=None, iterations=1):
    '''A pixel takes the value of the majority of its neighbors

    '''
    global majority_table
    if mask is None:
        masked_image = image
    else:
        masked_image = image.astype(bool).copy()
        masked_image[~mask] = False
    result = table_lookup(masked_image, majority_table, False, iterations)
    if not mask is None:
        result[~mask] = image[~mask]
    return result

Response: A pixel takes the value of the majority of its neighbors


Instruction:
Can you generate the documentation for the following Python 3 function
def remove(image, mask=None, iterations=1):
    '''Turn 1 pixels to 0 if their 4-connected neighbors are all 0

    ? 1 ?     ? 1 ?
    1 1 1  -> 1 0 1
    ? 1 ?     ? 1 ?
    '''
    global remove_table
    if mask is None:
        masked_image = image
    else:
        masked_image = image.astype(bool).copy()
        masked_image[~mask] = False
    result = table_lookup(masked_image, remove_table, False)
    if not mask is None:
        result[~mask] = image[~mask]
    return result

Response: Remove the image from the internal cache.


Instruction:
Make a summary of the following Python 3 code
def spur(image, mask=None, iterations=1):
    '''Remove spur pixels from an image

    0 0 0    0 0 0
    0 1 0 -> 0 0 0
    0 0 1    0 0 ?
    '''
    global spur_table_1,spur_table_2
    if mask is None:
        masked_image = image
    else:
        masked_image = image.astype(bool).copy()
        masked_image[~mask] = False
    index_i, index_j, masked_image = prepare_for_index_lookup(masked_image,
                                                              False)
    if iterations is None:
        iterations = len(index_i)
    for i in range(iterations):
        for table in (spur_table_1, spur_table_2):
            index_i, index_j = index_lookup(index_i, index_j,
                                            masked_image, table, 1)
    masked_image = extract_from_image_lookup(image, index_i, index_j)
    if not mask is None:
        masked_image[~mask] = image[~mask]
    return masked_image

Response: Remove spur pixels from an image


Instruction:
Can you tell what is the following Python 3 function doing
def thicken(image, mask=None, iterations=1):
    '''Thicken the objects in an image where doing so does not connect them

    0 0 0    ? ? ?
    0 0 0 -> ? 1 ?
    0 0 1    ? ? ?

    1 0 0    ? ? ?
    0 0 0 -> ? 0 ?
    0 0 1    ? ? ?
    '''
    global thicken_table
    if mask is None:
        masked_image = image
    else:
        masked_image = image.astype(bool).copy()
        masked_image[~mask] = False
    result = table_lookup(masked_image, thicken_table, False, iterations)
    if not mask is None:
        result[~mask] = image[~mask]
    return result

Response: Thicken the objects in an image where doing so does not connect them


Instruction:
Create a Python 3 function to
thin an image to lines preserving Euler number and thinning with Two Subiteration Algorithms

Response:
def thin(image, mask=None, iterations=1):
    '''Thin an image to lines, preserving Euler number

    Implements thinning as described in algorithm # 1 from
    Guo, "Parallel Thinning with Two Subiteration Algorithms",
    Communications of the ACM, Vol 32 #3 page 359.
    '''
    global thin_table, eight_connect
    if thin_table is None:
        thin_table = np.zeros((2,512),bool)
        for i in range(512):
            if (i & 16) == 0:
                # All zeros -> 0
                continue
            pat = pattern_of(i & ~ 16)
            ipat = pat.astype(int)
            if scind.label(pat, eight_connect)[1] != 1:
                thin_table[:,i] = True
                continue
            n1 = ((ipat[0,0] or ipat[0,1]) + (ipat[0,2] or ipat[1,2])+
                  (ipat[2,2] or ipat[2,1]) + (ipat[2,0] or ipat[1,0]))
            n2 = ((ipat[0,1] or ipat[0,2]) + (ipat[1,2] or ipat[2,2])+
                  (ipat[2,1] or ipat[2,0]) + (ipat[1,0] or ipat[0,0]))
            if min(n1,n2) not in (2,3):
                thin_table[:,i] = True
                continue
            thin_table[0,i] = ((pat[0,1] or pat[0,2] or not pat[2,2]) and
                               pat[1,2])
            thin_table[1,i] = ((pat[2,1] or pat[2,0] or not pat[0,0]) and
                               pat[1,0])
    if mask is None:
        masked_image = image.copy()
    else:
        masked_image = image.copy()
        masked_image[~mask] = False
    index_i, index_j, masked_image = prepare_for_index_lookup(masked_image, False)
    if iterations is None:
        iterations = len(index_i)
    for i in range(iterations):
        hit_count = len(index_i)
        for j in range(2):
            index_i, index_j, = index_lookup(index_i, index_j,
                                             masked_image,
                                             thin_table[j], 1)
        if hit_count == len(index_i):
            break
    masked_image = extract_from_image_lookup(image, index_i, index_j)
    if not mask is None:
        masked_image[~mask] = masked_image[~mask]
    return masked_image


Instruction:
Can you implement a function in Python 3 that
colors a labels matrix so that no adjacent labels have the same color.

Response:
def color_labels(labels, distance_transform = False):
    '''Color a labels matrix so that no adjacent labels have the same color

    distance_transform - if true, distance transform the labels to find out
         which objects are closest to each other.

    Create a label coloring matrix which assigns a color (1-n) to each pixel
    in the labels matrix such that all pixels similarly labeled are similarly
    colored and so that no similiarly colored, 8-connected pixels have
    different labels.

    You can use this function to partition the labels matrix into groups
    of objects that are not touching; you can then operate on masks
    and be assured that the pixels from one object won't interfere with
    pixels in another.

    returns the color matrix
    '''
    if distance_transform:
        i,j = scind.distance_transform_edt(labels == 0, return_distances=False,
                                           return_indices = True)
        dt_labels = labels[i,j]
    else:
        dt_labels = labels
    # Get the neighbors for each object
    v_count, v_index, v_neighbor = find_neighbors(dt_labels)
    # Quickly get rid of labels with no neighbors. Greedily assign
    # all of these a color of 1
    v_color = np.zeros(len(v_count)+1,int) # the color per object - zero is uncolored
    zero_count = (v_count==0)
    if np.all(zero_count):
        # can assign all objects the same color
        return (labels!=0).astype(int)
    v_color[1:][zero_count] = 1
    v_count = v_count[~zero_count]
    v_index = v_index[~zero_count]
    v_label = np.argwhere(~zero_count).transpose()[0]+1
    # If you process the most connected labels first and use a greedy
    # algorithm to preferentially assign a label to an existing color,
    # you'll get a coloring that uses 1+max(connections) at most.
    #
    # Welsh, "An upper bound for the chromatic number of a graph and
    # its application to timetabling problems", The Computer Journal, 10(1)
    # p 85 (1967)
    #
    sort_order = np.lexsort([-v_count])
    v_count = v_count[sort_order]
    v_index = v_index[sort_order]
    v_label = v_label[sort_order]
    for i in range(len(v_count)):
        neighbors = v_neighbor[v_index[i]:v_index[i]+v_count[i]]
        colors = np.unique(v_color[neighbors])
        if colors[0] == 0:
            if len(colors) == 1:
                # only one color and it's zero. All neighbors are unlabeled
                v_color[v_label[i]] = 1
                continue
            else:
                colors = colors[1:]
        # The colors of neighbors will be ordered, so there are two cases:
        # * all colors up to X appear - colors == np.arange(1,len(colors)+1)
        # * some color is missing - the color after the first missing will
        #   be mislabeled: colors[i] != np.arange(1, len(colors)+1)
        crange = np.arange(1,len(colors)+1)
        misses = crange[colors != crange]
        if len(misses):
            color = misses[0]
        else:
            color = len(colors)+1
        v_color[v_label[i]] = color
    return v_color[labels]


Instruction:
Here you have a function in Python 3, explain what it does
def skeletonize(image, mask=None, ordering = None):
    '''Skeletonize the image

    Take the distance transform.
    Order the 1 points by the distance transform.
    Remove a point if it has more than 1 neighbor and if removing it
    does not change the Euler number.

    image - the binary image to be skeletonized

    mask - only skeletonize pixels within the mask

    ordering - a matrix of the same dimensions as the image. The matrix
               provides the ordering of the erosion with the lowest values
               being eroded first. The default is to use the distance transform.
    '''
    global eight_connect
    if mask is None:
        masked_image = image
    else:
        masked_image = image.astype(bool).copy()
        masked_image[~mask] = False
    #
    # Lookup table - start with only positive pixels.
    # Keep if # pixels in neighborhood is 2 or less
    # Keep if removing the pixel results in a different connectivity
    #
    table = (make_table(True,np.array([[0,0,0],[0,1,0],[0,0,0]],bool),
                        np.array([[0,0,0],[0,1,0],[0,0,0]],bool)) &
             (np.array([scind.label(pattern_of(index), eight_connect)[1] !=
                        scind.label(pattern_of(index & ~ 2**4),
                                    eight_connect)[1]
                        for index in range(512) ]) |
              np.array([np.sum(pattern_of(index))<3 for index in range(512)])))

    if ordering is None:
        distance = scind.distance_transform_edt(masked_image)
    else:
        distance = ordering
    #
    # The processing order along the edge is critical to the shape of the
    # resulting skeleton: if you process a corner first, that corner will
    # be eroded and the skeleton will miss the arm from that corner. Pixels
    # with fewer neighbors are more "cornery" and should be processed last.
    #
    cornerness_table = np.array([9-np.sum(pattern_of(index))
                                 for index in range(512)])
    corner_score = table_lookup(masked_image, cornerness_table, False,1)
    i,j = np.mgrid[0:image.shape[0],0:image.shape[1]]
    result=masked_image.copy()
    distance = distance[result]
    i = np.ascontiguousarray(i[result],np.int32)
    j = np.ascontiguousarray(j[result],np.int32)
    result=np.ascontiguousarray(result,np.uint8)
    #
    # We use a random # for tiebreaking. Assign each pixel in the image a
    # predictable, random # so that masking doesn't affect arbitrary choices
    # of skeletons
    #
    np.random.seed(0)
    tiebreaker=np.random.permutation(np.arange(np.product(masked_image.shape)))
    tiebreaker.shape=masked_image.shape
    order = np.lexsort((tiebreaker[masked_image],
                        corner_score[masked_image],
                        distance))
    order = np.ascontiguousarray(order, np.int32)
    table = np.ascontiguousarray(table, np.uint8)
    skeletonize_loop(result, i, j, order, table)

    result = result.astype(bool)
    if not mask is None:
        result[~mask] = image[~mask]
    return result

Response: Skeletonize the image into a single skeleton.


Instruction:
Can you write a function in Python 3 where it
labels a skeleton so that each edge has a unique label.

Response:
def label_skeleton(skeleton):
    '''Label a skeleton so that each edge has a unique label

    This operation produces a labels matrix where each edge between
    two branchpoints has a different label. If the skeleton has been
    properly eroded, there are three kinds of points:
    1) point adjacent to 0 or 1 other points = end of edge
    2) point adjacent to two other points = in middle of edge
    3) point adjacent to more than two other points = at end of edge
            connecting to another edge
    4) a branchpoint

    We do all connected components here where components are 8-connected
    but a point in category 3 can't connect to another point in category 3.

    Returns the labels matrix and the count as a tuple
    '''
    bpts = branchpoints(skeleton)
    #
    # Count the # of neighbors per point
    #
    neighbors = scind.convolve(skeleton.astype(int), np.ones((3,3),int),
                               mode='constant').astype(int)
    neighbors[~skeleton] = 0
    neighbors[skeleton] -= 1
    #
    # Find the i/j coordinates of the relevant points
    #
    i,j = np.mgrid[0:skeleton.shape[0], 0:skeleton.shape[1]]
    skeleton_minus_bpts = skeleton & ~ bpts
    si = i[skeleton_minus_bpts]
    sj = j[skeleton_minus_bpts]
    bi = i[bpts]
    bj = j[bpts]
    i = np.hstack((bi, si))
    j = np.hstack((bj, sj))
    b_vnum = np.arange(len(bi))
    s_vnum = np.arange(len(si)) + len(bi)
    all_vnum = np.hstack((b_vnum, s_vnum))
    vertex_numbers=np.zeros(skeleton.shape, int)
    vertex_numbers[i,j] = all_vnum
    #
    # src and dest are the vertices linked by edges. Their values are the
    # vertex numbers. First, link every vertex to itself
    #
    src = all_vnum
    dest = all_vnum
    #
    # Now, for the non-branchpoints, link to all 8-connected neighbors
    # while obeying the rules
    #
    for ioff, joff in ((-1,-1), (-1,0), (-1,1),
                       ( 0,-1),         ( 0,1),
                       ( 1,-1), ( 1,0), ( 1,1)):
        consider = np.ones(len(si), bool)
        if ioff == -1:
            consider = si > 0
        elif ioff == 1:
            consider = si < skeleton.shape[0] - 1
        if joff == -1:
            consider = consider & (sj > 0)
        elif joff == 1:
            consider = consider & (sj < skeleton.shape[1] - 1)
        #
        # Forge a link if the offset point is in the skeleton
        #
        ci = si[consider]
        cj = sj[consider]
        link = (skeleton_minus_bpts[ci+ioff, cj+joff] &
                ((neighbors[ci,cj] < 3) | (neighbors[ci+ioff, cj+joff] < 3)))
        ci = ci[link]
        cj = cj[link]
        src = np.hstack((src, vertex_numbers[ci, cj]))
        dest = np.hstack((dest, vertex_numbers[ci+ioff, cj+joff]))

    labeling = all_connected_components(src, dest)
    vertex_numbers[i,j] = labeling + 1
    return (vertex_numbers,
            0 if len(labeling) == 0 else int(np.max(labeling)) + 1)


Instruction:
How would you implement a function in Python 3 that
computes the length of all skeleton branches for labeled skeletons.

Response:
def skeleton_length(labels, indices=None):
    '''Compute the length of all skeleton branches for labeled skeletons

    labels - a labels matrix
    indices - the indexes of the labels to be measured. Default is all

    returns an array of one skeleton length per label.
    '''
    global __skel_length_table
    if __skel_length_table is None:
        tbl = np.zeros(512, np.float32)
        for ii in range(-1, 2):
            for jj in range(-1, 2):
                if ii == 0 and jj == 0:
                    continue
                #
                # Set the bit to search for and the center bit
                #
                idx = 2 ** (ii + 1 + (jj + 1) * 3) | 16
                mask = (np.arange(512) & idx) == idx
                #
                # If we are four-connected to another pixel that is
                # connected to this one, they are 8-connected and that
                # is the distance.
                #
                #  bad     good
                #  x 1 0   0 0 0
                #  x 1 1   0 1 0
                #  x x x   0 x 1
                if ii != 0 and jj != 0:
                    for adjacent_i, adjacent_j in (
                        (ii-1, jj), (ii, jj-1), (ii+1, jj), (ii, jj+1)):
                        if any([_ < -1 or _ > 1
                                for _ in (adjacent_i, adjacent_j)]):
                            continue
                        aidx = 2 ** (adjacent_i+1 + (adjacent_j + 1) * 3)
                        mask = mask & ((np.arange(512) & aidx) != aidx)
                tbl[mask] += np.sqrt(ii*ii + jj*jj) / 2
        __skel_length_table = tbl
    if indices is None:
        indices = np.arange(1, np.max(labels)+1)
    else:
        indices = np.asanyarray(indices)
    if len(indices) == 0:
        return np.zeros(0)
    score = __skel_length_table[table_idx_from_labels(labels)]

    result = np.bincount(labels.ravel(),
                         weights = score.ravel(),
                         minlength=np.max(indices)+1)
    return result[indices]


Instruction:
Can you generate the documentation for the following Python 3 function
def distance_to_edge(labels):
    '''Compute the distance of a pixel to the edge of its object

    labels - a labels matrix

    returns a matrix of distances
    '''
    colors = color_labels(labels)
    max_color = np.max(colors)
    result = np.zeros(labels.shape)
    if max_color == 0:
        return result

    for i in range(1, max_color+1):
        mask = (colors==i)
        result[mask] = scind.distance_transform_edt(mask)[mask]
    return result

Response:
Compute the distance of a pixel to the edge of its object

    returns a matrix of distances


Instruction:
Can you generate the documentation for the following Python 3 function
def regional_maximum(image, mask = None, structure=None, ties_are_ok=False):
    '''Return a binary mask containing only points that are regional maxima

    image     - image to be transformed
    mask      - mask of relevant pixels
    structure - binary structure giving the neighborhood and connectivity
                in which to search for maxima. Default is 8-connected.
    ties_are_ok - if this is true, then adjacent points of the same magnitude
                  are rated as maxima.

    Find locations for which all neighbors as defined by the structure have
    lower values. The algorithm selects only one of a set of adjacent locations
    with identical values, first using a distance transform to find the
    innermost location, then, among equals, selected randomly.

    A location cannot be a local maximum if it is touching the edge or a
    masked pixel.
    '''
    global eight_connect
    if not ties_are_ok:
        #
        # Get an an initial pass with the ties.
        #
        result = regional_maximum(image, mask, structure, True)
        if not np.any(result):
            return result
        distance = scind.distance_transform_edt(result)
        #
        # Rank-order the distances and then add a randomizing factor
        # to break ties for pixels equidistant from the background.
        # Pick the best value within a contiguous region
        #
        labels, label_count = scind.label(result, eight_connect)
        np.random.seed(0)
        ro_distance = rank_order(distance)[0].astype(float)
        count = np.product(ro_distance.shape)
        ro_distance.flat += (np.random.permutation(count).astype(float) /
                             float(count))
        positions = scind.maximum_position(ro_distance, labels,
                                           np.arange(label_count)+1)
        positions = np.array(positions, np.uint32)
        result = np.zeros(image.shape, bool)
        if positions.ndim == 1:
            result[positions[0],positions[1]] = True
        else:
            result[positions[:,0],positions[:,1]] = True
        return result
    result = np.ones(image.shape,bool)
    if structure is None:
        structure = scind.generate_binary_structure(image.ndim, image.ndim)
    #
    # The edges of the image are losers because they are touching undefined
    # points. Construct a big mask that represents the edges.
    #
    big_mask = np.zeros(np.array(image.shape) + np.array(structure.shape), bool)
    structure_half_shape = np.array(structure.shape)//2
    big_mask[structure_half_shape[0]:structure_half_shape[0]+image.shape[0],
             structure_half_shape[1]:structure_half_shape[1]+image.shape[1]]=\
        mask if mask is not None else True
    for i in range(structure.shape[0]):
        off_i = i-structure_half_shape[0]
        for j in range(structure.shape[1]):
            if i == structure_half_shape[0] and j == structure_half_shape[1]:
                continue
            off_j = j-structure_half_shape[1]
            if structure[i,j]:
                result = np.logical_and(result, big_mask[i:i+image.shape[0],
                                                         j:j+image.shape[1]])
                #
                # Get the boundaries of the source image and the offset
                # image so we can make similarly shaped, but offset slices
                #
                src_i_min = max(0,-off_i)
                src_i_max = min(image.shape[0], image.shape[0]-off_i)
                off_i_min = max(0,off_i)
                off_i_max = min(image.shape[0], image.shape[0]+off_i)
                src_j_min = max(0,-off_j)
                src_j_max = min(image.shape[1], image.shape[1]-off_j)
                off_j_min = max(0,off_j)
                off_j_max = min(image.shape[1], image.shape[1]+off_j)
                min_mask = (image[src_i_min:src_i_max,
                                  src_j_min:src_j_max] <
                            image[off_i_min:off_i_max,
                                  off_j_min:off_j_max])
                result[src_i_min:src_i_max,
                       src_j_min:src_j_max][min_mask] = False
    return result

Response: Return a binary mask containing only points that are regional maxima.


Instruction:
Write a Python 3 script for
associating each label in i with a component # This function finds all connected components given an array of associations between labels i and j using a depth-first search. i & j give the edges of the graph. The first step of the algorithm makes bidirectional edges, (i->j and j<-i), so it's best to only send the edges in one direction (although the algorithm can withstand duplicates). returns a label for each vertex up to the maximum named vertex in i.

Response:
def all_connected_components(i,j):
    '''Associate each label in i with a component #

    This function finds all connected components given an array of
    associations between labels i and j using a depth-first search.

    i & j give the edges of the graph. The first step of the algorithm makes
    bidirectional edges, (i->j and j<-i), so it's best to only send the
    edges in one direction (although the algorithm can withstand duplicates).

    returns a label for each vertex up to the maximum named vertex in i.
    '''
    if len(i) == 0:
        return i
    i1 = np.hstack((i,j))
    j1 = np.hstack((j,i))
    order = np.lexsort((j1,i1))
    i=np.ascontiguousarray(i1[order],np.uint32)
    j=np.ascontiguousarray(j1[order],np.uint32)
    #
    # Get indexes and counts of edges per vertex
    #
    counts = np.ascontiguousarray(np.bincount(i.astype(int)),np.uint32)
    indexes = np.ascontiguousarray(np.cumsum(counts)-counts,np.uint32)
    #
    # This stores the lowest index # during the algorithm - the first
    # vertex to be labeled in a connected component.
    #
    labels = np.zeros(len(counts), np.uint32)
    _all_connected_components(i,j,indexes,counts,labels)
    return labels


Instruction:
Implement a function in Python 3 to
return all permutations of a set of groups This routine takes two vectors: i - the label of each group j - the members of the group. For instance, take a set of two groups with several members each: i | j ------ 1 | 1 1 | 2 1 | 3 2 | 1 2 | 4 2 | 5 2 | 6 The output will be i | j1 | j2 ----------- 1 | 1 | 2 1 | 1 | 3 1 | 2 | 3 2 | 1 | 4 2 | 1 | 5 2 | 1 | 6 2 | 4 | 5 2 | 4 | 6 2 | 5 | 6 etc

Response:
def pairwise_permutations(i, j):
    '''Return all permutations of a set of groups

    This routine takes two vectors:
    i - the label of each group
    j - the members of the group.

    For instance, take a set of two groups with several members each:

    i | j
    ------
    1 | 1
    1 | 2
    1 | 3
    2 | 1
    2 | 4
    2 | 5
    2 | 6

    The output will be
    i | j1 | j2
    -----------
    1 | 1  | 2
    1 | 1  | 3
    1 | 2  | 3
    2 | 1  | 4
    2 | 1  | 5
    2 | 1  | 6
    2 | 4  | 5
    2 | 4  | 6
    2 | 5  | 6
    etc
    '''
    if len(i) == 0:
        return (np.array([], int), np.array([], j.dtype), np.array([], j.dtype))
    #
    # Sort by i then j
    #
    index = np.lexsort((j,i))
    i=i[index]
    j=j[index]
    #
    # map the values of i to a range r
    #
    r_to_i = np.sort(np.unique(i))
    i_to_r_off = np.min(i)
    i_to_r = np.zeros(np.max(i)+1-i_to_r_off, int)
    i_to_r[r_to_i - i_to_r_off] = np.arange(len(r_to_i))
    #
    # Advance the value of r by one each time i changes
    #
    r = np.cumsum(np.hstack(([False], i[:-1] != i[1:])))
    #
    # find the counts per item
    #
    src_count = np.bincount(r)
    #
    # The addresses of the starts of each item
    #
    src_idx = np.hstack(([0], np.cumsum(src_count[:-1])))
    #
    # The sizes of each destination item: n * (n - 1) / 2
    # This is the number of permutations of n items against themselves.
    #
    dest_count = src_count * (src_count - 1) // 2
    #
    # The indexes of the starts of each destination item (+ total sum at end)
    #
    dest_idx = np.hstack(([0], np.cumsum(dest_count)))
    dest_size = dest_idx[-1]
    #
    # Allocate the destination arrays
    #
    d_r = np.zeros(dest_size, i.dtype)
    d_j1, d_j2 = np.zeros((2, dest_size), j.dtype)
    #
    # Mark the first item in the destination and then do a cumulative
    # sum trick ( (1 + 0 + 0 + 0 + 1 + 0 + 0) - 1 =
    #              0 , 0 , 0 , 0,  1,  1 , 1)
    # to label each member of d_i
    #
    not_empty_indices = np.arange(0, len(dest_idx)-1)
    not_empty_indices = not_empty_indices[dest_idx[:-1] != dest_idx[1:]]
    increments = not_empty_indices - np.hstack([[0], not_empty_indices[:-1]])
    d_r[dest_idx[not_empty_indices]] = increments
    d_r = np.cumsum(d_r)
    d_i = r_to_i[d_r]
    #
    # Index each member of the destination array relative to its start. The
    # above array would look like this: [0, 1, 2, 3, 0, 1, 2]
    #
    d_r_idx = np.arange(len(d_r)) - dest_idx[d_r]
    #
    # We can use a 2x2 matrix to look up which j1 and j2 for each of
    # the d_r_idx. The first slot in the matrix is the number of values
    # to permute (from src_count[d_r]) and the second slot is d_r_idx
    # So here, we make a sparse array for the unique values of src_count
    #
    unique_src_count = np.unique(src_count)
    unique_dest_len = (unique_src_count * (unique_src_count - 1) // 2).astype(int)
    #
    # src_count repeated once per permutation
    #
    i_sparse = np.hstack([np.ones(dlen, int) * c
                          for c, dlen in zip(unique_src_count,
                                             unique_dest_len)])
    #
    # The indexes from zero to the # of permutations
    #
    j_sparse = np.hstack([np.arange(dlen) for dlen in unique_dest_len])
    #
    # Repeat 0 n-1 times, 1 n-2 times, etc to get the first indexes in
    # the permutation.
    #
    v_j1_sparse = np.hstack(
        [ np.hstack(
            [np.ones(n-x-1, int) * x for x in range(n)])
          for n in unique_src_count])
    #
    # Spit out a range from 1 to n-1, 2 to n-1, etc
    #
    v_j2_sparse = np.hstack(
        [ np.hstack(
            [np.arange(x+1, n) for x in range(n)])
          for n in unique_src_count])

    if len(i_sparse) > 0:
        j1_sparse = scipy.sparse.coo_matrix((v_j1_sparse,
                                             (i_sparse, j_sparse))).tocsc()
        j2_sparse = scipy.sparse.coo_matrix((v_j2_sparse,
                                             (i_sparse, j_sparse))).tocsc()
    else:
        j1_sparse = j2_sparse = np.array([[]], j.dtype)
    #
    # And now we can spit out the j1 and j2 dest. This is whatever element
    # from the group in j indexed by either j1 or j2 sparse. We find the
    # group's start by using d_r to look up the group's start.
    #
    d_j1_idx = np.array(j1_sparse[src_count[d_r], d_r_idx]).flatten()
    d_j1 = j[src_idx[d_r] + d_j1_idx]
    d_j2_idx = np.array(j2_sparse[src_count[d_r], d_r_idx]).flatten()
    d_j2 = j[src_idx[d_r] + d_j2_idx]

    return (d_i, d_j1, d_j2)


Instruction:
Given the following Python 3 function, write the documentation
def is_local_maximum(image, labels, footprint):
    '''Return a boolean array of points that are local maxima

    image - intensity image
    labels - find maxima only within labels. Zero is reserved for background.
    footprint - binary mask indicating the neighborhood to be examined
                must be a matrix with odd dimensions, center is taken to
                be the point in question.
    '''
    assert((np.all(footprint.shape) & 1) == 1)
    footprint = (footprint != 0)
    footprint_extent = (np.array(footprint.shape)-1) // 2
    if np.all(footprint_extent == 0):
        return labels > 0
    result = (labels > 0).copy()
    #
    # Create a labels matrix with zeros at the borders that might be
    # hit by the footprint.
    #
    big_labels = np.zeros(np.array(labels.shape) + footprint_extent*2,
                          labels.dtype)
    big_labels[[slice(fe,-fe) for fe in footprint_extent]] = labels
    #
    # Find the relative indexes of each footprint element
    #
    image_strides = np.array(image.strides) // image.dtype.itemsize
    big_strides = np.array(big_labels.strides) // big_labels.dtype.itemsize
    result_strides = np.array(result.strides) // result.dtype.itemsize
    footprint_offsets = np.mgrid[[slice(-fe,fe+1) for fe in footprint_extent]]
    footprint_offsets = footprint_offsets[:, footprint]
    #
    # Order by distance, low to high and get rid of center pt.
    #
    d = np.sum(footprint_offsets **2, 0)
    footprint_offsets, d = footprint_offsets[:, d > 0], d[d > 0]
    footprint_offsets = footprint_offsets[:, np.lexsort([d])]

    fp_image_offsets = np.sum(image_strides[:, np.newaxis] *
                              footprint_offsets, 0)
    fp_big_offsets = np.sum(big_strides[:, np.newaxis] *
                            footprint_offsets, 0)
    #
    # Get the index of each labeled pixel in the image and big_labels arrays
    #
    indexes = np.mgrid[[slice(0,x) for x in labels.shape]][:, labels > 0]
    image_indexes = np.sum(image_strides[:, np.newaxis] * indexes, 0)
    big_indexes = np.sum(big_strides[:, np.newaxis] *
                         (indexes + footprint_extent[:, np.newaxis]), 0)
    result_indexes = np.sum(result_strides[:, np.newaxis] * indexes, 0)
    #
    # Now operate on the raveled images
    #
    big_labels_raveled = big_labels.ravel()
    image_raveled = image.ravel()
    result_raveled = result.ravel()
    #
    # A hit is a hit if the label at the offset matches the label at the pixel
    # and if the intensity at the pixel is greater or equal to the intensity
    # at the offset.
    #
    for fp_image_offset, fp_big_offset in zip(fp_image_offsets, fp_big_offsets):
        same_label = (big_labels_raveled[big_indexes + fp_big_offset] ==
                      big_labels_raveled[big_indexes])
        less_than = (image_raveled[image_indexes[same_label]] <
                     image_raveled[image_indexes[same_label]+ fp_image_offset])
        mask = ~same_label
        mask[same_label] = ~less_than
        result_raveled[result_indexes[~mask]] = False
        result_indexes = result_indexes[mask]
        big_indexes = big_indexes[mask]
        image_indexes = image_indexes[mask]

    return result

Response: Return a boolean array of points that are local maximum within image.


Instruction:
Here you have a function in Python 3, explain what it does
def angular_distribution(labels, resolution=100, weights=None):
    '''For each object in labels, compute the angular distribution
    around the centers of mass.  Returns an i x j matrix, where i is
    the number of objects in the label matrix, and j is the resolution
    of the distribution (default 100), mapped from -pi to pi.

    Optionally, the distributions can be weighted by pixel.

    The algorithm approximates the angular width of pixels relative to
    the object centers, in an attempt to be accurate for small
    objects.

    The ChordRatio of an object can be approximated by
    >>> angdist = angular_distribution(labels, resolution)
    >>> angdist2 = angdist[:, :resolution//2] + angdist[:, resolution//2] # map to widths, rather than radii
    >>> chord_ratio = np.sqrt(angdist2.max(axis=1) / angdist2.min(axis=1)) # sqrt because these are sectors, not triangles
    '''
    if weights is None:
        weights = np.ones(labels.shape)
    maxlabel = labels.max()
    ci, cj = centers_of_labels(labels)
    j, i = np.meshgrid(np.arange(labels.shape[0]), np.arange(labels.shape[1]))
    # compute deltas from pixels to object centroids, and mask to labels
    di = i[labels > 0] - ci[labels[labels > 0] - 1]
    dj = j[labels > 0] - cj[labels[labels > 0] - 1]
    weights = weights[labels > 0]
    labels = labels[labels > 0]
    # find angles, and angular width of pixels
    angle = np.arctan2(di, dj)
    # Use pixels of width 2 to get some smoothing
    width = np.arctan(1.0 / np.sqrt(di**2 + dj**2 + np.finfo(float).eps))
    # create an onset/offset array of size 3 * resolution
    lo = np.clip((angle - width) * resolution / (2 * np.pi), -resolution, 2 * resolution).astype(int) + resolution
    hi = np.clip((angle + width) * resolution / (2 * np.pi), -resolution, 2 * resolution).astype(int) + resolution
    # make sure every pixel counts at least once
    hi[lo == hi] += 1
    # normalize weights by their angular width (adding a bit to avoid 0 / 0)
    weights /= (hi - lo)
    onset = scipy.sparse.coo_matrix((weights, (labels - 1, lo)), (maxlabel, 3 * resolution)).toarray()
    offset = scipy.sparse.coo_matrix((weights, (labels - 1, hi)), (maxlabel, 3 * resolution)).toarray()
    # sum onset/offset to get actual distribution
    onoff = onset - offset
    dist = np.cumsum(onoff, axis=1)
    dist = dist[:, :resolution] + dist[:, resolution:2*resolution] + dist[:, 2*resolution:]
    return dist

Response: Compute the angular distribution of objects in a set of labels.


Instruction:
Can you generate the documentation for the following Python 3 function
def feret_diameter(chulls, counts, indexes):
    '''Return the minimum and maximum Feret diameter for each object

    This function takes the convex hull data, as generated by convex_hull
    and returns the minimum and maximum Feret diameter for each convex hull.

    chulls    - an n x 3 matrix giving the label #, the i coordinate and the
                j coordinate for each vertex in the convex hull
    counts    - the number of points in each convex hull

    returns two arrays, the minimum and maximum diameter per object.
    '''
    #
    # Method taken from Alsuwaiyel, "Algorithms, Design Techniques and
    # Analysis", World Scientific, 2003
    #
    # Given a vertex, p1 on the convex hull composed of points p1 to pm,
    # there is a line, pm <-> p1. Going from p(m-1) to p2, find the
    # distance from the point to the line until the next point in the
    # series has a lesser distance. This point forms an antipodal pair
    # with p1. Now take the line from p1 to p2 and continue to add points
    # as long as the point has a greater distance from the line than its
    # successor. The final point forms an antipodal point with both p1
    # and its succesor vertex.
    #
    # Note: as far as I (leek) can figure out, there is a bug here for
    # the case of a square:
    #
    #  0     1
    #
    #
    #  3     2
    #
    #  0 - 1 is generated first, then 0 - 2 and 0 - 3
    #  Then 1 - 3 is generated, missing 1 - 2. The author is only interested
    #  in max Feret diameter as are we.
    #
    # The minimum Feret diameter always has a parallel line of support
    # that runs through two antipodal pairs and the second that either
    # runs through an antipodal pair or a vertex.
    #
    if len(counts) == 0:
        return np.zeros(0)
    save_counts = counts
    counts = np.atleast_1d(counts)
    indexes = np.atleast_1d(indexes)
    chull_idx = np.hstack(([0], np.cumsum(counts[:-1])))
    save_chull_idx = chull_idx
    chull_pts = chulls[:,1:].astype(float)
    #
    # A list of antipode arrays
    #
    antipodes = []
    #
    # Get rid of degenerate cases
    #
    # A single point is its own antipode
    #
    antipodes.append(np.column_stack([chull_idx[counts==1]] * 2))
    #
    # A line has it's two points as antipodes of each other.
    #
    antipodes.append(np.column_stack([chull_idx[counts==2],
                                      chull_idx[counts==2] + 1]))
    chull_idx = chull_idx[counts > 2]
    counts = counts[counts > 2]
    if len(counts) > 0:
        #
        # Calculate distances from every vertex other than pm and p1
        # to the line between pm and p1
        #
        pm_idx = (chull_idx + counts - 1)
        pm = chull_pts[pm_idx,:]
        p1 = chull_pts[chull_idx,:]
        # There are counts - 2 vertices to be examined
        vvv = Indexes([counts-2])
        v_idx = chull_idx[vvv.rev_idx] + vvv.idx[0] + 1
        distances = distance2_to_line(chull_pts[v_idx],
                                      pm[vvv.rev_idx],
                                      p1[vvv.rev_idx])
        #
        # Find the largest distance, settling ties by picking the
        # one with the largest within-object index.
        #
        order = np.lexsort((vvv.idx[0], -distances, vvv.rev_idx))
        antipode_idx = order[vvv.fwd_idx] + 1 - vvv.fwd_idx
        vertex_idx = np.zeros(len(antipode_idx), int)
        while len(chull_idx) > 0:
            #
            # Add the current antipode / vertex pair
            #
            antipodes.append(np.column_stack((chull_idx + vertex_idx,
                                              chull_idx + antipode_idx)))
            if len(chull_idx) == 0:
                break
            #
            # Get the distance from the line to the current antipode
            #
            dc = distance2_to_line(chull_pts[chull_idx + antipode_idx, :],
                                   chull_pts[chull_idx + vertex_idx, :],
                                   chull_pts[chull_idx +vertex_idx + 1, :])
            #
            # Get the distance from the line to the next antipode
            #
            next_antipode_idx = antipode_idx + 1
            next_antipode_idx[next_antipode_idx == counts] = 0
            dn = distance2_to_line(chull_pts[chull_idx + next_antipode_idx, :],
                                   chull_pts[chull_idx + vertex_idx, :],
                                   chull_pts[chull_idx + vertex_idx + 1, :])
            #
            # If the distance to the next candidate is larger, advance
            #
            advance_antipode = dc <= dn
            antipode_idx[advance_antipode] += 1
            #
            # Otherwise, move to the next vertex.
            #
            vertex_idx[~ advance_antipode] += 1
            #
            # Get rid of completed convex hulls
            #
            to_keep = (antipode_idx < counts) & (vertex_idx != antipode_idx)
            antipode_idx = antipode_idx[to_keep]
            vertex_idx = vertex_idx[to_keep]
            chull_idx = chull_idx[to_keep]
            counts = counts[to_keep]
    antipodes = np.vstack(antipodes)
    l = chulls[antipodes[:,0], 0]

    pt1 = chulls[antipodes[:,0],1:]
    pt2 = chulls[antipodes[:,1],1:]
    distances = np.sum((pt1 - pt2) **2, 1)

    max_distance = np.sqrt(fixup_scipy_ndimage_result(scind.maximum(distances, l, indexes)))
    #
    # For the minimum distance, we have to take the distance from each
    # antipode to any line between alternate successive antipodes.
    #
    counts = save_counts
    chull_idx = save_chull_idx
    indexer = Indexes(counts)
    # Get rid of degenerates
    #
    degenerates = antipodes[:,0] == antipodes[:,1]
    if np.all(degenerates):
        return np.zeros(len(indexes)), max_distance
    l = l[~degenerates]
    antipodes = antipodes[~degenerates]
    # We duplicate the list so each antipode appears as both a first and second
    #
    l = np.hstack((l,l))
    antipodes = np.vstack((antipodes, antipodes[:,::-1]))
    v2_idx = indexer.idx[0][antipodes[:,1]]
    #
    # Vertex number zero can be the vertex after the last one. We therefore
    # add a second vertex of "count" for each one where the second vertex is zero.
    #
    extras = antipodes[v2_idx == 0,:]
    antipodes = np.vstack([antipodes, extras])
    l = np.hstack((l, indexes[indexer.rev_idx[extras[:,0]]]))
    v2_idx = np.hstack((v2_idx, indexer.counts[0][indexer.rev_idx[extras[:,1]]]))
    #
    # We sort by label, first and second to order them for the search.
    #
    order = np.lexsort((v2_idx, antipodes[:,0], l))
    antipodes = antipodes[order,:]
    l = l[order]
    v2_idx = v2_idx[order]
    #
    # Now, we only want antipode pairs where:
    # * the label is the same as it's successor
    # * the first antipode is the same as it's successor
    # * the second antipode is one less than it's successor
    #
    v1_idx = indexer.idx[0][antipodes[:,0]]
    good = np.hstack(((l[1:] == l[:-1]) &
                      (v1_idx[1:] == v1_idx[:-1]) &
                      (v2_idx[1:] == v2_idx[:-1]+1), [False]))
    if not np.any(good):
        return np.zeros(len(indexes)), max_distance
    antipodes = antipodes[good,:]
    l = l[good]
    v2_idx = v2_idx[good]
    v = chull_pts[antipodes[:,0],:]
    l0 = chull_pts[antipodes[:,1],:]
    #
    # The index of the second point in the line has to be done modulo
    #
    l1_idx = antipodes[:,1] + 1
    needs_modulo = v2_idx == indexer.counts[0][indexer.rev_idx[antipodes[:,1]]] - 1
    l1_idx[needs_modulo] -= indexer.counts[0][indexer.rev_idx[antipodes[needs_modulo,1]]]
    l1 = chull_pts[l1_idx,:]
    #
    # Compute the distances
    #
    distances = distance2_to_line(v, l0, l1)
    min_distance = np.sqrt(fixup_scipy_ndimage_result(scind.minimum(distances, l, indexes)))
    min_distance[np.isnan(min_distance)] = 0
    return min_distance, max_distance

Response:
This function takes the convex hull data generated by convex_hull
    and returns the minimum and maximum Feret diameter for each object.


Instruction:
Explain what the following Python 3 code does
def is_obtuse(p1, v, p2):
    '''Determine whether the angle, p1 - v - p2 is obtuse

    p1 - N x 2 array of coordinates of first point on edge
    v - N x 2 array of vertex coordinates
    p2 - N x 2 array of coordinates of second point on edge

    returns vector of booleans
    '''
    p1x = p1[:,1]
    p1y = p1[:,0]
    p2x = p2[:,1]
    p2y = p2[:,0]
    vx = v[:,1]
    vy = v[:,0]
    Dx = vx - p2x
    Dy = vy - p2y
    Dvp1x = p1x - vx
    Dvp1y = p1y - vy
    return Dvp1x * Dx + Dvp1y * Dy > 0

Response:
Determine whether the angle p1 - v - p2 is obtuse
    .


Instruction:
Create a Python 3 function for
getting the outline points of objects in clockwise order.

Response:
def get_outline_pts(labels, idxs):
    '''Get the outline points of objects in clockwise order

    Given a labels matrix of contiguously-labeled objects, trace
    the exteriors of those objects to get the points of the outline
    in clockwise order.

    labels - a labels matrix

    idxs - return points for the labels named by this array

    Returns an Nx2 array of points, a vector of offsets to the first point
    for each object and a vector of counts of points per
    indexed object.
    '''
    idxs = np.atleast_1d(idxs)
    if len(idxs) == 0:
        return np.zeros((0, 2), int), np.zeros(0, int), np.zeros(0, int)
    #
    # Do some preprocessing on the labels matrix to get a map of chosen labels
    #
    end_label = max(np.max(labels), np.max(idxs)) + 1
    chosen_labels = np.zeros(int(end_label), bool)
    chosen_labels[idxs] = True
    #
    # We trace clockwise, conceptually standing
    # to the left of the outline and putting our right hand on the
    # outline.
    # We remember the direction we are going and that gives
    # us an order for the points. For instance, if we are going
    # north:
    #
    #  2  3  4
    #  1  x  5
    #  0  7  6
    #
    # If "1" is available, our new direction is southwest:
    #
    #  5  6  7
    #  4  x  0
    #  3  2  1
    #
    #  Take direction 0 to be northeast (i-1, j-1). We look in
    #  this order:
    #
    #  3  4  5
    #  2  x  6
    #  1  0  7
    #
    # The directions are
    #
    #  0  1  2
    #  7     3
    #  6  5  4
    #
    traversal_order = np.array(
        #   i   j   new direction
        ((  1,  0,  5 ),
         (  1, -1,  6 ),
         (  0, -1,  7 ),
         ( -1, -1,  0 ),
         ( -1,  0,  1 ),
         ( -1,  1,  2 ),
         (  0,  1,  3 ),
         (  1,  1,  4 )))
    #
    # Pad the labels matrix if any objects touch the border. This keeps
    # us from having to handle border cases. For many labels matrices,
    # no object touches the border
    #
    border = np.hstack((labels[0, :], labels[:, 0], labels[-1, :], labels[:, -1]))
    border = np.unique(border)
    if any([idx in border for idx in idxs]):
        big_labels = np.zeros((labels.shape[0]+2, labels.shape[1]+2), np.uint32)
        big_labels[1:-1, 1:-1] = labels
        offset = np.ones(2, int)
        labels = big_labels
        del big_labels
    else:
        labels = np.ascontiguousarray(labels, np.uint32)
        offset = np.zeros(2, int)
    assert isinstance(labels, np.ndarray)
    strides = np.array(labels.strides) // labels.dtype.itemsize
    assert strides[1] == 1, "Last stride must be 1 for modulo unravel to work"
    stride_table = np.ascontiguousarray(
        np.sum(traversal_order[:, :2] * strides, 1), np.int32)
    new_direction_table = np.ascontiguousarray(traversal_order[:, 2], np.int32)
    #
    # The worst case is a line - each point in the outline is visited twice.
    #
    consider_only_these = (
        (scind.grey_erosion(labels, footprint=np.ones((3,3), bool)) !=
         scind.grey_dilation(labels, footprint=np.ones((3,3), bool))) &
        chosen_labels[labels])
    max_output = np.sum(consider_only_these) * 2
    output = np.zeros(max_output, np.uint32)
    #
    # The algorithm starts at the northwest corner of every object. Find the
    # point with the minimum i**2 + j**2 for each object.
    #
    i, j = np.mgrid[0:labels.shape[0], 0:labels.shape[1]]
    rlabels = labels[consider_only_these]
    if len(rlabels) == 0:
        # Corner case - no pixels for *any* label
        return np.zeros((0, 2), int), np.zeros(len(idxs), int), np.zeros(len(idxs), int)
    i = i[consider_only_these]
    j = j[consider_only_these]
    d = i * i + j * j
    order = np.lexsort((d, rlabels))
    stride_pts = i[order] * strides[0] + j[order] * strides[1]
    rlabels = rlabels[order]

    first = np.hstack(([True], rlabels[:-1] != rlabels[1:]))
    first_strides = np.ascontiguousarray(stride_pts[first], np.uint32)
    idx_of_first = rlabels[first]
    output_count = np.zeros(len(idx_of_first), np.uint32)
    trace_outlines(labels.ravel(),
                   first_strides,
                   stride_table,
                   new_direction_table,
                   output,
                   output_count)
    #
    # on output, we have to do some fixing up:
    #
    # need to convert output to i, j
    # need to arrange output_count in the same order as idxs
    #
    output = output[:np.sum(output_count)]
    mapper = np.zeros(np.max(idxs)+1, int)
    mapper[idxs] = np.arange(len(idxs))
    coord_offsets = np.zeros(len(idxs), output_count.dtype)
    coord_counts = np.zeros(len(idxs), output_count.dtype)
    coord_counts[mapper[idx_of_first]] = output_count
    if len(idx_of_first) > 1:
        coord_offsets[mapper[idx_of_first[1:]]] = np.cumsum(output_count[:-1],
                                                            dtype=output_count.dtype)
    coords = np.column_stack((
        output // strides[0] - offset[0],
        output % strides[0] - offset[1]))
    return coords, coord_offsets, coord_counts


Instruction:
Implement a function in Python 3 to
calculate the principal components of a vector of measurements and returns the correlation matrix that can be used to transform x into its components.

Response:
def princomp(x):
    """Determine the principal components of a vector of measurements

    Determine the principal components of a vector of measurements
    x should be a M x N numpy array composed of M observations of n variables
    The output is:
    coeffs - the NxN correlation matrix that can be used to transform x into its components

    The code for this function is based on "A Tutorial on Principal Component
    Analysis", Shlens, 2005 http://www.snl.salk.edu/~shlens/pub/notes/pca.pdf
    (unpublished)
    """

    (M,N)  = x.shape
    Mean   = x.mean(0)
    y      = x - Mean
    cov    = numpy.dot(y.transpose(),y) / (M-1)
    (V,PC) = numpy.linalg.eig(cov)
    order  = (-V).argsort()
    coeff  = PC[:,order]
    return coeff


Instruction:
Create a Python 3 function for
returning an array of all non - identity pairs of n

Response:
def all_pairs(n):
    '''Return an (n*(n - 1)) x 2 array of all non-identity pairs of n things

    n - # of things

    The array is (cleverly) ordered so that the first m * (m - 1) elements
    can be used for m < n things:

    n = 3
    [[0, 1], # n = 2
     [1, 0], # n = 2
     [0, 2],
     [1, 2],
     [2, 0],
     [2, 1]]
     '''
    # Get all against all
    i, j = [x.flatten() for x in np.mgrid[0:n, 0:n]]
    # Eliminate the diagonal of i == j
    i, j = [x[i != j] for x in (i,j)]
    # Order by smallest of i or j first, then j then i for neatness
    order = np.lexsort((j, i, np.maximum(i, j)))
    return np.column_stack((i[order], j[order]))


Instruction:
Can you write a function in Python 3 where it
normalizes an image to make the minimum zero and maximum one Taxonomy

Response:
def stretch(image, mask=None):
    '''Normalize an image to make the minimum zero and maximum one

    image - pixel data to be normalized
    mask  - optional mask of relevant pixels. None = don't mask

    returns the stretched image
    '''
    image = np.array(image, float)
    if np.product(image.shape) == 0:
        return image
    if mask is None:
        minval = np.min(image)
        maxval = np.max(image)
        if minval == maxval:
            if minval < 0:
                return np.zeros_like(image)
            elif minval > 1:
                return np.ones_like(image)
            return image
        else:
            return (image - minval) / (maxval - minval)
    else:
        significant_pixels = image[mask]
        if significant_pixels.size == 0:
            return image
        minval = np.min(significant_pixels)
        maxval = np.max(significant_pixels)
        if minval == maxval:
            transformed_image = minval
        else:
            transformed_image = ((significant_pixels - minval) /
                                 (maxval - minval))
        result = image.copy()
        image[mask] = transformed_image
        return image


Instruction:
Write a Python 3 script for
masking median filter with octagonal shape data - array of data to be median filtered.

Response:
def median_filter(data, mask, radius, percent=50):
    '''Masked median filter with octagonal shape

    data - array of data to be median filtered.
    mask - mask of significant pixels in data
    radius - the radius of a circle inscribed into the filtering octagon
    percent - conceptually, order the significant pixels in the octagon,
              count them and choose the pixel indexed by the percent
              times the count divided by 100. More simply, 50 = median
    returns a filtered array.  In areas where the median filter does
      not overlap the mask, the filtered result is undefined, but in
      practice, it will be the lowest value in the valid area.
    '''
    if mask is None:
        mask = np.ones(data.shape, dtype=bool)
    if np.all(~ mask):
        return data.copy()
    #
    # Normalize the ranked data to 0-255
    #
    if (not np.issubdtype(data.dtype, np.int) or
        np.min(data) < 0 or np.max(data) > 255):
        ranked_data, translation = rank_order(data[mask], nbins=255)
        was_ranked = True
    else:
        ranked_data = data[mask]
        was_ranked = False
    input = np.zeros(data.shape, np.uint8 )
    input[mask] = ranked_data

    mmask = np.ascontiguousarray(mask, np.uint8)

    output = np.zeros(data.shape, np.uint8)

    _filter.median_filter(input, mmask, output, radius, percent)
    if was_ranked:
        result = translation[output]
    else:
        result = output
    return result


Instruction:
How would you explain what the following Python 3 function does
def laplacian_of_gaussian(image, mask, size, sigma):
    '''Perform the Laplacian of Gaussian transform on the image

    image - 2-d image array
    mask  - binary mask of significant pixels
    size  - length of side of square kernel to use
    sigma - standard deviation of the Gaussian
    '''
    half_size = size//2
    i,j = np.mgrid[-half_size:half_size+1,
                   -half_size:half_size+1].astype(float) / float(sigma)
    distance = (i**2 + j**2)/2
    gaussian = np.exp(-distance)
    #
    # Normalize the Gaussian
    #
    gaussian = gaussian / np.sum(gaussian)

    log = (distance - 1) * gaussian
    #
    # Normalize the kernel to have a sum of zero
    #
    log = log - np.mean(log)
    if mask is None:
        mask = np.ones(image.shape[:2], bool)
    masked_image = image.copy()
    masked_image[~mask] = 0
    output = convolve(masked_image, log, mode='constant', cval=0)
    #
    # Do the LoG of the inverse of the mask. This finds the magnitude of the
    # contribution of the masked pixels. We then fudge by multiplying by the
    # value at the pixel of interest - this effectively sets the value at a
    # masked pixel to that of the pixel of interest.
    #
    # It underestimates the LoG, that's not a terrible thing.
    #
    correction = convolve((~ mask).astype(float), log, mode='constant', cval = 1)
    output += correction * image
    output[~ mask] = image[~ mask]
    return output

Response: Perform the Laplacian of Gaussian transform on the image array mask size sigma


Instruction:
Given the following Python 3 function, write the documentation
def canny(image, mask, sigma, low_threshold, high_threshold):
    '''Edge filter an image using the Canny algorithm.

    sigma - the standard deviation of the Gaussian used
    low_threshold - threshold for edges that connect to high-threshold
                    edges
    high_threshold - threshold of a high-threshold edge

    Canny, J., A Computational Approach To Edge Detection, IEEE Trans.
    Pattern Analysis and Machine Intelligence, 8:679-714, 1986

    William Green's Canny tutorial
    http://www.pages.drexel.edu/~weg22/can_tut.html
    '''
    #
    # The steps involved:
    #
    # * Smooth using the Gaussian with sigma above.
    #
    # * Apply the horizontal and vertical Sobel operators to get the gradients
    #   within the image. The edge strength is the sum of the magnitudes
    #   of the gradients in each direction.
    #
    # * Find the normal to the edge at each point using the arctangent of the
    #   ratio of the Y sobel over the X sobel - pragmatically, we can
    #   look at the signs of X and Y and the relative magnitude of X vs Y
    #   to sort the points into 4 categories: horizontal, vertical,
    #   diagonal and antidiagonal.
    #
    # * Look in the normal and reverse directions to see if the values
    #   in either of those directions are greater than the point in question.
    #   Use interpolation to get a mix of points instead of picking the one
    #   that's the closest to the normal.
    #
    # * Label all points above the high threshold as edges.
    # * Recursively label any point above the low threshold that is 8-connected
    #   to a labeled point as an edge.
    #
    # Regarding masks, any point touching a masked point will have a gradient
    # that is "infected" by the masked point, so it's enough to erode the
    # mask by one and then mask the output. We also mask out the border points
    # because who knows what lies beyond the edge of the image?
    #
    fsmooth = lambda x: gaussian_filter(x, sigma, mode='constant')
    smoothed = smooth_with_function_and_mask(image, fsmooth, mask)
    jsobel = convolve(smoothed, [[-1,0,1],[-2,0,2],[-1,0,1]])
    isobel = convolve(smoothed, [[-1,-2,-1],[0,0,0],[1,2,1]])
    abs_isobel = np.abs(isobel)
    abs_jsobel = np.abs(jsobel)
    magnitude = np.sqrt(isobel*isobel + jsobel*jsobel)
    #
    # Make the eroded mask. Setting the border value to zero will wipe
    # out the image edges for us.
    #
    s = generate_binary_structure(2,2)
    emask = binary_erosion(mask, s, border_value = 0)
    emask = np.logical_and(emask, magnitude > 0)
    #
    #--------- Find local maxima --------------
    #
    # Assign each point to have a normal of 0-45 degrees, 45-90 degrees,
    # 90-135 degrees and 135-180 degrees.
    #
    local_maxima = np.zeros(image.shape,bool)
    #----- 0 to 45 degrees ------
    pts_plus = np.logical_and(isobel >= 0,
                              np.logical_and(jsobel >= 0,
                                             abs_isobel >= abs_jsobel))
    pts_minus = np.logical_and(isobel <= 0,
                               np.logical_and(jsobel <= 0,
                                              abs_isobel >= abs_jsobel))
    pts = np.logical_or(pts_plus, pts_minus)
    pts = np.logical_and(emask, pts)
    # Get the magnitudes shifted left to make a matrix of the points to the
    # right of pts. Similarly, shift left and down to get the points to the
    # top right of pts.
    c1 = magnitude[1:,:][pts[:-1,:]]
    c2 = magnitude[1:,1:][pts[:-1,:-1]]
    m  = magnitude[pts]
    w  = abs_jsobel[pts] / abs_isobel[pts]
    c_plus  = c2 * w + c1 * (1-w) <= m
    c1 = magnitude[:-1,:][pts[1:,:]]
    c2 = magnitude[:-1,:-1][pts[1:,1:]]
    c_minus =  c2 * w + c1 * (1-w) <= m
    local_maxima[pts] = np.logical_and(c_plus, c_minus)
    #----- 45 to 90 degrees ------
    # Mix diagonal and vertical
    #
    pts_plus = np.logical_and(isobel >= 0,
                              np.logical_and(jsobel >= 0,
                                             abs_isobel <= abs_jsobel))
    pts_minus = np.logical_and(isobel <= 0,
                               np.logical_and(jsobel <= 0,
                                              abs_isobel <= abs_jsobel))
    pts = np.logical_or(pts_plus, pts_minus)
    pts = np.logical_and(emask, pts)
    c1 = magnitude[:,1:][pts[:,:-1]]
    c2 = magnitude[1:,1:][pts[:-1,:-1]]
    m  = magnitude[pts]
    w  = abs_isobel[pts] / abs_jsobel[pts]
    c_plus  = c2 * w + c1 * (1-w) <= m
    c1 = magnitude[:,:-1][pts[:,1:]]
    c2 = magnitude[:-1,:-1][pts[1:,1:]]
    c_minus =  c2 * w + c1 * (1-w) <= m
    local_maxima[pts] = np.logical_and(c_plus, c_minus)
    #----- 90 to 135 degrees ------
    # Mix anti-diagonal and vertical
    #
    pts_plus = np.logical_and(isobel <= 0,
                              np.logical_and(jsobel >= 0,
                                             abs_isobel <= abs_jsobel))
    pts_minus = np.logical_and(isobel >= 0,
                               np.logical_and(jsobel <= 0,
                                              abs_isobel <= abs_jsobel))
    pts = np.logical_or(pts_plus, pts_minus)
    pts = np.logical_and(emask, pts)
    c1a = magnitude[:,1:][pts[:,:-1]]
    c2a = magnitude[:-1,1:][pts[1:,:-1]]
    m  = magnitude[pts]
    w  = abs_isobel[pts] / abs_jsobel[pts]
    c_plus  = c2a * w + c1a * (1.0-w) <= m
    c1 = magnitude[:,:-1][pts[:,1:]]
    c2 = magnitude[1:,:-1][pts[:-1,1:]]
    c_minus =  c2 * w + c1 * (1.0-w) <= m
    cc = np.logical_and(c_plus,c_minus)
    local_maxima[pts] = np.logical_and(c_plus, c_minus)
    #----- 135 to 180 degrees ------
    # Mix anti-diagonal and anti-horizontal
    #
    pts_plus = np.logical_and(isobel <= 0,
                              np.logical_and(jsobel >= 0,
                                             abs_isobel >= abs_jsobel))
    pts_minus = np.logical_and(isobel >= 0,
                               np.logical_and(jsobel <= 0,
                                              abs_isobel >= abs_jsobel))
    pts = np.logical_or(pts_plus, pts_minus)
    pts = np.logical_and(emask, pts)
    c1 = magnitude[:-1,:][pts[1:,:]]
    c2 = magnitude[:-1,1:][pts[1:,:-1]]
    m  = magnitude[pts]
    w  = abs_jsobel[pts] / abs_isobel[pts]
    c_plus  = c2 * w + c1 * (1-w) <= m
    c1 = magnitude[1:,:][pts[:-1,:]]
    c2 = magnitude[1:,:-1][pts[:-1,1:]]
    c_minus =  c2 * w + c1 * (1-w) <= m
    local_maxima[pts] = np.logical_and(c_plus, c_minus)
    #
    #---- Create two masks at the two thresholds.
    #
    high_mask = np.logical_and(local_maxima, magnitude >= high_threshold)
    low_mask  = np.logical_and(local_maxima, magnitude >= low_threshold)
    #
    # Segment the low-mask, then only keep low-segments that have
    # some high_mask component in them
    #
    labels,count = label(low_mask, np.ones((3,3),bool))
    if count == 0:
        return low_mask

    sums = fix(scind.sum(high_mask, labels, np.arange(count,dtype=np.int32)+1))
    good_label = np.zeros((count+1,),bool)
    good_label[1:] = sums > 0
    output_mask = good_label[labels]
    return output_mask

Response: Edge filter an image using the Canny algorithm.


Instruction:
Write a Python 3 script for
finding edges using the Roberts algorithm and return the magnitude of the output of the two Roberts convolution kernels.

Response:
def roberts(image, mask=None):
    '''Find edges using the Roberts algorithm

    image - the image to process
    mask  - mask of relevant points

    The algorithm returns the magnitude of the output of the two Roberts
    convolution kernels.

    The following is the canonical citation for the algorithm:
    L. Roberts Machine Perception of 3-D Solids, Optical and
    Electro-optical Information Processing, MIT Press 1965.

    The following website has a tutorial on the algorithm:
    http://homepages.inf.ed.ac.uk/rbf/HIPR2/roberts.htm
    '''
    result = np.zeros(image.shape)
    #
    # Four quadrants and two convolutions:
    #
    # q0,0 | q0,1    1  |  0  anti-diagonal
    # q1,0 | q1,1    0  | -1
    #
    # q-1,0 | q0,0   0  |  1  diagonal
    # q-1,1 | q0,1  -1  |  0
    #
    # Points near the mask edges and image edges are computed unreliably
    # so make them zero (no edge) in the result
    #
    if mask is None:
        mask = np.ones(image.shape, bool)
    big_mask = binary_erosion(mask,
                              generate_binary_structure(2,2),
                              border_value = 0)
    result[big_mask==False] = 0
    q00   = image[:,:][big_mask]
    q11   = image[1:,1:][big_mask[:-1,:-1]]
    qm11  = image[:-1,1:][big_mask[1:,:-1]]
    diagonal = q00 - qm11
    anti_diagonal = q00 - q11
    result[big_mask] = np.sqrt(diagonal*diagonal + anti_diagonal*anti_diagonal)
    return result


Instruction:
Write a Python 3 function for
calculating the absolute magnitude Sobel to find the edges of the image.

Response:
def sobel(image, mask=None):
    '''Calculate the absolute magnitude Sobel to find the edges

    image - image to process
    mask - mask of relevant points

    Take the square root of the sum of the squares of the horizontal and
    vertical Sobels to get a magnitude that's somewhat insensitive to
    direction.

    Note that scipy's Sobel returns a directional Sobel which isn't
    useful for edge detection in its raw form.
    '''
    return np.sqrt(hsobel(image,mask)**2 + vsobel(image,mask)**2)


Instruction:
How would you code a function in Python 3 to
find the edge magnitude using the Prewitt transform image - image to process mask - mask of relevant points to process mask of relevant points Returns the square root of the horizontal and vertical Prewitt transforms.

Response:
def prewitt(image, mask=None):
    '''Find the edge magnitude using the Prewitt transform

    image - image to process
    mask  - mask of relevant points

    Return the square root of the sum of squares of the horizontal
    and vertical Prewitt transforms.
    '''
    return np.sqrt(hprewitt(image,mask)**2 + vprewitt(image,mask)**2)


Instruction:
Can you generate the documentation for the following Python 3 function
def hprewitt(image, mask=None):
    '''Find the horizontal edges of an image using the Prewitt transform

    image - image to process
    mask  - mask of relevant points

    We use the following kernel and return the absolute value of the
    result at each point:
     1   1   1
     0   0   0
    -1  -1  -1
    '''
    if mask is None:
        mask = np.ones(image.shape, bool)
    big_mask = binary_erosion(mask,
                              generate_binary_structure(2,2),
                              border_value = 0)
    result = np.abs(convolve(image, np.array([[ 1, 1, 1],
                                              [ 0, 0, 0],
                                              [-1,-1,-1]]).astype(float)/3.0))
    result[big_mask==False] = 0
    return result

Response: Find the horizontal edges of an image using the Prewitt transform and return the absolute value of the image at each point


Instruction:
Can you generate a brief explanation for the following Python 3 code
def enhance_dark_holes(image, min_radius, max_radius, mask=None):
    '''Enhance dark holes using a rolling ball filter

    image - grayscale 2-d image
    radii - a vector of radii: we enhance holes at each given radius
    '''
    #
    # Do 4-connected erosion
    #
    se = np.array([[False, True, False],
                   [True, True, True],
                   [False, True, False]])
    #
    # Invert the intensities
    #
    inverted_image = image.max() - image
    previous_reconstructed_image = inverted_image
    eroded_image = inverted_image
    smoothed_image = np.zeros(image.shape)
    for i in range(max_radius+1):
        eroded_image = grey_erosion(eroded_image, mask=mask, footprint = se)
        reconstructed_image = grey_reconstruction(eroded_image, inverted_image,
                                                  footprint = se)
        output_image = previous_reconstructed_image - reconstructed_image
        if i >= min_radius:
            smoothed_image = np.maximum(smoothed_image,output_image)
        previous_reconstructed_image = reconstructed_image
    return smoothed_image

Response: Enhance dark holes using a rolling ball filter image - grayscale 2 - d image radii - a vector of radii that is used to filter the dark holes


Instruction:
Can you create a Python 3 function that
blurs an image using a circular averaging filter

Response:
def circular_average_filter(image, radius, mask=None):
    '''Blur an image using a circular averaging filter (pillbox)

    image - grayscale 2-d image
    radii - radius of filter in pixels

    The filter will be within a square matrix of side 2*radius+1

    This code is translated straight from MATLAB's fspecial function
    '''

    crad = int(np.ceil(radius-0.5))
    x,y = np.mgrid[-crad:crad+1,-crad:crad+1].astype(float)
    maxxy = np.maximum(abs(x),abs(y))
    minxy = np.minimum(abs(x),abs(y))

    m1 = ((radius **2 < (maxxy+0.5)**2 + (minxy-0.5)**2)*(minxy-0.5) +
      (radius**2 >= (maxxy+0.5)**2 + (minxy-0.5)**2) *
      np.real(np.sqrt(np.asarray(radius**2 - (maxxy + 0.5)**2,dtype=complex))))
    m2 = ((radius**2 >  (maxxy-0.5)**2 + (minxy+0.5)**2)*(minxy+0.5) +
      (radius**2 <= (maxxy-0.5)**2 + (minxy+0.5)**2)*
      np.real(np.sqrt(np.asarray(radius**2 - (maxxy - 0.5)**2,dtype=complex))))

    sgrid = ((radius**2*(0.5*(np.arcsin(m2/radius) - np.arcsin(m1/radius)) +
          0.25*(np.sin(2*np.arcsin(m2/radius)) - np.sin(2*np.arcsin(m1/radius)))) -
         (maxxy-0.5)*(m2-m1) + (m1-minxy+0.5)) *
         ((((radius**2 < (maxxy+0.5)**2 + (minxy+0.5)**2) &
         (radius**2 > (maxxy-0.5)**2 + (minxy-0.5)**2)) |
         ((minxy == 0) & (maxxy-0.5 < radius) & (maxxy+0.5 >= radius)))) )

    sgrid = sgrid + ((maxxy+0.5)**2 + (minxy+0.5)**2 < radius**2)
    sgrid[crad,crad] = np.minimum(np.pi*radius**2,np.pi/2)
    if ((crad>0) and (radius > crad-0.5) and (radius**2 < (crad-0.5)**2+0.25)):
        m1  = np.sqrt(radius**2 - (crad - 0.5)**2)
        m1n = m1/radius
        sg0 = 2*(radius**2*(0.5*np.arcsin(m1n) + 0.25*np.sin(2*np.arcsin(m1n)))-m1*(crad-0.5))
        sgrid[2*crad,crad]   = sg0
        sgrid[crad,2*crad]   = sg0
        sgrid[crad,0]        = sg0
        sgrid[0,crad]        = sg0
        sgrid[2*crad-1,crad] = sgrid[2*crad-1,crad] - sg0
        sgrid[crad,2*crad-1] = sgrid[crad,2*crad-1] - sg0
        sgrid[crad,1]        = sgrid[crad,1]        - sg0
        sgrid[1,crad]        = sgrid[1,crad]        - sg0

    sgrid[crad,crad] = np.minimum(sgrid[crad,crad],1)
    kernel = sgrid/sgrid.sum()

    output = convolve(image, kernel, mode='constant')
    if mask is None:
        mask = np.ones(image.shape,np.uint8)
    else:
        mask = np.array(mask, np.uint8)
    output = masked_convolution(image, mask, kernel)
    output[mask==0] = image[mask==0]

    return output


Instruction:
Explain what the following Python 3 code does
def velocity_kalman_model():
    '''Return a KalmanState set up to model objects with constant velocity

    The observation and measurement vectors are i,j.
    The state vector is i,j,vi,vj
    '''
    om = np.array([[1,0,0,0], [0, 1, 0, 0]])
    tm = np.array([[1,0,1,0],
                   [0,1,0,1],
                   [0,0,1,0],
                   [0,0,0,1]])
    return KalmanState(om, tm)

Response: Return a KalmanState set up to model objects with constant velocity


Instruction:
Create a Python 3 function for
returning a KalmanState set up to model going backwards in time

Response:
def reverse_velocity_kalman_model():
    '''Return a KalmanState set up to model going backwards in time'''
    om = np.array([[1,0,0,0], [0, 1, 0, 0]])
    tm = np.array([[1,0,-1,0],
                   [0,1,0,-1],
                   [0,0,1,0],
                   [0,0,0,1]])
    return KalmanState(om, tm)


Instruction:
Write a Python 3 script to
return the kalman filter for the features in the new frame.

Response:
def kalman_filter(kalman_state, old_indices, coordinates, q, r):
    '''Return the kalman filter for the features in the new frame

    kalman_state - state from last frame

    old_indices - the index per feature in the last frame or -1 for new

    coordinates - Coordinates of the features in the new frame.

    q - the process error covariance - see equ 1.3 and 1.10 from Welch

    r - measurement error covariance of features - see eqn 1.7 and 1.8 from welch.

    returns a new KalmanState containing the kalman filter of
    the last state by the given coordinates.

    Refer to kalmanGainLinearMotion.m and
    http://www.cs.unc.edu/~welch/media/pdf/kalman_intro.pdf

    for info on the algorithm.
    '''
    assert isinstance(kalman_state, KalmanState)
    old_indices = np.array(old_indices)
    if len(old_indices) == 0:
        return KalmanState(kalman_state.observation_matrix,
                           kalman_state.translation_matrix)
    #
    # Cull missing features in old state and collect only matching coords
    #
    matching = old_indices != -1
    new_indices = np.arange(len(old_indices))[~matching]
    retained_indices = np.arange(len(old_indices))[matching]
    new_coords = coordinates[new_indices]
    observation_matrix_t = kalman_state.observation_matrix.transpose()
    if len(retained_indices) > 0:
        kalman_state = kalman_state.deep_copy()
        coordinates = coordinates[retained_indices]
        kalman_state.map_frames(old_indices[retained_indices])
        #
        # Time update equations
        #
        # From eqn 1.9 of Welch
        #
        state_vec = kalman_state.predicted_state_vec
        #
        # From eqn 1.10 of Welch
        #
        state_cov = dot_n(
            dot_n(kalman_state.translation_matrix, kalman_state.state_cov),
            kalman_state.translation_matrix.transpose()) + q[matching]
        #
        # From eqn 1.11 of welch
        #
        kalman_gain_numerator = dot_n(state_cov, observation_matrix_t)

        kalman_gain_denominator = dot_n(
            dot_n(kalman_state.observation_matrix, state_cov),
                  observation_matrix_t) + r[matching]
        kalman_gain_denominator = inv_n(kalman_gain_denominator)
        kalman_gain = dot_n(kalman_gain_numerator, kalman_gain_denominator)
        #
        # Eqn 1.12 of Welch
        #
        difference = coordinates - dot_n(kalman_state.observation_matrix,
                                         state_vec[:,:,np.newaxis])[:,:,0]
        state_noise = dot_n(kalman_gain, difference[:,:,np.newaxis])[:,:,0]
        state_vec = state_vec + state_noise
        #
        # Eqn 1.13 of Welch (factored from (I - KH)P to P - KHP)
        #
        state_cov = (state_cov -
                     dot_n(dot_n(kalman_gain, kalman_state.observation_matrix),
                           state_cov))
        #
        # Collect all of the state noise in one array. We produce an I and J
        # variance. Notes in kalmanGainLinearMotion indicate that you
        # might want a single variance, combining I & J. An alternate
        # might be R and theta, a variance of angular consistency and one
        # of absolute velocity.
        #
        # Add an index to the state noise in the rightmost column
        #
        idx = np.arange(len(state_noise))
        #
        # Stack the rows with the old ones
        #
        all_state_noise = np.vstack((kalman_state.state_noise, state_noise))
        all_state_noise_idx = np.hstack((kalman_state.state_noise_idx, idx))
        noise_var = np.zeros((len(idx), all_state_noise.shape[1]))
        for i in range(all_state_noise.shape[1]):
            noise_var[:, i] = fix(scind.variance(all_state_noise[:, i],
                                                 all_state_noise_idx,
                                                 idx))
        obs_vec = dot_n(kalman_state.observation_matrix,
                        state_vec[:,:,np.newaxis])[:,:,0]
        kalman_state = KalmanState(kalman_state.observation_matrix,
                                   kalman_state.translation_matrix,
                                   state_vec, state_cov, noise_var,
                                   all_state_noise,
                                   all_state_noise_idx)
    else:
        # Erase all previous features
        kalman_state = KalmanState(kalman_state.observation_matrix,
                                   kalman_state.translation_matrix)
    if len(new_coords) > 0:
        #
        # Fill in the initial states:
        #
        state_vec = dot_n(observation_matrix_t,
                          new_coords[:,:,np.newaxis])[:,:,0]
        #
        # The COV for the hidden, undetermined features should be large
        # and the COV for others should be small
        #
        nstates = kalman_state.state_len
        nnew_features = len(new_indices)
        cov_vec = SMALL_KALMAN_COV / np.dot(observation_matrix_t,
                                            np.ones(kalman_state.obs_len))
        cov_vec[~ np.isfinite(cov_vec)] = LARGE_KALMAN_COV
        cov_matrix = np.diag(cov_vec)
        state_cov = cov_matrix[np.newaxis,:,:][np.zeros(nnew_features,int)]
        #
        # The noise variance is all ones in Jaqman
        #
        noise_var = np.ones((len(new_indices), kalman_state.state_len))
        #
        # Map the retained indices to their new slots and new ones to empty
        # slots (=-1)
        #
        kalman_state.add_features(retained_indices,
                                  new_indices,
                                  state_vec, state_cov, noise_var)
    return kalman_state


Instruction:
Make a summary of the following Python 3 code
def line_integration(image, angle, decay, sigma):
    '''Integrate the image along the given angle

    DIC images are the directional derivative of the underlying
    image. This filter reconstructs the original image by integrating
    along that direction.

    image - a 2-dimensional array

    angle - shear angle in radians. We integrate perpendicular to this angle

    decay - an exponential decay applied to the integration

    sigma - the standard deviation of a Gaussian which is used to
            smooth the image in the direction parallel to the shear angle.
    '''
    #
    # Normalize the image so that the mean is zero
    #
    normalized = image - np.mean(image)
    #
    # Rotate the image so the J direction is perpendicular to the shear angle.
    #
    rotated = scind.rotate(normalized, -angle)
    #
    # Smooth in only the i direction
    #
    smoothed = scind.gaussian_filter1d(rotated, sigma) if sigma > 0 else rotated
    #
    # We want img_out[:,j+1] to be img_out[:,j] * decay + img[j+1]
    # Could be done by convolution with a ramp, maybe in FFT domain,
    # but we just do a bunch of steps here.
    #
    result_fwd = smoothed.copy()
    for i in range(1,result_fwd.shape[0]):
        result_fwd[i] += result_fwd[i-1] * decay
    result_rev = smoothed.copy()
    for i in reversed(range(result_rev.shape[0]-1)):
        result_rev[i] += result_rev[i+1] * decay
    result = (result_fwd - result_rev) / 2
    #
    # Rotate and chop result
    #
    result = scind.rotate(result, angle)
    ipad = int((result.shape[0] - image.shape[0]) / 2)
    jpad = int((result.shape[1] - image.shape[1]) / 2)
    result = result[ipad:(ipad + image.shape[0]),
                    jpad:(jpad + image.shape[1])]
    #
    # Scale the resultant image similarly to the output.
    #
    img_min, img_max = np.min(image), np.max(image)
    result_min, result_max = np.min(result), np.max(result)
    if (img_min == img_max) or (result_min == result_max):
        return np.zeros(result.shape)
    result = (result - result_min) / (result_max - result_min)
    result = img_min + result * (img_max - img_min)
    return result

Response: Integrate the image along the given angle and integrate the perpendicular to the shear angle.


Instruction:
Write a Python 3 script for
calculating a weighted variance of an image

Response:
def variance_transform(img, sigma, mask=None):
    '''Calculate a weighted variance of the image

    This function caluclates the variance of an image, weighting the
    local contributions by a Gaussian.

    img - image to be transformed
    sigma - standard deviation of the Gaussian
    mask - mask of relevant pixels in the image
    '''
    if mask is None:
        mask = np.ones(img.shape, bool)
    else:
        img = img.copy()
        img[~mask] = 0
    #
    # This is the Gaussian of the mask... so we can normalize for
    # pixels near the edge of the mask
    #
    gmask = scind.gaussian_filter(mask.astype(float), sigma,
                                  mode = 'constant')
    img_mean = scind.gaussian_filter(img, sigma,
                                     mode = 'constant') / gmask
    img_squared = scind.gaussian_filter(img ** 2, sigma,
                                        mode = 'constant') / gmask
    var = img_squared - img_mean ** 2
    return var


Instruction:
Can you generate a brief explanation for the following Python 3 code
def inv_n(x):
    '''given N matrices, return N inverses'''
    #
    # The inverse of a small matrix (e.g. 3x3) is
    #
    #   1
    # -----   C(j,i)
    # det(A)
    #
    # where C(j,i) is the cofactor of matrix A at position j,i
    #
    assert x.ndim == 3
    assert x.shape[1] == x.shape[2]
    c = np.array([ [cofactor_n(x, j, i) * (1 - ((i+j) % 2)*2)
                    for j in range(x.shape[1])]
                   for i in range(x.shape[1])]).transpose(2,0,1)
    return c / det_n(x)[:, np.newaxis, np.newaxis]

Response: given N matrices return N inverses


Instruction:
Can you tell what is the following Python 3 function doing
def det_n(x):
    '''given N matrices, return N determinants'''
    assert x.ndim == 3
    assert x.shape[1] == x.shape[2]
    if x.shape[1] == 1:
        return x[:,0,0]
    result = np.zeros(x.shape[0])
    for permutation in permutations(np.arange(x.shape[1])):
        sign = parity(permutation)
        result += np.prod([x[:, i, permutation[i]]
                           for i in range(x.shape[1])], 0) * sign
        sign = - sign
    return result

Response: given N matrices return N determinants


Instruction:
Write a Python 3 script to
return the cofactor of n matrices x at position i j

Response:
def cofactor_n(x, i, j):
    '''Return the cofactor of n matrices x[n,i,j] at position i,j

    The cofactor is the determinant of the matrix formed by removing
    row i and column j.
    '''
    m = x.shape[1]
    mr = np.arange(m)
    i_idx = mr[mr != i]
    j_idx = mr[mr != j]
    return det_n(x[:, i_idx[:, np.newaxis],
                      j_idx[np.newaxis, :]])


Instruction:
Given the following Python 3 function, write the documentation
def dot_n(x, y):
    '''given two tensors N x I x K and N x K x J return N dot products

    If either x or y is 2-dimensional, broadcast it over all N.

    Dot products are size N x I x J.
    Example:
    x = np.array([[[1,2], [3,4], [5,6]],[[7,8], [9,10],[11,12]]])
    y = np.array([[[1,2,3], [4,5,6]],[[7,8,9],[10,11,12]]])
    print dot_n(x,y)

    array([[[  9,  12,  15],
        [ 19,  26,  33],
        [ 29,  40,  51]],

       [[129, 144, 159],
        [163, 182, 201],
        [197, 220, 243]]])
    '''
    if x.ndim == 2:
        if y.ndim == 2:
            return np.dot(x, y)
        x3 = False
        y3 = True
        nlen = y.shape[0]
    elif y.ndim == 2:
        nlen = x.shape[0]
        x3 = True
        y3 = False
    else:
        assert x.shape[0] == y.shape[0]
        nlen = x.shape[0]
        x3 = True
        y3 = True
    assert x.shape[1+x3] == y.shape[0+y3]
    n, i, j, k = np.mgrid[0:nlen, 0:x.shape[0+x3], 0:y.shape[1+y3],
                          0:y.shape[0+y3]]
    return np.sum((x[n, i, k] if x3 else x[i,k]) *
                  (y[n, k, j] if y3 else y[k,j]), 3)

Response: given two tensors N x I x K and N x J return N dot products


Instruction:
Make a summary of the following Python 3 code
def permutations(x):
    '''Given a listlike, x, return all permutations of x

    Returns the permutations of x in the lexical order of their indices:
    e.g.
    >>> x = [ 1, 2, 3, 4 ]
    >>> for p in permutations(x):
    >>>   print p
    [ 1, 2, 3, 4 ]
    [ 1, 2, 4, 3 ]
    [ 1, 3, 2, 4 ]
    [ 1, 3, 4, 2 ]
    [ 1, 4, 2, 3 ]
    [ 1, 4, 3, 2 ]
    [ 2, 1, 3, 4 ]
    ...
    [ 4, 3, 2, 1 ]
    '''
    #
    # The algorithm is attributed to Narayana Pandit from his
    # Ganita Kaumundi (1356). The following is from
    #
    # http://en.wikipedia.org/wiki/Permutation#Systematic_generation_of_all_permutations
    #
    # 1. Find the largest index k such that a[k] < a[k + 1].
    #    If no such index exists, the permutation is the last permutation.
    # 2. Find the largest index l such that a[k] < a[l].
    #    Since k + 1 is such an index, l is well defined and satisfies k < l.
    # 3. Swap a[k] with a[l].
    # 4. Reverse the sequence from a[k + 1] up to and including the final
    #    element a[n].
    #
    yield list(x) # don't forget to do the first one
    x = np.array(x)
    a = np.arange(len(x))
    while True:
        # 1 - find largest or stop
        ak_lt_ak_next = np.argwhere(a[:-1] < a[1:])
        if len(ak_lt_ak_next) == 0:
            raise StopIteration()
        k = ak_lt_ak_next[-1, 0]
        # 2 - find largest a[l] < a[k]
        ak_lt_al = np.argwhere(a[k] < a)
        l =  ak_lt_al[-1, 0]
        # 3 - swap
        a[k], a[l]  = (a[l], a[k])
        # 4 - reverse
        if k < len(x)-1:
            a[k+1:] = a[:k:-1].copy()
        yield x[a].tolist()

Response: Given a listlike x return all permutations of x


Instruction:
Given the following Python 3 function, write the documentation
def convex_hull_transform(image, levels=256, mask = None,
                          chunksize = CONVEX_HULL_CHUNKSIZE,
                          pass_cutoff = 16):
    '''Perform the convex hull transform of this image

    image - image composed of integer intensity values
    levels - # of levels that we separate the image into
    mask - mask of points to consider or None to consider all points
    chunksize - # of points processed in first pass of convex hull

    for each intensity value, find the convex hull of pixels at or above
    that value and color all pixels within the hull with that value.
    '''
    # Scale the image into the requisite number of levels
    if mask is None:
        img_min = np.min(image)
        img_max = np.max(image)
    else:
        unmasked_pixels = image[mask]
        if len(unmasked_pixels) == 0:
            return np.zeros(image.shape, image.dtype)
        img_min = np.min(unmasked_pixels)
        img_max = np.max(unmasked_pixels)
    img_shape = tuple(image.shape)
    if img_min == img_max:
        return image

    scale = (img_min +
             np.arange(levels).astype(image.dtype) *
             (img_max - img_min) / float(levels-1))
    image = ((image - img_min) * (levels-1) / (img_max - img_min))
    if mask is not None:
        image[~mask] = 0
    #
    # If there are more than 16 levels, we do the method first at a coarse
    # scale. The dark objects can produce points at every level, so doing
    # two passes can reduce the number of points in the second pass to
    # only the difference between two levels at the coarse pass.
    #
    if levels > pass_cutoff:
        sub_levels = int(np.sqrt(levels))
        rough_image = convex_hull_transform(np.floor(image), sub_levels)
        image = np.maximum(image, rough_image)
        del rough_image
    image = image.astype(int)
    #
    # Get rid of any levels that have no representatives
    #
    unique = np.unique(image)
    new_values = np.zeros(levels, int)
    new_values[unique] = np.arange(len(unique))
    scale = scale[unique]
    image = new_values[image]
    #
    # Start by constructing the list of points which are local maxima
    #
    min_image = grey_erosion(image, footprint = np.ones((3,3), bool)).astype(int)
    #
    # Set the borders of the min_image to zero so that the border pixels
    # will be in all convex hulls below their intensity
    #
    min_image[0, :] = 0
    min_image[-1, :] = 0
    min_image[:, 0] = 0
    min_image[:, -1] = 0

    i,j = np.mgrid[0:image.shape[0], 0:image.shape[1]]
    mask = image > min_image

    i = i[mask]
    j = j[mask]
    min_image = min_image[mask]
    image = image[mask]
    #
    # Each point that is a maximum is a potential vertex in the convex hull
    # for each value above the minimum. Therefore, it appears
    #
    # image - min_image
    #
    # times in the i,j,v list of points. So we can do a sum to calculate
    # the number of points, then use cumsum to figure out the first index
    # in that array of points for each i,j,v. We can then use cumsum
    # again on the array of points to assign their levels.
    #
    count = image - min_image
    npoints = np.sum(count)
    # The index in the big array of the first point to place for each
    # point
    first_index_in_big = np.cumsum(count) - count
    #
    # The big array can be quite big, for example if there are lots of
    # thin, dark objects. We do two passes of convex hull: the convex hull
    # of the convex hulls of several regions is the convex hull of the whole
    # so it doesn't matter too much how we break up the array.
    #
    first_i = np.zeros(0, int)
    first_j = np.zeros(0, int)
    first_levels = np.zeros(0, int)
    chunkstart = 0
    while chunkstart < len(count):
        idx = first_index_in_big[chunkstart]
        iend = idx + chunksize
        if iend >= npoints:
            chunkend = len(count)
            iend = npoints
        else:
            chunkend = np.searchsorted(first_index_in_big, iend)
            if chunkend < len(count):
                iend = first_index_in_big[chunkend]
            else:
                iend = npoints
        chunk_first_index_in_big = first_index_in_big[chunkstart:chunkend] - idx
        chunkpoints = iend - idx
        #
        # For the big array, construct an array of indexes into the small array
        #
        index_in_small = np.zeros(chunkpoints, int)
        index_in_small[0] = chunkstart
        index_in_small[chunk_first_index_in_big[1:]] = 1
        index_in_small = np.cumsum(index_in_small)
        #
        # We're going to do a cumsum to make the big array of levels. Point
        # n+1 broadcasts its first value into first_index_in_big[n+1].
        # The value that precedes it is image[n]. Therefore, in order to
        # get the correct value in cumsum:
        #
        # ? + image[n] = min_image[n+1]+1
        # ? = min_image[n+1] + 1 - image[n]
        #
        levels = np.ones(chunkpoints, int)
        levels[0] = min_image[chunkstart] + 1
        levels[chunk_first_index_in_big[1:]] = \
            min_image[chunkstart+1:chunkend] - image[chunkstart:chunkend-1] + 1
        levels = np.cumsum(levels)
        #
        # Construct the ijv
        #
        ijv = np.column_stack((i[index_in_small], j[index_in_small], levels))
        #
        # Get all of the convex hulls
        #
        pts, counts = convex_hull_ijv(ijv, np.arange(1, len(unique)))
        first_i = np.hstack((first_i, pts[:, 1]))
        first_j = np.hstack((first_j, pts[:, 2]))
        first_levels = np.hstack((first_levels, pts[:, 0]))
        chunkstart = chunkend
    #
    # Now do the convex hull of the reduced list of points
    #
    ijv = np.column_stack((first_i, first_j, first_levels))
    pts, counts = convex_hull_ijv(ijv, np.arange(1, len(unique)))
    #
    # Get the points along the lines described by the convex hulls
    #
    # There are N points for each label. Draw a line from each to
    # the next, except for the last which we draw from last to first
    #
    labels = pts[:, 0]
    i = pts[:, 1]
    j = pts[:, 2]
    first_index = np.cumsum(counts) - counts
    last_index = first_index + counts - 1
    next = np.arange(len(labels)) + 1
    next[last_index] = first_index
    index, count, i, j = get_line_pts(i, j, i[next], j[next])
    #
    # use a cumsum to get the index of each point from get_line_pts
    # relative to the labels vector
    #
    big_index = np.zeros(len(i), int)
    big_index[index[1:]] = 1
    big_index = np.cumsum(big_index)
    labels = labels[big_index]
    #
    # A given i,j might be represented more than once. Take the maximum
    # label at each i,j. First sort by i,j and label. Then take only values
    # that have a different i,j than the succeeding value. The last element
    # is always a winner.
    #
    order = np.lexsort((labels, i, j))
    i = i[order]
    j = j[order]
    labels = labels[order]
    mask = np.hstack((((i[1:] != i[:-1]) | (j[1:] != j[:-1])),[True]))
    i = i[mask]
    j = j[mask]
    labels = labels[mask]
    #
    # Now, we have an interesting object. It's ordered by j, then i which
    # means that we have scans of interesting i at each j. The points
    # that aren't represented should have the minimum of the values
    # above and below.
    #
    # We can play a cumsum trick to do this, placing the difference
    # of a point with its previous in the 2-d image, then summing along
    # each i axis to set empty values to the value of the nearest occupied
    # value above and a similar trick to set empty values to the nearest
    # value below. We then take the minimum of the two results.
    #
    first = np.hstack(([True], j[1:] != j[:-1]))
    top = np.zeros(img_shape, labels.dtype)
    top[i[first], j[first]] = labels[first]
    top[i[~first], j[~first]] = (labels[1:] - labels[:-1])[~first[1:]]
    top = np.cumsum(top, 0)

    # From 0 to the location of the first point, set to value of first point
    bottom = np.zeros(img_shape, labels.dtype)
    bottom[0, j[first]] = labels[first]
    # From 1 + the location of the previous point, set to the next point
    last = np.hstack((first[1:], [True]))
    bottom[i[:-1][~first[1:]]+1, j[~first]] = (labels[1:] - labels[:-1])[~first[1:]]
    # Set 1 + the location of the last point to -labels so that all past
    # the end will be zero (check for i at end...)
    llast = last & (i < img_shape[0] - 1)
    bottom[i[llast]+1, j[llast]] = - labels[llast]
    bottom = np.cumsum(bottom, 0)
    image = np.minimum(top, bottom)
    return scale[image]

Response: Perform the convex hull transform of this image


Instruction:
Can you create a Python 3 function that
calculates the hessian of the image at each pixel.

Response:
def hessian(image, return_hessian=True, return_eigenvalues=True, return_eigenvectors=True):
    '''Calculate hessian, its eigenvalues and eigenvectors

    image - n x m image. Smooth the image with a Gaussian to get derivatives
            at different scales.

    return_hessian - true to return an n x m x 2 x 2 matrix of the hessian
                     at each pixel

    return_eigenvalues - true to return an n x m x 2 matrix of the eigenvalues
                         of the hessian at each pixel

    return_eigenvectors - true to return an n x m x 2 x 2 matrix of the
                          eigenvectors of the hessian at each pixel

    The values of the border pixels for the image are not calculated and
    are zero
    '''
    #The Hessian, d(f(x0, x1))/dxi/dxj for i,j = [0,1] is approximated by the
    #following kernels:

    #d00: [[1], [-2], [1]]
    #d11: [[1, -2, 1]]
    #d01 and d10: [[   1, 0,-1],
                  #[   0, 0, 0],
                  #[  -1, 0, 1]] / 2

    #The eigenvalues of the hessian:
    #[[d00, d01]
     #[d01, d11]]
     #L1 = (d00 + d11) / 2 + ((d00 + d11)**2 / 4 - (d00 * d11 - d01**2)) ** .5
     #L2 = (d00 + d11) / 2 - ((d00 + d11)**2 / 4 - (d00 * d11 - d01**2)) ** .5

     #The eigenvectors of the hessian:
     #if d01 != 0:
       #[(L1 - d11, d01), (L2 - d11, d01)]
    #else:
       #[ (1, 0), (0, 1) ]

    #Ideas and code borrowed from:
    #http://www.math.harvard.edu/archive/21b_fall_04/exhibits/2dmatrices/index.html
    #http://www.longair.net/edinburgh/imagej/tubeness/

    hessian = np.zeros((image.shape[0], image.shape[1], 2, 2))
    hessian[1:-1, :, 0, 0] = image[:-2, :] - (2 * image[1:-1, :]) + image[2:, :]
    hessian[1:-1, 1:-1, 0, 1] = hessian[1:-1, 1:-1, 0, 1] = (
        image[2:, 2:] + image[:-2, :-2] -
        image[2:, :-2] - image[:-2, 2:]) / 4
    hessian[:, 1:-1, 1, 1] = image[:, :-2] - (2 * image[:, 1:-1]) + image[:, 2:]
    #
    # Solve the eigenvalue equation:
    # H x = L x
    #
    # Much of this from Eigensystem2x2Float.java from tubeness
    #
    A = hessian[:, :, 0, 0]
    B = hessian[:, :, 0, 1]
    C = hessian[:, :, 1, 1]

    b = -(A + C)
    c = A * C - B * B
    discriminant = b * b - 4 * c

    # pn is something that broadcasts over all points and either adds or
    # subtracts the +/- part of the eigenvalues

    pn = np.array([1, -1])[np.newaxis, np.newaxis, :]
    L = (- b[:, :, np.newaxis] +
         (np.sqrt(discriminant)[:, :, np.newaxis] * pn)) / 2
    #
    # Report eigenvalue # 0 as the one with the highest absolute magnitude
    #
    L[np.abs(L[:, :, 1]) > np.abs(L[:, :, 0]), :] =\
      L[np.abs(L[:, :, 1]) > np.abs(L[:, :, 0]), ::-1]

    if return_eigenvectors:
        #
        # Calculate for d01 != 0
        #
        v = np.ones((image.shape[0], image.shape[1], 2, 2)) * np.nan
        v[:, :, :, 0] =  L - hessian[:, :, 1, 1, np.newaxis]
        v[:, :, :, 1] = hessian[:, :, 0, 1, np.newaxis]
        #
        # Calculate for d01 = 0
        default = np.array([[1, 0], [0, 1]])[np.newaxis, :, :]
        v[hessian[:, :, 0, 1] == 0] = default
        #
        # Normalize the vectors
        #
        d = np.sqrt(np.sum(v * v, 3))
        v /= d[:, :, :, np.newaxis]

    result = []
    if return_hessian:
        result.append(hessian)
    if return_eigenvalues:
        result.append(L)
    if return_eigenvectors:
        result.append(v)
    if len(result) == 0:
        return
    elif len(result) == 1:
        return result[0]
    return tuple(result)