Pseudolosowe etykietki domowej roboty (ImageMagick)

Andrzej Galiński · 2021-07-17

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

czarny kwadrat, biały tekst

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

jasnoniebieski kwadrat, biały tekst

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 przez tr 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

jasnoniebieski kwadrat, czarny tekst

Ż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

losowo wygenerowane plakietki w kontrastowych kolorach

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.

źle skadrowany obrazek

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

obrazek odbity we wszystkich osiach

Tak przygotowany obrazek wycinam w programie graficznym, bo do tego służą programy graficzne ;)

skadrowany obrazek

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).