Contoh penggunaan ImageMagick -- kuantisasi warna dan dithering
- Kata pengantar dan indeks contoh penggunaan ImageMagick
- Pengantar reduksi warna (apa saja yang terlibat)
- Warna-warna dalam sebuah gambar (warna apa saja yang dipakai sebuah gambar)
- Mengekstrak warna gambar
-
Kuantisasi warna (mengurangi jumlah warna dalam sebuah gambar)
- Kuantisasi warna dan ruang warna
- Kuantisasi tidak mempertahankan warna
-
Kuantisasi warna dan transparansi
Dithering koreksi error (atau dithering pseudo-acak)
- Cara kerja E-Dither
- E-Dither peka terhadap perubahan
- Bintik piksel E-Dither
- Gambar bitmap monokrom hasil dithering
- Kuantisasi dua warna
- Dither memakai peta warna terdefinisi
- Peta warna bersama atau 'terbaik'
- Memakai warna web-safe
- Peta warna seragam
- Peta warna 332 seragam
- Peta warna TrueColor 16 bit atau 556
- Peta warna seragam terkoreksi gamma
-
Dither acak dengan ambang batas
Dither pola teratur (memakai peta ambang batas yang di-tile)
- Dither halftone digital
- Dithering halftone dengan offset
- Peta ambang batas XML
- Ordered dither dengan tingkat warna seragam
-
Hasil ordered dither yang lebih baik
Pola dither dan peta ambang batas DIY (men-dither gambar dengan cara Anda sendiri)
- Peta ambang batas ordered dither DIY
- Dither garis horizontal DIY
- Dithering dengan pola simbol
Mengurangi jumlah warna atau mengganti warna tertentu adalah langkah yang sangat rumit dan sulit dalam pemrosesan gambar, dan itulah topik yang dibahas di halaman contoh ini. Termasuk di dalamnya menentukan warna apa saja yang akan dipakai (kuantisasi warna), dan bagaimana menempatkan warna-warna itu pada gambar (dithering dan pembuatan pola). Termasuk juga pembuatan gambar bitmap atau gambar dua warna, bahkan penanganan transparansi Boolean (on/off). Hal ini begitu penting sampai-sampai reduksi atau kuantisasi warna sering terjadi otomatis di balik layar, semata agar ImageMagick dapat menjalankan tugas utamanya yang semula: mengonversi gambar dari satu format file ke format lain yang warnanya lebih terbatas, seperti format GIF, XPixmap, dan XBitmap. Dengan memahami cara kerjanya, proses ini dapat dikendalikan lebih jauh, sehingga gambar hasil yang tersimpan dalam format file tertentu menjadi lebih baik.
Pengantar reduksi warna
Reduksi warna adalah aspek yang sangat penting dalam ImageMagick. Misalnya, untuk mengubah gambar JPEG atau PNG yang memuat jutaan warna menjadi gambar GIF yang paling banyak memuat 256 warna, Anda benar-benar harus bisa mengurangi warna secara efisien dan efektif. Sering kali hal ini terjadi otomatis di balik layar saat konversi format gambar, tetapi ada juga saatnya Anda ingin melakukannya sendiri. Mengurangi jumlah warna dalam gambar biasanya berlangsung dalam tiga langkah,
- Pertama, biasanya perlu dilakukan survei atas warna yang dipakai sebuah gambar. Bukan hanya untuk melihat berapa banyak warna yang benar-benar terpakai, melainkan juga seberapa sering sebuah warna tertentu dipakai. Tidak ada gunanya mempertahankan satu warna tertentu bila hanya satu piksel yang memakainya, meski kadang hal itu tetap perlu dilakukan.
- Berikutnya, kumpulan warna akhir yang akan membatasi gambar itu harus ditentukan. Bisa jadi Anda ingin IM mencoba menentukan kumpulan warna 'terbaik' untuk sebuah gambar tertentu. Di lain waktu, mungkin yang dibutuhkan adalah sesuatu yang lebih umum dan global sehingga bisa dipakai pada gambar apa pun. Bisa juga Anda ingin menambah atau membuang satu warna tertentu dari kumpulan warna yang akan dipakai.
- Dan terakhir, gambar itu harus diubah agar hanya memakai warna yang sudah dipilih. Sebaiknya hasilnya enak dipandang, atau mungkin yang diinginkan adalah hasil yang mudah dikompres, dibandingkan, atau dioptimalkan.
Yang membuat perkara ini makin rumit, ketiga langkah tadi kerap saling terkait, sebab satu metode penggantian warna sering hanya bisa diterapkan dengan kumpulan warna tertentu. Dan bila kumpulan warna itu sudah ditetapkan, survei warna tidak lagi diperlukan, atau mungkin justru perlu dibuat pengecualian untuk warna tertentu. Pada dasarnya, meski reduksi warna sering ditangani otomatis di balik layar, ada baiknya setidaknya menyadari apa yang sedang terjadi dan apa akibatnya.
Survei warna
Inilah yang barangkali paling tidak penting, dan meski IM menyediakan berbagai cara untuk melakukan survei, pengguna jarang melakukannya demi reduksi warna. Pembahasan selanjutnya saya serahkan ke bagian yang bersangkutan, Mengekstrak warna gambar.
Pemilihan warna (kuantisasi)
Untuk gambaran awal yang baik, lihat Wikipedia, Color Quantization. Ada empat metode dasar untuk memilih warna. Keempat metode kendali warna itu: Kuantisasi, Peta warna terdefinisi, Warna seragam, dan Ambang batas; semuanya punya keterbatasan masing-masing, seperti akan Anda lihat. Berikut contoh dari masing-masing keempat metode tersebut...
magick colorwheel.png +dither -colors 32 color_quantize.gif
magick colorwheel.png +dither -remap colortable.gif color_predefined.gif
magick colorwheel.png +dither -posterize 3 color_uniform.gif
magick colorwheel.png \
-separate -threshold 50% -combine color_threshold.gif
Jumlah warna pada tiap gambar akhir hanyalah kumpulan yang mewakili, kira-kira 32 warna untuk tiap kasus (kecuali ambang batas yang hanya punya 8). Dari sini Anda bisa membayangkan apa yang bisa diharapkan dari masing-masing metode. Semua metode lainnya memakai kumpulan warna yang tetap (sesuai argumen operatornya), tidak peduli gambar apa yang sedang direduksi warnanya. Hanya metode pertama ("-colors") yang benar-benar memilih warna berdasarkan isi gambar yang sedang diolah. Karena gambar uji ini didominasi putih, banyak warna terang yang terpilih. Metode ini menyurvei warna dalam gambar dengan teknik yang dikenal sebagai "Adaptive Spatial Subdivision" memakai oct-tree. Lalu ia berupaya memilih kumpulan warna tertentu yang paling cocok untuk sebuah gambar, dalam batas yang diberikan. Lihat Operator kuantisasi warna di bawah. Dengan "-remap" Anda dapat memberikan kumpulan warna terdefinisi milik sendiri kepada IM (lihat Peta warna buatan pengguna). Peta warna "colortable.gif" yang dipakai di atas adalah kumpulan 32 warna yang dipilih khusus untuk sebuah X Window Icon Library lama dan dirancang dengan ikon bergaya kartun dalam benak. (Lihat AIcon Library, X Icon Color Selection untuk rinciannya). "-posterize" juga dapat membagi tiap kanal warna secara matematis menjadi sejumlah tingkat atau intensitas warna sehingga menghasilkan 'peta warna seragam'. Artinya, sebuah peta warna dengan tiap kanal disetel ke sekumpulan nilai atau intensitas yang tetap. Dan terakhir, "-threshold" dapat diterapkan pada seluruh atau sebagian kanal warna gambar, yang pada dasarnya membuat tiap kanal warna menjadi murni Boolean alias on/off. Artinya, tiap kanal warna dapat diberi nilai nol atau MaxRGB (bergantung pada level 'Q' IM). Namun ini hanya menghasilkan kumpulan minimal sekitar 8 warna. Kumpulan warna yang sangat terbatas. Ambang batas juga setara dengan "-posterize" level '1' yang memilih 2 warna.
Menerapkan sebuah kumpulan warna
Setelah kumpulan warna tersedia, persoalan berikutnya adalah menerapkan warna itu pada gambar sehingga warna yang ada digantikan oleh kumpulan warna yang dipilih. Proses ini dikenal sebagai 'dithering', dan dinamai begitu karena sifatnya yang serba "pilih ini, atau pilih itu?". Pada dasarnya gagasan dithering adalah menempatkan piksel dengan warna berbeda saling berdekatan sedemikian rupa sehingga mata tertipu dan melihat lebih banyak warna dalam gambar daripada yang sebenarnya dipakai. Artinya, warna di area gambar itu jadi lebih mendekati warna asli gambar, karena cara mata manusia 'melebur' warna-warna yang bertetangga. Salah satu pengantar dithering terbaik ada di Wikipedia, meski bagian 'Audio Dithering' di awalnya perlu dilewati. Di situ tersaji kumpulan contoh yang sangat bagus tentang manfaat memakai pola piksel ber-dither ketika kumpulan warna yang tersedia terbatas. Gaya dasar penggantian warna mencakup...
- Pemetaan warna langsung (ambang batas dan posterisasi)
- Dither acak (penempatan piksel yang murni acak)
- Dither koreksi error (pola piksel pseudo-acak)
- Ordered dither piksel terdifusi (pola piksel yang teratur)
- Halftone digital (titik dengan ukuran berbeda-beda)
Pemetaan langsung ke warna terdekat dalam kumpulan yang diberikan adalah apa yang ditampilkan di atas. Pada dasarnya yang didapat adalah area-area warna polos yang jelas batasnya dan tidak berubah. Bila ini diterapkan pada gambar yang warnanya berubah perlahan, seperti foto langit sungguhan, akan muncul pita-pita warna di sepanjang gambar, terutama di bagian yang semestinya berupa gradien warna yang halus, seperti area langit. Hasilnya biasanya dianggap kurang bagus. Pemetaan warna langsung umumnya baru dianggap layak untuk logo, simbol, ikon, dan gambar bergaya kartun. Jadi ia sebenarnya jarang jadi pilihan. Karena itulah metode dithering yang biasa umumnya harus dimatikan bila Anda tidak ingin memetakan warna secara langsung pada gambar. Namun dither punya masalahnya sendiri. Begitu sebuah gambar di-dither, pola warna menjadi bagian dari gambar itu. Begitu pola semacam itu hadir, ia sangat sulit dihilangkan. Menerapkan dithering berulang kali pada sebuah gambar juga umumnya ide buruk, sebab hal itu hanya menurunkan mutu gambar. Karena itu, kebanyakan contoh kuantisasi di bawah akan menunjukkan cara membuat versi tanpa dither untuk tiap teknik. Cara itu dipakai supaya terlihat pilihan warna apa yang diambil sebelum dithering menyembunyikan informasi tersebut. Dithering acak adalah metode dithering paling sederhana yang pernah dibuat. Ia juga dianggap sebagai metode dithering paling buruk. Meski begitu ada beberapa kegunaan khususnya. Di dalam IM ia hanya bekerja dengan dua warna, jadi biasanya terbatas pada dithering bitmap untuk kasus khusus. Selengkapnya lihat Dither acak dengan ambang batas di bawah. Dithering koreksi error umumnya dianggap metode terbaik untuk men-dither warna pada gambar secara umum, sebab ia menghasilkan pendekatan yang paling dekat dengan warna asli tiap area gambar. Ia juga satu-satunya metode saat ini yang dapat men-dither kumpulan warna apa pun, sehingga dapat dipakai untuk keempat teknik reduksi warna. Lihat Cara kerja E-Dither di bawah untuk rinciannya. Namun dithering koreksi error punya masalah yang serius, terutama menyangkut animasi gambar. Dua teknik dithering terakhir, ordered dither piksel terdifusi dan halftone digital, juga dianggap metode yang bagus dan bekerja baik untuk animasi, tetapi saat ini keduanya tidak bisa memakai sembarang kumpulan warna, hanya kumpulan warna seragam yang tetap. Teknik ini memberi cara mewarnai gambar memakai pola, sehingga memungkinkan efek menarik yang selain dengan cara itu sulit dihasilkan.
Semua aspek reduksi warna ini adalah teknik penting, dan dengan memahaminya hasil operasi gambar Anda bisa diperbaiki, melampaui bawaan umum yang disediakan IM. Ini sungguh layak dipelajari.
Warna-warna dalam sebuah gambar
Informasi tentang gambar, seperti jumlah warna yang dipakai dan sebarannya secara keseluruhan, bisa sangat penting bagi program dan skrip yang berupaya memutuskan teknik terbaik yang akan dipakai. Di sini saya menengok beberapa metode yang bisa dipakai untuk memperoleh informasi semacam itu, dan bukan hanya untuk reduksi warna.
Mengekstrak warna gambar
Mengekstrak tabel warna
Palet warna sebuah gambar dapat diekstrak dengan "identify" mode verbose, memakai salah satu dari cara berikut yang pada dasarnya semuanya melakukan hal yang persis sama.
magick identify -verbose image.png
magick image.png miff:- | identify -verbose -
magick image.png -verbose -identify null:
magick image.png -verbose info:
| Keluaran dari identifikasi verbose mana pun di atas tidak akan mengembalikan tabel warna atau histogram bila warnanya lebih dari 1024! Karena itu, untuk gambar besar yang penuh warna cara ini untung-untungan dan tidak dianjurkan, meski tetap bisa berguna.
Cara yang lebih baik adalah menghasilkan "histogram:" dari gambar itu lalu mengekstrak komentar yang disertakan dalam hasilnya. | |
magick tree.gif -format %c -depth 8 histogram:info:-
![[IM Output]](../static/img/images/tree.gif)
| ![[IM Text]](../static/img/quantize/tree_histogram.txt.gif)
| Format keluaran "info:" ditambahkan pada IM v6.2.4. Untuk IM versi sebelumnya, pakai.. |
magick tree.gif histogram:- | identify -depth 8 -format %c -
Masalah dengan cara-cara ini adalah keluarannya berupa teks biasa berisi daftar warna, yang masih harus diuraikan sesuai kebutuhan. Namun sejak IM v6.2.8-8, operator "-unique-colors" akan mengubah sebuah gambar menjadi gambar yang lebih kecil, berisi satu piksel untuk tiap warna unik yang ditemukan pada gambar aslinya, semuanya dalam satu baris. Artinya, sebuah gambar dapat diubah menjadi gambar tabel warna yang lebih sederhana, yang mendaftar tiap warna yang hadir. Lebar gambar itu memberi tahu jumlah warnanya, dan bila daftar warnanya memang perlu ditampilkan, keluarannya bisa disimpan ke format gambar "txt:". Sebagai contoh, inilah tabel warna untuk gambar tree. |
magick tree.gif -unique-colors -scale 1000% tree_colors.gif
magick tree.gif -unique-colors -depth 16 txt:-
Tabel warna yang sudah diperkecil ini juga sangat penting sebagai cara menyimpan peta warna hasil pembangkitan dalam file yang sangat kecil. Peta semacam itu terutama penting bagi operator reduksi warna "-remap". (Lihat Peta warna terdefinisi di bawah) Bila Anda ingin memperoleh gambar yang memuat bukan hanya warna dalam sebuah gambar melainkan juga cacah tiap warnanya, berikut satu solusi histogram warna yang dikembangkan dari sebuah IM Forum Discussion.
magick rose: -colors 256 -format %c histogram:info:- |
sed 's/:.*#/ #/' |
while read count color colorname; do
magick -size 1x$count xc:$color miff:-
done |
magick - -alpha set -gravity south -background none +append \
unique_color_histogram.png
Perhatikan bahwa saya harus mereduksi warna gambarnya, sebab gambar bawaan "rose:" memuat 3020 warna unik, yang akan makan waktu lama dan menghasilkan gambar yang sangat panjang. Gambar GIF mawar yang ditampilkan di atas memuat kumpulan reduksi warna yang sama. Gambar hasilnya tetap memuat jumlah piksel yang sama, meski diberi tambahan piksel transparan, dan seperti terlihat ia menunjukkan dominasi abu-abu kehijauan, merah yang kuat, serta puncak yang sangat tinggi pada putih murni. Ini mungkin bukan metode histogram warna terbaik untuk umum, tetapi bekerja baik untuk gambar ini. |
Urutan warna, baik untuk "histogram:" maupun untuk operator "-unique-colors", tidak terdefinisi, tetapi tampaknya diurutkan berdasarkan nilai kanal merah, lalu hijau, dan terakhir biru. Ini mungkin bukan cara terbaik untuk sebuah gambar tertentu, tetapi mustahil mengurutkan warna 3 dimensi menjadi urutan 1 dimensi secara umum. |
|---|---|
Mengekstrak warna rata-rata
Warna rata-rata sebuah gambar dapat ditemukan dengan sangat cepat memakai "-scale" untuk memperkecil gambar menjadi satu piksel. Sebagai contoh, inilah warna rata-rata gambar bawaan "rose:". Warnanya saya keluarkan memakai format escape FX yang mengembalikan string warna yang bisa langsung dipakai IM tanpa perubahan.
magick rose: -scale 1x1\! -format '%[pixel:s]' info:-
Masalah dengan memakai escape FX "%[pixel:...]" adalah ia bisa mengembalikan nama warna seperti 'white' atau 'silver' alih-alih nilai RGB. Namun hal itu bisa ditiru dengan memakai tiga escape FX untuk mengembalikan nilai RGB sesungguhnya pada kedalaman bit yang diinginkan. Sebagai contoh...
magick rose: -scale 1x1\! \
-format '%[fx:int(255*r+.5)],%[fx:int(255*g+.5)],%[fx:int(255*b+.5)]' info:-
Sejak IM v6.3.9 ada sejumlah escape "-format" baru yang berguna untuk mengambil informasi yang lebih spesifik tentang gambar tanpa perlu menguraikan keluaran verbose "identify" atau "info:". Sebagai contoh, warna rata-rata kanal merah bisa diperoleh dengan mengambil nilai grayscale '%[mean]' dari gambar kanal merahnya.
magick rose: -channel R -separate -format '%[mean]' info:
Mengekstrak sebuah warna tertentu
Dari baris perintah ada dua cara dasar untuk mengambil warna satu piksel tertentu dari sebuah gambar. Bisa dengan escape FX seperti "%[pixel:...]" atau "%[fx:...]" (lihat di atas) pada lokasi piksel tertentu...
magick rose: -format '%[pixel:p{40,30}]' info:-
Cara lain, gambar itu bisa disederhanakan dengan "-crop" untuk memotong satu piksel yang Anda minati, lalu pakai salah satu cara sebelumnya. Sebagai contoh...
magick rose: -crop 1x1+40+30 -depth 8 txt:-
Mencacah warna tertentu (atau yang mendekati)
Cara ini dapat dipakai untuk memperoleh cacah piksel atau persentase sebuah warna tertentu. Yang dilakukan adalah menghitamkan segala sesuatu yang bukan warna itu, lalu memutihkan warna tersebut. Sebagai contoh, mari kita hitung jumlah warna pada matahari "yellow" dalam gambar 'tree'.
magick tree.gif -fill black +opaque yellow \
-fill white -opaque yellow \
-print "yellow sun pixels = %[fx:w*h*mean]\n" null:
Ada satu catatan: cara ini tidak bekerja bila warna yang diuji justru hitam. Untuk menangani hitam (atau warna yang sangat gelap), tukar isian warnanya agar warna non-hitam dipetakan ke putih, lalu negasikan hasilnya untuk menghasilkan mask putih dari semua piksel hitam. Ingat bahwa opsi "-print" setara dengan memakai "-format ... -write info:" dan bisa dipakai di mana saja dalam pemrosesan gambar. Gambar yang tidak diinginkan lalu saya buang memakai format file khusus "null:". Gambar itu juga bisa disimpan untuk dipakai sebagai mask pada pekerjaan berikutnya. Perhatikan, meski cara ini bekerja baik untuk gambar kecil, pada gambar yang jauh lebih besar (misalnya foto digital resolusi tinggi) 'mean' tidak cukup akurat untuk memperoleh cacah piksel yang persis! Pada dasarnya penggunaan 'mean' di atas cocok untuk menghasilkan rasio, bukan cacah piksel yang persis. Untuk cacah piksel yang persis, lebih baik pakai keluaran 'comment' histogram yang memuat cacah piksel yang tepat (lihat di atas). Perintah di atas juga bisa memakai opsi faktor fuzz "-fuzz" sebelum operator "-opaque" untuk menyertakan warna yang 'mendekati' juga.
Membandingkan dua warna
Katakanlah ada dua warna tertentu dan Anda ingin membandingkannya. Bisa dipakai "magick compare " untuk memperoleh RMSE (pada standard error)...
magick compare -metric RMSE xc:Navy xc:blue null:
Ini bagus karena memberikan jarak antara kedua warna, baik dalam bentuk nilai maupun sebagai persentase ternormalisasi dari jarak hitam ke putih. Namun cara ini tidak menangani transparansi dengan benar. Misalnya saat membandingkan 'hitam transparan penuh' dengan 'putih transparan penuh'.
magick compare -metric RMSE xc:'#0000' xc:'#FFF0' null:
Warna transparan seharusnya berjarak nol, sebab transparan penuh sama saja apa pun warna di baliknya. Yang kita dapat malah jarak hiperkubus 4 dimensi). Karena itu cara pengukuran jarak warna di atas hanya cocok untuk membandingkan warna yang sepenuhnya opak saja.
Alih-alih memperoleh jarak yang sesungguhnya, bisa juga dipakai faktor fuzz untuk memeriksa apakah dua warna berdekatan.
magick compare -fuzz 20% -metric AE xc:Navy xc:Blue null:
magick compare -fuzz 30% -metric AE xc:Navy xc:Blue null:
Ingat bahwa hasilnya akan '1' bila pikselnya tidak cocok (jumlah piksel error). Untuk memperoleh jarak faktor 'fuzz' sesungguhnya yang memisahkan nilai-nilai itu, pakailah metrik 'FUZZ'.
magick compare -metric FUZZ xc:Navy xc:Blue null:
Nilai 'normalized' menunjukkan bahwa jaraknya sesungguhnya 28,7%. Memakai faktor fuzz berbeda dengan menghitung RMSE bila transparansi ikut terlibat. Sebabnya, faktor fuzz dirancang agar dua warna yang transparan penuh diperlakukan sama. Karena itu 'hitam transparan penuh' dan 'putih transparan penuh' persis setara (menghasilkan nilai 0 alias tidak ada piksel error)...
magick compare -metric FUZZ xc:'#0000' xc:'#FFF0' null:
Cara lain membandingkan warna adalah mencoba mengganti warna dengan persentase faktor fuzz yang sesuai. Sebagai contoh...
magick xc:Navy -fuzz 20% -fill Blue -opaque Blue txt:
Karena 'Navy' tidak berubah menjadi 'Blue', berarti ia berbeda lebih dari 20% dari 'Blue'. Sedangkan
magick xc:Navy -fuzz 30% -fill Blue -opaque Blue txt:
Yang ini memang mengubah warnanya menjadi 'Blue', jadi sekarang kita tahu bahwa 'Navy' berjarak antara 20% dan 30% dari warna itu. Untuk melakukannya dalam sebuah skrip, pakai kira-kira seperti ini...
fuzz=%1
color1="red"
color2="#e00"
color2=`magick xc:"$color2" -format '%[pixel:s]' info:`
result=`magick xc:"$color1" -alpha set -channel RGBA -fuzz $fuzz \
-fill $color2 -opaque $color2 -format '%[pixel:s]' info:`
if [ "$result" = "$color2" ]; then
echo "Colors match according to Fuzz Factor"
else
echo "Colors DO NOT match"
fi
Opsi khusus "-alpha set -channel RGBA" penting agar kita bisa melakukan pencocokan fuzzy pada warna transparan dan yang mendekati transparan.
Kuantisasi warna
Operator kuantisasi warna
Tulang punggung utama kuantisasi warna, dan yang dipakai secara internal untuk semua reduksi warna otomatis, adalah operator "-colors". Operator ini mengimplementasikan algoritma reduksi warna "Adaptive Spatial Subdivision", dan algoritmanya sangat bagus. Berikut contoh yang khas: saya punya gambar 'colorwheel' yang memuat banyak sekali warna, lalu kita minta IM mengurangi jumlah warnanya hingga tinggal 64 warna, memakai berbagai metode dither.
magick colorwheel.png -dither None -colors 64 colors_64_no.gif
magick colorwheel.png -dither Riemersma -colors 64 colors_64_rm.gif
magick colorwheel.png -dither FloydSteinberg \
-colors 64 colors_64_fs.gif
Secara bawaan IM memakai 'dither' untuk menyebarkan gradasi warna pada gambar. Hal ini mencegah perubahan warna yang mendadak pada gradien yang berubah halus. Bila dithering dimatikan (memakai 'None' atau pengaturan "+dither"), terlihat jelas warna mana saja yang dilebur menjadi satu untuk menghasilkan apa yang dianggap IM sebagai kumpulan warna terbaik bagi gambar ini. Terlihat juga perubahan warna mendadak yang muncul pada gradien warna bila dithering tidak dilakukan. Tentu saja gambar ini memakai jauh lebih banyak warna daripada kebanyakan gambar. Karena itu, meski batas 64 warna sering memadai untuk banyak gambar, batas itu sama sekali tidak memadai untuk gambar ini. Dengan kata lain, kuantisasi warna berusaha menemukan kumpulan warna terbaik bagi sebuah gambar tertentu. Berikut contoh kuantisasi warna untuk sebagian logo IM, dengan jumlah warna yang teramat sedikit.
magick logo: -resize 40% -crop 100x100+105+50\! -normalize logo.png
magick logo.png +dither -colors 8 colors_8_no.gif
magick logo.png -dither Riemersma -colors 8 colors_8_rm.gif
magick logo.png -dither FloydSteinberg \
-colors 8 colors_8_fs.gif
Bandingkan dengan beberapa hasil untuk gambar foto bawaan "rose:".
magick rose: +dither -colors 16 colors_16_no.gif
magick rose: -dither Riemersma -colors 16 colors_16_rm.gif
magick rose: -dither FloydSteinberg \
-colors 16 colors_16_fs.gif
Seperti terlihat, gambar bergaya kartun butuh jauh lebih sedikit warna daripada foto sungguhan untuk menghasilkan hasil yang wajar. | _Saat ini hanya satu algoritma kuantisasi warna, "Adaptive Spatial Subdivision", yang diimplementasikan dalam IM, dan karena ia bekerja sangat baik, hampir tidak ada kebutuhan menambah yang lain. Meski begitu, berkat masukan pengguna algoritma ini terus diperbaiki.
SELINGAN: Sebagai rujukan, program "Gifsicle" mendaftar sejumlah metode kuantisasi warna lain (lewat opsi "--color-method"-nya). Saya sama sekali tidak tahu sebaik apa metode-metode kuantisasi warna itu dibanding IM. Bila Anda menemukan rujukan yang bagus tentang berbagai metode kuantisasi warna, tolong kirimi saya email._
Seluk-beluk kuantisasi warna
Proses memilih sejumlah kecil warna yang akan dipakai dalam sebuah gambar disebut kuantisasi warna, dan prosesnya sangat rumit serta melibatkan banyak faktor. Uraian teknis lengkapnya ada di situs web ImageMagick, Color Reduction Algorithm. Namun di sini saya akan mencoba menjelaskan beberapa aspeknya yang lebih penting. Faktor terbesarnya barangkali adalah warna yang benar-benar dipakai dalam gambar itu. Tidak ada gunanya memilih sebuah warna tertentu untuk sebuah gambar bila piksel yang 'dekat' dengan warna itu sangat sedikit. Karena itu pilihan warna bergantung bukan hanya pada warna yang dipakai dalam gambar, melainkan juga pada jumlah piksel yang 'dekat' dengan warna tersebut. Hal ini bisa saya tunjukkan dengan cukup mudah, yaitu dengan mencoba mereduksi dua gambar dua warna yang berbeda menjadi satu warna yang sama.
magick -size 4x1 xc:blue -draw 'fill red point 0,0' \
-scale 20 colors_rb.gif
magick -size 4x1 xc:red -draw 'fill blue point 3,0' \
-scale 20 colors_br.gif
magick colors_rb.gif -colors 1 colors_rb2.gif
magick colors_br.gif -colors 1 colors_br2.gif
Seperti terlihat, satu warna akhir itu bergantung bukan hanya pada warna yang hadir, melainkan juga pada banyaknya tiap warna dalam gambar.
magick -size 20x640 gradient: -rotate 90 gradient.png
magick gradient.png +dither -colors 5 colors_gradient.gif
Perhatikan bahwa kuantisasi warnanya seragam, dalam ruang warna yang sedang dipakai.
FUTURE: Just what are the effects of the "[-treedepth](https://imagemagick.org/command-line-options/#treedepth)" setting?
Mail me if you know
Kuantisasi warna dan ruang warna
Pengaruh besar lainnya atas warna mana yang terpilih adalah bagaimana kita mendefinisikan warna yang 'dekat' atau 'berdekatan'. Hal ini ditentukan oleh ruang warna yang dipakai untuk kuantisasi (pemilihan warna), dan (sejak IM v6.2.8-6) dikendalikan oleh pengaturan ruang warna "-quantize". Pengaturan "-quantize" menjadi sangat penting ketika jumlah warna yang dipilih sangat sedikit. Sebagai peragaan, mari kita reduksi gambar 'colorwheel' standar memakai bermacam ruang warna yang berbeda dan mendefinisikan 'jarak warna' yang berbeda-beda.
for S in RGB CMY sRGB GRAY \
XYZ LAB LUV \
HSL HSB HWB \
YIQ YUV OHTA ; do \
magick colorwheel.png -quantize $S +dither -colors 16 \
-fill black -gravity SouthWest -annotate +2+2 $S \
colors_space_$S.gif; \
done
Seperti terlihat, warna yang terpilih sangat bergantung pada bagaimana ruang warnanya disusun. Kubus warna sRGB (Red, Green, Blue) umumnya menghasilkan setidaknya warna-warna yang dekat dengan warna primer. Ruang warna sRGB sangat bagus untuk memilih warna bagi gambar bergaya kartun dan ikon, tetapi sebenarnya ruang warna yang buruk untuk foto pada umumnya. Ruang warna CMY persis sama dengan ruang warna sRGB, sebab kanal warnanya cukup dinegasikan untuk berpindah antara ruang warna sRGB dan CMY. Karena itu warna hasil kuantisasinya berujung pada solusi yang kurang lebih sama. | _Ruang warna CMYK (tidak ditampilkan) juga menghasilkan hal yang sama tetapi karena alasan yang berbeda. Karena secara internal kanal 'K' dan 'colormap' sebuah gambar memakai penunjuk data yang sama (lihatKanal palet), IM mengonversinya kembali ke CMY sebelum kuantisasi.
Ruang warna sRGB, seperti diperkirakan, menghasilkan hasil yang serupa dengan RGB, tetapi dipelintir sehingga jumlah warna yang mendekati hitam dalam ruang warna itu berkurang. Karena itu warna yang tersedia untuk pusat colorwheel jadi lebih sedikit, sehingga muncul bercak 'yang tidak begitu hitam' yang lebih besar. Ruang warna XYZ juga sangat sangat mirip dengan ruang warna RGB linear. Bedanya yang besar di sini, sumbu warnanya telah bergeser agar lebih baik mencakup SELURUH warna yang bisa (dan bahkan yang biasanya tidak bisa) kita lihat, sehingga data warna dalam colorwheel terkompresi sedikit lebih rapat, dan akibatnya kuantisasinya tampak lebih tersebar. Ruang warna LAB dan LUV berdasar pada sumbu warna yang berbeda tetapi mirip satu sama lain. Hasilnya adalah susunan kuantisasi warna yang berbeda. Ruang warna khusus yang melibatkan kanal 'Hue', seperti HSL (Hue, Saturation, Lightness), HSB (Hue, Saturation, Brightness), dan HWB (Hue, White, Black), semuanya punya representasi roda warna yang siklis sebagai bagian dari ruang warnanya. Sebenarnya colorwheel ini justru dibuat memakai ruang warna HSL. Lihat Membuat roda warna. | _Saat tulisan ini dibuat, algoritma jarak warna yang dipakai IM tidak memperhitungkan sifat siklis dari 'Hue' dalam ruang warna. Algoritma untuk itu sangat berbeda. Karena itu terjadi diskontinuitas yang kuat di sepanjang jalur 'merah', tempat 'Hue' berputar kembali, dan akibatnya sangat sedikit warna merah yang terpilih dalam proses kuantisasi warna.
YIQ dan YUV dirancang untuk menghasilkan gradasi warna 'pastel' dan 'mid-tone' yang lebih alami, yang jauh lebih cocok untuk foto dan gambar dunia nyata yang melibatkan gradasi warna halus, terutama warna kulit. Helmut Dersch mencatat di situs webnya bahwa sebaiknya ruang warna LAB dipertimbangkan untuk distorsi. | _Pada IM versi lama (tepatnya IM versi 5) ruang warna yang dipakai untuk kuantisasi disetel dengan opsi "-colorspace". Namun pada IM versi 6 operator ini dipakai untuk mengubah cara gambar disimpan di memori, jadi bukan pengaturan untuk kuantisasi warna.
Karena itu pada IM v6.2.8-6 disediakan pengaturan "-quantize" untuk pekerjaan tersebut. Namun ia hanya pengaturan bagi proses kuantisasi warna "-colors". Ia tidak berpengaruh pada penggantian dan dithering warna memakai operator seperti "-remap" dan "-posterize", atau berbagai teknik dithering._
Untuk daftar lengkap ruang warna yang tersedia, lihat operator "-colorspace". Pengaruh ruang warna terhadap pemilihan warna bisa dilihat lebih jauh pada contoh di Bercak acak warna polos. Di sana kuantisasi warna dipakai untuk mengurangi jumlah warna dalam gambar acak memakai berbagai ruang warna. Kuantisasi TIDAK mempertahankan warnaPerhatikan bahwa pada semua gambar di atas, warna hitam murni tidak pernah benar-benar dipilih oleh kuantisasi warna. Perlu dicatat, memang hanya ada satu piksel hitam murni, dan warna yang mendekati hitam pun tidak banyak dalam gambar itu. Akibatnya satu-satunya hitam yang muncul pada gambar akhir adalah yang ditambahkan belakangan sebagai bagian dari pelabelan gambar. Bahkan gambar dengan ruang warna 'GRAY' pun tidak menghasilkan warna hitam murni. Nyatanya tidak satu pun gambar itu memuat warna primer atau sekunder, seperti: merah, biru, hijau, cyan, magenta! Satu-satunya perkecualian adalah putih, sebab gambar-gambar itu memang memuat cukup banyak putih murni, sehingga putih menjadi 'warna pilihan' (lihat di bawah). Namun keadaan ini bukanlah bug! Pertama, warna 'black' umumnya tidak terpilih pada contoh di atas, biasanya karena hitam dalam gambar aslinya sangat sedikit, sehingga kuantisasi warna umumnya tidak terlalu peduli pada warna gelap. Ia justru menghasilkan lebih banyak warna terang karena warna itulah yang lebih umum dalam gambar. Lihat bagian sebelumnya untuk contoh yang spesifik. Kedua, karena kuantisasi berusaha memilih warna yang dekat dengan sebanyak mungkin piksel warna yang ada dalam gambar, hal itu paling baik dicapai dengan TIDAK mencocoki warna primer atau sekunder yang 'murni', sebab warna-warna itu selalu berada di ujung paling ekstrem ruang warna yang dipakai. Sebuah warna yang 'agak melenceng' cenderung cocok dengan lebih banyak warna daripada warna 'primer', jadi warna semacam itulah yang lebih sering terpilih. Jadi biar saya perjelas... Kuantisasi warna (" -colors") umumnya akan menghindari pemilihan warna primer!
Sejak IM versi 6.3 fungsi kuantisasi warna diubah agar berusaha menyertakan warna yang sangat umum dalam gambar aslinya. Karena itu, bila sebuah gambar memuat area dengan satu warna saja (seperti 'white' pada contoh di atas), warna itu umumnya akan disertakan dalam peta warna akhir. Hal ini sedikit memperbaiki keadaan, terutama untuk gambar bergaya 'kartun' atau gambar dengan latar belakang warna polos. Warna 'polos' itu umumnya akan terpilih sehingga membantu menghindari bintik dither yang akan kita lihat di bawah. Solusi untuk warna tertentu dalam peta warna Saat ini hanya ada sedikit cara untuk menjamin sebuah 'warna tertentu' masuk ke dalam warna terpilih yang nantinya dipakai untuk dithering. Salah satu caranya, kuantisasi gambar seperti biasa, lalu keluarkan peta warna yang dihasilkan (memakai "-unique-colors"). Sekarang peta warna itu bisa disesuaikan agar warna yang Anda maksud benar-benar warna tersebut. Terakhir, pakai operator remap warna untuk men-dither gambar memakai peta warna yang sudah disediakan itu. Peta warnanya mungkin tidak lagi berisi warna TERBAIK untuk gambar itu, dan beberapa warna lain barangkali juga perlu disesuaikan, tetapi hasilnya akan mendekati peta warna yang diinginkan. Cara lain, tempelkan (yang memperbesar gambar) bidang-bidang besar berisi warna tertentu yang ingin dipertahankan pada gambar, sebelum memakai "-colors". Penambahan 'petak' besar sebuah warna tertentu membuat warna itu lebih mungkin terpilih dalam peta warna akhir. Semua warna lainnya pun lalu otomatis disesuaikan agar lebih cocok dengan peta warna itu). Bila cara ini berhasil, petak warna yang ditambahkan tadi mestinya tidak berubah (tidak di-dither). Sesudahnya, gambar itu bisa dipangkas untuk membuang petak yang ditambahkan tadi. Bila tidak berhasil, setidaknya IM mestinya sudah menambahkan warna yang dekat dengan 'warna tertentu' yang diinginkan, sehingga peta warna hasilnya hanya perlu sedikit disesuaikan sebelum remap warna diterapkan pada gambar aslinya. Bila Anda mencoba cara ini, berhasil atau gagal, tolong beri tahu saya bagaimana hasilnya. Idealnya, yang ingin saya lihat adalah cara untuk menentukan sejumlah kecil warna tertentu yang wajib menjadi bagian peta warna akhir, lalu entah bagaimana meminta IM memilih warna terbaik untuk sisa warna dalam peta warna itu, bagi sebuah gambar tertentu.
Kuantisasi warna dan transparansi
Secara bawaan ImageMagick bukan hanya menghasilkan warna yang sepenuhnya opak, melainkan juga berupaya menghasilkan warna semitransparan. Dengan begitu, gambar yang memuat bayangan transparan atau efek overlay lain tidak kehilangan efek tersebut. Namun sejak IM v6.2.6, kuantisasi warna yang melibatkan transparansi diubah sehingga semua warna yang transparan penuh diperlakukan sebagai warna yang sama. Perubahan ini bersifat linear, jadi warna yang hanya setengah transparan pun dianggap lebih berdekatan dibanding bila keduanya opak penuh. Karena perubahan itu, kuantisasi warna IM tetap menghasilkan warna semitransparan, tetapi lebih memusatkan perhatian pada warna opak dan kurang pada warna yang transparan penuh dalam gambar. Sebagai contoh, di sini saya membuat gradien pelangi, dengan gambar yang opak penuh di bagian atas dan transparan penuh di bagian bawah (Catatan penerjemah: naskah asli menulis 'at the top' dua kali; yang kedua semestinya bagian bawah). Gambar-gambarnya saya tampilkan di atas pola latar belakang supaya terlihat seberapa transparan gambarnya.
magick xc:red xc:yellow xc:green1 xc:cyan xc:blue \
+append -filter Cubic -resize 100x100\! -size 100x100 \
gradient: -alpha off -compose CopyOpacity -composite alpha_gradient.png
magick alpha_gradient.png +dither -colors 256 alpha_colors_256.png
magick alpha_gradient.png +dither -colors 64 alpha_colors_64.png
magick alpha_gradient.png +dither -colors 15 alpha_colors_15.png
Seperti terlihat, ketika kita meminta IM mengurangi jumlah warna yang dibutuhkan gambar ini, ia membuat jauh lebih banyak warna opak dan memakai lebih sedikit warna yang sangat transparan untuk bagian yang lebih tembus pandang. Hasilnya adalah sebaran warna terpilih yang sangat bagus, terutama ketika jumlah warnanya sangat sedikit. Namun seperti yang saya tunjukkan di atas, bukan hanya warna primer yang tidak terpilih, warna yang transparan penuh pun tidak akan terpilih karena alasan yang persis sama. Nyatanya bahkan warna yang opak penuh pun tidak terpilih! Dengan kata lain, setiap warna dalam gambar hasil kuantisasi pada contoh sebelumnya bersifat semitransparan. Biar saya perjelas.
Bila transparansi terlibat, kuantisasi warna IM
bisa jadi tidak memilih warna yang opak penuh maupun yang transparan penuh!
Tentu saja sejak IM v6.3, dengan perbaikan bug 'warna umum' (lihat Kuantisasi TIDAK mempertahankan warna di atas), hal itu lebih kecil kemungkinannya terjadi bila gambarnya memuat banyak warna opak dan transparan penuh, yang lazim terjadi. Karena sebagian gambar bisa memuat banyak warna semitransparan, misalnya gambar dengan efek asap atau bayangan, ada baiknya dijalankan uji coba dulu untuk memastikan sebuah warna transparan penuh ikut terpilih dalam gambar hasilnya. Setelah itu warna yang paling transparan bisa dipetakan ke transparan penuh, lalu lakukan sendiri remap warna-nya. Bila Anda benar-benar ingin memastikan gambar hasilnya memuat warna opak penuh sekaligus transparan penuh, kanal alpha-nya bisa di-normalisasi atau contrast-stretch. Sebagai contoh, di sini saya memastikan warna utama yang terpilih dibuat opak dengan memakai "-contrast-stretch". Meski untuk keadaan yang lebih normal cara ini barangkali agak berlebihan.
magick alpha_gradient.png +dither -colors 15 \
-channel A -contrast-stretch 10% alpha_colors_15n.png
Ini BUKAN masalah bagi gambar GIF yang memang tidak mengizinkan warna semitransparan, atau JPG yang tidak mengizinkan transparansi, atau bahkan PNG yang tidak perlu kuantisasi untuk disimpan dengan benar. Ini baru jadi masalah pada kasus khusus ketika reduksi warna dipaksakan pada gambar yang melibatkan banyak warna semitransparan. Ingat, untuk format GIF menyimpan warna semitransparan adalah usaha yang sia-sia. Karena itu, bila Anda berencana melakukan sendiri kuantisasi warna untuk format gambar semacam itu, IM perlu diberi tahu agar mengabaikan transparansi gambar saat membangkitkan kumpulan warna yang diperkecil. Hal itu bisa dilakukan dengan pengaturan ruang warna khusus "-quantize" bernilai 'transparent'.
magick alpha_gradient.png -quantize transparent \
+dither -colors 15 alpha_colors_15qt.png
Perhatikan bagaimana kuantisasi warna sama sekali mengabaikan transparansi warnanya, dan sedikit pun tidak menyentuh kanal alpha gambar. Artinya, kanal alpha bisa diolah dengan cara yang lebih sesuai untuk gambar Anda, sepenuhnya terpisah dari warna lainnya. Bahkan hal itu bisa dilakukan sebelum atau sesudah memakai "-colors" tanpa masalah. Hasilnya sama saja. Karena itu ruang warna kuantisasi ini dianjurkan saat mengurangi jumlah warna sebuah gambar yang akan disimpan ke format dengan transparansi Boolean atau tanpa transparansi, seperti format gambar GIF atau XPM. Bila jumlah warna yang dihasilkan dihitung, akan terlihat pula bahwa jumlahnya persis sebanyak yang diminta. Karena itu, bila sebuah warna transparan penuh juga dibutuhkan (biasanya begitu), argumen "-colors" harus dikurangi setidaknya satu, agar tersisa ruang untuknya dalam tabel warna akhir gambar. Jadi, untuk menangani batas tabel warna 256 warna pada format file GIF, warnanya harus direduksi ke 255, bukan 256, menyisakan ruang tambahan untuk indeks warna transparan penuh, sebagaimana ditentukan oleh pengaturan "-transparent-color". Sesuaikan angka ini untuk ukuran tabel warna yang lebih kecil. Perilaku kuantisasi ini otomatis ketika IM menyimpan ke format file GIF, tetapi menjadi penting ketika kuantisasinya perlu Anda kerjakan sendiri sambil membangkitkan tabel warna global atau bersama. Tentu saja piksel semitransparan tetap perlu ditangani, agar sesuai dengan tampilan yang Anda inginkan untuk gambar itu.
FUTURE: This last part will probably move to a new section on 'Dithering
Alpha Channel' to be created in the near future. And a reference to this
section added here.
Berikut beberapa contoh dithering hanya pada kanal alpha menjadi sekadar nilai Boolean alias on/off, tanpa memengaruhi kanal warna lainnya dalam gambar.
magick alpha_gradient.png \
-channel A -threshold 50% alpha_dither_threshold.gif
magick alpha_gradient.png \
-channel A -ordered-dither checks alpha_dither_checks.gif
magick alpha_gradient.png \
-channel A -ordered-dither o8x8 alpha_dither_ordered.gif
magick alpha_gradient.png \
-channel A -ordered-dither h8x8a alpha_dither_halftone.gif
magick alpha_gradient.png -channel RGBA -separate \
\( +clone -monochrome \) \
+swap +delete -combine alpha_dither_monochrome.gif
magick alpha_gradient.png -channel RGBA -separate \
\( +clone -dither FloydSteinberg -monochrome \) \
+swap +delete -combine alpha_dither_monochrome_fs.gif
magick alpha_gradient.png -channel RGBA -separate \
\( +clone -remap pattern:gray50 \) \
+swap +delete -combine alpha_dither_map.gif
magick alpha_gradient.png -channel RGBA -separate \
\( +clone -dither FloydSteinberg -remap pattern:gray50 \) \
+swap +delete -combine alpha_dither_map_fs.gif
| _Saat men-dither salinan kanal alpha, agar bisa di-dither memakai "-monochrome" atau "-remap", pastikan gambarnya benar-benar gambar grayscale, bukan mask bentuk yang memuat transparansi. Kalau tidak, hasilnya kemungkinan besar masih memuat efek non-linear dari kanal alpha yang belum hilang.
Ada beberapa cara untuk mengekstrak dan mengembalikan kanal alpha sebuah gambar sebagai mask grayscale agar bisa di-dither. Contoh di atas memakai pemisahan kanal dan combine untuk itu. Cara lain memakai ekstraksi alpha dengan komposisi CopyOpacity._
Dithering koreksi error
Seperti dibahas dalam pengantar, dither koreksi error umumnya dianggap pilihan terbaik untuk menghasilkan representasi paling setia dari gambar asli dengan kumpulan warna yang diperkecil. Ia juga membatasi diri pada palet warna terdefinisi apa pun, entah dipasok pengguna atau ditentukan oleh rutin kuantisasi warna IM. Karena itu ia menjadi pilihan bawaan yang masuk akal untuk reduksi warna umum yang disediakan operator IM, "-colors", "-remap", "-posterize" dan "-monochrome".
Metode E-Dither
Sejak versi 6.4.2-9, IM menyediakan lebih dari satu gaya atau metode dithering, yang dapat dipilih lewat pengaturan "-dither". Sebelumnya IM terbatas pada satu varian Riemersma Dither, atau Hilbert Curve Dither. yang dapat disetel dengan "-dither Riemersma". Kini bisa dipilih juga Floyd-Steinberg Dither memakai "-dither FloydSteinberg". Metode dither apa saja yang sudah diimplementasikan dalam IM versi Anda dapat dilihat dengan...
magick -list dither
Sebagai contoh, inilah colorwheel yang di-dither memakai metode dithering yang berbeda-beda.
magick colorwheel.png -dither Riemersma -colors 16 dither_riemersma.gif
magick colorwheel.png -dither FloydSteinberg -colors 16 dither_floyd.gif
Seperti terlihat, dither Floyd-Steinberg menghasilkan pola dither yang jauh lebih seragam daripada dither Riemersma bawaan. Perbedaan terbesar keduanya adalah bagaimana masing-masing menyebarkan 'error warna' ke piksel-piksel bertetangga. Jadi mari kita lihat bagaimana sebenarnya E-Dither bekerja.
Cara kerja E-Dither
Sedang ditulis ulang
Metode khusus yang dipakai IM untuk dithering umum adalah varian dari "Hilbert Curve Error Correction Dither". Ini sebenarnya teknik dithering yang sangat bagus, terdefinisi dengan baik dan cukup cepat. Untuk uraian lengkapnya (dan sebuah varian yang sangat mirip) lihat... Riemersma Dither. Pada dasarnya tiap piksel dalam gambar ditelusuri mengikuti jalur yang sangat rumit yang dikenal sebagai 'Hilbert Curve'. Piksel itu diberi warna yang paling dekat dengan nilainya, dan selisih apa pun antara warna asli piksel dan warna yang terpilih disimpan lalu ditambahkan ke nilai warna piksel berikutnya (yang selalu piksel bertetangga) sebelum warna baru dipilih lagi. Dengan begitu, ragam selisih warna antara warna terpilih dan warna asli gambar tersebar ke piksel lain di area yang sama. Hasilnya, meski hanya warna tertentu yang diberikan pada gambar akhir, warna keseluruhan area itu pada dasarnya tetap mendekati gambar aslinya. Sebagai contoh, berikut sebuah gambar abu-abu kecil yang saya minta IM men-dither-nya memakai kumpulan warna yang tidak memuat warna aslinya. Gambar hasilnya diperbesar supaya tiap piksel berwarna yang diberikan terlihat jelas.
magick -size 10x10 xc:'#999999' -scale 80x80 dither_not.gif
magick -size 10x10 xc:'#999999' \
-remap colortable.gif -scale 80x80 dither.gif
Seperti terlihat, karena warna asli gambar itu tidak ada dalam peta warna yang ditentukan, warna aslinya didekati memakai pola dari tiga warna terdekat yang ada dalam tabel warna yang diberikan. Bila warna yang dihasilkan pola dither di atas kita rata-ratakan, kita akan memperoleh warna
, yang sangat dekat dengan warna rata-rata asli gambar itu, yaitu
, dan itulah seluruh maksud dari pola dither yang dihasilkan. Namun karena 'jalur' yang dipakai untuk menetapkan warna itu rumit (meski umumnya tetap berada di area setempat), penetapan warnanya menghasilkan pola yang pada dasarnya acak. Secara teknis ia tidak acak, sebab gambar yang sama akan menghasilkan pola yang sama, tetapi hasilnya boleh dibilang acak, atau setidaknya pseudo-acak. Dither "F-S" sebenarnya hanyalah satu (yang pertama) dari beberapa 'Rasterized E-Dither' yang dikembangkan sejak kemunculannya pada awal 1970-an. Ia juga barangkali yang paling luas diimplementasikan, meski tidak dianggap yang terbaik. Lihat makalah Dithering Algorithms untuk ringkasan yang lebih lengkap tentang algoritma semacam itu. Sejak IM v6.4.3 ia juga tersedia langsung dalam IM, dan diimplementasikan mengikuti jalur 'serpentin' baris demi baris dari bagian atas gambar ke bagian bawah.
magick -size 10x10 xc:'#999999' -dither FloydSteinberg \
-remap colortable.gif -scale 80x80 dither_fs.gif
Khususnya "Floyd-Steinberg Dither", menurut saya, menghasilkan pola piksel yang lebih menyerupai arsiran silang daripada "Hilbert Curve Dither", dan memang begitulah ia dirancang. Pola yang teratur semacam itu membuat pembersihan manual tingkat rendah pada gambar ikon berwarna berukuran kecil jauh lebih mudah. Dulu saya banyak melakukannya untuk Anthony's Icon Library, tetapi hal semacam itu sudah jarang dibutuhkan sekarang, kecuali mungkin untuk gambar monokrom kecil.
Masalah E-Dither - peka terhadap perubahan
Salah satu masalah terbesar saat memakai dither koreksi error adalah pola pikselnya pada dasarnya acak, dan sekaligus sangat peka terhadap perubahan. Sebagai contoh, ambil gambar abu-abu yang asli lalu ganti satu pikselnya dengan warna lain sebelum di-dither ulang. Hasilnya adalah pergeseran total pola dithering pada setiap piksel yang berada lebih jauh di sepanjang jalur yang ditempuh Hilbert Curve Dither.
magick -size 10x10 xc:'#999999' -draw 'fill #C28 point 2,2' \
-remap colortable.gif -scale 80x80 dither_modified.gif
magick compare dither.gif dither_modified.gif dither_difference.gif
dither asli |
satu piksel berubah |
|
perbandingan perubahan
Seperti terlihat, cukup dengan menambahkan satu piksel saja ke gambar, pola dither-nya berubah drastis! Hanya perlu perubahan satu bit untuk membuat gambar hasilnya berbeda, meski tampilan keseluruhan gambar (bila tidak diperbesar) pada dasarnya masih sama (yang memang jadi tujuan algoritma dither yang baik). Gambar "magick compare" juga menunjukkan seberapa luas perubahan pola dither-nya. Dalam kasus ini, kira-kira 80% pikselnya diberi warna yang sama sekali berbeda. Pada Hilbert Curve Dither, perubahan satu piksel sebenarnya membuat setiap piksel sesudahnya berpotensi berbeda, artinya 0 sampai 100 persen pola dither bisa berubah. Semuanya bergantung pada di bagian mana kurva Hilbert yang rumit itu perubahan tadi terjadi. Sementara dither Floyd-Steinberg hanya berjalan melintasi gambar dalam satu arah, jadi perubahan satu piksel hanya mengubah pola di satu sisi perubahan itu.
magick -size 10x10 xc:'#999999' -draw 'fill #C28 point 2,2' \
-dither FloydSteinberg -remap colortable.gif \
-scale 80x80 dither_fs_modified.gif
magick compare dither_fs.gif dither_fs_modified.gif dither_fs_difference.gif
Dither FS |
satu piksel berubah |
|
perbandingan perubahan
Seperti terlihat, masalahnya persis sama. Perubahan satu piksel menyebabkan perubahan yang nyaris menyeluruh pada pola dither di area gambar yang diproses setelah piksel itu. Artinya, dari baris tersebut ke bawah. Untuk satu gambar tunggal, pola warna hasil dither itu tidak penting. Warna rata-rata polanya mestinya memberi gambar itu warna yang pas untuk area tersebut. Tetapi bila Anda punya animasi yang satu gambarnya disusul gambar lain yang sangat mirip, dengan area luas berwarna tetap, pola dither yang berubah-ubah itu jadi sangat kentara dan mengganggu sebagai 'noise' latar tingkat rendah. Sebagai contoh, di sini saya membuat animasi 3 gambar dengan warna ber-dither yang sama tetapi dengan satu piksel yang berubah pada tiap frame. Bagian tengahnya juga saya perbesar supaya perubahan pola itu terlihat lebih jelas.
magick -size 80x80 xc:'#999999' \
\( +clone -draw 'fill #C28 point 2,2' \) \
\( +clone -draw 'fill #28C point 2,2' \) \
-remap colortable.gif -set delay 50 -loop 0 dither_anim.gif
magick dither_anim.gif -crop 10x10+40+40 +repage \
-scale 80x80 dither_anim_magnify.gif
| Seperti terlihat, latar gambarnya jadi seperti bergolak, disebabkan oleh keacakan semu yang dihasilkan E-Dither. Pada kebanyakan kasus warna yang dipakai cukup berdekatan sehingga 'noise dither' ini tidak terlihat. Tetapi bila warna dithering-nya jelas berbeda (dalam kasus ini dipaksakan lewat penggunaan peta warna), hal itu jelas menjadi persoalan. Lihat Optimisasi warna video untuk contoh animasi yang lebih praktis yang memperlihatkan 'noise dither' ini. Perubahan pola juga menimbulkan masalah dalam mengoptimalkan animasi. Artinya, pola yang berbeda membuat optimisasi frame sederhana gagal memperkecil ukuran overlay frame. Untuk salah satu solusinya lihat optimisasi warna fuzzy, meski itu hanya bekerja bila golakannya memakai warna yang sangat mirip. | Tidak seperti metode dithering lain (sepertiambang batas dan ordered-dither), pengaturan "-channel" tidak memengaruhi kuantisasi warna maupun dither koreksi error. Pada dasarnya ia tidak punya tempat dalam cara kerja operasi gambar ini. |
|---|---|
| Ordered dither tidak punya satu pun masalah ini, sebab perubahan tertahan di area setempat di sekitar perubahan itu. Sayangnya ia umumnya juga terbatas pada kumpulan warna yang diturunkan secara matematis. (Lihat Ordered dither memakai peta warna seragam). |
Bintik piksel E-Dither
Masalah lain E-Dither adalah ia sesekali menghasilkan piksel berwarna aneh di area yang semestinya cukup seragam warnanya. Misalnya piksel hijau yang muncul sesekali pada gambar grayscale. Atau seperti pada contoh di bawah, piksel putih di area yang seharusnya biru rata polos. Hal ini terutama terjadi pada gambar besar yang memuat objek dengan banyak warna berdampingan dengan area berwarna polos yang tidak berubah. Ini khas pada objek berwarna yang ditimpakan pada latar belakang berwarna rata, seperti yang sering ditemui pada diagram dan gambar ilustrasi. Piksel berwarna aneh semacam itu bisa dilihat pada perbesaran contoh uji di atas, tempat sebuah piksel ungu muda tambahan muncul cukup jauh dari perubahan satu piksel yang kecil tadi. Namun piksel berwarna aneh pada contoh di atas tidak langsung kentara dan peta warnanya memang mencakup gambar itu dengan cukup baik, sehingga piksel anehnya cukup dekat dengan tiga warna normal yang dipakai untuk men-dither gambar tersebut. Sebagai contoh yang lebih ekstrem, di sini saya punya latar belakang gradien yang di-blur, yang warnanya saya reduksi habis-habisan menjadi 64 warna untuk benar-benar menguji dither koreksi error. |
magick -size 100x60 xc:SkyBlue \
-fill DodgerBlue -draw 'circle 50,70 15,35' \
-fill RoyalBlue -draw 'circle 50,70 30,45' \
-blur 0x5 -colors 64 speckle_gradient.gif
![[IM Output]](../static/img/quantize/speckle_gradient.gif)
Seperti terlihat, dengan peta warna yang sudah sangat diperkecil ini, dither koreksi error cukup baik dalam mewakili gradien aslinya. Tetapi bila kita menambahkan sebidang putih murni pada gambar di atas... |
magick -size 100x60 xc:SkyBlue \
-fill DodgerBlue -draw 'circle 50,70 15,35' \
-fill RoyalBlue -draw 'circle 50,70 30,45' -blur 0x5 \
-fill white -draw 'rectangle 40,40 60,55' \
-colors 64 speckle_problem.gif
![[IM Output]](../static/img/quantize/speckle_problem.gif)
Terlihat bahwa E-dither tiba-tiba mulai menaburkan piksel putih di bagian atas gambar, yang sebelumnya tidak ada sama sekali. Berikut perbesaran sebagian kecilnya supaya piksel-piksel itu terlihat lebih jelas... |
magick speckle_problem.gif -crop 15x15+75+0 +repage \
-scale 90x90 speckle_prob_mag.gif
![[IM Output]](../static/img/quantize/speckle_prob_mag.gif)
Piksel berwarna aneh itu disebabkan dua faktor. Pertama, kuantisasi warna terpaksa menyertakan satu warna putih murni (tetapi tanpa warna putih-biru anti-aliasing lainnya) ke dalam peta warna akhir gambar, sehingga proses dithering boleh memakai warna tambahan ini. Sementara itu E-Dither perlahan menumpuk error, terutama di area dengan warna ekstrem, seperti di bagian atas gambar di atas. Pada akhirnya error itu menumpuk sampai nilainya cukup besar untuk membuat satu warna tambahan tadi menjadi padanan terdekat. Karena itu, sesekali sebuah piksel putih yang sangat kontras dikeluarkan untuk 'mengoreksi error', di lokasi yang pseudo-acak. Hasilnya adalah taburan tipis piksel putih. Makin lambat penumpukan error-nya, makin jarang sebaran piksel putih itu dan makin tampak tidak pada tempatnya. Solusi terbaiknya adalah beralih ke format gambar lain yang tabel warnanya tidak terbatas. Misalnya, ubah gambar format GIF Anda ke PNG. Dengan begitu kuantisasi (reduksi) warna tidak diperlukan, sehingga dithering atas warna yang diperkecil pun tidak perlu. Solusi berikutnya adalah mengganti E-dither dengan metode dithering lain yang 'melokalkan' error, seperti ordered dithering. Namun saat ini hal itu tidak mudah diterapkan di IM. Lihat Hasil ordered dither yang lebih baik untuk salah satu metodenya, sampai metode yang lebih umum ditemukan. Bila beralih ke format gambar lain atau memakai metode dithering yang berbeda tidak praktis (dan sering memang begitu), yang tersisa adalah upaya membereskan keadaan untuk gambar tertentu itu saja. Perbaikan terbaiknya adalah memastikan tersedia warna-warna lain tepat di luar kelompok besar warna yang menyebabkan penumpukan error E-Dither. Namun kuantisasi warna yang biasa tidak melakukan hal itu. Ia cenderung memilih sekumpulan warna rata-rata yang mewakili kelompok warna. Yang dibutuhkan adalah warna tambahan yang 'memagari' tepi sebuah kelompok warna besar, bukan sekadar warna rata-rata. Di sini misalnya, saya memakai lingkaran alih-alih persegi, sehingga bukan hanya warna putih murni yang ditambahkan, melainkan juga sejumlah warna putih-biru. Warna-warna itu ditambahkan otomatis akibat anti-aliasing pada tepi lingkaran, untuk menghaluskan tampilannya. |
magick -size 100x60 xc:SkyBlue \
-fill DodgerBlue -draw 'circle 50,70 15,35' \
-fill RoyalBlue -draw 'circle 50,70 30,45' -blur 0x5 \
-fill white -draw 'circle 50,45 40,40' \
-colors 64 speckle_fixed.gif
![[IM Output]](../static/img/quantize/speckle_fixed.gif)
Dan perbesaran area yang sama seperti sebelumnya. |
magick speckle_fixed.gif -crop 15x15+85+0 +repage \
-scale 90x90 speckle_fix_mag.gif
![[IM Output]](../static/img/quantize/speckle_fix_mag.gif)
Seperti terlihat, warna tambahan tadi menyediakan warna ekstra tepat di luar gradien biru-cyan. Memang warna ekstra ini berarti warna yang tersedia untuk gradien itu sendiri jadi lebih sedikit, tetapi ia menyediakan warna biru-putih lain yang memungkinkan E-dither mengoreksi dirinya lebih cepat dan lebih sering, sebelum error yang menumpuk sempat membesar. Ini bukan berarti kita telah mencegah bintik E-dither, hanya menyediakan warna yang lebih baik untuk dikerjakan algoritma dither. Bila bagian gambar yang diperbesar itu diamati, pola bintiknya masih terlihat, tetapi warnanya lebih dekat dengan warna latar belakang, dan jumlahnya jauh lebih banyak sehingga sebaran bintiknya lebih merata. Cara lain adalah membuat tabel warna sendiri, mungkin berdasar tabel yang dihasilkan IM, lalu menambahkan warna yang sesuai untuk mencegah penumpukan error. Namun hal ini tidak mudah dilakukan, apalagi dengan ruang warna 3 dimensi. Untuk contoh gambar khusus ini, satu cara mencegah 'bintik' adalah membuat dan men-dither latar belakangnya secara terpisah, dengan jumlah warna sedikit di bawah yang dibutuhkan, lalu menimpakan kotak putih beserta warna tambahannya. |
magick -size 100x60 xc:SkyBlue \
-fill DodgerBlue -draw 'circle 50,70 15,35' \
-fill RoyalBlue -draw 'circle 50,70 30,45' -blur 0x5 \
-colors 63 \
-fill white -draw 'rectangle 40,40 60,55' speckle_perfect.gif
![[IM Output]](../static/img/quantize/speckle_perfect.gif)
Cara ini menambahkan satu warna 'putih' ke gambar, tetapi latar belakangnya tidak akan berbintik sama sekali, sebab putih belum tersedia saat dither koreksi error dijalankan. Hasilnya adalah gambar dengan persis 64 warna dan tanpa bintik sedikit pun. Namun ini sangat bergantung pada gambarnya dan pada apa yang ingin dicapai, jadi bukan solusi umum untuk masalah bintik.
Alternatif yang lebih umum daripada menambah warna ekstra adalah mencoba membuang bintik dari gambar akhir hasil dither. Artinya, membersihkan gambar itu dengan cara tertentu. Namun ini sendiri persoalan yang pelik, sebab piksel yang justru bagian dari pola dithering yang normal jangan sampai ikut terbuang. Yang kita butuhkan adalah menemukan piksel warna yang entah bagaimana sangat berbeda dari semua warna di sekelilingnya, tetapi sekaligus terpencil cukup jauh dari semua warna serupa lainnya. Punya solusi filter gambar yang lebih baik? Ringkasan Bagi saya bintik adalah masalah yang sangat menjengkelkan, terutama untuk gambar ikon desktop yang tabel warnanya sangat terbatas. Saya sendiri sering menyunting gambar 'ikon' berukuran kecil untuk membuang bintik atau membetulkan efek dithering lain, seperti pita vertikal. Bila Anda tahu solusi lain yang lebih baik, tolong beri tahu saya.
Gambar bitmap monokrom hasil dithering
Operator "-monochrome" adalah bentuk khusus dari operator "-colors" untuk menghasilkan gambar bitmap. Karena itu ia operator yang ideal untuk memperagakan bukan hanya 'Hilbert Curve Dithering', melainkan juga untuk menengok pemilihan warna lebih rinci. Berikut contoh yang khas. |
magick logo.png -monochrome monochrome.gif
![[IM Output]](../static/img/quantize/monochrome.gif)
Operator ini men-dither gambar semata berdasarkan 'intensitas' atau 'level' kecerlangan grayscale-nya, namun ia tidak men-dither seluruh rentang grayscale secara langsung melainkan memaksa nilai yang paling ekstrem ke nilai maksimumnya lewat ambang batas. Hal ini bisa kita lihat dengan meminta IM men-dither sebuah gambar gradien.
magick -size 15x640 gradient: -rotate 90 \
-monochrome monochrome_gradient.gif
Seperti terlihat, hanya sekitar 50% bagian tengah warna gradien itu yang di-dither oleh operator "-monochrome". Terima kasih khusus kepada Ivanova flamingivanova@punkass.com yang menunjukkan fakta menarik tentang cara kerja IM ini. Bila Anda ingin men-dither memakai seluruh rentang grayscale, pakailah operator "-remap" dengan peta warna hitam-putih murni (disediakan oleh gambar pattern bawaan). |
magick logo.png -remap pattern:gray50 mono_remap.gif
magick -size 15x640 gradient: -rotate 90 \
-remap pattern:gray50 mono_remap_gradient.gif
Dengan memilih warna secara lebih cermat memakai "-remap", dapat dihasilkan rentang 'ambang batas' yang sama dengan yang dipakai operator "-monochrome", atau rentang ambang batas lain sesuka Anda. |
magick xc:gray20 xc:white +append ctrl_colors.gif
magick logo.png -colorspace Gray \
-remap ctrl_colors.gif -normalize mono_remap_ctrl.gif
magick -size 15x640 gradient: -rotate 90 \
-remap ctrl_colors.gif -normalize mono_remap_grad_ctrl.gif
Yang sebenarnya dilakukan "-monochrome" adalah mengubah gambar yang diberikan lebih dulu menjadi gambar grayscale, setelah itu ia menjalankan 'kuantisasi warna' dua warna untuk menentukan warna ambang batas yang dipakai men-dither gambar tersebut. Inilah yang akan digali pada bagian contoh berikutnya. |
Pengaturan "+dither" saat ini tidak berpengaruh pada hasil "-monochrome". Namun hal itu bisa berubah di kemudian hari, jadi pastikan ia tidak dimatikan dalam skrip Anda saat memakai operator ini. |
|---|---|
Kuantisasi dua warna
Alih-alih memilih sendiri kedua warna kendali itu, kuantisasi warna dapat dipakai untuk memilih dua warna terbaik dalam gambar lewat operator "-colors". |
magick logo.png -colors 2 -colorspace gray -normalize \
colors_monochrome.gif
![[IM Output]](../static/img/quantize/colors_monochrome.gif)
Namun hasilnya tidak akan sama dengan memakai "-monochrome", sebab gambarnya tidak lebih dulu diubah ke grayscale. Sebaliknya, gambar itu langsung di-dither di antara dua nilai warna non-abu-abu yang terpilih. Artinya, dua warna terbaiklah yang dipilih untuk men-dither gambar, bukan dua tingkat kecerlangan grayscale. Akibatnya hasilnya lebih baik untuk, katakanlah, gambar yang hanya memakai warna dengan 'level' grayscale yang kurang lebih sama. Di sini misalnya kita memakai "-colors", dan juga operator dithering bitmap "-monochrome", pada gradien merah-biru. Dari situ terlihat bahwa hasilnya tidak sama.
magick -size 20x640 gradient:red-blue -rotate 90 gradient_rb.png
magick gradient_rb.png -colors 2 -colorspace gray \
-normalize colors_threshold.gif
magick gradient_rb.png -monochrome mono_threshold.gif
Operator "-monochrome" pada contoh di atas gagal menemukan perbedaan apa pun untuk di-dither menjadi bitmap, sebab biru dan merah intensitasnya nyaris sama. Sementara metode kuantisasi "-colors" sama sekali tidak kesulitan menemukan warna yang layak untuk dijadikan pasangan dither. Terlihat pula bahwa hanya bagian tengah warnanya yang di-dither. Ini karena kuantisasi warna memilih warna di tengah dua 'gugus' warna yang dipilihnya. Warna yang berada di 'luar' warna terpilih itu praktis langsung dipaksa ke warna tersebut lewat ambang batas, tanpa dithering. Hal ini menunjukkan bahwa warna di luar ruang warna kuantisasi tidak ikut di-dither, meski kenyataan ini sulit dimanfaatkan secara praktis. Dengan menyetel "-colorspace" ke grayscale sebelum kuantisasi, operasi internal operator "-monochrome" akan tereproduksi. |
magick logo.png -colorspace gray -colors 2 -normalize \
monochrome_equivelent.gif
![[IM Output]](../static/img/quantize/monochrome_equivelent.gif)
Dan terakhir, dengan mematikan dithering, dapat dihasilkan pemisahan warna dalam gambar yang lebih otomatis dibanding hasil pengaturan "-threshold" yang tetap. |
magick logo.png -colorspace gray +dither -colors 2 -normalize \
threshold_two_grays.gif
![[IM Output]](../static/img/quantize/threshold_two_grays.gif)
| Ingat, "-monochrome" saat ini mengabaikan pengaturan "+dither", jadi operator itu tidak bisa begitu saja dipakai untuk melakukan 'ambang batas cerdas'.
Bila "-colorspace" dihapus dari tahap kuantisasi warna dalam pemrosesan gambar, ambang batas dapat diterapkan pada gambar berdasar pemisahan warna terbaik (bukan pemisahan warna grayscale) yang mungkin untuk gambar itu. |
magick logo.png +dither -colors 2 -colorspace gray -normalize \
threshold_two_color.gif
Dither memakai peta warna terdefinisi
Seperti ditunjukkan di atas, "-colors" berusaha memilih kumpulan warna terbatas yang optimal untuk mewakili sebuah gambar. Dengan "-remap", Andalah yang memberi IM kumpulan warna akhir yang ingin dipakai untuk gambar itu, entah warna-warna tersebut hendak di-dither atau sekadar diganti dengan tetangga terdekatnya. Argumennya diberikan berupa sebuah gambar yang memuat semua warna yang ingin dipakai. Bila gambar besar berisi banyak warna ingin diperkecil menjadi sekadar daftar warnanya, pakai "-unique-colors" sebelum menyimpannya untuk dipakai nanti oleh "-remap". | _Perhatikan, meski operator "-remap" menerima gambar apa pun, jangan pakai gambar JPEG untuk keperluan ini atau akan muncul banyak warna tambahan akibat 'kompresi lossy'-nya yang membangkitkan warna ekstra.
Di sisi lain, memakai JPEG untuk membangkitkan warna tambahan justru bisa membantu mengatasi masalah 'bintik' yang tadi kita lihat!_
Sebagai contoh, di sini saya membatasi warna yang dipakai logo IM ke sebuah peta warna X window bernama yang sudah ditentukan. Bawaannya adalah dither 'Riemersma', tetapi sejak IM v6.4.4 "-dither" diperluas sehingga metode dither lain seperti 'FloydSteinberg' bisa dipilih. Tentu saja dithering tetap bisa dimatikan dengan opsi "+dither".
magick logo.png -dither None -remap colortable.gif remap_logo_no.gif
magick logo.png -dither Riemersma -remap colortable.gif remap_logo_rm.gif
magick logo.png -dither FloydSteinberg \
-remap colortable.gif remap_logo_fs.gif
Seperti terlihat, IM berusaha cukup baik mewakili gambar itu hanya dengan warna yang diberikan, meski hasilnya jauh dari sebagus gambar yang diperoleh bila IM dibiarkan memilih sendiri kumpulan warnanya. Perlu dicatat, gambar "colortable.gif" ini tidak pernah dirancang untuk men-dither gambar, melainkan sebagai kumpulan warna untuk merancang ikon berwarna bergaya kartun bagi layar warna X window lama yang lebih primitif (lihat Anthony's X Window Icon Library dan AIcons Color Selection untuk rinciannya). Perhatikan pula bahwa gambar akhirnya tidak memakai seluruh 32 warna yang disediakan peta ini, meski lebih banyak warna dalam peta itu terpakai ketika suatu bentuk dithering diaktifkan (
dan
berturut-turut) dibanding ketika dithering dimatikan (
). Contoh terakhir ini menunjukkan betapa pentingnya memilih peta warna yang baik. Karena itu saya menganjurkan agar IM dibiarkan mengoptimalkan pemilihan warna sebuah gambar dengan operator "-colors", lalu hasilnya dimodifikasi sesuai kebutuhan, kecuali ada alasan yang lebih mendesak untuk tidak melakukannya. Satu catatan terakhir: meski ruang warna tempat "-colors" mencari kumpulan warna terbaik dapat ditentukan, saat ini ruang warna untuk tahap pemetaan warna atau dithering TIDAK dapat ditentukan. Semua percobaan saya tampaknya menunjukkan bahwa kumpulan warna itu diterapkan (baik dither koreksi error maupun penggantian ke warna terdekat) berdasar ruang RGB. Pengaturan ruang warna "-quantize" hanya dipakai untuk pemilihan warna, bukan pemetaannya. Jadi kalau memakai peta warna ternyata ide yang begitu buruk, mengapa Anda ingin memakainya? Ada sejumlah alasan yang umum, biasanya karena Anda butuh kendali lebih atas palet warna tertentu yang dipakai sebuah gambar. Seorang pengguna lain juga memisahkan peta warnanya agar bisa dipakai pada Risograph (sebuah sistem pencetakan digital). Bila Anda tahu alasan lain memakai operator "-remap" yang belum saya sajikan di bawah - kirimi saya email.
Peta warna bersama atau 'terbaik'
Teknik lainnya, saat menangani banyak gambar sekaligus, adalah membangkitkan satu tabel warna bersama untuk semua gambar yang terlibat. Pada dasarnya semua gambar ditempelkan menjadi satu gambar besar, lalu operator "-colors" dipakai untuk menemukan peta warna yang baik dan berlaku bagi semua gambar itu. Setelah gambar peta warna itu ada, ia bisa dipakai untuk mewarnai ulang tiap gambar aslinya memakai peta warna terdefinisi yang baru saja dibuat itu. Cara lain, pakai operator khusus "+remap" yang melakukan hal yang sama untuk peta warna 255 warna. Ia mencacah warnanya, menjalankan kuantisasi warna untuk membentuk peta warna bersama yang baik, lalu men-dither gambar-gambar itu agar memakai peta tersebut, bila perlu. Namun baik bentuk "-remap" maupun "+remap" punya satu fitur yang sangat penting untuk animasi GIF. Keduanya mengubah semua gambar menjadi "-type" 'Palette' dengan semua gambar memakai palet warna yang sama. Alasannya, saat menulis gambar GIF, palet warna gambar pertama akan dipakai sebagai 'global colormap' format file itu. Lalu ketika tiap gambar ditulis, IM mencatat bahwa gambar-gambar itu memakai kumpulan warna yang sama, sehingga ia TIDAK membuat 'local colormap'. Hal ini bisa menghemat sampai 256 × 3, atau 768 byte ruang peta warna untuk tiap gambar dalam file GIF akhirnya. Hanya operator "-remap" yang bisa melakukan ini. Jadi saat menangani GIF, terutama animasi GIF, hal ini penting diingat. Untuk rincian dan contohnya lihat Animasi GIF, tabel warna global.
Pewarnaan web-safe
Ketika WWW baru diciptakan, layar komputer hanya punya rentang warna yang terbatas, dan peramban web biasanya memakai kumpulan warna yang lebih sederhana untuk gambar. Karena itu lazim dilakukan pewarnaan ulang gambar ke kumpulan warna ini, supaya ukurannya lebih kecil sekaligus memastikan tampilannya cukup baik di peramban pengguna. Untuk rinciannya lihat Web Style Guide, Dithering. Untuk membantu hal ini, IM menyediakan gambar peta warna bawaan berisi tabel khusus 216 warna tersebut, yang disebut "netscape:". Jadi mari kita lihat bagaimana tampilan gambar uji kita pada layar peramban web lama yang memakai warna-warna ini.
magick logo.png -remap netscape: remap_netscape.gif
magick logo.png +dither -remap netscape: remap_netscape_nd.gif
Kumpulan warna ini adalah palet yang ditentukan secara matematis, dirancang oleh para insinyur layar dan komputer, bukan oleh seniman grafis, dan meski cukup umum sehingga bekerja lumayan baik untuk gambar nyata seperti foto, ia sangat buruk untuk gambar yang memuat area warna rata yang luas, seperti logo, latar belakang, gambar buatan komputer seperti grafik, dan gambar bergaya kartun. Pada dasarnya ia bekerja pada area yang warnanya sangat beragam, tetapi untuk area rata berwarna tetap yang lebih luas, diterapkan dithering tiga warna (pada umumnya), seperti pada kemeja biru pudar di gambar uji logo IM (di atas). Dengan kata lain, bila Anda merancang gambar atau logo untuk dipakai di web, umumnya Anda akan berusaha memakai warna dari palet ini untuk area rata yang luas, dan hanya memakai warna ber-dither di area yang gradasi warnanya beragam. Perintah di atas memungkinkan Anda menguji gambar untuk melihat tampilannya pada layar komputer yang lebih primitif, dan menyunting gambar agar memakai warna-warna ini sehingga hasilnya baik. Hal ini terutama penting untuk simbol dan gambar navigasi. Tentu saja sekarang, berkat tuntutan pengguna game dan web, boleh cukup yakin bahwa kebanyakan pengguna memakai layar komputer modern yang tidak lagi punya keterbatasan warna lama itu; meski begitu penggunaan "palet web-safe" ini masih ada, sebab ia punya manfaat lain, misalnya kompresi gambar. Untuk pembahasan tentang penggunaan warna web-safe di dunia modern lihat Death of the Web-safe Color Palette?, dan barangkali pandangan yang lebih penting dari perancang grafis yang pertama kali mengenali peta warna ini, Lynda Weinman.
Membangkitkan peta warna
Menentukan peta warna yang baik untuk sembarang gambar, atau untuk sekumpulan gambar tertentu, bisa sangat penting. Hal ini menjadi sangat penting ketika yang ditangani adalah rangkaian gambar yang akan dipakai untuk animasi GIF. Pada dasarnya rangkaian itu diusahakan hanya butuh satu tabel warna untuk semua frame animasinya, bukan tabel warna terpisah untuk tiap frame. Dengan kata lain, yang diinginkan adalah satu peta warna tunggal untuk semua gambar. Dalam hal ini sebenarnya hanya ada dua pilihan. Anda bisa mencoba membuat peta warna yang bekerja baik untuk gambar apa pun, atau mencoba mengoptimalkan sebuah peta warna bagi kumpulan gambar tertentu yang akan dikenainya.
Peta warna web-safe
Metode pertama biasanya berupa peta warna yang dibangkitkan secara matematis, seperti peta warna bawaan IM "netscape:". Peta ini menyediakan 216 warna, yang pas masuk ke dalam batas 256 warna format GIF dan masih menyisakan ruang untuk menangani transparansi gambar, atau bahkan menambahkan beberapa warna ekstra untuk keperluan khusus seperti bayangan atau overlay teks. Peta warna ini dibangkitkan dengan membuat 6 tingkat warna untuk masing-masing dari tiga kanal warna, sehingga menghasilkan 6×6×6 warna alias 216 warna. Angka sang Binatang. Karena hanya 216 warna yang terpakai, masih tersisa ruang (untuk gambar GIF) guna menambahkan lebih banyak warna ke peta warna itu bagi keperluan tertentu. Misalnya sebuah warna transparan, serta lebih banyak gradasi grayscale. Sebuah versi Macintosh lama dari peta web-safe justru melakukan persis itu untuk mencoba memperbaiki hasil keseluruhannya, tetapi peta itu hanya dipakai pada klien web Macintosh. Inilah barangkali peta warna 'seragam' (atau yang diturunkan secara matematis) yang paling umum dipakai, berkat kesederhanaannya dan penggunaannya yang luas di World Wide Web.
Peta warna 332 seragam
Pemetaan warna seragam lain yang umum dipakai adalah "peta warna RGB 332". Angkanya merujuk pada jumlah bit yang dipakai untuk mewakili tiap warna dalam indeks warna 8 bit. Yaitu 3 bit (atau 8 tingkat) untuk merah, 3 untuk hijau, dan 2 bit (atau 4 tingkat warna) untuk biru, mengingat mata kita tidak begitu peka terhadap biru. Ini memberi 3+3+2 bit alias indeks warna 8 bit, atau 256 warna. Pas benar untuk tabel warna GIF yang terbatas. Namun ia tidak menyisakan ruang sama sekali untuk warna transparan GIF, atau warna keperluan khusus lainnya. Berikut satu cara agar IM membangkitkan peta warna ini... |
magick -size 16x16 xc: -channel R -fx '(i%8)/7' \
-channel G -fx '(j%8)/7' \
-channel B -fx '((i>>3&1)|(j>>2&2))/3' \
-scale 600% colormap_332.png
![[IM Output]](../static/img/quantize/colormap_332.png)
| Operator penggeser bit '>>' dan '<<' belum ada pada operator "-fx" sampai IM versi 6.2.9-2.
Cara yang lebih sederhana untuk hal yang sama adalah memakai Ordered dither dengan tingkat warna seragam lewat operasi "-ordered-dither threshold,8,8,4" (lihat area contoh tersebut). Teknik ini jauh lebih mudah dan cepat daripada metode FX rakitan sendiri di atas, dan bahkan memungkinkan pemakaian peta dithering bawaan lain untuk penanganan gradien yang lebih baik. Satu-satunya kekurangan peta ini adalah ia sama sekali tidak menyediakan warna 'abu-abu'. Namun kekurangan itu bisa jadi keuntungan ketika dithering dipakai, sebab selisih warna yang tipis mengurangi efek perubahan batas warna pada gradien grayscale, sehingga tampak sedikit lebih halus.
Peta warna TrueColor 16 bit
Peta warna seragam yang mirip dengan 'peta warna 332' di atas dipakai oleh X windows pada kelas visual 16 bit yang jarang digunakan. Di sini 16 bit dipakai untuk indeks warna, yang dibagi menjadi 5 bit untuk merah, 5 untuk hijau, dan 6 untuk biru. Dengan kata lain, peta warna ini lebih mirip "peta warna 556", dan paling baik dicapai dengan Ordered dither dengan tingkat warna seragam memakai peta dither 'threshold'. Tepatnya operasi "-ordered-dither threshold,32,32,64". Namun peta warna 16 bit jarang terlihat karena gambar yang memakai peta warna umumnya perlu tabel warna 8 bit. Karena itu saya tidak akan membahasnya lebih jauh.
Peta warna seragam terkoreksi gamma
Saat ini IM belum menangani peta warna terkoreksi gamma secara langsung. Yang sebaiknya dilakukan adalah mengonversi gambar Anda (dengan asumsi IM yang dipakai punya kualitas waktu kompilasi Q16 atau lebih baik), dari sRGB atau tingkat gamma apa pun yang dimiliki gambar itu ke model RGB linear, sebelum melakukan dithering. Hal yang sama berlaku untuk banyak operasi pemrosesan gambar lain, seperti resize, blur, dan sebagainya. Lihat Mengubah ukuran dengan koreksi gamma untuk contohnya.
Posterize, mewarnai ulang dengan peta warna seragam
Tujuan awal operator ini (dengan argumen '2') adalah mewarnai ulang gambar hanya dengan 8 warna dasar, seolah-olah gambar itu dihasilkan lewat metode cetak poster sederhana dan murah yang cuma memakai warna-warna dasar. Dari situlah operator ini mendapat namanya. Operator "-posterize" sebenarnya adalah operator reduksi warna khusus yang membangkitkan peta warna berdasarkan jumlah 'tingkat' warna yang diberikan, untuk tiap kanal warna dalam gambar, lalu men-dither gambar itu memakai dither koreksi error.
magick netscape: -scale 50% +dither -posterize 2 posterize_2_ns.gif
magick netscape: -scale 50% +dither -posterize 3 posterize_3_ns.gif
magick netscape: -scale 50% +dither -posterize 6 posterize_6_ns.gif
Seperti terlihat, argumen '2' pada "-posterize" berarti hanya menyediakan 2 warna per kanal warna, sehingga menghasilkan peta yang cuma berisi 8 warna untuk gambar RGB 3 kanal seperti di atas. Pada dasarnya gambar diwarnai ulang memakai himpunan 8 warna hasil ambang batas tersebut. Argumen '3' akan memetakan warna gambar berdasarkan peta warna 27 warna, termasuk warna nada tengah. Sedangkan argumen '4' membangkitkan tabel warna 64 warna, dan '5' menghasilkan peta warna 125 warna. Tentu saja, seperti disebut di atas, argumen '6' akan menghasilkan kembali himpunan 216 warna yang sama dengan yang disediakan gambar bawaan "netscape:". Perhatikan bahwa argumen '0' atau '1' pada "-posterize" tidak masuk akal, dan pada rilis IM terbaru cuma mengubah gambar menjadi hitam murni (yang meski logis, sama sekali tidak berguna). Hasilnya, gambar diwarnai ulang memakai peta warna yang diturunkan secara matematis alias 'seragam'. Hal ini terlihat lebih jelas pada gambar gradien, yang menghasilkan sebaran tingkat abu-abu terposterisasi yang merata.
#magick -size 20x640 gradient: -rotate 90 gradient.png
magick gradient.png +dither -posterize 5 posterize_gradient.gif
Sebagai contoh, mari kita posterisasi gambar logo IM pada berbagai tingkat...
magick logo.png +dither -posterize 2 posterize_logo.gif
magick logo.png -posterize 2 posterize_logo_dither.gif
magick logo.png -posterize 6 posterize_6_logo.gif
Sebagai uji yang lebih baik, mari kita posterisasi gambar "colorwheel" yang bergradasi.
magick colorwheel.png +dither -posterize 2 posterize_2_cw.gif
magick colorwheel.png +dither -posterize 3 posterize_3_cw.gif
magick colorwheel.png +dither -posterize 6 posterize_6_cw.gif
Dan berikut hal yang sama dengan dithering diaktifkan...
Tentu saja banyak dither bitmap yang kita lihat di bagian berikutnya juga bisa menghasilkan ordered dither tingkat 2, dengan berbagai macam gaya dither. Namun hanya sedikit yang bisa memakai tingkat abu-abu yang lebih banyak. Ordered dither sejak IM v6.2.9 juga merupakan metode posterisasi, karena keterbatasannya saat ini yang hanya bisa men-dither memakai peta warna seragam. Namun pola dithernya lebih seragam, dengan pilihan gaya yang lebih banyak, dibanding dither semi-acak yang dihasilkan "-posterize". Bandingkan ini dengan versi "-posterize" ber-dither di atas.
magick colorwheel.png -ordered-dither o8x8,2 posterize_2_od.gif
magick colorwheel.png -ordered-dither o8x8,3 posterize_3_od.gif
magick colorwheel.png -ordered-dither o8x8,6 posterize_6_od.gif
Peta dither 'threshold' (menggantikan 'o8x8' yang dipakai di atas) secara efektif mengubah "-ordered-dither" menjadi metode posterisasi tanpa dither. Terakhir, Ordered dither memang memungkinkan penentuan jumlah tingkat warna yang berbeda untuk tiap kanal warna. Sesuatu yang saat ini belum bisa dilakukan operator "-posterize".
Metode dithering ambang batas
Gambar ambang batas
Metode paling sederhana untuk mengubah gambar menjadi gambar bitmap hitam putih (dua warna) adalah memakai "-threshold". Ini sebenarnya operator matematis sederhana yang cuma menyediakan nilai batas. Apa pun yang sama dengan atau di bawah nilai itu menjadi hitam, sedangkan yang lebih besar menjadi putih.
magick logo.png -threshold -1 threshold_0.gif
magick logo.png -threshold 25% threshold_25.gif
magick logo.png -threshold 50% threshold_50.gif
magick logo.png -threshold 75% threshold_75.gif
magick logo.png -threshold 100% threshold_100.gif
Seperti terlihat, nilai '-1' akan mengubah semua warna menjadi putih, sedangkan '100%' mengubah semua warna menjadi hitam. Tentu saja '50%' adalah nilai yang paling umum dipakai. Nilai '0' adalah kasus khusus yang membuat semua warna yang bukan hitam murni menjadi putih. Tentu kalau gambarnya tidak punya warna hitam murni, yang didapat cuma gambar putih polos! |
magick logo.png -threshold 0 threshold_black.gif
![[IM Output]](../static/img/quantize/threshold_black.gif)
Kalau Anda memang ingin mengubah semua warna yang bukan putih murni menjadi hitam, saya sarankan menerapkan ambang batas pada gambar yang dinegasikan alih-alih repot mencari nilai ambang batas yang tepat (satu tingkat di bawah 'MaxRGB' IM saat ini), nilai yang bergantung pada pengaturan Quality, atau 'Q' waktu kompilasi IM yang dipakai. |
magick logo.png -negate -threshold 0 -negate threshold_white.gif
![[IM Output]](../static/img/quantize/threshold_white.gif)
Operator "-threshold" bisa digolongkan sebagai operator 'kontras' yang paling ekstrem, karena memaksimalkan perbedaan warna berdasarkan tingkat ambang batas. Namun ini operator grayscale, artinya pengaturan "-channel" dapat dipakai untuk menentukan kanal warna mana yang akan dikenai operator ini. Sebagai contoh, tiap kanal gambar bisa diberi ambang batas satu per satu untuk menghasilkan efek yang sama dengan operasi "-posterize" tingkat 2 tanpa dither. |
magick logo.png -channel R -threshold 50% \
-channel G -threshold 50% \
-channel B -threshold 50% threshold_posterize.gif
![[IM Output]](../static/img/quantize/threshold_posterize.gif)
| Perhatikan bahwa "-threshold" memperlakukan transparansi apa pun dalam gambar sebagai kanal matte, bukan kanal alpha (persis seperti cara IM menyimpannya secara internal). Karena itu berhati-hatilah bila Anda berencana menerapkan operator ini pada kanal alpha. Lihat Kanal matte untuk detail lebih lanjut.
Untuk teknik ambang batas yang lebih otomatis, gunakan teknik Kuantisasi dua warna yang sudah kita tunjukkan sebelumnya. Sebagai contoh, perintah berikut memberi ambang batas pada gambar berdasarkan dua warna terbaik yang ditemukan di dalamnya. Kedua warna itu belum tentu grayscale atau bahkan berlawanan, hanya dua warna yang paling mewakili keseluruhan gambar. Kedua warna tersebut lalu dipetakan (memakai "-normalize") menjadi hitam dan putih murni. |
magick logo.png +dither -colors 2 -colorspace gray -normalize \
threshold_two_color.gif
Dither acak dan ambang batas
Operator "-random-threshold" adalah bentuk khusus dari pengubah gambar bitmap. Di sini ia memakai "dither acak" yang sangat sederhana untuk menentukan apakah suatu piksel akan menjadi piksel putih atau piksel hitam. Berbeda dengan operator "-threshold" atau "-monochrome", bahkan berbeda dari variasi di bagian sebelumnya, kanal-kanal terpilih (ditentukan dengan "-channels") tidak digabung menjadi satu kanal grayscale lalu di-dither sebagai satu kesatuan. Sebaliknya, "-random-threshold" bekerja pada tiap kanal terpilih sepenuhnya secara terpisah satu sama lain. Tentu saja memakai operator ini secara langsung akan menghasilkan posterisasi 2 tingkat pada gambar dengan dither acak. |
magick logo.png -random-threshold 0x100% random_posterize.gif
![[IM Output]](../static/img/quantize/random_posterize.gif)
Mengonversi ke grayscale akan menyamakan semua kanal dalam gambar sebelum di-dither. Tetapi karena tiap kanal di-dither terpisah satu sama lain dan secara acak, hasilnya bukan gambar bitmap seperti yang diduga. Yang muncul justru cipratan piksel berwarna, terutama pada warna nada tengah. |
magick logo.png -colorspace Gray -random-threshold 0x100% \
random_greyscale.gif
![[IM Output]](../static/img/quantize/random_greyscale.gif)
Berikut cara yang benar untuk menghasilkan gambar bitmap ber-dither acak yang semestinya. |
magick logo.png -colorspace Gray -channel B \
-random-threshold 0x100% -separate random_monochome.gif
![[IM Output]](../static/img/quantize/random_monochome.gif)
Pada dasarnya yang dilakukan adalah men-dither satu kanal saja dari gambar grayscale itu, lalu memakai operator kanal "-separate" untuk mengambil kanal tersebut sebagai gambar bitmap akhir. Rumit, tetapi ampuh. Sebagai fitur khusus operator ini, IM akan memastikan gambar bitmap dihasilkan bila opsi "-channels" khusus 'All' dipakai. |
magick logo.png -channel All -random-threshold 0x100% random_all.gif
![[IM Output]](../static/img/quantize/random_all.gif)
Namun perlu diperhatikan bahwa kanal alpha apa pun akan diabaikan dan hilang dengan metode ini, sehingga cara ini biasanya tidak disarankan. Saya sendiri baru menemukan fitur kuno ini secara tidak sengaja dari kode sumber. Sekarang setelah Anda tahu cara memakai operator ini untuk menghasilkan bitmap dari gambar berwarna dengan benar, mari kita lihat bagaimana argumennya memengaruhi rentang dithering. Contoh berikut juga memperlihatkan dengan jelas 'penggumpalan' piksel yang dihasilkan dither ini.
#magick -size 20x640 gradient: -rotate 90 gradient.png
magick gradient.png -channel All \
-random-threshold 0x100% random_grad_0x100.gif
magick gradient.png -channel All \
-random-threshold 10x90% random_grad_10x90.gif
magick gradient.png -channel All \
-random-threshold 25x75% random_grad_25x75.gif
magick gradient.png -channel All \
-random-threshold 50x50% random_grad_50x50.gif
Pengaturan "-random-threshold" '0x100%' akan menghasilkan 'Dither Acak' murni pada gambar. Jika kedua batasnya diberi nilai yang sama (atau bahkan saling melewati), yang dihasilkan hanyalah gambar "-threshold" murni. Memakai pasangan batas lain (biasanya ditulis dalam persen) akan memberi ambang batas pada bitmap di luar rentang yang diberikan, sekaligus menghasilkan pola dither acak untuk nilai-nilai di dalam rentang itu. Hasil terbaik diperoleh dengan memakai rentang yang sedikit lebih sempit, seperti yang didapat dari operator "-monochrome". Nilai sekitar '30x80%' mungkin memberi hasil terbaik untuk sebagian besar kasus. |
magick logo.png -channel All -random-threshold 30x80% random_30x80.gif
![[IM Output]](../static/img/quantize/random_30x80.gif)
Tentu saja hasilnya masih belum bagus. Tetapi memang inilah bentuk dithering paling sederhana sekaligus paling buruk yang bisa didapat. Yang sebenarnya terjadi adalah pola dither acak cenderung menghasilkan 'gumpalan' piksel alih-alih pola dither yang halus. Ini disebabkan oleh 'noise' frekuensi tinggi pada pembangkit bilangan acak. Namun pada resolusi yang sangat tinggi, dither acak terbukti memberi hasil yang sangat bagus, asalkan cukup acak. IM memakai keacakan setingkat kriptografi sehingga hasilnya kemungkinan besar sangat acak, meski gambar jarang dipakai pada resolusi yang cukup tinggi untuk membuatnya berguna dengan cara ini. Satu 'perbaikan' untuk dither ini yang pernah diusulkan adalah memakai pembangkit 'blue noise' acak (filter frekuensi tinggi, kebalikan dari filter 'pink noise' frekuensi rendah yang dipakai dalam produksi suara). Cara itu semestinya menghilangkan penggumpalan piksel, tetapi sangat sulit diwujudkan secara digital. Belum ditemukan implementasi 'dither acak blue noise' mana pun, dan rasanya tidak akan pernah dibuat.
Ordered dithering
Kalau dither acak menghasilkan gumpalan piksel yang acak, dan berbagai dither koreksi error menghasilkan pola titik yang pada dasarnya acak, ordered dithering justru kebalikannya. Ia dirancang agar sedeterministik mungkin secara matematis. Begitu deterministiknya sampai-sampai polanya harus ditentukan sendiri untuk men-dither gambar. Operator "-ordered-dither" akan men-dither tiap "-channels" terpilih dalam gambar dengan pola yang sudah ditentukan sebelumnya. Argumennya menentukan pola (yang dikenal sebagai peta ambang batas) yang dipakai. Peta ambang batas ini terbagi menjadi tiga gaya dasar. Dither piksel terdifusi, yaitu piksel ditempatkan sejauh mungkin satu sama lain agar terhindar dari 'penggumpalan' dan artefak tiling. Atau justru menggumpalkannya menjadi titik-titik rapat yang lebih mudah dicetak secara mekanis, dalam teknik yang dikenal sebagai Halftoning digital. Ada pula beberapa peta ambang batas artistik khusus yang akan kita lihat juga, bahkan kita akan merancang pola dither atau peta ambang batas kita sendiri. Dalam setiap kasus, jumlah piksel yang menyala atau mati di peta ambang batas bergantung pada intensitas tingkat abu-abu gambar (atau kanal warna masing-masing) yang sedang di-dither menjadi bitmap. Peta itu menambahkan tingkat ambang batas piksel secara konsisten, sehingga begitu sebuah piksel menyala pada suatu 'ambang batas', ia tetap menyala untuk warna abu-abu yang lebih terang mana pun. Konsistensi ini sangat penting, sebab kalau tidak, artefak akan muncul di sepanjang batas perubahan pola dither. Poin pentingnya adalah hasil untuk tiap piksel dalam gambar ditentukan murni secara matematis, terlepas dari piksel lain mana pun dalam gambar itu. Karena itu perubahan kecil apa pun pada gambar asli sama sekali tidak berpengaruh pada gambar di area lain, sebuah masalah yang dimiliki Dither koreksi error, seperti kita lihat di atas. Poin ini sangat penting bagi dithering yang konsisten pada gambar video dan animasi yang dioptimalkan.
Dithering piksel terdifusi
Tujuan awal ordered dithering, dan apa yang diharapkan sebagian besar pemrogram grafis ketika memakai ordered dither, kadang lebih tepat disebut "Diffused Pixel Ordered Dither". Maksudnya, piksel ditambahkan ke peta yang di-tile seiring naiknya intensitas ambang batas, sehingga letaknya sejauh mungkin satu sama lain dan sebarannya semerata mungkin. Ini menghasilkan pola yang sangat konsisten, terlihat cukup halus dan nyaris tak kelihatan pada kebanyakan layar modern. Pola semacam itu sudah dirumuskan untuk ukuran tiling yang merupakan pangkat 2, yaitu ukuran tile 2, 4, dan 8. Meski begitu IM juga menyediakan pola ambang batas yang lumayan untuk tile peta ambang batas 3 kali 3. Berikut kumpulan ordered dither bawaan yang saat ini disediakan IM. Ingat, argumennya mencerminkan ukuran tile ordered dither itu.
magick logo.png -ordered-dither o2x2 logo_o2x2.gif
magick logo.png -ordered-dither o3x3 logo_o3x3.gif
magick logo.png -ordered-dither o4x4 logo_o4x4.gif
magick logo.png -ordered-dither o8x8 logo_o8x8.gif
Perhatikan bahwa ukuran tile yang lebih besar memungkinkan Anda menyimulasikan lebih banyak 'tingkat warna', tetapi juga memunculkan cacat atau larik piksel persegi panjang yang lebih kentara pada tingkat tertentu. | _Ordered dither 'o8x8' sudah lama menjadi bagian kode inti IM, tetapi tidak dipakai. Ia baru ditambahkan sebagai opsi operator "-ordered-dither" pada IM v6.2.9, saat IM Examples mulai membahas pemakaian operator ini secara rinci.
Pada saat itu peta-peta tersebut diberi nama yang lebih pasti agar operator "-ordered-dither" bisa dikembangkan lebih jauh, meski nama 'ukuran tile' lama yang kompatibel ke belakang tetap dipertahankan sebagai alias untuk nama-nama baru itu.
Selain itu 'peta' yang menghasilkan 'o3x3' dan 'o4x4' dirombak total agar menghasilkan pola dither 'diffused pixel' yang lebih baik. Sebelumnya peta-peta itu menghasilkan 'gumpalan' piksel yang kentara.
Lihat halaman catatan Ordered Dither Upgrade untuk contoh pola lama sebelum diperbaiki, serta perubahan lain yang dilakukan selama pengembangan menuju rilis resmi peningkatan tersebut di IM v6.3.0._
Tentu saja gambar perlu dikonversi ke grayscale lebih dulu untuk menghasilkan bitmap yang benar dari semua kanal dalam gambar, namun karena prosesnya tidak acak, gambar tidak perlu diproses ulang seperti pada operator -random-threshold" sehingga urusannya jauh lebih sederhana.
magick logo.png -colorspace Gray -ordered-dither o2x2 logo_bw_o2x2.gif
magick logo.png -colorspace Gray -ordered-dither o3x3 logo_bw_o3x3.gif
magick logo.png -colorspace Gray -ordered-dither o4x4 logo_bw_o4x4.gif
magick logo.png -colorspace Gray -ordered-dither o8x8 logo_bw_o8x8.gif
Sebagai rujukan, berikut tiap pola 'diffused pixel' "-ordered-dither" yang diterapkan pada gradien grayscale, agar terlihat jelas seperti apa bentuknya.
# Threshold Non-Dither / Minimal Checkerboard Dither
magick gradient.png -ordered-dither threshold od_threshold.gif
magick gradient.png -ordered-dither checks od_checks.gif
# Diffused Pixel Dither
magick gradient.png -ordered-dither o2x2 od_o2x2.gif
magick gradient.png -ordered-dither o3x3 od_o3x3.gif
magick gradient.png -ordered-dither o4x4 od_o4x4.gif
magick gradient.png -ordered-dither o8x8 od_o8x8.gif
Jumlah pola tingkat efektif atau semu yang dihasilkan suatu ordered dither biasanya (walau tidak selalu) sama dengan jumlah piksel dalam pola tersebut ditambah satu. Karena itu ordered dither 'o3x3' akan menghasilkan 3×3+1 atau 10 tingkat abu-abu efektif per kanal (hitam, putih, dan 8 pola abu-abu buatan) pada gambar hasilnya. Di atas juga ditampilkan dua peta ambang batas dither minimal khusus:
- non-dither 'ambang batas 50%' lurus, yang tidak menghasilkan tingkat abu-abu tambahan, dan
- pola dither 'checks' atau papan catur, yang hanya menyisipkan satu pola untuk menambah satu 'tingkat semu' ekstra pada gradien hasilnya.
Dither halftone digital
"-ordered-dither" diperluas pada IM v6.2.8-6 dengan sekumpulan pola dither halftone digital (terima kasih Glenn Randers-Pehrson). Semuanya disetel untuk menghasilkan pola titik 45 derajat yang sederhana. Pada IM v6.3.0 hal ini diperluas lagi dengan kumpulan halftone tanpa sudut yang lebih besar. |
Sebelum rilis IM v6.3.0, screen halftone dipilih memakai argumen berbentuk '{number}x1'. Dengan Re-Development of Ordered Dither, batasan itu dihapus, penamaan yang lebih baik dipilih, dan screen halftone tambahan (bentuk ortogonal) ditambahkan (lihat contoh argumen di bawah). |
|---|---|
| Perhatikan bahwa halftoning digital bukan benar-benar screen halftone sejati, yang dirancang untuk menangani titik-titik tinta bundar yang diletakkan secara mekanis pada medium seperti kertas, karton, bahkan logam. Titik semacam itu bisa saling tumpang tindih dan melebar selama proses pencetakan, sehingga perlu penyesuaian tingkat yang tidak linear. Hal ini tidak diperlukan untuk menghasilkan efek halftone yang murni digital. Untuk detail prosesnya, lihat dokumen Dithering and Halftoning (PDF). Meski begitu, pola halftone digital Ordered dither memang memberi efek dasar yang sama dengan yang terlihat di koran dan majalah cetakan murah. |
# Halftone Screen (45 degree angle)
magick logo.png -ordered-dither h4x4a logo_h4x4a.gif
magick logo.png -ordered-dither h6x6a logo_h6x6a.gif
magick logo.png -ordered-dither h8x8a logo_h8x8a.gif
# Halftone Screen (orthogonal)
magick logo.png -ordered-dither h4x4o logo_h4x4o.gif
magick logo.png -ordered-dither h6x6o logo_h6x6o.gif
magick logo.png -ordered-dither h8x8o logo_h8x8o.gif
Sekali lagi, gunakan operator "-colorspace" untuk menghasilkan dither bitmap sejati dari sebuah gambar.
# Halftone Screen (45 degree angle)
magick logo.png -colorspace Gray -ordered-dither h4x4a logo_bw_h4x4a.gif
magick logo.png -colorspace Gray -ordered-dither h6x6a logo_bw_h6x6a.gif
magick logo.png -colorspace Gray -ordered-dither h8x8a logo_bw_h8x8a.gif
# Halftone Screen (orthogonal)
magick logo.png -colorspace Gray -ordered-dither h4x4o logo_bw_h4x4o.gif
magick logo.png -colorspace Gray -ordered-dither h6x6o logo_bw_h6x6o.gif
magick logo.png -colorspace Gray -ordered-dither h8x8o logo_bw_h8x8o.gif
Dan terakhir, satu lagi gambar gradien rujukan untuk memperlihatkan dengan jelas pola dither halftone, serta bagaimana gumpalan piksel di dalam pola dither saling menyatu seiring berubahnya tingkat abu-abu.
# Halftone Screen (45 degree angle)
magick gradient.png -ordered-dither h4x4a od_h4x4a.gif
magick gradient.png -ordered-dither h6x6a od_h6x6a.gif
magick gradient.png -ordered-dither h8x8a od_h8x8a.gif
# Halftone Screen (orthogonal)
magick gradient.png -ordered-dither h4x4o od_h4x4o.gif
magick gradient.png -ordered-dither h6x6o od_h6x6o.gif
magick gradient.png -ordered-dither h8x8o od_h8x8o.gif
magick gradient.png -ordered-dither h16x16o od_h16x16o.gif
# Circle Halftones (black and white)
magick gradient.png -ordered-dither c7x7b od_c7x7b.gif
magick gradient.png -ordered-dither c7x7w od_c7x7w.gif
Sampai ImageMagick versi 6.2.9, semua peta ordered dither ambang batas di atas adalah satu-satunya yang mungkin dilakukan IM. Kini hal itu berubah, pengguna bisa menambahkan pola mereka sendiri, bahkan menyumbangkannya ke komunitas IM. Ambang batas halftone 'Circle' ditambahkan oleh Glenn Randers-Pehrson pada IM v6.6.5-6.
Dither halftone offset
Satu-satunya masalah pada dither halftone di atas adalah peta ambang batas (tile) yang persis sama diterapkan ke semua kanal warna dengan cara yang sama pula. Artinya himpunan warna primer yang sama disusun dalam titik-titik dengan 'pusat' yang sama. Untuk mendapatkan apa yang dikenal sebagai 'Offset Printing', pola ambang batasnya diputar mengikuti pola tertentu sehingga warna-warnanya membentuk 'pola roset' berskala kecil yang menghancurkan pola interferensi (moiré) yang jauh lebih buruk dilihat, yang kalau tidak begitu akan muncul. Diagram berikut pada dasarnya menjelaskan prosesnya, dan diuraikan sangat rinci di halaman Wikipedia, Halftone.
![[IM Output]](../static/img/img_diagrams/cmyk_offset.png)
Namun perhatikan bahwa screen yang diputar tidak bisa di-tile dengan baik, sehingga ide terbaiknya adalah membangkitkan pola yang sudah diputar itu secara langsung, bukan memakai pola ambang batas yang di-tile. Berikut satu cara memberi gambar tampilan cetak offset-halftone, memakai pola papan catur 2x2 piksel kecil yang diputar, kira-kira 'screen' terkecil yang bisa dipakai. |
magick colorwheel.png -set option:distort:viewport '%wx%h+0+0' \
-colorspace CMYK -separate null: \
\( -size 2x2 xc: \( +clone -negate \) \
+append \( +clone -negate \) -append \) \
-virtual-pixel tile -filter gaussian \
\( +clone -distort SRT 60 \) +swap \
\( +clone -distort SRT 30 \) +swap \
\( +clone -distort SRT 45 \) +swap \
\( +clone -distort SRT 0 \) +swap +delete \
-compose Overlay -layers composite \
-set colorspace CMYK -combine -colorspace RGB \
offset_colorwheel.png
![[IM Output]](../static/img/quantize/offset_colorwheel.png)
Perhatikan bahwa keempat 'screen' yang diputar diterapkan ke gambar secara keseluruhan, dan hanya langkah "-combine" dalam ruang warna CMYK yang benar-benar mengambil 4 kanal warna berbeda dari gambar-gambar yang sudah di-screen. Selain itu, distorsi 'no-op' untuk kanal 'black' terakhir itu penting karena ia akan mem-blur pola papan catur masukan sesuai filter Gaussian yang dipakai pada kanal lain selama perputarannya, walaupun screen itu sendiri tidak diputar. Dan di sini saya memakai fungsi penskalaan SRT Distort yang dipakai untuk membangkitkan tile yang diputar, agar tercipta 'pola screen' yang sedikit lebih besar dan lebih buram.
magick parrots_med.png -set option:distort:viewport '%wx%h+0+0' \
-colorspace CMYK -separate null: \
\( -size 2x2 xc: \( +clone -negate \) \
+append \( +clone -negate \) -append \) \
-virtual-pixel tile -filter gaussian \
\( +clone -distort SRT 2,60 \) +swap \
\( +clone -distort SRT 2,30 \) +swap \
\( +clone -distort SRT 2,45 \) +swap \
\( +clone -distort SRT 2,0 -blur 0x0.7 \) +swap +delete \
-compose Overlay -layers composite \
-set colorspace CMYK -combine -colorspace RGB \
offset_parrots.png
Perhatikan bahwa polanya tetap sangat 'menyerupai persegi', terutama screen hitam yang menjadi asal semua screen lainnya. FUTURE POSSIBILITY: Replace the 2 pixel checkboard in the above with "pattern:gray50" pixel level checkerboard pattern. Gaussian filter options can be used to adjust the bluriness of the scaled pattern. Alternatively, you can blurry scale the pattern, and thresholded it, to make rounder dots. This can then be rotated as previously to create the 4 color screens. It also would be better is a larger screen using a pattern of hexagonal dots could be used, rather than the checkerboard pattern I have used above.
Penting dicatat bahwa cara ini sebenarnya tidak membangkitkan titik-titik warna seperti cetak offset sejati, melainkan memalsukannya dengan sekadar mengalikan screen warna terhadap gambar aslinya. Hal ini terlihat dari perubahan warna yang tajam di sepanjang tepi burung nuri merah terhadap latar belakang hijau. Cetak offset sejati yang hanya memakai titik warna murni tidak akan punya perubahan warna di tengah titik. Yang berubah semestinya adalah ukuran titik berwarna murni itu, bergantung pada rata-rata warna di area yang diwakili titik tersebut pada gambar sumber. Untuk benar-benar menghasilkan gambar cetak offset sejati yang hanya berisi titik bundar berukuran pas di tiap kanal warna, dibutuhkan kerja yang jauh lebih banyak. Warna rata-rata tiap titik di tiap kanal warna perlu ditentukan lebih dulu, lalu dari situ dibangkitkan titik berwarna (lingkaran anti-alias) dengan ukuran yang sesuai. Ada yang mau mencoba? Yang di atas berasal dari diskusi di forum IM, CMYK Halftone Effect, yang membahas bagaimana Photoshop 'memalsukannya', dan bagaimana ImageMagick bisa mencapai efek yang sama. Diskusi ini juga berkaitan dengan B/W Halftone Dither yang menelaah lebih rinci pembuatan screen halftone sejati memakai titik nyata berukuran pas. Namun diskusi itu tidak melangkah ke tahap berikutnya, yaitu memakai screen offset (yang diputar). Screen semacam itu mungkin menuntut gambar diputar untuk membangkitkan titik-titiknya, lalu pola titiknya diputar kembali untuk kanal warna tertentu itu.
Peta ambang batas XML
Sejak IM versi 6.3.0, alih-alih memakai kumpulan peta tetap yang ditanam di kode sumber IM (seperti ditunjukkan sebelumnya), peta-peta itu kini dibaca dari sekumpulan file data XML di luar programnya sendiri. Sebagai bagian dari perubahan ini, kini daftar 'peta ambang batas' yang tersedia dan bisa dipakai operator "-ordered-dither" dapat ditampilkan.
magick identify -list threshold
Daftar di atas tidak hanya menampilkan peta ambang batas yang tersedia, tetapi juga alias yang disediakan demi kompatibilitas ke belakang atau penamaan alternatif, serta peta yang saya definisikan di file data XML "thresholds.xml" pribadi saya (disimpan di sub-direktori ".magick" pada direktori home saya). Ketika "-ordered-dither" mencari sebuah peta, peta pertama yang ditemukannya dalam daftar di ataslah yang dipakai. Karena itu pola ambang batas yang didefinisikan sistem tidak bisa ditimpa. File sistem "thresholds.xml" (yang path-nya diberikan oleh opsi "-list" di atas) berisi ringkasan lengkap format file XML tersebut. Formatnya cukup sederhana (dengan pemeriksaan error oleh IM) sehingga pengguna bisa mendefinisikan dan membuat peta ambang batas ordered dither mereka sendiri. Sebagai contoh, berikut salinan peta ambang batas 'diag5x5' yang saya definisikan di file "threshold.xml" pribadi saya.
Kalau diperhatikan, definisi itu membentuk peta 5x5 sederhana berisi satu garis diagonal yang menebal seiring naiknya tingkat ambang batas. Nomor tingkat dalam peta berjalan dari 0 sampai 5, satu kurang dari divisor, yang menyatakan berapa banyak 'abu-abu' yang diperlukan untuk membagi gradien warna. Berikut sebuah gradien yang di-dither memakai peta ambang batas pribadi ini.
magick gradient.png -ordered-dither diag od_diag.gif
Dan berikut contoh pemakaian ambang batas itu untuk men-dither kanal alpha sebuah gambar berbayang sederhana, keperluan yang memang menjadi alasan saya merancangnya.
magick -size 70x60 xc:none -font Candice -pointsize 50 \
-fill black -annotate +10+45 'A' -channel RGBA -blur 0x5 \
-fill white -stroke black -draw "text 5,40 'A'" shadow.png
magick shadow.png -channel A -ordered-dither diag shadow_diag.gif
Keren, kan! Nanti akan ada lagi soal dithering kanal alpha. Tapi pertama-tama saya perlu menunjukkan cara memakai kemampuan pewarnaan operator "-ordered-dither" yang diperluas.
Ordered dither dengan tingkat warna seragam
Dengan rilis IM v6.3.0, bukan hanya peta ambang batas yang dipakai "-ordered-dither" diubah agar dibaca dari file eksternal, tetapi operasi internalnya juga ditingkatkan sehingga bisa memakai peta warna 'terposterisasi' yang didefinisikan secara matematis. Artinya kita bisa menghasilkan dithering gambar yang lebih deterministik daripada yang bisa dicapai dengan 'dithering koreksi error'. Ini sangat penting untuk reduksi warna yang melibatkan animasi, sebab tidak akan muncul masalah akibat perbedaan warna antar-frame. Tingkat posterisasi diberikan ke argumen "-ordered-dither" lewat daftar angka tambahan yang dipisahkan koma dan dilekatkan pada nama peta ambang batas yang dipakai. Bila tidak ada angka yang diberikan, operator ini kembali ke peta warna 2 warna biasa (atau posterize tingkat 1). Sebagai contoh, argumen 'checks,6' akan memakai Peta warna web-safe klasik (posterisasi tingkat 6) yang juga didefinisikan oleh gambar peta warna bawaan "netscape:. Namun karena peta dither minimal 'checks' yang dipakai, satu tingkat dithering ekstra disisipkan di antara tiap-tiap dari 6 tingkat warna itu sehingga tercipta 11 tingkat warna semu di tiap kanal gambar. Dengan kata lain, meski hanya 6 tingkat warna per kanal yang dipakai (menghasilkan 6^3 atau 216 warna), satu pola dither di antara tingkat-tingkat itu menaikkan dither menjadi 11 tingkat efektif (menghasilkan 11^3 atau 1331 warna efektif). Sebagai contoh, berikut gradien grayscale yang di-dither memakai 6 tingkat abu-abu dan berbagai peta ambang batas. Peta pertama 'threshold' adalah peta ambang batas ordered dither khusus yang tidak melakukan dithering, dan hanya menampilkan warna-warna yang dipakai.
magick gradient.png -ordered-dither threshold,6 od_threshold_6.gif
magick gradient.png -ordered-dither checks,6 od_checks_6.gif
magick gradient.png -ordered-dither o2x2,6 od_o2x2_6.gif
magick gradient.png -ordered-dither o4x4,6 od_o4x4_6.gif
magick gradient.png -ordered-dither o8x8,6 od_o8x8_6.gif
Seperti terlihat, meski hanya 6 warna yang dipakai, dengan ordered dithering jumlah warna efektif untuk mendefinisikan gradien itu bertambah sampai Anda akan kesulitan menyadari betapa sedikitnya warna yang sebenarnya terpakai! Bukan hanya jumlah tingkat posterisasi untuk semua kanal yang bisa ditentukan, tetapi berbeda dengan opsi dither koreksi error "-posterize", tingkatnya bisa ditentukan untuk tiap kanal. Angka-angkanya diberikan ke kanal sesuai pengaturan "-channels". Sebagai contoh, di sini kita men-dither gradien memakai peta warna 332 khusus (8 tingkat untuk merah dan hijau, 4 untuk biru) yang mendefinisikan total 256 warna.
magick gradient.png -ordered-dither o8x8,8,8,4 od_o8x8_884.gif
Karena jumlah tingkat warna per kanal berbeda-beda, gambar di atas tidak hanya berisi warna abu-abu murni, melainkan juga sejumlah piksel kebiruan dan kekuningan yang saling meniadakan sehingga menghasilkan tingkat abu-abu tambahan. Sekarang bandingkan versi ber-O-dither dengan versi ber-dither koreksi error memakai tingkat posterisasi 2 dan 6, serta "peta warna 332" (8 tingkat merah dan hijau, 4 biru).
magick logo.png -ordered-dither o8x8 logo_o8x8_2.gif
magick logo.png -posterize 2 logo_posterize_2.gif
magick logo.png -ordered-dither o8x8,6 logo_o8x8_6.gif
magick logo.png -posterize 6 logo_posterize_6.gif
magick logo.png -ordered-dither o8x8,8,8,4 logo_o8x8_332.gif
magick logo.png -remap colormap_332.png logo_remap_332.gif
Gambar pertama tiap pasangan di atas di-dither secara ordered matematis, sedangkan yang kedua di-dither 'koreksi error' secara semi-acak. Pasangan terakhir memakai 'peta warna 332' khusus (lihat Membangkitkan peta warna), yang dianggap mungkin merupakan peta warna posterize terbaik untuk gambar umum dengan batas 256 warna. Perbedaan ganjil pada tingkat kanal itulah yang menghasilkan gradasi warna sedikit lebih baik untuk gambar bergaya kartun ini. Justru demi memungkinkan pembuatan 'peta warna 332' itulah operator "-ordered-dither" dibekali kemampuan menentukan tingkat terpisah untuk tiap kanal warna.
Hasil ordered dither yang lebih baik
Mari kita lihat lebih rinci O-Dither tingkat 6 yang baru saja kita hasilkan.
magick logo.png -ordered-dither o8x8,6 -format %k info:
Seperti terlihat, untuk gambar ini kita bahkan tidak mendekati penuhnya tabel warna GIF (batas 256). Pada dasarnya, karena gambar itu umumnya terdiri atas warna biru, sangat sedikit gradasi merah atau bahkan hijau dari peta warna seragam tingkat 6 yang terpakai. Namun dengan menambah jumlah tingkat posterisasi, kita bisa mengisi tabel warna GIF dengan lebih baik, sehingga menghasilkan gambar ber-O-dither yang lebih baik.
magick logo.png -ordered-dither o8x8,13 -format %k info:
Ini menghasilkan cukup banyak warna sehingga hanya sedikit di bawah batas tabel warna GIF. Dengan bertambahnya jumlah warna, hasilnya tampak jauh lebih baik daripada hasil peta warna seragam standar yang sederhana. |
magick logo.png -ordered-dither o8x8,13 logo_o8x8_13.gif
![[IM Output]](../static/img/quantize/logo_o8x8_13.gif)
Seperti terlihat, dengan nilai 'levels' yang tinggi, "-ordered-dither" bisa menghasilkan gambar yang setara dengan hasil kuantisasi warna, setara dengan pemilihan warna spesifik yang dihasilkan kuantisasi warna plus dither koreksi error. Poin utama gambar-gambar ini bukanlah kualitasnya yang tinggi. Lagi pula Kuantisasi warna penuh lebih mudah menghasilkan peta warna yang lebih baik untuk gambar itu. Poinnya adalah pola dither tingkat rendah di dalam gambar bersifat tetap, terlepas dari perubahan kecil apa pun yang mungkin terjadi. Hanya area yang berubah yang akan berubah pada gambar hasil ordered dither. Artinya, gambar-gambar itu tidak punya Kepekaan E-Dither terhadap perubahan yang menimbulkan masalah bagi Optimisasi frame pada animasi GIF. (lihat Masalah optimisasi)Tentu saja untuk animasi, semua gambar perlu digabung dengan "-append" lebih dulu sebelum memeriksa berapa warna yang sebenarnya terpakai. Perlu juga memakai opsi khusus "+remap" setelah memakai "-ordered-dither" untuk memaksa IM membangkitkan 'peta warna global bersama' bagi SEMUA gambar, walaupun reduksi warna dan dithering sudah dilakukan. Metode penentuan jumlah tingkat warna ini tidak mudah ditentukan, tetapi memang berhasil. Saya berharap bisa menemukan cara agar IM menentukan tingkat terbaik secara otomatis, terutama untuk animasi GIF.
Pola dither dan peta ambang batas DIY
Sebelumnya saya menunjukkan bahwa operator "-ordered-dither" yang baru bisa menerima pola dithering yang didefinisikan pengguna. Di sini saya akan menunjukkan cara membuat pola dither Anda sendiri. Khususnya sebuah pola khusus yang menurut saya berguna untuk membangkitkan bayangan yang tersusun dari garis-garis horizontal.
Pola dither multi-gambar
Langkah pertama adalah membuat sekumpulan gambar yang mendefinisikan pola yang ingin dibuat. Pola itu sebaiknya diawali gambar hitam polos berukuran tepat sebagai gambar pertama (semua piksel mati) dan gambar putih polos di ujung lainnya (semua piksel menyala). Gambar berikutnya sebaiknya berupa pola abu-abu 50% di tengah, yang menentukan gaya dasar dithering yang ingin dicapai. Sebagai contoh, berikut pola dither DIY awal saya. Yang saya simpan ke sebuah file GIF multi-gambar (bukan animasi GIF)...
magick -size 2x2 xc:black \
\( +clone -draw 'fill white line 0,0 1,0' \) \
xc:white dpat_hlines2x2.gif
montage dpat_hlines2x2.gif -tile x1 -background none -frame 2 \
-filter box -geometry 32x32+5+0 dpat_hlines2x2_imgs.gif
Ini kira-kira kumpulan gambar pola dither paling sederhana yang bisa didapat, dan sangat mirip dengan 'checks' atau 'Checkerboard Dither', hanya saja memakai garis horizontal, bukan pola papan catur. Supaya terlihat seperti apa nantinya pola dither ini, berikut ordered dither DIY yang cukup sederhana, yang langsung memanfaatkan kumpulan gambar dithering ambang batas tersebut.
magick gradient.png dpat_hlines2x2.gif \
-virtual-pixel tile -fx 'u[(floor((n-1)*u)+1) % n]' dgrad_hlines2x2.gif
Seperti terlihat, pola dithernya biasa saja. Fungsi "-fx" adalah variasi dari fungsi Tabel pencarian warna, yaitu fungsi bertipe 'IM Dither Lookup Patterns'. Dan dengan pengaturan "-virtual-pixel" 'tile', fungsi itu bahkan tidak perlu tahu ukuran gambar pola dither yang Anda pakai. |
Pemakaian "-virtual-pixel" oleh operator "-fx" dengan indeks terhitung seperti ini rusak sebelum IM versi 6.2.9-2. |
|---|---|
| Mari kita coba lagi kumpulan pola dither ini, tetapi dengan gambar berbayang sederhana... |
magick shadow.png dpat_hlines2x2.gif -channel A \
-virtual-pixel tile -fx 'u[floor((n-1)*u)+1].g' \
shadow_dpat_hlines2x2.gif
Peta ambang batas ordered dither DIY
Pola dither DIY di atas kira-kira sesederhana pola dither yang bisa didapat, sehingga kita bisa mengubahnya langsung menjadi peta ambang batas XML, agar operator bawaan "-ordered-dither" yang cepat itu bisa memanfaatkannya. Berikut definisi XML akhirnya, yang saya simpan di file peta ambang batas pribadi saya "~/.magick/thresholds.xml" dalam direktori "$HOME" saya.
Format XML-nya sangat sederhana, dan mendefinisikan peta 2x2 piksel. Gambar hitam pertama diberi nilai nol, dan tidak punya piksel, sehingga tidak ada nilai nol yang muncul. Piksel yang menyala (dibuat putih) pada gambar tengah diberi nilai '1', sedangkan piksel gambar sisanya atau gambar kedua diberi nilai '2'. 'divisor=' menentukan jumlah gambar, atau tingkat warna semu (tingkat warna palsu) yang diwakili pola dither ini, jadi nilainya '3'. Nilai itu membagi nilai piksel untuk menentukan pada tingkat warna berapa piksel tersebut dinyalakan. Karena itu dua piksel atas menyala untuk warna yang lebih besar dari 1/3, sedangkan dua piksel bawah menyala untuk nilai warna yang lebih besar dari 2/3. Artinya, tiap nilai piksel mewakili satu tingkat 'ambang batas', dan itulah sebabnya pola dither disebut juga peta ambang batas. Sisa definisinya menentukan nama (dan alias opsional) yang bisa dipakai untuk merujuk peta ambang batas itu pada operator ordered dither. Jadi mari kita coba...
magick gradient.png -ordered-dither hlines2x2 od_hlines2x2.gif
magick shadow.png -channel A \
-ordered-dither hlines2x2 shadow_hlines2x2.gif
Seperti terlihat, hasilnya lumayan bagus, tetapi kita bisa melakukan hal lain untuk memperbaikinya. Dengan menyesuaikan nilai ambang batas dalam peta itu, kita bisa mengubah batas-batasnya, sehingga ruang warna tidak terbagi menjadi 3 area yang sama besar...
Perhatikan bahwa saya menaikkan divisor menjadi '10', supaya tingkat warna terbagi menjadi sepuluh bagian yang sama. Lalu saya mengubah pengaturan ambang batasnya sehingga polanya dimulai pada ambang batas 30% di ujung transparan (hitam), sampai 90% untuk yang sepenuhnya opak (putih). Dan berikut hasil dari perubahan peta ambang batas tersebut.
magick gradient.png -ordered-dither hlines2x2a od_hlines2x2a.gif
magick shadow.png -channel A \
-ordered-dither hlines2x2a shadow_hlines2x2a.gif
Seperti terlihat, ini memperlebar rentang piksel semitransparan yang memakai garis horizontal murni sebagai pola dither. Hasilnya efek bayangan yang lebih baik, meski sebaiknya hanya dipakai dengan bayangan yang tidak sekabur contoh di sini. Namun perhatikan bahwa perubahan semacam ini pada sebuah ambang batas sangat jarang dilakukan. Meski dalam kasus ini beralasan untuk keperluan yang dimaksud. Pada dasarnya cara itu tidak mendefinisikan gradien dengan benar, dan tidak memungkinkan gradasi pola yang lebih terang maupun lebih gelap. Untuk itu kita perlu membuat peta ambang batas yang jauh lebih rumit, dengan lebih banyak piksel dan lebih banyak pola.
Dither garis horizontal DIY
Di sini saya memperluas pola dither garis horizontal sederhana yang saya buat di atas menjadi sekumpulan pola, untuk menghasilkan gradien yang lebih halus dari 'mati' ke 'menyala'. Inilah hasilnya.
montage dpat_hlines.gif -filter box -geometry 60x20+2+0 \
-tile x1 -background none -frame 2 dpat_hlines_images.gif
magick gradient.png dpat_hlines.gif \
-virtual-pixel tile -fx 'u[(floor((n-1)*u)+1) % n]' \
dgrad_dpat_hlines.gif
magick shadow.png dpat_hlines.gif -channel A \
-virtual-pixel tile -fx 'u[floor((n-1)*u)+1].g' \
shadow_dpat_hlines.gif
Seperti terlihat, sekarang isinya 9 gambar berukuran 12x4 piksel. Kumpulan itu tidak mewakili semua pola piksel yang mungkin ada, tetapi justru memperkuat efek garisnya. Saya juga menggandakan tingginya agar celah pada garis-garis itu bergeser sebagaimana mestinya. Berikut contoh lain pemakaian pola dither ini...
magick -size 120x55 xc:white -draw 'fill #777 ellipse 50,43 30,5 0,360' \
-motion-blur 0x15+180 -blur 0x2 sphere_shadow.png
magick sphere_shadow.png dpat_hlines.gif \
-virtual-pixel tile -fx 'u[(floor((n-1)*u)+1) % n]' \
sphere_shadow_dither.gif
magick sphere_shadow_dither.gif -fill red -stroke firebrick \
-draw 'circle 35,25 35,5' sphere_shadow_hlines.gif
Langkah berikutnya adalah mengubah kumpulan pola dither ini menjadi satu gambar peta ambang batas tunggal, bukan kumpulan banyak gambar. Ini dicapai lewat beberapa manipulasi gambar yang cukup rumit untuk menggabungkan semua gambar itu menjadi satu. ![[IM Output]](../static/img/quantize/od_hlines2x2a.gif)
![[IM Output]](../static/img/quantize/shadow_hlines2x2a.gif)
|
magick -size 1x10 gradient: -flip -crop 1x1 +repage -delete 0,-1 \
-scale 12x4\! null: \( dpat_hlines.gif -delete 0 \) \
-alpha off -compose CopyOpacity -layers Composite \
-reverse -compose Over -flatten -alpha off dmap_hlines.png
![[IM Output]](../static/img/quantize/dmap_hlines_mag.png)
Nilai '10' adalah satu lebih banyak daripada jumlah gambar dalam pola dither, sedangkan "-scale 12x4\!" adalah ukuran pola dither yang sedang diubah menjadi peta ambang batas. Hasilnya peta grayscale, tanpa warna hitam atau putih murni. Tingkat abu-abu yang dipakai untuk sebuah piksel berarti bahwa jika tingkat warnanya sama dengan nilai abu-abu itu atau lebih tinggi, piksel tersebut harus dinyalakan. Artinya, tiap tingkat abu-abu adalah tingkat 'ambang batas' saat nilai warna beralih dari hitam ke putih. Kalau gambar itu dilihat dari sudut lain, piksel gelap umumnya membuat piksel tersebut menyala pada lebih banyak tingkat warna. Sementara piksel terang baru menyala ketika warna gambar menjadi sangat cerah. Ini nyaris kebalikan dari tampilan gambar yang sebenarnya, tetapi kalau dipikir-pikir memang masuk akal. Saya juga memakai gambar PNG alih-alih GIF untuk petanya karena hanya satu gambar yang perlu disimpan, dan yang lebih penting, untuk mencoba mempertahankan tingkat kualitas 16 bit bagi nilai ambang batasnya. GIF hanya sanggup menangani tingkat warna 8 bit. Sekarang kita bisa men-dither gambar kita cukup dengan satu gambar, dan perbandingan ambang batas yang jauh lebih sederhana antara tiap piksel langsung terhadap gambar (atau peta) ambang batas dithering itu.
magick gradient.png dmap_hlines.png \
-virtual-pixel tile -fx 'u>=v' dgrad_dmap_hlines.gif
| Lihat betapa jauh lebih sederhananya peta ambang batas. Hanya ada satu gambar, dan satu perbandingan langsung per piksel, untuk tiap kanal yang di-dither. Ini membuat dithering dengan peta ambang batas menjadi sangat sangat cepat. Jauh lebih cepat daripada kuantisasi warna penuh. Kesederhanaan inilah alasan ImageMagick dan sebagian besar perangkat lunak grafis memakai peta ambang batas untuk menyimpan berbagai pola dither. | Uji 'lebih besar atau sama dengan' ('>=') baru ditambahkan ke operator "-fx" pada IM versi 6.2.9-2. Kalau ini jadi masalah, pakai uji terbalik 'v<u' pada contoh di atas. |
|---|---|
Namun kesederhanaan ini menjadi jauh lebih rumit bila pengguna ingin men-dither memakai banyak tingkat warna. Bukti konsepnya pertama kali dirumuskan pada contoh-contoh di halaman Posterized Ordered Dither sebelum dimasukkan ke fungsi inti IM. Sekarang setelah kita punya gambar ambang batas gabungan, berikutnya kita perlu mengubah gambar di atas menjadi peta ambang batas XML yang bisa langsung dibaca IM dan dipakai operator "-ordered-dither". Untuk itu kita perlu mengeluarkan gambar kita sebagai angka-angka yang mewakili 9 tingkat abu-abu di dalamnya. Cara terbaiknya adalah memakai format gambar NetPBM atau PBMplus, dengan penyesuaian kedalaman memakai perangkat lunak pemrosesan gambar "NetPbm". Paket ini umumnya sudah terpasang standar di Linux, jadi kebanyakan orang sudah memilikinya, atau bisa memasangnya dari distribusi perangkat lunak biasa mereka. Angka "pnmdepth" sekali lagi adalah jumlah tingkat abu-abu yang dikandung gambar ambang batas itu. |
magick dmap_hlines.png pgm:- | pnmdepth 9 | pnmnoraw > dmap_hlines.pgm
Semua angka (selain pengenal magic gambar 'P2') di atas adalah angka yang diperlukan untuk membangkitkan 'peta ambang batas' yang sesuai, yang bisa Anda tambahkan ke file "thresholds.xml" pribadi Anda. Sebagai contoh, berikut entri peta ambang batas yang dihasilkan dari contoh di atas.
Dan berikut contoh pemakaian peta ambang batas ini.
magick shadow.png -channel A -ordered-dither hlines shadow_hlines.gif
Begitulah cara membangkitkan peta ambang batas yang rumit dari sederet gambar bertahap.
Dithering dengan pola simbol
Meski untuk sebagian besar operasi dithering Anda bisa memakai satu peta ambang batas atau satu gambar ambang batas alih-alih kumpulan pola multi-gambar, bukan berarti peta multi-gambar tidak punya kegunaan sendiri. Sekumpulan gambar pencarian dapat dipakai untuk men-tile banyak area sekaligus, bukan satu per satu. Misalnya dengan menskalakan gambar sederhana, lalu mengganti tiap piksel dalam gambar dengan simbol tertentu. Sebagai contoh, di sini saya mengambil gambar 'eyes' yang sangat kecil
dan mengganti masing-masing pikselnya dengan berbagai simbol, sehingga tercipta pola semacam itu untuk tiap piksel pada gambar aslinya.
montage dpat_symbols.gif -geometry +5+0 \
-tile x1 -background none -mattecolor blue -frame 3 \
dpat_syms_images.gif
magick eyes.gif -alpha off -colorspace sRGB -grayscale Average \
-alpha off -scale 1600% -negate \
dpat_symbols.gif -virtual-pixel tile -fx 'u[floor(15.9999*u)+1]' \
eyes_syms.gif
montage dipakai untuk membentangkan gambar GIF multi-gambar itu supaya isinya terlihat, tanpa menjadi 'animasi'. Anda bisa memilih metode intensitas "-grayscale" mana yang ingin dipakai, dari 'Rec709Luminance' yang normal sampai 'Rec709Luma' yang lebih gelap, atau memakai 'average' dari ruang warna 'sRGB yang non-linear maupun ruang warna 'RGB' yang linear. Penskalaan "-gamma" atas nilai-nilai itu pun bisa disetel untuk mendapat sebaran warna terbaik. Kemungkinannya banyak, dan mana yang bagus lebih bergantung pada susunan simbol Anda daripada metode yang dipilih. Kuncinya pada contoh di atas adalah memastikan tiap warna pada gambar masukan menghasilkan simbol yang unik, dan itu bisa sangat sulit dicapai. Contoh ini bisa dipakai untuk membuat panduan sulam silang atau rajut yang dapat diikuti para penghobi, menghasilkan karya berskala besar dari gambar komputer yang kecil. Teknik ini bisa dipakai untuk men-tile gambar grayscale dengan sekumpulan gambar warna yang di-tile. Hasilnya sedikit mirip peta lanskap yang terlihat di banyak permainan perang komputer lawas.
montage dpat_map.gif -geometry +5+0 -tile x1 -background none \
dpat_map_images.gif
magick -seed 100 \
-size 200x200 plasma:'gray(50%)-gray(50%)' -blur 0x15 \
-channel G -auto-level +channel -set colorspace sRGB \
dpat_map.gif -virtual-pixel tile -fx 'u[floor(5.999*u.g)+1]' \
map.gif
Perhatikan bahwa saya perlu memastikan IM menganggap gambar grayscale itu sudah berada di ruang warna sRGB akhir (seperti halnya gambar tile-nya), padahal sebenarnya data RGB linear-lah yang dipakai untuk pencarian indeks FX. Tanpa itu, 'peta' yang dihasilkan condong ke lanskap berhutan, dengan sedikit sekali peluang munculnya area air. Seperti terlihat, kumpulan gambar apa pun bisa dipakai sebagai tile, gambar-gambarnya bahkan tidak perlu sejajar satu sama lain, atau berukuran tile yang sama. Tentu saja kalau tile-nya berukuran sama dan saling berkaitan erat, seperti pada 3 tile 'laut' biru itu, pola tile-nya bisa 'mengalir' dari satu area ke area lain. Dengan mengganti tile itu dengan gambar angka, Anda juga bisa menghasilkan semacam panduan mewarnai berdasarkan nomor. Namun mungkin perlu pemrosesan tambahan untuk memberi batas pada area yang berbeda. Ini saya tinggalkan sebagai latihan, kirimkan solusi Anda ke saya, dan nama Anda bisa tercantum di IM examples sebagai penulis teknik ini.
Catatan acak ordered dither dan kemungkinan ke depan
Sedang dikerjakan
Ordered dither dengan jumlah warna sedikit.
Ketika memakai sedikit warna untuk gambar kecil, dither semi-acak seperti dither koreksi error kurva hilbert milik IM, atau bahkan dither koreksi error Floyd-Steinberg yang lebih sederhana (lihat Dither koreksi error di atas ), menghasilkan tampilan yang buruk. Idealnya Ordered dither sebaiknya dipakai untuk gambar berwarna sedikit dan berukuran kecil seperti ikon agar hasilnya jauh lebih enak dilihat. Namun untuk saat ini ordered dither di IM hanya bisa memakai tabel warna 'tetap' yang dibangkitkan secara matematis, bukan sekadar kumpulan warna 'terbaik'.
Ordered dither dengan peta warna apa saja
Ada algoritma tertentu yang memungkinkan pemakaian sekumpulan warna spesifik untuk ordered dithering. Pada dasarnya dengan menambahkan 'warna semu' yang bisa dibangkitkan algoritma ordered dither (yang mungkin melibatkan dithering tiga warna) ke peta warna yang diberikan, untuk 'melengkapinya'. Setelah itu masing-masing piksel gambar bisa 'dipetakan' ke 'peta warna yang diperluas' itu. Dari pemetaan awal ke suatu warna semu tersebut, warna sebenarnya bisa dipilih dari peta ambang batas yang membangkitkan warna semu itu, sehingga area berwarna tersebut bisa di-ordered dither, dan dengan demikian seluruh gambar dipetakan ke sekumpulan warna yang diberikan. Karena latar belakang saya di dunia ikon, dan sekarang animasi GIF, saya sangat ingin melihat ordered dither untuk warna apa saja diimplementasikan, tetapi saya belum menemukan rujukan praktis tentang cara melakukan ordered dither dengan sekumpulan warna tetap.
![[IM Output]](../static/img/images/colorwheel.png)
![[IM Output]](../static/img/quantize/color_quantize.gif)
![[IM Output]](../static/img/quantize/color_predefined.gif)
![[IM Output]](../static/img/quantize/color_uniform.gif)
![[IM Output]](../static/img/quantize/color_threshold.gif)
![[IM Output]](../static/img/quantize/tree_colors.gif)
![[IM Text]](../static/img/quantize/tree_colors.txt.gif)
![[IM Output]](../static/img/images/rose.gif)
![[IM Output]](../static/img/quantize/unique_color_histogram.png)
![[IM Text]](../static/img/quantize/rose_average.txt.gif)
![[IM Text]](../static/img/quantize/rose_fx_rgb.txt.gif)
![[IM Text]](../static/img/quantize/rose_red_mean.txt.gif)
![[IM Text]](../static/img/quantize/compare_navy.txt.gif)
![[IM Text]](../static/img/quantize/compare_transparency.txt.gif)
![[IM Text]](../static/img/quantize/compare_blue-navy.txt.gif)
![[IM Text]](../static/img/quantize/compare_blue-navy_fuzz.txt.gif)
![[IM Text]](../static/img/quantize/compare_fuzz_trans.txt.gif)
![[IM Text]](../static/img/quantize/fuzz_navy.txt.gif)
![[IM Text]](../static/img/quantize/fuzz_navy2.txt.gif)
![[IM Output]](../static/img/quantize/colors_64_no.gif)
![[IM Output]](../static/img/quantize/colors_64_rm.gif)
![[IM Output]](../static/img/quantize/colors_64_fs.gif)
![[IM Output]](../static/img/quantize/logo.png)
![[IM Output]](../static/img/quantize/colors_8_no.gif)
![[IM Output]](../static/img/quantize/colors_8_rm.gif)
![[IM Output]](../static/img/quantize/colors_8_fs.gif)
![[IM Output]](../static/img/quantize/colors_16_no.gif)
![[IM Output]](../static/img/quantize/colors_16_rm.gif)
![[IM Output]](../static/img/quantize/colors_16_fs.gif)
![[IM Output]](../static/img/quantize/colors_rb.gif)
![[IM Output]](../static/img/quantize/colors_rb2.gif)
![[IM Output]](../static/img/quantize/colors_br.gif)
![[IM Output]](../static/img/quantize/colors_br2.gif)
![[IM Output]](../static/img/quantize/colors_gradient.gif)
![[IM Output]](../static/img/quantize/colors_space_sRGB.gif)
![[IM Output]](../static/img/quantize/colors_space_CMY.gif)
![[IM Output]](../static/img/quantize/colors_space_RGB.gif)
![[IM Output]](../static/img/quantize/colors_space_GRAY.gif)
![[IM Output]](../static/img/quantize/colors_space_XYZ.gif)
![[IM Output]](../static/img/quantize/colors_space_LAB.gif)
![[IM Output]](../static/img/quantize/colors_space_LUV.gif)
![[IM Output]](../static/img/quantize/colors_space_HSL.gif)
![[IM Output]](../static/img/quantize/colors_space_HSB.gif)
![[IM Output]](../static/img/quantize/colors_space_HWB.gif)
![[IM Output]](../static/img/quantize/colors_space_YIQ.gif)
![[IM Output]](../static/img/quantize/colors_space_YUV.gif)
![[IM Output]](../static/img/quantize/colors_space_OHTA.gif)
![[IM Output]](../static/img/quantize/alpha_gradient.png)
![[IM Output]](../static/img/quantize/alpha_colors_256.png)
![[IM Output]](../static/img/quantize/alpha_colors_64.png)
![[IM Output]](../static/img/quantize/alpha_colors_15.png)
![[IM Output]](../static/img/quantize/alpha_colors_15n.png)
![[IM Output]](../static/img/quantize/alpha_colors_15qt.png)
![[IM Output]](../static/img/quantize/alpha_dither_threshold.gif)
![[IM Output]](../static/img/quantize/alpha_dither_checks.gif)
![[IM Output]](../static/img/quantize/alpha_dither_ordered.gif)
![[IM Output]](../static/img/quantize/alpha_dither_halftone.gif)
![[IM Output]](../static/img/quantize/alpha_dither_monochrome.gif)
![[IM Output]](../static/img/quantize/alpha_dither_monochrome_fs.gif)
![[IM Output]](../static/img/quantize/alpha_dither_map.gif)
![[IM Output]](../static/img/quantize/alpha_dither_map_fs.gif)
![[IM Text]](../static/img/quantize/dithers.txt.gif)
![[IM Output]](../static/img/quantize/dither_riemersma.gif)
![[IM Output]](../static/img/quantize/dither_floyd.gif)
![[IM Output]](../static/img/quantize/dither_not.gif)
![[IM Output]](../static/img/quantize/dither_anim.gif)
![[IM Output]](../static/img/quantize/dither_anim_magnify.gif)
![[IM Output]](../static/img/quantize/monochrome_gradient.gif)
![[IM Output]](../static/img/quantize/mono_remap.gif)
![[IM Output]](../static/img/quantize/mono_remap_gradient.gif)
![[IM Output]](../static/img/quantize/mono_remap_ctrl.gif)
![[IM Output]](../static/img/quantize/mono_remap_grad_ctrl.gif)
![[IM Output]](../static/img/quantize/gradient_rb.png)
![[IM Output]](../static/img/quantize/colors_threshold.gif)
![[IM Output]](../static/img/quantize/mono_threshold.gif)
![[IM Output]](../static/img/quantize/threshold_two_color.gif)
![[IM Output]](../static/img/images/colortable.gif)
![[IM Output]](../static/img/quantize/remap_logo_no.gif)
![[IM Output]](../static/img/quantize/remap_logo_rm.gif)
![[IM Output]](../static/img/quantize/remap_logo_fs.gif)
![[IM Output]](../static/img/quantize/remap_netscape.gif)
![[IM Output]](../static/img/quantize/remap_netscape_nd.gif)
![[IM Output]](../static/img/quantize/posterize_2_ns.gif)
![[IM Output]](../static/img/quantize/posterize_3_ns.gif)
![[IM Output]](../static/img/quantize/posterize_6_ns.gif)
![[IM Output]](../static/img/quantize/posterize_gradient.gif)
![[IM Output]](../static/img/quantize/posterize_logo.gif)
![[IM Output]](../static/img/quantize/posterize_logo_dither.gif)
![[IM Output]](../static/img/quantize/posterize_6_logo.gif)
![[IM Output]](../static/img/quantize/posterize_2_cw.gif)
![[IM Output]](../static/img/quantize/posterize_3_cw.gif)
![[IM Output]](../static/img/quantize/posterize_6_cw.gif)
![[IM Output]](../static/img/quantize/posterize_2_dither.gif)
![[IM Output]](../static/img/quantize/posterize_3_dither.gif)
![[IM Output]](../static/img/quantize/posterize_6_dither.gif)
![[IM Output]](../static/img/quantize/posterize_2_od.gif)
![[IM Output]](../static/img/quantize/posterize_3_od.gif)
![[IM Output]](../static/img/quantize/posterize_6_od.gif)
![[IM Output]](../static/img/quantize/threshold_0.gif)
![[IM Output]](../static/img/quantize/threshold_25.gif)
![[IM Output]](../static/img/quantize/threshold_50.gif)
![[IM Output]](../static/img/quantize/threshold_75.gif)
![[IM Output]](../static/img/quantize/threshold_100.gif)
![[IM Output]](../static/img/quantize/random_grad_0x100.gif)
![[IM Output]](../static/img/quantize/random_grad_10x90.gif)
![[IM Output]](../static/img/quantize/random_grad_25x75.gif)
![[IM Output]](../static/img/quantize/random_grad_50x50.gif)
![[IM Output]](../static/img/quantize/logo_o2x2.gif)
![[IM Output]](../static/img/quantize/logo_o3x3.gif)
![[IM Output]](../static/img/quantize/logo_o4x4.gif)
![[IM Output]](../static/img/quantize/logo_bw_o2x2.gif)
![[IM Output]](../static/img/quantize/logo_bw_o3x3.gif)
![[IM Output]](../static/img/quantize/logo_bw_o4x4.gif)
![[IM Output]](../static/img/quantize/logo_bw_o8x8.gif)
![[IM Output]](../static/img/quantize/od_threshold.gif)
![[IM Output]](../static/img/quantize/od_checks.gif)
![[IM Output]](../static/img/quantize/od_o2x2.gif)
![[IM Output]](../static/img/quantize/od_o3x3.gif)
![[IM Output]](../static/img/quantize/od_o4x4.gif)
![[IM Output]](../static/img/quantize/od_o8x8.gif)
![[IM Output]](../static/img/quantize/logo_h4x4a.gif)
![[IM Output]](../static/img/quantize/logo_h6x6a.gif)
![[IM Output]](../static/img/quantize/logo_h8x8a.gif)
![[IM Output]](../static/img/quantize/logo_h4x4o.gif)
![[IM Output]](../static/img/quantize/logo_h6x6o.gif)
![[IM Output]](../static/img/quantize/logo_h8x8o.gif)
![[IM Output]](../static/img/quantize/logo_bw_h4x4a.gif)
![[IM Output]](../static/img/quantize/logo_bw_h6x6a.gif)
![[IM Output]](../static/img/quantize/logo_bw_h8x8a.gif)
![[IM Output]](../static/img/quantize/logo_bw_h4x4o.gif)
![[IM Output]](../static/img/quantize/logo_bw_h6x6o.gif)
![[IM Output]](../static/img/quantize/logo_bw_h8x8o.gif)
![[IM Output]](../static/img/quantize/od_h4x4a.gif)
![[IM Output]](../static/img/quantize/od_h6x6a.gif)
![[IM Output]](../static/img/quantize/od_h8x8a.gif)
![[IM Output]](../static/img/quantize/od_h4x4o.gif)
![[IM Output]](../static/img/quantize/od_h6x6o.gif)
![[IM Output]](../static/img/quantize/od_h8x8o.gif)
![[IM Output]](../static/img/quantize/od_h16x16o.gif)
![[IM Output]](../static/img/quantize/od_c7x7b.gif)
![[IM Output]](../static/img/quantize/od_c7x7w.gif)
![[IM Output]](../static/img/quantize/offset_parrots.png)
![[IM Output]](../static/img/quantize/tmaps_list.txt.gif)
![[IM Output]](../static/img/quantize/tmap_diag.txt.gif)
![[IM Output]](../static/img/quantize/od_diag.gif)
![[IM Output]](../static/img/quantize/shadow.png)
![[IM Output]](../static/img/quantize/shadow_diag.gif)
![[IM Output]](../static/img/quantize/od_threshold_6.gif)
![[IM Output]](../static/img/quantize/od_checks_6.gif)
![[IM Output]](../static/img/quantize/od_o2x2_6.gif)
![[IM Output]](../static/img/quantize/od_o4x4_6.gif)
![[IM Output]](../static/img/quantize/od_o8x8_6.gif)
![[IM Output]](../static/img/quantize/od_o8x8_884.gif)
![[IM Output]](../static/img/quantize/logo_o8x8_2.gif)
![[IM Output]](../static/img/quantize/logo_posterize_2.gif)
![[IM Output]](../static/img/quantize/logo_o8x8_6.gif)
![[IM Output]](../static/img/quantize/logo_posterize_6.gif)
![[IM Output]](../static/img/quantize/logo_o8x8_332.gif)
![[IM Output]](../static/img/quantize/logo_remap_332.gif)
![[IM Text]](../static/img/quantize/logo_color_6.txt.gif)
![[IM Text]](../static/img/quantize/logo_color_13.txt.gif)
![[IM Output]](../static/img/quantize/dpat_hlines2x2_imgs.gif)
![[IM Output]](../static/img/quantize/dgrad_hlines2x2.gif)
![[IM Output]](../static/img/quantize/shadow_dpat_hlines2x2.gif)
![[IM Output]](../static/img/quantize/tmap_hlines2x2.txt.gif)
![[IM Output]](../static/img/quantize/od_hlines2x2.gif)
![[IM Output]](../static/img/quantize/shadow_hlines2x2.gif)
![[IM Output]](../static/img/quantize/tmap_hlines2x2a.txt.gif)
![[IM Output]](../static/img/quantize/dpat_hlines_images.gif)
![[IM Output]](../static/img/quantize/dgrad_dpat_hlines.gif)
![[IM Output]](../static/img/quantize/shadow_dpat_hlines.gif)
![[IM Output]](../static/img/quantize/sphere_shadow.png)
![[IM Output]](../static/img/quantize/sphere_shadow_dither.gif)
![[IM Output]](../static/img/quantize/sphere_shadow_hlines.gif)
![[IM Output]](../static/img/quantize/dgrad_dmap_hlines.gif)
![[IM Text]](../static/img/quantize/dmap_hlines.pgm.gif)
![[IM Output]](../static/img/quantize/tmap_hlines.txt.gif)
![[IM Output]](../static/img/quantize/shadow_hlines.gif)
![[IM Output]](../static/img/quantize/dpat_syms_images.gif)
![[IM Output]](../static/img/quantize/eyes_syms.gif)
![[IM Output]](../static/img/quantize/dpat_map_images.gif)
![[IM Output]](../static/img/quantize/map.gif)