Wróć do katalogu
Season 32 5 Odcinki 21 min 2026

scikit-image: The AI Image Pipeline

v0.26 — Edycja 2026. 5-odcinkowy kurs na temat wykorzystania scikit-image v0.26 (Edycja 2026) jako głównego silnika do preprocessingu obrazów i augmentacji danych w nowoczesnych pipeline'ach AI i deep learning.

Przetwarzanie obrazu Nauka o danych Widzenie komputerowe
scikit-image: The AI Image Pipeline
Teraz odtwarzane
Click play to start
0:00
0:00
1
Pipeline obrazów w AI: NumPy jako fundament
Odkryj, jak scikit-image reprezentuje obrazy jako ndarrays w NumPy. Dowiedz się, dlaczego ten projekt czyni go idealnym silnikiem do preprocessingu dla frameworków deep learningowych, takich jak PyTorch i TensorFlow.
4m 18s
2
Mówiąc tym samym językiem: Dtypes i OpenCV
Opanuj typy danych obrazów, aby zapobiec najczęstszym cichym błędom w wizji komputerowej. Dowiedz się, jak płynnie zintegrować scikit-image z OpenCV i wejściami sieci neuronowych.
4m 22s
3
Kontrast, ekspozycja i odporność AI
Dowiedz się, jak używać korekcji kontrastu i wyrównywania histogramu do standaryzacji zbiorów danych. Te techniki są kluczowe, aby modele AI były odporne na zmienne warunki oświetleniowe.
4m 22s
4
Przekształcenia geometryczne do augmentacji danych
Sprawdź, jak zmieniać rozmiar obrazów, aby dopasować je do wejść sieci neuronowych, oraz jak stosować przekształcenia afiniczne. Jest to niezbędne do budowania solidnych pipeline'ów augmentacji danych.
4m 01s
5
Klasyczna segmentacja jako punkt wyjścia dla AI
Odkryj, jak użyć klasycznej segmentacji watershed, aby automatycznie generować idealne maski treningowe dla modeli deep learningowych, oszczędzając godziny ręcznego etykietowania.
4m 03s

Odcinki

1

Pipeline obrazów w AI: NumPy jako fundament

4m 18s

