简体   繁体   中英

python 2D list with average of surrounding cells in original 2D list

I need to write a function blur(l) that replaces each cell in a 2D matrix (ie a 2D list that is rectangular) with the average of its neighbors and itself. Each cell has at most 8 neighbors, hence the average is computed over at most 9 cells. The only thing you may assume about the given list is that it is rectangular, and that it only contains integers.

! this has to be done without numpy !

I tried using this code:

def blur(l):
    result = []
    for x in range(len(l) - 1):
        tussen = []
        for y in range(len(l) - 1):
            t = get_neighbor_sum(l, x, y)
            tussen.append(t)
        result.append(tussen)
    return result

def get_value(list, i, j):
    new_i = len(list) - 1
    new_j = len(list[new_i]) - 1
    print(i, j, new_i, new_j)
    return list[new_i][new_j]


def get_neighbor_sum(list, i, j):
    sum = 0
    for offset_i in [-1, 0, 1]:
        for offset_j in [-1, 0, 1]:
            sum += get_value(list, i + offset_i, j + offset_j)
    return sum

but for some reason it only returns the original list, can someone explain me why and how to fix this?

import itertools

def get_neighbors(lst, point):
    x,y = point
    neighbors = []
    for offset_x, offset_y in itertools.product([-1, 0, 1], repeat=2):
        new_x, new_y = x + offset_x, y + offset_y
        try:
            neighbors.append(lst[new_y][new_x])
        except IndexError:
            continue
    return neighbors

This should give you all the values of the neighboring cells in lst with respect to the point point (which should be a tuple of the form (column, row) ). From there you should be able to iterate through your list and replace that element with sum(result)/len(result) .

for y, row in enumerate(lst):
    for x, _ in enumerate(row):
        result = get_neighbors(lst, (x, y))
        lst[y][x] = sum(result) / len(result)

You could even blur the results a bit more by changing your get_neighbors function to accept a parameter for how wide an offset to search and how strong a weight to give to each offset.

import itertools

def get_neighbors(lst, point, n=1, weights=None):
    if weights is None:
        weights = dict()
    x, y = point
    neighbors = []
    offsets = range(-n, n+1)
    for offset_x, offset_y in itertools.product(offsets, repeat=2)):
        new_x, new_y = x + offset_x, y + offset_y
        weight = weights.get(new_y, {}).get(new_x, 1)
        # defaults to 1.
        # Same as try: weights[new_y][new_x] except KeyError: 1
        try:
            neighbors.append(lst[new_y][new_x] * weight)
        except IndexError:
            continue
    return neighbors

This expects that weights is a dict of dicts that defines how much weight should be given to an offset x and y, such as:

{-1: {-1: 0.5,
       0: 1,
       1: 0.5},
  1: {-1: 0.5,
       0: 1,
       1: 0.5}}

Any missing keys are assumed to have full weight, so this would give half weight to the squares marked as X below

X O X
O O O
X O X

There might be a nicer implementation but I can't think of one off-hand :)

get_value() is supposed to check whether the indexes you are feeding the function are valid for the list. If they indexes are not valid, get_neighbor_sum() should not add them, In the corners, the average value could only be between four values. This is difficult to do with two functions, because we have to return a value for get_value() that is invalid (a float maybe), have the get_neighbor_sum() notice this and not add it...

I think that you don't need the function get_value() at all, and can make it easier by making a get_neighbor_average function:

def get_neighbor_average(list, i, j):
    sum = 0
    div = 0
    for offset_i in [-1, 0, 1]:
        for offset_j in [-1, 0, 1]:
            new_i = i + offset_i
            new_j = j + offset_j
            if (new_i >= 0 and new_j >= 0 and new_i < len(list) and new_j < len(list)):
                sum += list[new_i][new_j]
                div += 1
    avg = sum / div
    return avg

This gives you an average of all the neighbors, but is safe because it checks whether the list element we are trying to access is valid or not.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM