Mastodon
Zdjęcie okładkowe wpisu Skrypt w fish, kompresujący 130 tysięcy zdjęć w ImageOptim automatycznie (cz. II)

Skrypt w fish, kompresujący 130 tysięcy zdjęć w ImageOptim automatycznie (cz. II)

8
Dodane: 7 lat temu

Przedwczoraj wspominałem o instalacji fisha i Homebrew, a wczoraj o pierwszej wersji skryptu, który miał być punktem wyjścia do jego drugiej, finalnej wersji. Nie podkreśliłem wystarczająco bardzo, że całość miała nie tylko służyć do skompresowania plików, ale poznania też syntaxu fisha. Więc podkreślam.

Wersja 2

Pisałem wczoraj, że ImageOptim w takiej sytuacji jest bardzo nieefektywny – nie potrafi wykorzystać potencjału procesora – ani jego rdzeni, ani wątków. Zdecydowałem się na zapuszczenie pętli, która będzie otwierała po 20 plików.

#!/usr/local/bin/fish

# Send all files in specified folder to ImageOptim for compression, 20 at a time.
# @morid1n

# Usage: imagecompress2.sh <full folder path>

if set -q argv
    set imagefilelist (find $argv -type f)
    echo File paths loaded.
    set imagefilelistcounter (count $imagefilelist)
    set imagefilelistcounterleft (count $imagefilelist)
    echo Files found: $imagefilelistcounter
    set i 1
    set k 20
    while math "$imagefilelistcounterleft > 0"
        /Applications/ImageOptim.app/Contents/MacOS/ImageOptim $imagefilelist[$i..(math "$i + $k - 1")]
        set imagefilelistcounterleft (math $imagefilelistcounter-(math "$i + $k"))
        if math "$imagefilelistcounterleft < $k"
            set i (math "$imagefilelistcounter - $k")
        else
            set i (math "$i + $k")
        end
        echo Files left: $imagefilelistcounterleft
    end
else
    echo "Usage: imagecompress2.sh <full folder path>"
end

Przy okazji posprzątałem po wczoraj i dodałem argumenty, aby skrypt działał na wybranym przez użytkownika folderze (czyli na wszystkich plikach znajdujących się w tym folderze oraz subfolderach; ImageOptim odrzuca pliki, które nie są grafikami sam z siebie). Komenda do jego uruchomienia wygląda następująco:

./imagecompress2.sh "/Przykładowa/ścieżka/do/folderu/"

Względem wczorajszej wersji, największą zmianą jest przejście z pętli for na while, która działa tak długo, dopóki licznik pozostałych plików jest większy od zera. Sama pętla uruchamia ImageOptim z argumentem, w którym podaję 20 pierwszych plików (można to zmienić za pomocą zmiennej $k). Niestety, taka konstrukcja wymaga też sprawdzania czy nie próbuję załadować plików spoza listy, stąd if else w skrypcie.

Wersja 3

Łukasz Motyka dzisiaj rano zwrócił moją uwagę na komendę xargs – nie wiedziałem, że można w niej precyzować ilość plików ani wątków, które ma wywołać. To zdecydowanie uprościło skrypt. Łukasz też sugerował komendę exec, którą znałem, ale ona nie rozwiązywała problemu (1) nauki składni fisha oraz (2) uruchamiania więcej niż jednego pliku jednocześnie. xargs już tak.

#!/usr/local/bin/fish

# Send all files in specified folder to ImageOptim for compression, 20 at a time in 1 thread.
# @morid1n

# Usage: imagecompress3.sh <full folder path>

if set -q argv
    find $argv -type f -print0 | xargs -0 -P1 -n20 /Applications/ImageOptim.app/Contents/MacOS/ImageOptim
else
    echo "Usage: imagecompress3.sh <full folder path>"
end

Powyższy skrypt robi dokładnie to samo co ten wklejony na początku, ale ogranicza kod z linii 9-24 do jednej komendy na linii 9 – argument -P1xargs otwiera jedną instancję ImageOptim, do której wrzuca 20 plików z listy, za pomocą argumentu -n20.

W ramach testów wydajności skryptu, sprawdziłem też jak się sprawdza z czterem instancjami ImageOptim (zmiana argumentu -P1 na -P4):

#!/usr/local/bin/fish

# Send all files in specified folder to ImageOptim for compression, 20 at a time in 4 threads.
# @morid1n

# Usage: imagecompress3.sh <full folder path>

if set -q argv
    find $argv -type f -print0 | xargs -0 -P4 -n20 /Applications/ImageOptim.app/Contents/MacOS/ImageOptim
else
    echo "Usage: imagecompress3.sh <full folder path>"
end

Wydajnosć – test 1

Wygenerowałem 1240 plików typu JPG – 1000 px na długim boku, każdy ważący około 120 KB – i stworzyłem z nich cztery niezależne foldery. Następnie, na każdym z tych folderów, przepuściłem każdy z czterech skryptów. Wyniki wyglądają następująco:

  • imagecompress.sh – 769,48 s
  • imagecompress2.sh – 83,35 s
  • imagecompress3.sh (1 instancja) – 82,33 s
  • imagecompress4.sh (4 instancje) – 62,19 s