Odkryj, jak scikit-image reprezentuje obrazy jako ndarrays w NumPy. Dowiedz się, dlaczego ten projekt czyni go idealnym silnikiem do preprocessingu dla frameworków deep learningowych, takich jak PyTorch i TensorFlow.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. scikit-image: The AI Image Pipeline, odcinek 1 z 5. Zanim model deep learningowy będzie w stanie rozpoznać twarz, musi przetworzyć siatkę liczb. Jeśli podasz tę siatkę w złej kolejności, twój model niczego się nie nauczy, a przetwarzanie zwolni do ślimaczego tempa. The AI Image Pipeline: NumPy at the Core to rozwiązanie, które pozwala poprawnie ustrukturyzować tę siatkę. Kiedy ładujesz obraz za pomocą scikit-image, nie dostajesz żadnego własnościowego obiektu obrazu. Dostajesz standardowy N-wymiarowy array, znany jako ndarray. Obraz to po prostu macierz pikseli. Ponieważ jest to zwykły array w NumPy, możesz użyć dowolnej standardowej operacji, żeby bezpośrednio robić slice'y, maskować lub manipulować danymi obrazu. Oto kluczowa sprawa. Sposób, w jaki scikit-image indeksuje te arraye, sprawia problemy wielu developerom. W standardowej geometrii używasz współrzędnych x i y, gdzie x to poziom, a y to pion. scikit-image rezygnuje z tego na rzecz notacji macierzowej. Pierwszy indeks to wiersz, który odpowiada pozycji pionowej. Drugi indeks to kolumna, która odpowiada pozycji poziomej. Jeśli masz kolorowy obraz, trzeci indeks to kanał koloru. Standardowy dwuwymiarowy kolorowy obraz to tak naprawdę trójwymiarowy array uporządkowany jako wiersz, kolumna, kanał. Załóżmy, że przygotowujesz batch kolorowych obrazów, żeby przekazać je do konwolucyjnej sieci neuronowej. Twoja sieć oczekuje określonego shape'u. Jeśli twój obraz ma 256 pikseli wysokości i 256 pikseli szerokości z kanałami czerwonym, zielonym i niebieskim, shape pojedynczego obrazu to 256, 256, 3. Ale batch takich obrazów dodaje na początku czwarty wymiar, reprezentujący liczbę obrazów. Kiedy stosujesz funkcje scikit-image do tych danych, funkcja musi wiedzieć, który wymiar zawiera wartości kolorów, żeby nie przetwarzała ich jako danych przestrzennych. Odpowiada za to argument channel_axis. Ustawiając argument channel_axis na minus jeden, mówisz funkcji, że kanały kolorów zawsze znajdują się w ostatnim wymiarze arraya. To gwarantuje, że funkcja odniesie się do właściwych danych kolorów, niezależnie od tego, czy przekażesz jej pojedynczy obraz, czy duży batch z dodatkowymi wymiarami przestrzennymi. To prowadzi nas do pamięci. To jest ta część, która ma znaczenie dla szybkości przetwarzania. Arraye w NumPy są przechowywane w ciągłych blokach pamięci, domyślnie używając kolejności w stylu C. Oznacza to, że ostatni wymiar w shape'ie arraya zmienia się najszybciej w pamięci fizycznej. W naszym standardowym arrayu obrazu składającym się z wiersza, kolumny i kanału, poszczególne kanały kolorów dla pojedynczego piksela siedzą tuż obok siebie w twoim sprzętowym RAM-ie. Jeśli piszesz customowy kod, żeby loopować po tych pikselach, musisz respektować ten układ pamięci. Zasada jest bezwzględna: skrajny prawy wymiar twojego arraya powinien być przetwarzany w najbardziej wewnętrznej pętli. Iterujesz po wierszach w zewnętrznej pętli, potem po kolumnach, a wewnątrz po kanałach. Jeśli to odwrócisz i będziesz loopować po wierszach wewnątrz, zmusisz CPU do skakania tam i z powrotem po pofragmentowanych adresach pamięci. To niszczy cache locality i sprawia, że twój kod działa znacznie wolniej. Najważniejszą rzeczą do zapamiętania jest to, że scikit-image nie używa współrzędnych poziomych i pionowych; używa ścisłego indeksowania macierzowego wierszy, kolumn i kanałów, a dopasowanie twoich pętli do tej dokładnej kolejności w pamięci to coś, co utrzymuje szybkość twojego data pipeline'u. Jeśli chcesz pomóc w utrzymaniu tego podcastu, możesz wesprzeć program, wyszukując DevStoriesEU na Patreonie. To wszystko w tym odcinku. Dzięki za słuchanie i buduj dalej!
2

Mówiąc tym samym językiem: Dtypes i OpenCV

4m 22s

