Cel: Chcę mieć metodę szybkiego tworzenia łatwych do odróżnienia plakietek z kolorowym tłem i kilkuznakowym tekstem.
Wstęp
ImageMagick to popularny opensourcowy zestaw narzędzi do pracy z grafiką. Jest dość potężny i zarazem prosty w użyciu, choć ma trochę nieintuicyjną składnię. Warto nauczyć się paru komend, przydają się do wszelkich zautomatyzowanych operacji na obrazkach.
Więcej: https://imagemagick.org/
Do czego zazwyczaj używam ImageMagicka?
Do zmiany rozmiaru / orientacji obrazka:
convert a.png resize 100x100 a_mini.png
#...najczęściej w skryptach:
for f in *jpg; do
convert $f -rotate 90 -resize 50% res/$f
done
Do zmiany formatów:
convert a.png a.jpg
Do rekompresji JPGów:
convert ladny.jpg -quality 0.9 maly.jpg
Do dowiadywania się czegoś więcej o obrazkach:
identify -verbose foo.png
Do zamiany animowanych GIFów w serię obrazków i odwrotnie:
convert a.gif a.png
convert a*png b.gif
Do podpisywania obrazków:
convert krakow.jpg -resize 200x150 \
-strokewidth 2 -stroke black -fill white -font Helvetica-Bold \
-pointsize 30 -gravity north -annotate +0+10 "STRZEŻ SIĘ" \
-pointsize 45 -gravity south -annotate +0+0 "LAGUNA" \
lagun.jpg
Do składania siatek obrazków:
montage -geometry +1+1 -tile 4x res_*.png all.png
…a ostatnio także do robienia etykietek do użycia w charakterze favikon.
Krok po kroku
Najpierw prosta plakietka. Rysuję czarny kwadrat z zaokrąglonymi rogami o wymiarach 100x100 pikseli i białą etykietkę o treści “TEST”, wpisującą się w obszar 90x90 pikseli. Składam oba obrazki w jeden, wyśrodkowując je:
convert -size 90x90 -background none -gravity center -fill white -font Source-Sans-Pro-Bold label:TEST lbl.png
convert -size 100x100 xc:none -fill black -draw 'roundrectangle 0,0 100,100 20,20' bg.png
composite -gravity center lbl.png bg.png res.png
Chciałbym, żeby plakietki miały losowe kolory, zależne od treści. ImageMagick
przyjmuje różne określenia kolorów, w tym HEX (#fae36a
itp.); można to
wykorzystać, licząc sumę kontrolną md5 tekstu (znaki będą z zakresu 0-f i
dość dobrze wymieszane) i wycinając jego fragment jako definicję koloru:
$ echo "TEST" | md5sum
2debfdcf79f03e4a65a667d21ef9de14 -
$ echo "TEST" | md5sum | cut -b1-6
2debfd
Rysuję kolorową plakietkę:
txt="TEST"
convert -size 100x100 xc:none -fill "#$(echo "$txt" | md5sum | cut -b1-6)" -draw 'roundrectangle 0,0 100,100 20,20' bg1.png
convert -size 90x90 -background none -gravity center -fill white -font Source-Sans-Pro-Bold label:"$txt" lbl1.png
composite -gravity center lbl1.png bg1.png res1.png
Biały tekst na jasnoniebieskim tle. W niekorzystnym wypadku tło wyjdzie
jeszcze jaśniejsze, niemal białe - i nic nie będzie widać.
Ponieważ kolory tła są i tak “losowe”, dobrane kolory uzupełniające nie muszą
być szczególnie piękne; wystarczy mi, że jasne plakietki będą miały czarny
tekst, a jasne biały. Każdy kanał (czerwony, zielony, niebieski) może
przyjmować wartości od 00
do FF
, uśrednię je i sprawdzę, czy średnia jest
większa, czy mniejsza od połowy zakresu (7F = 127
).
Muszę utworzyć odpowiednie wyrażenie i wykorzystać bc
.
- kolor wygląda tak:
2debfd
bc
do obliczeń w układzie szesnastkowym potrzebuje wielkich liter. Można je uzyskać np. przepuszczając dane przeztr a-z A-Z
- podstawę układu liczbowego dla danych wejściowych wybiera się tak:
ibase=16
- chcę wyciąć z danych wejściowych po 2 znaki, mogę do tego użyć funkcji
substr
. Całe wyrażenie wygląda tak:
$ echo 2debfd | tr a-z A-Z | awk '{print "ibase=16; (" substr($1,1,2) "+" substr($1,3,2) "+" substr($1,5,2) ")/3 > 7F" }'
ibase=16; (2D+EB+FD)/3 > 7F
Wystarczy przekierować wynik do bc
(... | bc
) i gotowe.
Ale to nie koniec, bc
ma również wyrażenia warunkowe:
$ fg=$(echo 2debfd | tr a-z A-Z | awk '{print "ibase=16; if( (" substr($1,1,2) "+" substr($1,3,2) "+" substr($1,5,2) ")/3 > 7F ) \"black\" else \"white\"" }')
$ echo $fg
ibase=16; if( (2D+EB+FD)/3 > 7F ) "black" else "white"
$ echo $fg | bc
black
Po niewielkiej reorganizacji:
txt="TEST"
bgcolor=$(echo "$txt" | md5sum | cut -b1-6 | tr a-z A-Z)
fgcolor=$(echo $bgcolor | awk '{print "ibase=16; if( (" substr($1,1,2) "+" substr($1,3,2) "+" substr($1,5,2) ")/3 > 7F ) \"black\" else \"white\"" }' | bc)
convert -size 100x100 xc:none -fill "#$bgcolor" -draw 'roundrectangle 0,0 100,100 20,20' bg2.png
convert -size 90x90 -background none -gravity center -fill "$fgcolor" -font Source-Sans-Pro-Bold label:"$txt" lbl2.png
composite -gravity center lbl2.png bg2.png res2.png
Żeby pokazać, że automatyczny dobór kolorów naprawdę działa, wygeneruję kilka losowych etykietek:
for i in $(seq 1 12); do
txt=$(echo $RANDOM | md5sum | cut -b1-4)
bgcolor=$(echo "$txt" | md5sum | cut -b1-6 | tr a-z A-Z)
fgcolor=$(echo $bgcolor | awk '{print "ibase=16; if( (" substr($1,1,2) "+" substr($1,3,2) "+" substr($1,5,2) ")/3 > 7F ) \"black\" else \"white\"" }' | bc)
convert -size 100x100 xc:none -fill "#$bgcolor" -draw 'roundrectangle 0,0 100,100 20,20' bg_$txt.png
convert -size 90x90 -background none -gravity center -fill "$fgcolor" -font Source-Sans-Pro-Bold label:"$txt" lbl_$txt.png
composite -gravity center lbl_$txt.png bg_$txt.png res_$txt.png
rm lbl_$txt.png bg_$txt.png
done
montage -geometry +1+1 -tile 4x res_*.png all.png
Bonus - rozszerzanie obrazka przez odbicia lustrzane
Czasem fajne zdjęcie jest źle skadrowane: aż się prosi, żeby mieć dodatkowy kawałek tła, żeby móc rozszerzyć kadr.
Na pewno są lepsze metody poradzenia sobie z tym problemem, ale ja stosuję taką, jeśli tło pozwala:
convert jedzenie.jpg \
\( -clone 0 -flop -clone 0 \) +append \
\( -clone 0 -flip -clone 0 \) -flip -flop -append \
jedzenie3x3.jpg
Tak przygotowany obrazek wycinam w programie graficznym, bo do tego służą programy graficzne ;)
Rozmaitości
- żeby szybko obejrzeć obrazek wynikowy, można go zapisać na wyjście standardowe jako GIF i przekierować strumień do
display
:
convert rose: gif:- | display
- Jakie czcionki mam dostępne w systemie?
$ convert -list font
Path: /etc/ImageMagick-6/type-ghostscript.xml
Font: AvantGarde-Book
family: AvantGarde
style: Normal
stretch: Normal
weight: 400
glyphs: /usr/share/fonts/type1/gsfonts/a010013l.pfb
Font: AvantGarde-BookOblique
family: AvantGarde
(...)
# Trochę za dużo tekstu, interesują mnie same nazwy.
$ convert -list font | awk -F': ' '/Font: /{print $2}'
AvantGarde-Book
AvantGarde-BookOblique
AvantGarde-Demi
AvantGarde-DemiOblique
Bookman-Demi
(...)
- Do zarządzania zdjęciami używam Digikama, a do prostej edycji grafiki GIMPa (rastrowa) lub Inkscape’a (wektorowa).