Как выполнить быстрое срезание нулевого края в Python?

У меня есть двоичное изображение размером 256x256x256, где область переднего плана расположена в небольшой области, и у меня много нулевых полей. Я хочу обрезать нулевые края, найдя минимальную и максимальную координаты точек, в изображениях которых есть ненулевой пиксель. Это сработало, но на это уходит много времени. Я отправляю свой код и не могли бы вы сказать мне, как мы можем сделать его быстрее?

Для размера образа 256x256x256 требуется около 0,13024640083312988 секунды. Это код, и вы можете запустить его онлайн по адресу https://repl.it/repls/AnxiousExoticBackup

import numpy as np
import time

def cut_edge(image, keep_margin):
    '''
    function that cuts zero edge
    '''
    D, H, W = image.shape
    D_s, D_e = 0, D - 1
    H_s, H_e = 0, H - 1
    W_s, W_e = 0, W - 1

    while D_s < D:
        if image[D_s].sum() != 0:
            break
        D_s += 1
    while D_e > D_s:
        if image[D_e].sum() != 0:
            break
        D_e -= 1
    while H_s < H:
        if image[:, H_s].sum() != 0:
            break
        H_s += 1
    while H_e > H_s:
        if image[:, H_e].sum() != 0:
            break
        H_e -= 1
    while W_s < W:
        if image[:, :, W_s].sum() != 0:
            break
        W_s += 1
    while W_e > W_s:
        if image[:, :, W_e].sum() != 0:
            break
        W_e -= 1

    if keep_margin != 0:
        D_s = max(0, D_s - keep_margin)
        D_e = min(D - 1, D_e + keep_margin)
        H_s = max(0, H_s - keep_margin)
        H_e = min(H - 1, H_e + keep_margin)
        W_s = max(0, W_s - keep_margin)
        W_e = min(W - 1, W_e + keep_margin)

    return int(D_s), int(D_e)+1, int(H_s), int(H_e)+1, int(W_s), int(W_e)+1

image = np.zeros ((256,256,256),dtype=np.float32)
ones_D_min, ones_D_max, ones_H_min, ones_H_max,ones_W_min, ones_W_max= 100,200, 90,150, 60,200
image[ones_D_min: ones_D_max,ones_H_min:ones_H_max, ones_W_min:ones_W_max]=1
t0=time.time()
ones_D_min_result, ones_D_max_result, ones_H_min_result, ones_H_max_result, ones_W_min_result, ones_W_max_result= cut_edge(image,0)
t1=time.time()
print ('Time consuming ', t1-t0)
print (ones_D_min, ones_D_max, ones_H_min, ones_H_max,ones_W_min, ones_W_max)
print (ones_D_min_result, ones_D_max_result, ones_H_min_result, ones_H_max_result, ones_W_min_result, ones_W_max_result)
Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
T - 1Bits: Генерация последовательного массива
T - 1Bits: Генерация последовательного массива
По мере того, как мы пишем все больше кода, мы привыкаем к определенным способам действий. То тут, то там мы находим код, который заставляет нас...
Что такое деструктуризация массива в JavaScript?
Что такое деструктуризация массива в JavaScript?
Деструктуризация позволяет распаковывать значения из массивов и добавлять их в отдельные переменные.
2
0
202
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ваша функция может быть значительно улучшена с помощью встроенных функций numpy:

def cut_edge(image, keep_margin):
    '''
    function that cuts zero edge
    '''

    #Calculate sum along each axis
    D_sum = np.sum(image, axis=(1,2)) #0
    H_sum = np.sum(image, axis=(0,2)) #1
    W_sum = np.sum(image, axis=(0,1)) #2

    #Find the non-zero values
    W_nz = np.nonzero(W_sum)[0]
    H_nz = np.nonzero(H_sum)[0]
    D_nz = np.nonzero(D_sum)[0]

    #Take the first and last entries for start and end
    D_s = D_nz[0]
    D_e = D_nz[-1]
    H_s = H_nz[0]
    H_e = H_nz[-1]
    W_s = W_nz[0]
    W_e = W_nz[-1]


    if keep_margin != 0:
        D_s = max(0, D_s - keep_margin)
        D_e = min(D - 1, D_e + keep_margin)
        H_s = max(0, H_s - keep_margin)
        H_e = min(H - 1, H_e + keep_margin)
        W_s = max(0, W_s - keep_margin)
        W_e = min(W - 1, W_e + keep_margin)

    return D_s, D_e+1, H_s, H_e+1, W_s, W_e+1

И результат:

Time consuming  0.0963144302368164
Ответ принят как подходящий

Вы можете использовать тот факт, что если вы используете sum по оси вашего 3D-массива, тогда значение все равно будет 0, если в строке (или столбце или третьем измерении) нет 1, в зависимости от того, какой параметр axis. Затем, используя any в одном из двух других направлений и np.argwhere, вы получите индекс, в котором на другой оси стоит хотя бы одна 1. Использование min и max даст то значение, которое вы ищете. Вот функция:

def cut_edge_2(image, keep_margin):
    im_sum0 = (image.sum(0) !=0)
    im_sum1 = (image.sum(1) !=0)
    ones_D = np.argwhere(im_sum1.any(1))
    ones_H = np.argwhere(im_sum0.any(1))
    ones_W = np.argwhere(im_sum0.any(0))
    if keep_margin != 0:
        D, H, W = image.shape
        return (max( 0, ones_D.min() - keep_margin), min(D, ones_D.max() + keep_margin+1), 
                max( 0, ones_H.min() - keep_margin), min(H, ones_H.max() + keep_margin+1),
                max( 0, ones_W.min() - keep_margin), min(W, ones_W.max() + keep_margin+1))
    return (ones_D.min(), ones_D.max() +1, 
            ones_H.min(), ones_H.max() +1,           
            ones_W.min(), ones_W.max() +1)

Вы получите тот же результат, что и с вашей функцией:

print (cut_edge(image,0))
#(100, 200, 90, 150, 60, 200)
print (cut_edge_2(image,0))
#(100, 200, 90, 150, 60, 200)

print (cut_edge(image,60))
#(40, 256, 30, 210, 0, 256)
print (cut_edge_2(image,60))
#(40, 256, 30, 210, 0, 256)

и немного timeit:

%timeit cut_edge(image,0)
#93 ms ± 7.62 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit cut_edge_2(image,0)
#25.3 ms ± 8.4 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit cut_edge_2(image,1)
#26.2 ms ± 4.73 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit cut_edge(image,1)
#95.4 ms ± 6.63 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Это быстрее.

Идеально!. Позвольте мне проверить свой компьютер

John 29.11.2018 18:25

Т: Я обнаружил одну ошибку. результат неверен с настройкой. Давай проверим на repl.it/repls/AnxiousExoticBackup

John 30.11.2018 23:21

@John действительно, это была плохая копия и вставка. Я исправил это в ответе. В min это всегда был D, а не H, а затем W, как предполагалось для каждого направления. Простите за это

Ben.T 01.12.2018 21:00

Другие вопросы по теме