Opanuj typy danych obrazów, aby zapobiec najczęstszym cichym błędom w wizji komputerowej. Dowiedz się, jak płynnie zintegrować scikit-image z OpenCV i wejściami sieci neuronowych.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. scikit-image: The AI Image Pipeline, odcinek 2 z 5. Najczęstszy cichy błąd w AI do computer vision nie leży w architekturze sieci neuronowej. Jest nim niedopasowanie typów danych. Wrzucasz obraz do modelu, kod przechodzi bez rzucania błędami, ale na wyjściu dostajesz kompletne śmieci. Dzieje się tak, gdy twoje biblioteki fundamentalnie nie zgadzają się co do tego, jak piksel powinien być reprezentowany liczbowo. Dzisiaj to naprawimy, omawiając temat Mówienie tym samym językiem: dtypes i OpenCV. W scikit-image obrazy są przechowywane jako standardowe tablice numpy. Ale liczby wewnątrz tych tablic zachowują się inaczej w zależności od ich dokładnego typu danych. Domyślny format z kamer internetowych i standardowych plików graficznych to ośmiobitowy unsigned integer, znany jako uint8. Te wartości idą od zera do 255, gdzie zero to absolutna czerń, a 255 to maksymalna intensywność. Jednak naukowe funkcje do przetwarzania obrazu i frameworki do deep learningu prawie zawsze preferują liczby zmiennoprzecinkowe. W scikit-image, obraz typu float oczekuje, że intensywności pikseli będą przeskalowane do ścisłego przedziału, zazwyczaj od zera do jednego. Oto kluczowa sprawa. Nie używaj metody astype z numpy do konwersji twoich obrazów uint8 na floaty. Jeśli weźmiesz tablicę uint8 i po prostu wywołasz astype float, numpy zmieni tylko typ pamięci pod spodem. Jasny piksel o wartości 255 stanie się po prostu 255 kropka zero. To nie przeskaluje wartości. Jeśli przekażesz taką tablicę do funkcji scikit-image albo modelu PyTorch, który oczekuje maksymalnej jasności na poziomie jeden kropka zero, obliczenia się sypią. Twoje biele są nagle traktowane tak, jakby były 255 razy jaśniejsze niż maksymalna możliwa wartość. Zamiast tego, musisz użyć wbudowanych funkcji narzędziowych ze scikit-image. Najważniejsza z nich nazywa się img_as_float. Ta funkcja sprawdza wejściowy typ danych i automatycznie zajmuje się matematycznym przeskalowaniem. Bezpiecznie kompresuje zakres uint8 od zera do 255 w dół, do precyzyjnego zakresu float od zera do jeden kropka zero. Typy danych to tylko połowa sukcesu. Musisz też dopasować kanały kolorów, zwłaszcza jeśli przechwytujesz wideo. Jeśli wczytasz klatkę używając OpenCV, dostaniesz tablicę numpy typu uint8. Ale OpenCV ma pewną historyczną zaszłość. Przechowuje kanały kolorów w kolejności Blue Green Red, znanej jako BGR. scikit-image i większość współczesnych modeli AI oczekuje Red Green Blue, czyli RGB. Jeśli zapomnisz zamienić kanały, czerwone jabłka będą wyglądać na niebieskie, a ludzka skóra będzie wyglądać zupełnie obco. Nie musisz wywoływać ciężkiej funkcji do konwersji kolorów z OpenCV, żeby to naprawić. Ponieważ obraz to po prostu tablica numpy, możesz użyć podstawowego slicingu tablicy. Robisz slice tablicy przez jej trzy wymiary. Bierzesz wszystkie wiersze, wszystkie kolumny, a następnie dla ostatniego wymiaru reprezentującego kanały kolorów, podajesz krok minus jeden. To mówi numpy, żeby przeszedł wstecz przez kanały, natychmiastowo odwracając BGR na RGB w pamięci. Przejdźmy przez kompletny pipeline wejściowy dla modelu deep learningowego. Najpierw pobierasz klatkę ze strumienia wideo OpenCV, co daje ci tablicę BGR typu uint8. Następnie odwracasz kanały kolorów używając slicingu z numpy, żeby przekonwertować ją na RGB. Potem przekazujesz tę tablicę do img_as_float. Tablica jest teraz macierzą zmiennoprzecinkową, idealnie przeskalowaną między zerem a jedynką. Na koniec konwertujesz tę czystą tablicę numpy na tensor w PyTorch. Sieć neuronowa nie powie ci, kiedy jej dane wejściowe są źle przeskalowane, po prostu uczy się złych wzorców albo wywala się po cichu. Kontrolowanie typów danych i kolejności kanałów już na etapie wczytywania danych zapewnia, że twój pipeline opiera się na solidnych matematycznych fundamentach. To wszystko w tym odcinku. Dzięki za wysłuchanie i buduj dalej!
3

Kontrast, ekspozycja i odporność AI

4m 22s

