Сжатие бинарного массива в 4 раза

Есть бинарный массив α (из 0 и 1) размером AxA.
Хочу получить новый бинарный массив β размером BxB.
B в 2 раза меньше A, следовательно массив BxB в 4 раза меньше, чем AxA.

Правило следующее:

  • "Пиксель" (x;y) массива β равен единице, если единице равен хотя бы один из следующих элементов массива α: (2x; 2y), (2x+1; 2y), (2x; 2y+1), (2x+1; 2y+1)

Имею такой код:

def compress_4_times (canvas):
    new_size = 16
    field = [[0]*new_size]*new_size

    for y in range(0, new_size):
        for x in range(0, new_size):
            _x, _y = 2*x, 2*y
            count_active = 0

            if is_pixel_active(canvas, _x, _y):
                count_active += 1
            if is_pixel_active(canvas, _x+1, _y):
                count_active += 1
            if is_pixel_active(canvas, _x, _y+1):
                count_active += 1
            if is_pixel_active(canvas, _x+1, _y+1):
                count_active += 1

            if count_active > 0:
                field[x][y] = True
            else:
                field[x][y] = False

    return field

На вход подается массив α, возвращаться должен β. В данном примере размер α - 32.

Проблема: По ходу выполнения цикла все работает нормально, я проверял принтами: некоторые пиксели становятся True, и судя по всему даже правильные.
Но функция возвращает массив с одними False! В ней нет ни одного True!

Такое ощущение, что после завершения цикла, field почему-то сбрасывается. Почему такое может быть?

Надеюсь, я правильно вывел правило перевода. Например, я хочу превратить такое:

 0 0 0 1
 0 1 1 0
 0 0 0 1
 0 0 1 1

в такое:

 1 1
 0 1

Ответы (2 шт):

Автор решения: CrazyElf

Эта проблема уже много раз обсуждалась тут, нужно только уметь её распознать. Замените инициализацию списка списков на такую:

field = [[0] * new_size for _ in range(new_size)]

Проблема вашей инициализации в том, что второе умножение там не создаёт новые списки, а клонирует ссылку на один и тот же список много раз. Из-за этого любые изменения в одном из списков тут же отражаются во всех остальных списках - ведь это один и тот же список.

А вот с первым умножением проблем нет, потому что 0 - это скаляр и при его "размножении" создаётся нормальный список из значений, а не из ссылок.

Проверки ваши, кстати, можно сильно сократить (и ускорить - проверки будут выполняться не все, а до первой успешной):

            field[x][y] = any((
                is_pixel_active(canvas, _x, _y),
                is_pixel_active(canvas, _x+1, _y),
                is_pixel_active(canvas, _x, _y+1),
                is_pixel_active(canvas, _x+1, _y+1)
            ))

А вообще операция, которую вы делаете, называется pooling, есть такой штатный слой в нейросетях, в частности в библиотеке Keras.

→ Ссылка
Автор решения: SergFSM

можно еще попробовать такой способ сжатия:

matr = [[0,0,0,1,0,0],
        [1,0,0,1,0,1],
        [0,0,1,1,0,0],
        [0,0,1,0,0,0],
        [0,0,0,1,0,1],
        [0,0,0,1,0,0]
       ]
matr2=[]

for i in range(0,len(matr),2):
    m = [x or y for x,y in zip(*matr[i:i+2])]
    matr2.append([x or y for x,y in zip(m[::2],m[1::2])])
print(matr2)

результат:

[[1, 1, 1], 
 [0, 1, 0], 
 [0, 1, 1]]

UPD

для других степеней сжатия придется этот код частично переписать. Например для сжатия в 9 раз получится так:

matr = [[0,0,0,1,0,0],
        [1,0,0,1,0,1],
        [0,0,1,1,0,0],
        [0,0,0,0,0,0],
        [0,0,0,1,0,1],
        [0,0,0,1,0,0]
       ]
matr2=[]

for i in range(0,len(matr),3):
    m = [x or y or z for x,y,z in zip(*matr[i:i+3])]
    matr2.append([x or y or z for x,y,z in zip(m[::3],m[1::3],m[2::3])])
print(matr2)

результат:

[[1, 1], 
 [0, 1]]
→ Ссылка