Самый оптимизированный способ фильтрации позиций патча на изображении - PullRequest
0 голосов
/ 27 декабря 2018

Таким образом, моя проблема заключается в следующем: у меня есть изображение RGB в виде массива значений numpy (4086, 2048, 3), я разделил это измерение изображения на патчи 96x96 и вернул позиции этих патчей в массиве numpy,Я всегда получаю 96х96 патчей в каждом случае.Если размеры изображения не позволяют мне создавать «чистые» патчи размером 96x96 по оси x или y, я просто добавляю к ним отступ слева, чтобы последние патчи немного перекрывались с патчем перед ним.

Теперь, имея эти позиции в руках, я хочу избавиться от всех патчей 96x96, для которых значение RGB составляет 255 во всех трех каналах для каждого пикселя в патче, в самый быстрый способ из возможных , и я хочучтобы вернуть все патчи позиции , которые не имеют этого значения.

Я хотел бы знать:

  1. Какой самый быстрый способ извлечь96x96 исправляет позиции из размера изображения?(на данный момент у меня есть цикл for)
  2. Как избавиться от чистых белых пятен (со значением 255 на 3 каналах) наиболее подходящим способом ?(на данный момент у меня есть цикл for)

У меня есть много таких изображений для обработки таким образом, с разрешением изображений, доходящим до (39706, 94762, 3), поэтому мой цикл for становится быстро неэффективным.Спасибо за вашу помощь!(Я беру решения, которые тоже используют графический процессор)

Вот псевдокод, чтобы дать вам представление о том, как это делается на данный момент:

patches = []
patch_y = 0
y_limit = False
slide_width = 4086
slide_height = 2048
# Lets imagine this image_slide has 96x96 patches which value is 255
image_slide = np.random.rand(slide_width, slide_height, 3)
while patch_y < slide_height:
    patch_x = 0
    x_limit = False
    while patch_x < slide_width:
        # Extract the patch at the given position and return it or return None if it's 3 RGB
        # channels are 255
        is_white = PatchExtractor.is_white(patch_x, patch_y, image_slide)

        # Add the patches position to the list if it's not None (not white)
        if not is_white:
            patches.append((patch_x, patch_y))

        if not x_limit and patch_x + crop_size > slide_width - crop_size:
            patch_x = slide_width - crop_size
            x_limit = True
        else:
            patch_x += crop_size
    if not y_limit and patch_y + crop_size > slide_height - crop_size:
        patch_y = slide_height - crop_size
        y_limit = True
    else:
        patch_y += crop_size

return patches

В идеале, я бы хотелполучить свои позиции патчей за пределами цикла for, а затем, получив их, я могу проверить, являются ли они белыми или нет вне цикла for , а также с меньшим количеством возможных вызовов numpy (поэтому код обрабатывается вслой C NumPy и не идет вперед и назад к питону)

1 Ответ

0 голосов
/ 27 декабря 2018

Как вы и подозревали, вы можете векторизовать все, что вы делаете.Это занимает примерно целое число, кратное потребности памяти вашего исходного изображения.Алгоритм довольно прост: добавьте изображение так, чтобы в нем поместилось целое число патчей, разрезайте его на патчи, проверьте, все ли белые патчи, оставьте остальные:

import numpy as np                                                                    