Dowiedz się, jak używać korekcji kontrastu i wyrównywania histogramu do standaryzacji zbiorów danych. Te techniki są kluczowe, aby modele AI były odporne na zmienne warunki oświetleniowe.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. scikit-image: The AI Image Pipeline, odcinek 3 z 5. Sieci neuronowe świetnie radzą sobie ze znajdowaniem wzorców, ale z radością zapamiętają konkretne oświetlenie pomieszczenia zamiast obiektu, który chcesz, żeby wykrywały. Aby to naprawić, musimy ustandaryzować wariancję w naszych datasetach poprzez kontrast, ekspozycję i AI Robustness. Weźmy pod uwagę dataset medycznych zdjęć rentgenowskich zebranych z kilkunastu różnych szpitali. Skany pochodzą z różnych maszyn o skrajnie różnych kalibracjach. Niektóre obrazy są ciemne i zamazane, podczas gdy inne są prześwietlone i jasne. Jeśli wrzucisz te surowe dane do sieci neuronowej, prawdopodobnie zrobi ona overfit do warunków oświetleniowych konkretnych skanerów, zamiast nauczyć się rozpoznawać faktyczną patologię. Musisz znormalizować ekspozycję przed rozpoczęciem treningu. Pierwszym krokiem w tej standaryzacji jest często usunięcie nieistotnych informacji. Jeśli liczy się tylko struktura, kolor tylko rozprasza. Możesz użyć funkcji o nazwie rgb to gray, aby przekonwertować kolorowe obrazy na jednokanałowe arraye w skali szarości. Zmniejsza to wymiarowość twoich danych i zmusza model do oceny czystej luminancji. Kiedy pracujesz już wyłącznie z luminancją, musisz wyrównać bazową jasność w całym swoim datasecie. I tutaj wkracza funkcja rescale intensity. Ta funkcja wykonuje liniowe rozciągnięcie danych twojego obrazu. Bierze najciemniejszy piksel i mapuje go na najniższą możliwą wartość, na przykład zero, a najjaśniejszy piksel mapuje na najwyższą możliwą wartość, na przykład dwieście pięćdziesiąt pięć. Każdy piksel pomiędzy jest skalowany liniowo. I tu pojawia się kluczowy wniosek. Proste rozciągnięcie od minimum do maksimum jest bardzo podatne na błędy. Pojedynczy martwy, czarny piksel albo jasny artefakt od drobinki kurzu na matrycy zdominuje całą skalę. Rozciągnięcie skompresuje twoje właściwe dane anatomiczne do wąskiego, bezużytecznego pasma szarości, tylko po to, by uwzględnić ten jeden skrajny outlier. Aby to rozwiązać, używasz percentile clipping. Zamiast rozciągać od absolutnego minimum i maksimum, obliczasz drugi i dziewięćdziesiąty ósmy percentyl wartości pikseli na twoim obrazie. Następnie przekazujesz te percentyle do funkcji rescale intensity jako swój zakres wejściowy. Funkcja odetnie skrajne dwa procent jasnych i ciemnych pikseli, ustawiając je na czystą biel i czystą czerń, a następnie liniowo rozciągnie pozostałe dziewięćdziesiąt sześć procent danych. To gwarantuje, że większość twoich danych strukturalnych wykorzysta pełny zakres dynamiczny, całkowicie ignorując losowe artefakty. Czasami liniowe rozciągnięcie to za mało. Możesz mieć zdjęcie rentgenowskie, na którym dane zostały zarejestrowane, ale wszystkie piksele są skupione wokół kilku konkretnych odcieni szarości, przez co obraz wygląda płasko, a detale są zamazane. W tym celu używasz funkcji equalize hist, co oznacza wyrównanie histogramu. Wyrównanie histogramu to proces nieliniowy. Zamiast po prostu rozciągać granice, analizuje częstotliwość każdej wartości piksela na obrazie. Następnie rozkłada najczęściej występujące wartości intensywności na całe dostępne spektrum. Jeśli duża część twojego zdjęcia rentgenowskiego utknęła w wąskim paśmie ciemnych szarości, wyrównanie histogramu rozciągnie te szarości, przypisując im nowe wartości, od czerni do bieli. To sztucznie podbija lokalny kontrast, ujawniając subtelne tekstury i granice, które wcześniej były ukryte w zamazanych obszarach skanu. Standaryzacja twojego datasetu za pomocą tych technik gwarantuje, że twój model oceni rzeczywisty kształt i teksturę obiektu. Solidny pipeline usuwa nieistotną wariancję kalibracji sprzętowej, zmuszając sieć neuronową do uczenia się sygnału, a nie szumu. Dzięki za spędzenie ze mną tych kilku minut. Do usłyszenia następnym razem, trzymaj się.
4

Przekształcenia geometryczne do augmentacji danych

4m 01s

