MC, 13:04 sobota, 28.04.2012 r.
Ilustracja do artykułu: Linux i masowa zmiana zdjęć

Linux i masowa zmiana zdjęć

Masowa konwersja plików graficznych na inne formaty to umiejętność bardzo przydatna niemal każdemu użytkownikowi komputera. W tym wpisie chciałbym pokazać, jak prosto zrobić to na systemach Linux. Będzie to też ładny (mam nadzieję) przykład na to jak wielki potencjał stanowi możność łączenia ze sobą różnych programów, co jest jakby jednym z filarów Linuksa.

Co nam będzie potrzebne?

Właściwie wszystko, czego będziemy potrzebować do masowej zmiany formatu plików graficznych, a czego w naszym systemie może jeszcze nie być, to program convert, który znajduje się w pakiecie imagemagick. Jeżeli więc nie mamy dostępu do tego polecenia, zaopatrzmy się w to narzędzie:
sudo apt-get install imagemagick

To jeszcze gwoli wyjaśnienia...

Program convert to istny kombajn. Jego umiejętności nie ograniczają się jedynie do konwersji zdjęć, a wybiegają znacznie dalej ponadto. I choć ten artykuł będzie dotykał jedynie problemu konwersji, to można to, co tutaj jest zapisane ekstrapolować na pozostałe opcje convert'a i w sposób masowy zmieniać rozmiary zdjęć, nakładać znaki wodne i wiele innych. W przyszłości planuję napisać jakiś artykuł o możliwościach tego programu i będzie to stanowiło niejako uzupełnienie do tego co można przeczytać tutaj.

To do rzeczy...

Należałoby teraz pokazać jak programu convert użyć. Składnia jest bardzo prosta:
convert plik_wejściowy -format format_pliku_wyjściowego plik_wyjściowy

Wiemy już, czego użyjemy do samego aktu konwersji zdjęć, pytanie jak uzyskać pożądaną masowość tej operacji? Z pomocą przyjdzie nam polecenie find, którego zalety też można by długo wychwalać. Jedną z większych jest jednak to, że możemy jego pracę wygodnie łączyć z innymi programami.

I w tym miejscu należałoby artykuł rozczłonować na dwa podejścia do wykorzystania polecenia find. Jedno z nich wiąże się z poleceniem xargs, o którym pisałem ostatnio (xargs - Przesyłanie standardowego wejścia jako parametry do programu) a dzięki któremu będziemy mieli możliwość przesłania wyników pracy polecenia find jako argumenty do polecenia convert.
Drugie podejście wykorzystuje możliwość, jaką daje już samo polecenie find polegające na wykonywaniu wskazanego programu dla każdego znalezionego pliku.

Czas na przykłady

Czas przejść do działania. Na potrzeby tego artykułu załóżmy, że mamy katalog ze zdjęciami PNG, które chcemy przekonwertować do JPG. Najpierw omówimy składnię z wykorzystaniem polecenia xargs. Aby dokonać masowej konwersji w zarysowanej sytuacji, należałoby wydać polecenie:
find . -name "*.png" | xargs -n 1 -I XXX convert XXX -format jpg XXX.jpg
Omówmy powyższe polecenie fragment po fragmencie. Pierwsza część 'find . -name "*.png"' jak nietrudno się domyślić, odpowiada za odnalezienie wszystkich plików o rozszerzeniu .png, które znajdują się w katalogu, w którym wydajemy to polecenie. Następnie znakiem | łączymy sygnał wyjściowy programu find ze strumieniem wejściowym polecenia xargs. W dalszej kolejności oznaczamy sobie nasz argument, czyli de facto nazwę pliku przez XXX i przekazujemy to do polecenia convert.

Drugie podejście jest w zasadzie całkiem podobne, tylko tak jak wspomniałem — nie użyjemy polecenia xargs jako pośrednika między find a convert.

find . -name "*.png" -exec convert {} -format jpg {}.jpg \;
Pierwsza część polecenia pozostaje niezmienna, dalej jednak pojawia się flaga -exec, która mówi o tym, jaki program chcemy wykonać. Warto tutaj wyjaśnić dwie rzeczy. Jak widzimy {} reprezentuje nam argument przesyłany przed find, czyli nazwę pliku, który obecnie chcemy przekonwertować. Bardzo ważne są ostatnie dwa znaki tego polecenia, czyli \; - mówią one, że w tym miejscu kończymy polecenie, które chcemy wykonać dla wyniku find. Backslash jest bardzo ważny i nie należy go pomijać!

Czy to wszystko?

Właściwie to można by na tym artykuł zakończyć. Zostaje jednak mały problem, mimo że masowo zmieniliśmy format zdjęć. Problemem tym jest ich nazwa. Zamiast nadać nową nazwę, z nowym rozszerzeniem, jedynie dopisaliśmy do starej nazwy, nowe rozszerzenie co w efekcie daje np. nazwę JakieśZdjęcie.png.jpg. Nie wygląda to specjalnie elegancko, dlatego wypadałoby jeszcze powiedzieć, jak masowo te nazwy zmienić na poprawne. Użyjmy polecenia rename:
rename 's/png.jpg$/jpg/' *.png.jpg

Co tutaj widzimy? Najpierw wybieramy wszystkie pliki kończące się na .png.jpg (*.png.jpg), a następnie przy pomocy wyrażenia regularnego wskazujemy, co z nazwami tych plików chcemy zrobić. Wyrażenie to wygląda mniej więcej tak: 'jak_traktować/co_zamienić/na_co_zamienić'. Czyli w naszym przypadku mówimy, że nazwy plików mamy traktować jako ciąg znaków (string), następnie, że interesuje nas zmiana png.jpg znajdującego się na końcu przesyłanej nazwy pliku, na jpg.

Na zakończenie...

To jeszcze na zakończenie tego wpisu, mała uwaga dla tych, którzy nie wiedzą jakie podejście do konwersji zastosować, z xargs, czy bez niego. Z przeprowadzonego przeze mnie testu, wynika, że rozwiązanie z -exec jest nieznacznie szybsze, więc można to traktować jak argument za tym podejściem.

Komentarze (4)

Imię:
Treść:
Jerzy 2022-02-23 22:06:26
Wszystko fajnie, tylko teraz jak zmienić masowo we wszystkich folderach nazwy plików? ta funkcja rename 's/png.jpg$/jpg/' *.png.jpg zmienia tylko w danym folderze
Administrator 2018-07-17 21:33:48
@Dariusz, dziękuję za uzupełnienie artykułu wartościowym komentarzem :)
Dariusz 2018-07-17 10:58:37
Przykładowy test przy kasowaniu 1000 plików pokazuje różnicę w szybkości działania. touch file{1..1000} time find . -name "*.png" -exec convert {} -format jpg {}.jpg \; real 0m5.028s user 0m0.188s sys 0m4.563s touch file{1..1000} time find . -type f -exec rm '{}' +; real 0m0.865s user 0m0.000s sys 0m0.859s
Dariusz 2018-07-17 10:55:44
Znacznie szybciej wykona się polecenie: find . -name "*.png" -exec convert {} -format jpg {}.jpg +;