GIMP Script-Fu Первый Дан. Линейные преобразования на плоскости

от автора

Библиотека функций к Script-fu

Итак, настоящая киллер функция по отображению изображений это gimp-item-transform-matrix. Именно на её основе мы и будем строить все отображения изображений в нашем проекте.

Для использования функции GIMP gimp-item-transform-matrix, осуществляющей линейное преобразования заданного отображаемого объекта, нам нужна структура в которой мы могли бы хранить матрицу преобразования. На основе этой структуры, мы построим абстракцию линейного двумерного преобразования на плоскости. Я посмотрел все эти преобразования и увидел, что матрица имеет всего 6 значимых полей, остальные два ноль и одно единица, поэтому наша структура будет хранить всего 6 полей.

подготовка
;;(define path-home "D:") ;;для виндовс (define path-home (getenv "HOME")) (define path-lib (string-append path-home "/work/gimp/lib/")) (define path-work (string-append path-home "/work/gimp/")) (load (string-append path-lib "util.scm")) (load (string-append path-lib "struct2.scm")) (load (string-append path-lib "point.scm"))

(struct tr2d (m11 m12 m21 m22 dx dy)) 

Теперь мы можем определить операции преобразования. Первым делом определим операцию преобразования точки.

(define (p-tr2d p tr)   (let ((x (p-x p))         (y (p-y p)))     (p! (+ (* x (tr2d-m11 tr))            (* y (tr2d-m21 tr))            (tr2d-dx tr))         (+ (* x (tr2d-m12 tr))            (* y (tr2d-m22 tr))            (tr2d-dy tr))))) 

Вообще говоря, если понимать под точкой, вектор, а преобразование — это матрица, то это умножение вектора на матрицу, с некоторыми особенностями, точка то у нас на плоскости имеет 2 координаты, а вектор умножается 3х значный, одна координата которого всегда 1, которая не храниться в составе точки.

Комбинация преобразований будет тоже двумерным преобразованием, умножение трёхмерных матриц, с особенностями используемой нами матричной модели преобразований, хранятся всего 6 элементов.

(define (comb-tr2d m n)   (let ((m11 (tr2d-m11 m))         (m12 (tr2d-m12 m))         (m21 (tr2d-m21 m))         (m22 (tr2d-m22 m))         (mx  (tr2d-dx  m))         (my  (tr2d-dy  m))         (n11 (tr2d-m11 n))         (n12 (tr2d-m12 n))         (n21 (tr2d-m21 n))         (n22 (tr2d-m22 n))         (nx  (tr2d-dx  n))         (ny  (tr2d-dy  n)))     (tr2d! (+ (* m11 n11) (* m12 n21))    (+ (* m11 n12) (* m12 n22))            (+ (* m21 n11) (* m22 n21))    (+ (* m21 n12) (* m22 n22))            (+ (* mx n11)  (* my  n21) nx) (+ (* mx  n12) (* my  n22) ny))))

Для отладки может понадобиться печать преобразования.

(define (prn-tr2d m)   (display "[ ") (display (tr2d-m11 m))  (display " ")   (display (tr2d-m12 m)) (display " 0 ]") (newline)   (display "[ ") (display (tr2d-m21 m))  (display " ")   (display (tr2d-m22 m)) (display " 0 ]") (newline)   (display "[ ") (display (tr2d-dx  m))  (display " ")   (display (tr2d-dy  m)) (display " 1 ]") (newline))

Теперь опишем несколько конструкторов создающих простые линейные преобразования.

;; преобразование вращения. (define (make-tr2d-rot alfa)   (let* ((alfa-r (gr2rad alfa))         (cos-a  (cos alfa-r))         (sin-a  (sin alfa-r)))   (tr2d! cos-a     sin-a          (- sin-a) cos-a          0         0)))  ;;преобразование перемещения (define (make-tr2d-move x y)   (tr2d! 1     0          0     1          x     y))  ;;преобразование масштабирования (define (make-tr2d-scale sx sy)   (tr2d! sx     0          0      sy          0      0))  ;;сдвиг вдоль оси х на заданный угол. (define (make-tr2d-shear-x angle)   (tr2d! 1     (tan (gr2rad angle))          0     1          0     0))  ;;сдвиг вдоль оси у на угол (define (make-tr2d-shear-y angle)   (tr2d! 1     0          (tan (gr2rad angle)) 1          0     0))  ;;преобразование отражения относительно оси х (define (make-tr2d-reflect-x)   (tr2d! 1     0          0    -1          0     0)) ;;преобразование отражения относительно оси y (define (make-tr2d-reflect-y)   (tr2d! -1    0           0    1           0    0)) 

Теперь на основе построенной абстракции, можно написать функцию преобразования изображения, принимающую данное преобразование в качестве аргумента.

(define (draw-from-image-trans dest src tr)   (let* ((dw2 (car (gimp-layer-new-from-drawable                     (car (gimp-image-get-active-layer src)) dest)))          (m11 (tr2d-m11 tr))          (m12 (tr2d-m12 tr))          (m21 (tr2d-m21 tr))          (m22 (tr2d-m22 tr))          (mx  (tr2d-dx  tr))          (my  (tr2d-dy  tr)))     (gimp-image-add-layer dest dw2 0)     (gimp-item-transform-matrix dw2                                 m11 m21 mx                                 m12 m22 my                                 0   0   1)     (gimp-image-merge-visible-layers dest CLIP-TO-IMAGE)     ))

Все эти преобразования определены в файле tr2d.scm. Давайте выполним демонстрационный пример.