# generate some dummy data and shapes
imsize = (1024, 2048)
patchsize = 96
image = np.random.randint(0, 256, size=imsize + (3,), dtype=np.uint8)
# seed some white patches: cut a square hole in the random noise
image[image.shape[0]//2:3*image.shape[0]//2, image.shape[1]//2:3*image.shape[1]//2] = 255

# pad the image to necessary size; memory imprint similar size as the input image
# white pad for simplicity for now
nx,ny = (np.ceil(dim/patchsize).astype(int) for dim in imsize) # number of patches
if imsize[0] % patchsize or imsize[1] % patchsize:
    # we need to pad along at least one dimension
    padded = np.pad(image, ((0, nx * patchsize - imsize[0]),
                            (0, ny * patchsize - imsize[1]), (0,0)),
                    mode='constant', constant_values=255)
else:
    # no padding needed
    padded = image

# reshape padded image according to patches; doesn't copy memory
patched = padded.reshape(nx, patchsize, ny, patchsize, 3).transpose(0, 2, 1, 3, 4) 
# patched is shape (nx, ny, patchsize, patchsize, 3)
# appending .copy() as a last step to the above will copy memory but might speed up
# the next step; time it to find out

# check for white patches; memory imprint the same size as the padded image
filt = ~(patched == 255).all((2, 3, 4))
# filt is a bool, one for each patch that tells us if it's _not_ all white
# (i.e. we want to keep it)

patch_x,patch_y = filt.nonzero() # patch indices of non-whites from 0 to nx-1, 0 to ny-1
patch_pixel_x = patch_x * patchsize  # proper pixel indices of each pixel
patch_pixel_y = patch_y * patchsize
patches = np.array([patch_pixel_x, patch_pixel_y]).T
# shape (npatch, 2) which is compatible with a list of tuples

# if you want the actual patches as well:
patch_images = patched[filt, ...]
# shape (npatch, patchsize, patchsize, 3),
# patch_images[i,...] is an image with patchsize * patchsize pixels

Как вы можетесм. выше, я использовал белые отступы для получения конгруэнтного дополненного изображения.Я считаю, что это соответствует философии того, что вы пытаетесь сделать.Если вы хотите повторить то, что вы делаете в цикле точно , вы можете дополнить свое изображение вручную, используя перекрывающиеся пиксели, которые вы бы учли у края.Вам нужно выделить мягкое изображение правильного размера, а затем вручную нарезать перекрывающиеся пиксели исходного изображения, чтобы задать краевые пиксели в дополнительном результате.


Поскольку вы упомянули, что вашИзображения огромны, и, следовательно, заполнение приводит к чрезмерному использованию памяти, вы можете избежать заполнения с помощью некоторого количества локтевой смазки.Вы можете использовать фрагменты своего огромного изображения (которое не создает копию), но тогда вам придется вручную обрабатывать края, где у вас нет полных фрагментов.Вот как это делается:

def get_patches(img, patchsize):
    """Compute patches on an input image without padding: assume "congruent" patches

    Returns an array shaped (npatch, 2) of patch pixel positions"""
    mx,my = (val//patchsize for val in img.shape[:-1])
    patched = img[:mx*patchsize, :my*patchsize, :].reshape(mx, patchsize, my, patchsize, 3)
    filt = ~(patched == 255).all((1, 3, 4))
    patch_x,patch_y = filt.nonzero() # patch indices of non-whites from 0 to nx-1, 0 to ny-1
    patch_pixel_x = patch_x * patchsize  # proper pixel indices of each pixel
    patch_pixel_y = patch_y * patchsize
    patches = np.stack([patch_pixel_x, patch_pixel_y], axis=-1)
    return patches

# fix the patches that fit inside the image
patches = get_patches(image, patchsize)

# fix edge patches if necessary
all_patches = [patches]
if imsize[0] % patchsize:
    # then we have edge patches along the first dim
    tmp_patches = get_patches(image[-patchsize:, ...], patchsize)
    # correct indices
    all_patches.append(tmp_patches + [imsize[0] - patchsize, 0])
if imsize[1] % patchsize:
    # same along second dim
    tmp_patches = get_patches(image[:, -patchsize:, :], patchsize)
    # correct indices
    all_patches.append(tmp_patches + [0, imsize[1] - patchsize])
if imsize[0] % patchsize and imsize[1] % patchsize:
    # then we have a corner patch we still have to fix
    tmp_patches = get_patches(image[-patchsize:, -patchsize:, :], patchsize)
    # correct indices
    all_patches.append(tmp_patches + [imsize[0] - patchsize, imsize[1] - patchsize])

# gather all the patches into an array of shape (npatch, 2)
patches = np.vstack(all_patches)

# if you also want to grab the actual patch values without looping:
xw, yw = np.mgrid[:patchsize, :patchsize]
patch_images = image[patches[:,0,None,None] + xw, patches[:,1,None,None] + yw, :]
# shape (npatch, patchsize, patchsize, 3),
# patch_images[i,...] is an image with patchsize * patchsize pixels

Это также точно скопирует ваш зацикливающийся код, поскольку мы явно берем патчи с краями так, чтобы они перекрывались с предыдущими патчами (нет никаких ложных белых отступов).Если вы хотите, чтобы патчи были в заданном порядке, вам придется их отсортировать сейчас.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...