Sprawdź, jak zmieniać rozmiar obrazów, aby dopasować je do wejść sieci neuronowych, oraz jak stosować przekształcenia afiniczne. Jest to niezbędne do budowania solidnych pipeline'ów augmentacji danych.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. scikit-image: The AI Image Pipeline, odcinek 4 z 5. Konwolucyjna sieć neuronowa może idealnie rozpoznać kota. Ale odwróć tego kota do góry nogami albo przesuń o trzy piksele w lewo, a model nagle staje się całkowicie ślepy. Naprawisz to za pomocą transformacji geometrycznych do data augmentation. Zanim zrobimy jakikolwiek data augmentation, musimy przygotować baseline. Sieci neuronowe wymagają stałego kształtu inputu. Ogarniesz to używając funkcji resize w scikit-image. Przekazujesz jej obraz i docelowe wymiary outputu, a ona matematycznie rozciąga lub kurczy tablicę pikseli, żeby ją dopasować, automatycznie interpolując nowe wartości pikseli. Ale sam resize sprawia, że twój model jest podatny na overfitting. Zapamięta dokładnie, gdzie obiekty znajdują się w kadrze. Aby temu zapobiec, budujesz pipeline do data augmentation, który w locie aplikuje losowe transformacje do obrazów treningowych. W scikit-image transformacje przestrzenne manipulują przestrzenią współrzędnych obrazu za pomocą matematyki na macierzach. Przesunięcie lub obrót obrazu oznacza pomnożenie współrzędnych jego pikseli przez macierz transformacji. I tu jest kluczowa sprawa. Translacji, czyli przesunięcia obrazu wzdłuż osi X lub Y, nie policzysz za pomocą standardowego mnożenia macierzy dwa na dwa. Mnożenie macierzy ogarnia skalowanie i rotację, ale przesunięcie wymaga dodawania. Aby to rozwiązać, scikit-image używa współrzędnych jednorodnych. Dodając sztuczną trzecią współrzędną, jedynkę, do każdego punktu dwuwymiarowego, system ulepsza matematykę. Pozwala to na jednoczesne policzenie translacji, rotacji i skalowania jako pojedynczego mnożenia macierzy trzy na trzy. Nie musisz pisać tych macierzy trzy na trzy ręcznie. scikit-image dostarcza klasy transformacji, które zrobią tę matematykę za ciebie. W naszym pipeline anty-overfittingowym używasz klasy transformacji Euclidean. Transformacja Euclidean zachowuje odległości i kąty, co oznacza, że obsługuje tylko rotację i translację. Inicjalizujesz ją, przekazując kąt rotacji i wektor translacji. Jeśli potrzebowałbyś dodać shearing albo zmienić skalę, przeszedłbyś na transformację Affine. Jeśli musiałbyś zasymulować zmianę perspektywy, użyłbyś transformacji Projective. Ale do losowych obrotów i przesunięć, Euclidean jest dokładnie tym, czego potrzebujesz. Kiedy twoja macierz transformacji jest już zdefiniowana, musisz zaaplikować ją do swojego obrazu. Zrobisz to używając funkcji warp. Przekazujesz do funkcji warp swój obraz wejściowy i obiekt transformacji Euclidean. I tu robi się ciekawie. Funkcja warp nie oblicza, gdzie piksele z inputu powinny trafić na nowym obrazie. Jeśli wypchniesz piksele do przodu na nową siatkę, matematyka rotacji stworzy ułamkowe współrzędne. Kiedy zaokrąglą się one do najbliższego pełnego piksela, skończysz z brakującymi pikselami, czyli dziurami rozsianymi po twoim outpucie. Zamiast tego, warp działa wstecz. Bierze odwrotność twojej macierzy transformacji. Sprawdza każdą pustą współrzędną piksela w docelowym outpucie, mapuje ją wstecz do oryginalnej przestrzeni obrazu i interpoluje prawidłową wartość koloru. To odwrotne mapowanie gwarantuje solidny output bez żadnych dziur. Dla twojego pipeline'u do data augmentation logika jest prosta. Wygeneruj losowy kąt i losowy zestaw przesunięć. Przekaż je do transformacji Euclidean. Wrzuć tę transformację i swój obraz treningowy do funkcji warp. Output trafia prosto do twojej sieci neuronowej. Transformacje geometryczne nie tylko tworzą więcej danych treningowych; zmuszają twój model do oddzielenia obiektu, który ma rozpoznać, od przypadkowych współrzędnych, które akurat zajmuje. To wszystko w tym odcinku. Dzięki za wysłuchanie i budujcie dalej!
5

