И снова о Script-Fu в GIMP
Очередной заход по автоматизации GIMP начался с тривиальной в общем-то задачки. Мне принесли кучу отсканированных документов, где у сканера была некорректно выставлена цветопередача. Итог - вместо белой бумаги, получалась какая-то бурая муть. Вот, например:

лечится такое в GIMP элементарно: берется инструмент "уровни" в цветах, белой пипеткой тыкается в точку где должно быть белое, гамма ставится в 0,6 для придания уклона цветам в "темноту".

И всё. Получается что-то такое:

Проблема лишь в том что таких файлов - не один десяток, и делать такое с каждым - отупляюще и утомительно. Вывод - надо сделать так чтобы редактор сам делал все эти действия. Как минимум. Как максимум - чтобы при этом он и файлы сам открывал-записывал.
Итак, что должен делать минимальный скрипт:
- найти "белую" точку на картинке;
- задать её в "уровнях" как белую;
- выставить гамму в 0,6;
- обработать картинку.
Возникает лишь один вопрос - как нам определить "белую" точку? Решений "в лоб" - два: брать какую-либо фиксированную точку (например посредине листа) и пробежать по диагонали листа, найдя самую яркую. Первый вариант будет работать, но кроме случаев когда посредине листа точка черная. Второй будет слишком медленным - сканы "большие".
Совместив оба способа реализуем такую идею: возьмем 100 точек в случайных местах картинки, и посчитаем среднее арифметическое от них. Во-первых - относительно быстро (только 100 точек), во вторых - среднее будет цветом фона (оно же матожидание, оно же наиболее вероятное значение, т.е. именно цвет фона для листа с текстом), в третьих это будет действительно фон - за счет случайности выбора точек - попасть в точки "текста" намного менее вероятно чем в "фон".
Среднее по каждому цвету будем считать отдельно, и уровни править соответственно по отдельности. Ну и гамму добавим отдельно, для простоты.
(while (< y 100) (set! i 0) (set! px (cadr (gimp-drawable-get-pixel dr (rand (- (car (gimp-drawable-width dr)) 1)) (rand (- (car (gimp-drawable-height dr)) 1)) )) ) (while (< i 3) (aset xc i (+ (aref xc i) (aref px i) ) ) (set! i (+ i 1)) ) (set! y (+ y 1)) (gimp-progress-update (/ y 100)) )предварительно xc должен быть проинициализирован нулями. в реализации scheme в gimp (или в моем понимании) есть глюк, поэтому инициализацию пришлось делать явно:
(set! i 0) (while (< i 3) (aset xc i 0) (set! i (+ i 1)) )ну и последнее - вычислим среднее и поправим уровни:
(set! i 0) (while (< i 3) (set! y (round (/ (aref xc i) 100 ) )) (gimp-levels dr (+ i 1) 0 y 1.0 0 255) (set! i (+ i 1)) )всё в месте, добавив "стандартную" обвязку, выглядит так:
(define (script-fu-back-to-white img) (gimp-context-push) (gimp-image-undo-group-start img) (gimp-progress-set-text "Correcting background") (let ( (xc #(0 0 0)) (px #(0 0 0)) (dr 0) (y 0) (i 0) ) (set! xc #(0 0 0)) (set! dr (car (gimp-image-get-active-drawable img))) (set! i 0) (while (< i 3) (aset xc i 0) (set! i (+ i 1)) ) ;(write_xc_message "before: " xc) (while (< y 100) (set! i 0) (set! px (cadr (gimp-drawable-get-pixel dr (rand (- (car (gimp-drawable-width dr)) 1)) (rand (- (car (gimp-drawable-height dr)) 1)) )) ) (while (< i 3) (aset xc i (+ (aref xc i) (aref px i) ) ) (set! i (+ i 1)) ) (set! y (+ y 1)) (gimp-progress-update (/ y 100)) ) ;(write_xc_message "after: " xc) (set! i 0) (while (< i 3) (set! y (round (/ (aref xc i) 100 ) )) (gimp-levels dr (+ i 1) 0 y 1.0 0 255) (set! i (+ i 1)) ) (gimp-levels dr 0 0 255 0.60 0 255) ) (gimp-image-undo-group-end img) (gimp-displays-flush) (gimp-progress-update 1.0) (gimp-context-pop))теперь малость переделаем функцию из туториала по скрипт-фу из гимпа, для работы с файлами:
(define (batch-back-to-white pattern) (let* ( (filelist (cadr (file-glob (string-append pattern "\\*.jpg") 1))) ) (while (not (null? filelist)) (let* ( (filename (car filelist) ) (image (car (gimp-file-load RUN-NONINTERACTIVE filename filename))) (drawable (car (gimp-image-get-active-layer image)))) (script-fu-back-to-white image) (gimp-file-save RUN-NONINTERACTIVE image drawable filename filename) (gimp-image-delete image) ) (set! filelist (cdr filelist)) ) ))и осталось лишь зарегистрировать наш новый плагин в редакторе:
(script-fu-register "script-fu-back-to-white" "Reset background to white" "Reset background, by changing channels levels upper value to average" "Leonid Koninin" "Leonid Koninin" "2011" "RGB*, GRAY*" SF-IMAGE "Image" 0)(script-fu-register "batch-back-to-white" "Reset background to white (all *.jpg in directory)" "Reset background, by changing channels levels upper value to average" "Leonid Koninin" "Leonid Koninin" "2011" "RGB*, GRAY*" SF-DIRNAME "Directory" "")(script-fu-menu-register "script-fu-back-to-white" "<Image>/Filters/Leon")(script-fu-menu-register "batch-back-to-white" "<Image>/Filters/Leon")вот и всё, хотя для такого маленького скрипта я изрядно повозился - опыта не хватает. сам скрипт можно
скачать здесь.