4 Gimp plugin "Heal selection"
6 Copyright 2009 lloyd konneker (bootch at nc.rr.com)
7 Based on smart_remove.scm Copyright 2000 by Paul Harrison.
10 1.0 lloyd konneker lkk 9/21/2009 Initial version in python.
11 (See release notes for differences over P. Harrison's prior version in scheme language.)
15 This program is free software; you can redistribute it and/or modify
16 it under the terms of the GNU General Public License as published by
17 the Free Software Foundation; either version 2 of the License, or
18 (at your option) any later version.
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
25 The GNU Public License is available at
26 http://www.gnu.org/copyleft/gpl.html
32 gettext.install("resynthesizer", gimp.locale_directory, unicode=True)
36 def heal_selection(timg, tdrawable, samplingRadiusParam=50, directionParam=0, orderParam=0):
38 Create stencil selection in a temp image to pass as source (corpus) to plugin resynthesizer,
39 which does the substantive work.
41 if pdb.gimp_selection_is_empty(timg):
42 pdb.gimp_message(_("You must first select a region to heal."))
45 pdb.gimp_image_undo_group_start(timg)
47 targetBounds = tdrawable.mask_bounds
49 # In duplicate image, create the sample (corpus).
50 # (I tried to use a temporary layer but found it easier to use duplicate image.)
51 tempImage = pdb.gimp_image_duplicate(timg)
53 raise RuntimeError, "Failed duplicate image"
55 # !!! The drawable can be a mask (grayscale channel), don't restrict to layer.
56 work_drawable = pdb.gimp_image_get_active_drawable(tempImage)
58 raise RuntimeError, "Failed get active drawable"
61 grow and punch hole, making a frisket iow stencil iow donut
64 orgSelection = pdb.gimp_selection_save(tempImage) # save for later use
65 pdb.gimp_selection_grow(tempImage, samplingRadiusParam)
66 # ??? returns None , docs say it returns SUCCESS
68 # !!! Note that if selection is a bordering ring already, growing expanded it inwards.
69 # Which is what we want, to make a corpus inwards.
71 grownSelection = pdb.gimp_selection_save(tempImage)
73 # Cut hole where the original selection was, so we don't sample from it.
74 # !!! Note that gimp enums/constants are not prefixed with GIMP_
75 pdb.gimp_image_select_item(tempImage, CHANNEL_OP_SUBTRACT, orgSelection)
78 Selection (to be the corpus) is donut or frisket around the original target T
84 # crop the temp image to size of selection to save memory and for directional healing!!
85 frisketBounds = grownSelection.mask_bounds
86 frisketLowerLeftX = frisketBounds[0]
87 frisketLowerLeftY = frisketBounds[1]
88 frisketUpperRightX = frisketBounds[2]
89 frisketUpperRightY = frisketBounds[3]
90 targetLowerLeftX = targetBounds[0]
91 targetLowerLeftY = targetBounds[1]
92 targetUpperRightX = targetBounds[2]
93 targetUpperRightY = targetBounds[3]
95 frisketWidth = frisketUpperRightX - frisketLowerLeftX
96 frisketHeight = frisketUpperRightY - frisketLowerLeftY
98 # User's choice of direction affects the corpus shape, and is also passed to resynthesizer plugin
99 if directionParam == 0: # all around
100 # Crop to the entire frisket
101 newWidth, newHeight, newLLX, newLLY = ( frisketWidth, frisketHeight,
102 frisketLowerLeftX, frisketLowerLeftY )
103 elif directionParam == 1: # sides
104 # Crop to target height and frisket width: XTX
105 newWidth, newHeight, newLLX, newLLY = ( frisketWidth, targetUpperRightY-targetLowerLeftY,
106 frisketLowerLeftX, targetLowerLeftY )
107 elif directionParam == 2: # above and below
108 # X Crop to target width and frisket height
111 newWidth, newHeight, newLLX, newLLY = ( targetUpperRightX-targetLowerLeftX, frisketHeight,
112 targetLowerLeftX, frisketLowerLeftY )
113 # Restrict crop to image size (condition of gimp_image_crop) eg when off edge of image
114 newWidth = min(pdb.gimp_image_width(tempImage) - newLLX, newWidth)
115 newHeight = min(pdb.gimp_image_height(tempImage) - newLLY, newHeight)
116 pdb.gimp_image_crop(tempImage, newWidth, newHeight, newLLX, newLLY)
118 # Encode two script params into one resynthesizer param.
119 # use border 1 means fill target in random order
120 # use border 0 is for texture mapping operations, not used by this script
122 useBorder = 1 # User wants NO order, ie random filling
123 elif orderParam == 1 : # Inward to corpus. 2,3,4
124 useBorder = directionParam+2 # !!! Offset by 2 to get past the original two boolean values
126 # Outward from image center.
127 # 5+0=5 outward concentric
128 # 5+1=6 outward from sides
129 # 5+2=7 outward above and below
130 useBorder = directionParam+5
132 # Note that the old resynthesizer required an inverted selection !!
136 gimp.Display(tempImage)
137 gimp.displays_flush()
138 except RuntimeError: # thrown if non-interactive
140 from time import sleep
143 # Not necessary to restore image to initial condition of selection, activity,
144 # the original image should not have been changed,
145 # and the resynthesizer should only heal, not change selection.
147 # Note that the API hasn't changed but use_border param now has more values.
148 pdb.plug_in_resynthesizer(timg, tdrawable, 0,0, useBorder, work_drawable.ID, -1, -1, 0.0, 0.117, 16, 500)
150 # Clean up (comment out to debug)
151 gimp.delete(tempImage)
152 pdb.gimp_image_undo_group_end(timg)
156 "python_fu_heal_selection",
157 N_("Heal the selection from surroundings as if using the heal tool."),
158 "Requires separate resynthesizer plugin.",
160 "2009 Lloyd Konneker", # Copyright
162 N_("_Heal selection..."),
165 (PF_IMAGE, "image", "Input image", None),
166 (PF_DRAWABLE, "drawable", "Input drawable", None),
167 (PF_INT, "samplingRadiusParam", _("Context sampling width (pixels):"), 50),
168 (PF_OPTION,"directionParam", _("Sample from:"),0,[_("All around"),_("Sides"),_("Above and below")]),
169 (PF_OPTION, "orderParam", _("Filling order:"), 0, [_("Random"),
170 _("Inwards towards center"), _("Outwards from center") ])
174 menu="<Image>/Filters/Enhance",
175 domain=("resynthesizer", gimp.locale_directory)