Jak można było się spodziewać, pierwsza wersja skryptu jest beznadziejna – blisko dziesięciokrotnie wolniejsza od jego docelowej, drugiej wersji. Uproszenie skryptu z 28 do 12 linii niewiele zmieniło pod względem wydajności – 1 sekunda może być wynikiem tego, że system coś robił w tle. Odpalenie czterech instancji po 20 plików oszczędziło natomiast 20 sekund – znaczący wzrost. Co ciekawe, w tym ostatnim przypadku w Terminalu pojawiały się błędy sypane przez ImageOptim – jakieś problemy z przeniesieniem plików do kosza – które ostatecznie jednak nie wpłynęły na kompresję plików, która została wykonana prawidłowo.

Nie zadowoliły mnie te wyniki – poszedłem na skróty. Jak wspominałem wczoraj, pliki PNG kompresują się wielokrotnie dłużej niż JPG-i, więc powyższy test nie odzwierciedla warunków rzeczywistych. Idealnie też przetestowałbym różne liczby instancji ImageOptim z różną liczbą załadowanych plików do każdej z nich, np. 20, 40, 100 itd.

Cwany traci dwa razy, więc przygotowałem drugi test, chociaż nadal nie tak precyzyjny, jakbym chciał.

Wydajność – test 2

Tym razem wygenerowałem folder składający się z 400 PNG i 400 JPG. PNG mają 500 px na długim boku i ważą po 139 KB. JPG są większe – 1000 px i 222 KB. To daje w sumie 800 plików. Dlaczego 50/50 PNG vs. JPG? Tak mniej więcej rozkładają się grafiki na serwerze iMagazine.

  • imagecompress.sh (1 instancja po 1 pliku) – 1569,45 s
  • imagecompress2.sh (1 instancja po 20 plików) – 306,69 s
  • imagecompress3.sh (1 instancja po 20 plików) – 304,01 s
  • imagecompress4.sh (4 instancje po 20 plików) – 275,32 s
  • imagecompress5.sh (8 instancji po 100 plików) – 266,37 s

Wyniki są jednak bardzo zbliżone do pierwszego testu (i widać jak bardzo PNG wszystko wydłużają), z tą różnicą, że 8 instancji po 100 plików okazało się jeszcze szybsze – to mnie zaskoczyło. Procesor w tym czasie był oczywiście obciążony w 100%…

Teraz jestem ciekawy, co by się stało, gdybym załadował jeszcze więcej instancji… Więc to zrobiłem. 16 instancji po 50 plików. Niestety, ImageOptim się wysypał z błędem. Oops.

A jak z grafikami na iMagazine.pl?

Po pierwsze, w tytule pisałem o 130 tys. plików. Mój błąd. Było ich ponad 200 tysięcy, licząc od 2012 roku. Oryginalnie całość zajmowała 28 GB. Po skompresowaniu całości udało się odzyskać 13 GB – całość teraz zajmuje 15 GB. Istotna różnica, pomimo, że nie osiągnąłem spodziewanego wyniku w rejonie 13-14 GB – wpłynął na to prawdopodobnie fakt, że od ponad roku w większości kompresujemy zdjęcia wrzucane na serwer, więc zostały one pominięte przez ImageOptim.

Font w Terminalu

Dla tych z Was, którzy są ciekawi jakiego fontu monospaced używam… Po pierwsze, podpowiem, że używam kilku na zmianę (co kilka miesięcy), jak któryś mi się znudzi. Ulubione to Consolas, który jest częścią BBEdit1, Operator Mono lub systemowy SF Mono. O tych dwóch ostatnich pisałem na łamach Weekly.

SF Mono i Operator Mono – moje dwie miłości

  1. Mają wzorowy slogan dla tego app: „It doesn’t suck.”

Wojtek Pietrusiewicz

Wydawca, fotograf, podróżnik, podcaster – niekoniecznie w tej kolejności. Lubię espresso, mechaniczne zegarki, mechaniczne klawiatury i zwinne samochody.

Zapraszamy do dalszej dyskusji na Mastodonie lub Twitterze .

Komentarze: 8

Ech gdybym miał więcej chęci i czasu to wygenerowałbym chętnie więcej przypadków. Dzięki. 🙂

Znam, widziałem, ale nie chciałem się w to pakować. Krzysiek też mi zabronił to odpalać na serwerze iMaga. 😆

A czy jest jakiś dodatek do aplikacji Zdjęcia na Macu który optymalizowałby zdjęcia? Tak żeby można było wybrać zdjęcie, zmniejszyć jego rozmiar i żeby wszystko wskoczyło do iCloud.

Nie chcesz tego robić – oryginałów nigdy nie odzyskasz. Można taki skrypt stworzyć.

Bardzo chcę tak robić. Nie potrzebuję zdjęcia paragonu ważącego 3MB a takich “technicznych” zdjęć mam dużo. Muszę zgłębić temat.

Takie to wrzucaj do Evernote który robi OCR na paragonach. Techniczne trzymaj gdzie indziej, np. Google Photos, Dropbox, itp. Rozdziel je od zwykłych.