Próbuję znaleźć najszybsze podejście, aby przeczytać kilka obrazów z katalogu w tablicy numpy. Moim celem końcowym jest obliczenie statystyk takich jak maksymalny, minimalny i n-ty percentyl pikseli ze wszystkich tych obrazów. Jest to proste i szybkie, gdy piksele ze wszystkich obrazów znajdują się w jednej dużej tablicy typu numpy, ponieważ mogę korzystać z wbudowanych metod tablicowych, takich jak .max
i .min
oraz funkcja np.percentile
.Najszybsze podejście do czytania tysięcy obrazów w jedną dużą tablicę numpy
Poniżej przedstawiono przykładowe czasy z 25 obrazami tiff (512 x 512 pikseli). Te testy porównawcze są z używania %%timit
w notatniku Juwatera. Różnice są zbyt małe, by mieć praktyczne implikacje dla zaledwie 25 zdjęć, ale zamierzam przeczytać tysiące obrazów w przyszłości.
# Imports
import os
import skimage.io as io
import numpy as np
Dołączanie do listy
%%timeit imgs = [] img_path = '/path/to/imgs/' for img in os.listdir(img_path): imgs.append(io.imread(os.path.join(img_path, img))) ## 32.2 ms ± 355 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Korzystanie ze słownika
%%timeit imgs = {} img_path = '/path/to/imgs/' for img in os.listdir(img_path): imgs[num] = io.imread(os.path.join(img_path, img)) ## 33.3 ms ± 402 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
dla listy i słownik podejść wyżej Próbowałem zastępując pętlę z odpowiednie zrozumienie z symulacją ilar wyniki w czasie. Próbowałem również wstępnego przydzielenia kluczy słownika bez znaczącej różnicy w czasie wykonania. Aby uzyskać obrazy z listy do dużej tablicy, użyłbym np.concatenate(imgs)
, który zajmuje tylko ~ 1 ms.
Preallocating do numpy szeregu wzdłuż pierwszego wymiaru
%%timeit imgs = np.ndarray((512*25,512), dtype='uint16') img_path = '/path/to/imgs/' for num, img in enumerate(os.listdir(img_path)): imgs[num*512:(num+1)*512, :] = io.imread(os.path.join(img_path, img)) ## 33.5 ms ± 804 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Preallocating do numpy wzdłuż trzeciego wymiaru
%%timeit imgs = np.ndarray((512,512,25), dtype='uint16') img_path = '/path/to/imgs/' for num, img in enumerate(os.listdir(img_path)): imgs[:, :, num] = io.imread(os.path.join(img_path, img)) ## 71.2 ms ± 2.22 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
początkowo przemyślanej Podejrzane podejścia do wstępnej alokacji byłyby szybsze, ponieważ w pętli nie ma dynamicznej ekspansji zmiennej, ale wydaje się, że tak nie jest. Podejście, które uważam za najbardziej intuicyjne, to ostatnie, w którym każdy obraz zajmuje osobny wymiar wzdłuż trzeciej osi tablicy, ale jest on również najwolniejszy. Dodatkowy czas nie jest związany z samą alokacją, która trwa tylko ~ 1 ms.
mam trzy pytanie o to:
- Dlaczego preallocation numpy zbliża się nie szybciej niż słownik i lista rozwiązań?
- Jaka jest najszybsza metoda odczytywania tysięcy obrazów w jedną dużą tablicę?
- Czy mogę odnieść korzyści z patrzenia poza numpy i obraz scikit, aby jeszcze szybszy moduł do czytania w obrazach? Próbowałem
plt.imread()
, ale modułscikit-image.io
jest szybszy.
Czy próbowałeś zainicjować tablicę '(25, 512, 512)'? Pierwszy wymiar to zewnętrzny. 'np.array (imgs)' z pierwszego podejścia listy tworzy ten kształt. Większość z tego 33 ms czasu to ładunek, a nie magazyn. Aby to sprawdzić, próbuj ładować bez gromadzenia tablic. – hpaulj
Dzięki @hpaulj! Twoje wskazówki na temat zewnętrznych wymiarów i to, że większość czasu spędziłem z góry, były pomocne. Próbowałem z 300 tiffami o wyższej rozdzielczości (1024x1024 pikseli) i numpy podejście z zewnętrznym wymiarem (albo [300, 1024, 1024] albo [1024, 300, 1024]) jest teraz najszybsze (~ 1s). Drugi to lista i rozwiązania słownikowe (~ 1,7 s), a numpy wymiar wewnętrzny (?) [1024, 1024, 300] jest martwy ostatni (~ 4.6s). Jeśli dodasz odpowiedź, mogę dodać te testy porównawcze i je zaakceptować. –
@hpaulj Jeśli mógłbyś dodać link do bardziej szczegółowych informacji na temat tego, co oznacza, że pierwsze (i drugie?) Wymiary są zewnętrzne, naprawdę bym to docenił. Nie mogę znaleźć niczego istotnego w moich wyszukiwaniach. –