подготовка
;;догрузим необходимые библиотеки. (load (string-append path-lib "img.scm")) ;;(load (string-append path-lib "img2.6.scm")) ;;для гимп 2.6 (load (string-append path-lib "tr2d.scm"))  (load (string-append path-lib "defun.scm")) ;;для загрузки draw-from-image-trans (load (string-append path-lib "brush.scm")) (load (string-append path-lib "fig.scm"))   ;; или определите её через консоль. ;;(load (string-append path-lib "brush-2.6.scm")) ;;для гимп 2.6 ;;(load (string-append path-lib "fig2.6.scm"))   ;; или определите её через консоль.

;;подготовимся к рисованию (define i1 (create-1-layer-img 640 480)) (define isource1 (car (file-png-load 1                                      (string-append path-work "i1.png") "i1")))  ;;определим две трансформации (define tr2d-pos1 (make-tr2d-move 100 100)) ;;перемещение в точку 100, 100 (define tr2d30 (make-tr2d-rot 30))          ;; поворот на 30 градусов.  (draw-from-image-trans i1 isource1 tr2d30) ;;поворот в начале координат (draw-from-image-trans i1 isource1 (comb-tr2d tr2d30 tr2d-pos1 )) ;;поворот и перенос 
"Использование двумерных трансформаций для  поворота изображения "

«Использование двумерных трансформаций для поворота изображения «

Вот ещё пара примеров использования преобразований:

;;увеличим ширину в 2 раза, а высоту уменьшим на половину, повернем на 30 гр. и переместим. (draw-from-image-trans i1 isource1 (comb-tr2d                                     (comb-tr2d                                      (make-tr2d-scale 2 0.5)                                      tr2d30)                                     tr2d-pos1 )) ;;уменьшим ширину в 2 раза, а высоту увеличим в полтора, повернем на 45 гр. и переместим. (draw-from-image-trans i1 isource1 (comb-tr2d                                     (comb-tr2d                                      (make-tr2d-scale 0.5 1.5)                                      (make-tr2d-rot  45))                                     (make-tr2d-move 200 100) )) 
"Использование двумерных трансформаций изображения "

«Использование двумерных трансформаций изображения «

Получилась очень удачная функция заменяющая все ранее написанные функции отображения и преобразования изображений. Единственно, что мне не нравиться. это каскадные вызовы функции comb-tr2d, давайте перепишем её, чтобы она принимала не произвольное количество аргументов, обрабатывая произвольную последовательность трансформаций.

;;эта функция есть в util.scm (define (fold f init lst)   (do ((rez init (f rez (car l)))        (l   lst  (cdr l)))       ((null? l) rez)))  (define (comb-tr2d . lst)   (let  ((tr (lambda (m n)                (let ((m11 (tr2d-m11 m))                      (m12 (tr2d-m12 m))                      (m21 (tr2d-m21 m))                      (m22 (tr2d-m22 m))                      (mx  (tr2d-dx  m))                      (my  (tr2d-dy  m))                      (n11 (tr2d-m11 n))                      (n12 (tr2d-m12 n))                      (n21 (tr2d-m21 n))                      (n22 (tr2d-m22 n))                      (nx  (tr2d-dx  n))                      (ny  (tr2d-dy  n)))                  (tr2d! (+ (* m11 n11) (* m12 n21))    (+ (* m11 n12) (* m12 n22))                         (+ (* m21 n11) (* m22 n21))    (+ (* m21 n12) (* m22 n22))                         (+ (* mx n11)  (* my  n21) nx) (+ (* mx  n12) (* my  n22) ny))))))     (fold tr (car lst) (cdr lst)))) 

Это практически таже самая функция, которая была ранее, только её тело перенесли в лямбду, и последовательно с помощю fold применили ко всем преобразованиям из списка аргументов.

Для демонстрации работы нашей функции, давайте напишем функцию рисования серий изображений, расположенных по кругу.

(define (rot-img3 dest src x y sc-x sc-y shear-x shear-y num)   (gimp-image-undo-group-start dest)   (let ((a1 (floor (/ 360 num)))         (a-cur 0)         (tr-xy (make-tr2d-move x y))         (tr-pre (comb-tr2d                  (make-tr2d-scale sc-x sc-y)                  (make-tr2d-shear-y shear-x)                  (make-tr2d-shear-x shear-y))))     (while (< a-cur 360)       (draw-from-image-trans i1 isource1 (comb-tr2d                                              tr-pre                                              (make-tr2d-rot     a-cur)                                              tr-xy))       (set! a-cur (+ a-cur a1))))   (gimp-image-undo-group-end dest)   ) 

Обратите внимание, мы разделили создание трансформаций, те из них которые не меняются, мы создаём один раз и затем просто используем в окончательной комбинации трансформаций.

Выполним несколько примеров:

(rot-img3 i1 isource1 100 200 1 1 0 0 6) (rot-img3 i1 isource1 100 200 1 0.5 20 0 6) (rot-img3 i1 isource1 330 200 1.3 0.4 20 10 6) (rot-img3 i1 isource1 215 370 0.5 0.8 10 20 12) (rot-img3 i1 isource1 30 370 0.5 0.8 10 20 1) (rot-img3 i1 isource1 30 370 0.5 0.8 10 20 3) (rot-img3 i1 isource1 500 50 1 1 0 0 1)
"Пример использования rot-img3"

«Пример использования rot-img3»

Таким образом мы создали удобный инструмент для линейных преобразований на плоскости, используя который мы можем задать любое линейное преобразование, а функции, такие как gimp-item-transform-matrix позволят преобразовать и отобразить любое заданное изображение.

На этом с преобразованиями мы заканчиваем и в следующей статье ещё немного поработаем с самим языком Script-fu.


ссылка на оригинал статьи https://habr.com/ru/articles/861000/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *