[Part I. GIMP基礎功]

[Part II. 一種效果的誕生]
[Part III. Script-Fu的秘密]



顯示具有 第28章 電路條紋(Circuit) 標籤的文章。 顯示所有文章
顯示具有 第28章 電路條紋(Circuit) 標籤的文章。 顯示所有文章

28.1 電路有點短路

  • Circuit,在2.2版時,譯詞是「電路圖」。
  • 節標題「電路有點短路」意思是,舊版的 GIMP 的電路圖,其質感比較像電路圖。


預設參數下的電路圖
  • 執行[影像] / 濾鏡 / 描繪 / Circuit【註1】,可繪製出電路的圖案。
  • 此功能雖是位於「濾鏡」選單,但這是一個 Script-Fu 。
  • 下圖是「Script-Fu:Circuit」對話盒,執行後,可產生電路的圖案。Oilify mask size越小,則電路的圖案越規則,越像迷宮。


  • 下圖是,GIMP 1.2版,預設參數下的電路圖。
  • 舊版的 GIMP 的電路圖,比較像電路圖。


  • 下圖是,GIMP 2.6版,預設參數下的電路圖,
  • 下圖線條密集,比較不像電路圖。



選單及項目名稱的英文:
【註1】[Image] / Filters / Render / Circuit

28.2 三個濾鏡描繪出電路

Circuit是電路的意思,Circuit.scm的作者Adrian Likins,他巧妙運用了3個濾鏡,製造電路版的紋路效果。

Circuit.scm程式碼中,最重要的就是下面這3行:
  • (plug-in-maze RUN-NONINTERACTIVE image active-layer 5 5 TRUE 0 seed 57 1)
  • (plug-in-oilify RUN-NONINTERACTIVE image active-layer mask-size 0)
  • (plug-in-edge RUN-NONINTERACTIVE image active-layer 2 1 0)

這3行的作用為:
  • 使用迷宮濾鏡【註1】,產生黑白二色的迷宮圖案。
  • 使用油畫化濾鏡【註2】,製造油畫的筆觸。
  • 使用邊緣濾鏡【註3】,尋找黑白交替的邊緣。


視窗介面裡,中文、英文的選單及項目名稱:
【註1】[影像] / 濾鏡 / 描繪 / 圖樣 / 迷宮,[Image] / Filters / Render / Pattern / Maze
【註2】[影像] / 濾鏡 / 藝術效果 / 油畫化,[Image] / Filters / Artistic / Oilify
【註3】[影像] / 濾鏡 / 邊緣偵測 / 邊緣,[Image] / Filters / Edge-Detect / Edge

28.3 一步一步做,讓線條顯現

以下,操作GIMP圖形介面,一步一步做,來製作電路圖案。藉由這過程,來瞭解程式碼的意思。

Step 1. 新增一影像,長寬為256x256。
Step 2. 執行迷宮濾鏡,參數的設定如下圖,Seed設為3,其餘參數使用預設值。


下圖展示了迷宮的圖案。


Step 3. 執行油畫化濾鏡,參數的設定,如下圖的對話盒所示。

28.4 一步一步做,讓線路蝕刻

接續前一節,繼續以圖形使用者介面來生成電路圖的效果,只是步驟從新編號,從1號開始編號。

Step 1. 執行邊緣濾鏡,參數的設定,如對話盒所示。
  • 「Sobel」產生的線條密集,比較不像電路圖。
  • 「Differential」或「漸層」產生的線條稍微稀疏一點。

下圖,「演算法」設為「Sobel」。


下圖,將「演算法」設為「Differential」。



Step 2. 如下圖所示,再用曲線對話盒(Curve),減少白色的模糊邊緣,就會更像電路圖。


Step 3. 下圖顯示最終成品,相較於2.6版的Script-fu所生成的電路圖,這樣比較像電路圖。

28.5 程式circuit.scm分三部份



若要徹底瞭解circuit.scm,可仿傚第22章的方式,單步執行,來分析每一行程式碼的作用。

若用PSPad開啟程式檔(或任何文字編輯器),看到的Script-Fu程式碼,變數會對得很整齊。HTML會把大量的空白省略,因此程式碼的排版變成了下面這樣子。我替Script-Fu程式碼加上行號乎,方便讀者閱讀。



1~31:註解說明1: ; GIMP - The GNU Image Manipulation Program
2: ; Copyright (C) 1995 Spencer Kimball and Peter Mattis
3: ;
4: ; Circuit board effect
5: ; Copyright (c) 1997 Adrian Likins
6: ; aklikins@eos.ncsu.ed
7: ;
8: ; Genrates what looks a little like the back of an old circuit board.
9: ; Looks even better when gradmapped with a suitable gradient.
10: ;
11: ; This script doesnt handle or color combos well. ie, black/black
12: ; doesnt work..
13: ; The effect seems to work best on odd shaped selections because of some
14: ; limitations in the maze codes selection handling ablity
15: ;
16: ;
17: ; This program is free software; you can redistribute it and/or modify
18: ; it under the terms of the GNU General Public License as published by
19: ; the Free Software Foundation; either version 2 of the License, or
20: ; (at your option) any later version.
21: ;
22: ; This program is distributed in the hope that it will be useful,
23: ; but WITHOUT ANY WARRANTY; without even the implied warranty of
24: ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25: ; GNU General Public License for more details.
26: ;
27: ; You should have received a copy of the GNU General Public License
28: ; along with this program; if not, write to the Free Software
29: ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30:
31:

32~140:定義函式
32: (define (script-fu-circuit image
33: drawable
34: mask-size
35: seed
36: remove-bg
37: keep-selection
38: separate-layer)
39: (let* (
40: (type (car (gimp-drawable-type-with-alpha drawable)))
41: (image-width (car (gimp-image-width image)))
42: (image-height (car (gimp-image-height image)))
43: (active-selection 0)
44: (from-selection 0)
45: (selection-bounds 0)
46: (select-offset-x 0)
47: (select-offset-y 0)
48: (select-width 0)
49: (select-height 0)
50: (effect-layer 0)
51: (active-layer 0)
52: )
53:
54: (gimp-context-push)
55:
56: (gimp-image-undo-group-start image)
57:
58: (gimp-layer-add-alpha drawable)
59:
60: (if (= (car (gimp-selection-is-empty image)) TRUE)
61: (begin
62: (gimp-selection-layer-alpha drawable)
63: (set! active-selection (car (gimp-selection-save image)))
64: (set! from-selection FALSE))
65: (begin
66: (set! from-selection TRUE)
67: (set! active-selection (car (gimp-selection-save image)))))
68:
69: (set! selection-bounds (gimp-selection-bounds image))
70: (set! select-offset-x (cadr selection-bounds))
71: (set! select-offset-y (caddr selection-bounds))
72: (set! select-width (- (cadr (cddr selection-bounds)) select-offset-x))
73: (set! select-height (- (caddr (cddr selection-bounds)) select-offset-y))
74:
75: (if (= separate-layer TRUE)
76: (begin
77: (set! effect-layer (car (gimp-layer-new image
78: select-width
79: select-height
80: type
81: "effect layer"
82: 100
83: NORMAL-MODE)))
84:
85: (gimp-image-add-layer image effect-layer -1)
86: (gimp-layer-set-offsets effect-layer select-offset-x select-offset-y)
87: (gimp-selection-none image)
88: (gimp-edit-clear effect-layer)
89: (gimp-selection-load active-selection)
90: (gimp-edit-copy drawable)
91:
92: (let ((floating-sel (car (gimp-edit-paste effect-layer FALSE))))
93: (gimp-floating-sel-anchor floating-sel)
94: )
95: (gimp-image-set-active-layer image effect-layer ))
96: (set! effect-layer drawable)
97: )
98: (set! active-layer effect-layer)
99:
100: (if (= remove-bg TRUE)
101: (gimp-context-set-foreground '(0 0 0))
102: (gimp-context-set-foreground '(14 14 14))
103: )
104:
105: (gimp-selection-load active-selection)
106: (plug-in-maze RUN-NONINTERACTIVE image active-layer 5 5 TRUE 0 seed 57 1)
107: (plug-in-oilify RUN-NONINTERACTIVE image active-layer mask-size 0)
108: (plug-in-edge RUN-NONINTERACTIVE image active-layer 2 1 0)
109: (if (= type RGBA-IMAGE)
110: (gimp-desaturate active-layer))
111:
112: (if (and
113: (= remove-bg TRUE)
114: (= separate-layer TRUE))
115: (begin
116: (gimp-by-color-select
117: active-layer
118: '(0 0 0)
119: 15
120: 2
121: TRUE
122: FALSE
123: 10
124: FALSE)
125: (gimp-edit-clear active-layer)))
126:
127: (if (= keep-selection FALSE)
128: (gimp-selection-none image))
129:
130: (gimp-image-remove-channel image active-selection)
131: (gimp-image-set-active-layer image drawable)
132:
133: (gimp-image-undo-group-end image)
134:
135: (gimp-displays-flush)
136:
137: (gimp-context-pop)
138: )
139: )
140:

141~159:註冊函式
141: (script-fu-register "script-fu-circuit"
142: _"_Circuit..."
143: _"Fill the selected region (or alpha) with traces like those on a circuit board"
144: "Adrian Likins <adrian@gimp.org>"
145: "Adrian Likins"
146: "10/17/97"
147: "RGB* GRAY*"
148: SF-IMAGE "Image" 0
149: SF-DRAWABLE "Drawable" 0
150: SF-ADJUSTMENT _"Oilify mask size" '(17 3 50 1 10 0 1)
151: SF-ADJUSTMENT _"Circuit seed" '(3 1 3000000 1 10 0 1)
152: SF-TOGGLE _"No background (only for separate layer)" FALSE
153: SF-TOGGLE _"Keep selection" TRUE
154: SF-TOGGLE _"Separate layer" TRUE
155: )
156:
157: (script-fu-menu-register "script-fu-circuit"
158: "<Image>/Filters/Render")
159:

28.6 電路圖,修改成可「單行」執行的 Script-Fu

修改成一行一行可執行的程式碼
  • 本節要將 circuit.scm 修改成在 Console 內可一行一行執行的 Scheme 程式碼。
  • circuit.scm 完整的程式碼可參考 28.5 節
  • 也可到 GIMP 系統的 scripts 目錄下找到該檔案。
  • 最主要修改的部份是,將「對話框接收輸入變數值」修改成「直接指定輸入變數值」。
  • 先要瞭解,circuit.scm 程式碼的執行流程。


最主要修改的部份
  • 不使用 let* 改用 define
  • 原本的程式使用 let* 定義與指定區域變數值。
  • (let* (()()()...) ()() ...) 這樣的語法括號太多。
  • 若要一行一行分析程式碼的意義,let* 語法,括號有好幾層,分析時,會造成一些干擾。
  • 現在不使用 let* 改用 define 來定義與指定變數值。


捨棄函數 script-fu-circuit,直接指定輸入變數值
  • 修改成最多的地方就是下列這部份。
  • 要注意括號的對齊。
  • 記得要刪除多餘的右括號 ) ,不然會出現錯誤訊息。
  • 修改後的程式碼,不再需要預先定義函數 script-fu-circuit ,但是要預先設定變數名稱、變數值 。

(define (script-fu-circuit image
                         drawable
                         mask-size
                         seed
                         remove-bg
                         keep-selection
                         separate-layer)
(let* (
      (type (car (gimp-drawable-type-with-alpha drawable)))
      (image-width (car (gimp-image-width image)))
      (image-height (car (gimp-image-height image)))
      (active-selection 0)
      (from-selection 0)
      (selection-bounds 0)
      (select-offset-x 0)
      (select-offset-y 0)
      (select-width 0)
      (select-height 0)
      (effect-layer 0)
      (active-layer 0)
      )


從下列的程式碼得知變數值的型態與大小
SF-IMAGE      "Image"             0
SF-DRAWABLE   "Drawable"          0
SF-ADJUSTMENT _"Oilify mask size" '(17 3 50 1 10 0 1)
SF-ADJUSTMENT _"Circuit seed"     '(3 1 3000000 1 10 0 1)
SF-TOGGLE     _"No background (only for separate layer)" FALSE
SF-TOGGLE     _"Keep selection"   TRUE
SF-TOGGLE     _"Separate layer"   TRUE


修改(if (= separate-layer TRUE) ...)這區塊
  • 要注意此區塊括號的對齊。
  • 下列這段程式碼 ,不使用 let 改用 set! 與 define。
  • 變數的定義 (define .....) 移到 if 之前。


(let ((floating-sel (car (gimp-edit-paste effect-layer FALSE))))
          (gimp-floating-sel-anchor floating-sel)
)

修改後的程式碼如下所列:

(define floating-sel 0)
(if (= separate-layer TRUE)
......
(set! floating-sel (car (gimp-edit-paste effect-layer FALSE)))
(gimp-floating-sel-anchor floating-sel)
......
)


修改後的程式碼,與測試的結果圖
  • 下面是修改後的程式碼。
  • 啟動 GIMP Script-fu Console
  • 將下面的 Scheme 程式碼,全部選取、複製、再貼入 Script-fu Console 文字框,按下 Enter 。
  • 過一會,就會看到結果圖。


(define inSize 256)
(define theWidth inSize)
(define theHeight inSize)
(define theImage (car (gimp-image-new theWidth theHeight RGB)))
(define baseLayer (car (gimp-layer-new theImage theWidth theHeight RGBA-IMAGE "Background" 100 NORMAL-MODE)))
(gimp-image-add-layer theImage baseLayer 0)
; 可以把顯示影像物件 theImage 提前到這裡
(gimp-display-new theImage)

; 前面的程式碼是為了生成「一個透明的圖層」
; 作為之後生成電路圖案的參考圖層
; 此參考圖層規範了電路圖案的長寬

(define image theImage) ; 指派影像物件
(define drawable baseLayer)  ; 指派圖層物件

(define mask-size 17)
(define seed 3)
(define remove-bg FALSE)
(define keep-selection TRUE)
(define separate-layer TRUE)

(define type (car (gimp-drawable-type-with-alpha drawable)))
(define image-width (car (gimp-image-width image)))
(define image-height (car (gimp-image-height image)))
(define active-selection 0)
(define from-selection 0)
(define selection-bounds 0)
(define select-offset-x 0)
(define select-offset-y 0)
(define select-width 0)
(define select-height 0)
(define effect-layer 0)
(define active-layer 0)

(gimp-context-push)
(gimp-image-undo-group-start image)

(gimp-layer-add-alpha drawable)

(if (= (car (gimp-selection-is-empty image)) TRUE)
(begin
(gimp-selection-layer-alpha drawable)
(set! active-selection (car (gimp-selection-save image)))
(set! from-selection FALSE))
(begin
(set! from-selection TRUE)
(set! active-selection (car (gimp-selection-save image)))
) ; 此為 begin 的右括號
) ; 此為 if 的右括號

(set! selection-bounds (gimp-selection-bounds image))
(set! select-offset-x (cadr selection-bounds))
(set! select-offset-y (caddr selection-bounds))
(set! select-width (- (cadr (cddr selection-bounds)) select-offset-x))
(set! select-height (- (caddr (cddr selection-bounds)) select-offset-y))

(define floating-sel 0)
(if (= separate-layer TRUE)
(begin
(set! effect-layer (car (gimp-layer-new image select-width select-height type "effect layer" 100 NORMAL-MODE)))

(gimp-image-add-layer image effect-layer -1)
(gimp-layer-set-offsets effect-layer select-offset-x select-offset-y)
(gimp-selection-none image)
(gimp-edit-clear effect-layer)
(gimp-selection-load active-selection)
(gimp-edit-copy drawable)

(set! floating-sel (car (gimp-edit-paste effect-layer FALSE)))
(gimp-floating-sel-anchor floating-sel)

(gimp-image-set-active-layer image effect-layer )) ; 此為 begin 的右括號
(set! effect-layer drawable)  ; 只有一行敘述可以不用 (begin ......)
) ; 此為 if 的右括號

(set! active-layer effect-layer)

(if (= remove-bg TRUE)
(gimp-context-set-foreground '(0 0 0))
(gimp-context-set-foreground '(14 14 14))
)

(gimp-selection-load active-selection)
(plug-in-maze RUN-NONINTERACTIVE image active-layer 5 5 TRUE 0 seed 57 1)
(plug-in-oilify RUN-NONINTERACTIVE image active-layer mask-size 0)
(plug-in-edge RUN-NONINTERACTIVE image active-layer 2 1 0)
(if (= type RGBA-IMAGE)
(gimp-desaturate active-layer))

(if (and
(= remove-bg TRUE)
(= separate-layer TRUE))
(begin (gimp-by-color-select active-layer '(0 0 0) 15 2 TRUE FALSE 10 FALSE)
(gimp-edit-clear active-layer)))

(if (= keep-selection FALSE)
(gimp-selection-none image))

(gimp-image-remove-channel image active-selection)
(gimp-image-set-active-layer image drawable)

(gimp-image-undo-group-end image)

(gimp-displays-flush)

(gimp-context-pop)

28.7 電路圖,修改成可「單行」執行的 Python-Fu

修改後的程式碼,與測試的結果圖
  • 啟動 GIMP Python Console
  • 將下面的程式碼,全部選取、複製、再貼到 Python Console 提示符號 >>> 之後,按下 Enter 。
  • 過一會,就會看到結果圖。


inSize = 256
theWidth = inSize
theHeight = inSize
theImage = pdb.gimp_image_new(theWidth, theHeight, RGB)
baseLayer = pdb.gimp_layer_new(theImage, theWidth, theHeight, RGBA_IMAGE, "Background", 100, NORMAL_MODE)
pdb.gimp_image_add_layer(theImage, baseLayer, 0)
# 可以把顯示影像物件 theImage 提前到這裡
pdb.gimp_display_new(theImage)

# 前面的程式碼是為了生成「一個透明的圖層」
# 作為之後生成電路圖案的參考圖層
# 此參考圖層規範了電路圖案的長寬

image = theImage  # 指派影像物件
drawable = baseLayer  # 指派圖層物件

mask_size = 17
seed = 3
remove_bg = FALSE
keep_selection = TRUE
separate_layer = TRUE

type = pdb.gimp_drawable_type_with_alpha(drawable)
image_width = pdb.gimp_image_width(image)
image_height = pdb.gimp_image_height(image)
active_selection = 0
from_selection = 0
selection_bounds = 0
select_offset_x = 0
select_offset_y = 0
select_width = 0
select_height = 0
effect_layer = 0
active_layer = 0

pdb.gimp_context_push()
pdb.gimp_image_undo_group_start(image)

pdb.gimp_layer_add_alpha(drawable)

if pdb.gimp_selection_is_empty(image):
  pdb.gimp_selection_layer_alpha(drawable)
  active_selection = pdb.gimp_selection_save(image)
  from_selection = FALSE
else:
  from_selection = TRUE
  active_selection = pdb.gimp_selection_save(image)


selection_bounds = pdb.gimp_selection_bounds(image)
select_offset_x = selection_bounds[1]
select_offset_y = selection_bounds[2]
select_width = selection_bounds[3] - select_offset_x
select_height = selection_bounds[4] - select_offset_y

floating_sel = 0

if separate_layer:
  effect_layer = pdb.gimp_layer_new(image, select_width, select_height, type, "effect layer", 100, NORMAL_MODE)
  pdb.gimp_image_add_layer(image, effect_layer, -1)
  pdb.gimp_layer_set_offsets(effect_layer, select_offset_x, select_offset_y)
  pdb.gimp_selection_none(image)
  pdb.gimp_edit_clear(effect_layer)
  pdb.gimp_selection_load(active_selection)
  pdb.gimp_edit_copy(drawable)
  floating_sel = pdb.gimp_edit_paste(effect_layer, FALSE)
  pdb.gimp_floating_sel_anchor(floating_sel)
  pdb.gimp_image_set_active_layer(image, effect_layer)
else:
  effect_layer = drawable

active_layer = effect_layer

if remove_bg:
  color = (0, 0, 0)
  pdb.gimp_context_set_foreground(color)
  color = (14, 14, 14)
  pdb.gimp_context_set_foreground(color)


pdb.gimp_selection_load(active_selection)
pdb.plug_in_maze(image, active_layer, 5, 5, TRUE, 0, seed, 57, 1)
pdb.plug_in_oilify(image, active_layer, mask_size, 0)
pdb.plug_in_edge(image, active_layer, 2, 1, 0)

if (type == RGBA_IMAGE):
  pdb.gimp_desaturate(active_layer)

if (remove_bg and separate_layer):
  color = (0, 0, 0)
  pdb.gimp_by_color_select(active_layer, color, 15, 2, TRUE, FALSE, 10, FALSE)
  pdb.gimp_edit_clear(active_layer)

if (keep_selection == FALSE):
  pdb.gimp_selection_none(image)

pdb.gimp_image_remove_channel(image, active_selection)
pdb.gimp_image_set_active_layer(image, drawable)

pdb.gimp_image_undo_group_end(image)

pdb.gimp_displays_flush()

pdb.gimp_context_pop()

28.8 電路圖,修改過程中,須注意的 Python 語法特點

計算出選取區域的長寬
Scheme code
(set! selection-bounds (gimp-selection-bounds image))
(set! select-offset-x (cadr selection-bounds))
(set! select-offset-y (caddr selection-bounds))
(set! select-width (- (cadr (cddr selection-bounds)) select-offset-x))
(set! select-height (- (caddr (cddr selection-bounds)) select-offset-y))

Python code
selection_bounds = pdb.gimp_selection_bounds(image)
select_offset_x = selection_bounds[1]
select_offset_y = selection_bounds[2]
select_width = selection_bounds[3] - select_offset_x
select_height = selection_bounds[4] - select_offset_y


if-else 條件控制的敘述
Scheme code 要注意括號的對齊

(if (= (car (gimp-selection-is-empty image)) TRUE)
(begin
(gimp-selection-layer-alpha drawable)
(set! active-selection (car (gimp-selection-save image)))
(set! from-selection FALSE))
(begin
(set! from-selection TRUE) ; 條件不成立,會執行這兩行
(set! active-selection (car (gimp-selection-save image)))
) ; 此為 begin 的右括號
) ; 此為 if 的右括號

Python code 要注意縮排

if pdb.gimp_selection_is_empty(image):
  pdb.gimp_selection_layer_alpha(drawable)
  active_selection = pdb.gimp_selection_save(image)
  from_selection = FALSE
else:
  from_selection = TRUE # 條件不成立,會執行這兩行
  active_selection = pdb.gimp_selection_save(image)


RGB 顏色的指定
Scheme code
(gimp-context-set-foreground '(0 0 0))

Python code 拆成兩行來達到設定前景色的功能
color = (0, 0, 0)
pdb.gimp_context_set_foreground(color)