Klasyczna segmentacja jako punkt wyjścia dla AI

4m 03s

Odkryj, jak użyć klasycznej segmentacji watershed, aby automatycznie generować idealne maski treningowe dla modeli deep learningowych, oszczędzając godziny ręcznego etykietowania.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. scikit-image: The AI Image Pipeline, odcinek 5 z 5. Modele segmentacji oparte na deep learningu są niesamowicie potężne, ale też niesamowicie głodne. Wymagają tysięcy pixel-perfect, narysowanych przez człowieka masek, zanim w ogóle będą mogły się czegokolwiek nauczyć. Klasyczna segmentacja do bootstrapowania AI wypełnia tę lukę. Trenujesz nowoczesnego U-Neta do wykrywania komórek lub monet, które się ze sobą stykają. Potrzebujesz etykiet ground-truth. Ręczne rysowanie tych masek zajmuje tygodnie. Możesz spróbować odpalić prosty detektor krawędzi, taki jak Canny, żeby zautomatyzować ten proces. Canny świetnie radzi sobie ze znajdowaniem ostrych przejść, ale często nie potrafi zamknąć pętli. Daje ci pofragmentowane kontury, a nie spójne regiony. AI wytrenowane na przerywanych konturach będzie zwracać przerywane kontury. Segmentacja oparta na regionach rozwiązuje ten problem. A konkretnie algorytm Watershed. Traktuje on twój obraz jak topograficzny krajobraz. Wysokie wartości pikseli to góry, a niskie to doliny. I tu jest kluczowa sprawa. Zamiast próbować łączyć przerwane krawędzie, Watershed zalewa obraz od znanych punktów startowych, aż woda spotka się na najwyższych grzbietach. To gwarantuje zamknięte, spójne regiony. Najpierw budujesz teren. Robisz to, generując mapę wysokości przy użyciu filtra Sobela. Filtr Sobela oblicza gradienty przestrzenne, uwydatniając krawędzie. Kiedy zaaplikujesz go do swojego obrazu, granice między nakładającymi się na siebie monetami stają się wysokimi grzbietami na twojej mapie. Płaskie powierzchnie monet i tło stają się dolinami. Następnie umieszczasz markery. Musisz powiedzieć algorytmowi, gdzie woda powinna zacząć się podnosić. Jeśli to pominiesz, algorytm zaleje obraz z każdego najmniejszego lokalnego minimum i rozbije twój obraz na setki bezużytecznych fragmentów. Tworzysz array markerów o dokładnie takim samym rozmiarze jak twój oryginalny obraz. Znajdujesz pewne tło, wybierając piksele poniżej określonego progu intensywności i przypisujesz im wartość jeden. Następnie znajdujesz pewny pierwszy plan, czyli spójne środki monet, wybierając piksele powyżej wyższego progu intensywności. Przypisujesz im wartość dwa. Na koniec odpalasz zalewanie. Przekazujesz swoją mapę wysokości i array markerów do funkcji watershed z modułu segmentation w scikit-image. Algorytm wypełnia regiony, zaczynając od jedynek i dwójek. W miarę jak symulowana woda się podnosi, regiony się rozszerzają. Kiedy w końcu spotkają się na wysokich grzbietach mapy wysokości Sobela, algorytm buduje granicę. Funkcja zwraca integer array z idealnie oetykietowanymi, zamkniętymi regionami. Stykające się obiekty są rozdzielone dokładnie na granicy. Masz teraz czysty array masek, w którym każda moneta jest odrębnym, spójnym obiektem. Możesz puścić ten pipeline przez cały swój nieoetykietowany dataset, żeby automatycznie wygenerować tysiące masek. Następnie wrzucasz te maski bezpośrednio do swojego U-Neta jako dane treningowe ground-truth. Bootstrapowanie AI nie wymaga ludzkiej pracy, jeśli wiesz, jak połączyć klasyczną logikę terenu z zakresu image processing z nowoczesnymi architekturami modeli. Zachęcam cię do przejrzenia oficjalnej dokumentacji scikit-image i spróbowania zbudowania mapy wysokości hands-on, a jeśli masz pomysł na naszą kolejną serię, wpadnij na devstories.eu i daj mi znać. To wszystko w tym odcinku. Dzięki za słuchanie i buduj dalej!