mirror of
https://github.com/Diolinux/PhotoGIMP.git
synced 2026-04-05 19:51:58 +02:00
Improving Plugns scripts & Add Editor Config
This commit is contained in:
9
.editorconfig
Normal file
9
.editorconfig
Normal file
@@ -0,0 +1,9 @@
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -4,3 +4,6 @@
|
||||
# Ignore Personal GIMP Files
|
||||
.var/app/org.gimp.GIMP/data
|
||||
.var/app/org.gimp.GIMP/current
|
||||
|
||||
# Ignore VSCode Files
|
||||
.vscode/*
|
||||
|
||||
@@ -49,6 +49,7 @@ def heal_selection(timg, tdrawable, samplingRadiusParam=50, directionParam=0, or
|
||||
# In duplicate image, create the sample (corpus).
|
||||
# (I tried to use a temporary layer but found it easier to use duplicate image.)
|
||||
tempImage = pdb.gimp_image_duplicate(timg)
|
||||
|
||||
if not tempImage:
|
||||
raise RuntimeError, "Failed duplicate image"
|
||||
|
||||
@@ -72,7 +73,7 @@ def heal_selection(timg, tdrawable, samplingRadiusParam=50, directionParam=0, or
|
||||
|
||||
# Cut hole where the original selection was, so we don't sample from it.
|
||||
# !!! Note that gimp enums/constants are not prefixed with GIMP_
|
||||
pdb.gimp_image_select_item(tempImage, CHANNEL_OP_SUBTRACT, orgSelection)
|
||||
pdb.gimp_selection_combine(orgSelection, CHANNEL_OP_SUBTRACT)
|
||||
|
||||
'''
|
||||
Selection (to be the corpus) is donut or frisket around the original target T
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''
|
||||
"""
|
||||
Gimp plugin "Heal transparency"
|
||||
|
||||
Copyright 2010 lloyd konneker (bootch at nc.rr.com)
|
||||
@@ -23,12 +23,13 @@ License:
|
||||
The GNU Public License is available at
|
||||
http://www.gnu.org/copyleft/gpl.html
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
from gimpfu import *
|
||||
|
||||
gettext.install("resynthesizer", gimp.locale_directory, unicode=True)
|
||||
|
||||
|
||||
def heal_transparency(timg, tdrawable, samplingRadiusParam=50, orderParam=2):
|
||||
|
||||
# precondition should be enforced by Gimp according to image modes allowed.
|
||||
@@ -44,7 +45,7 @@ def heal_transparency(timg, tdrawable, samplingRadiusParam=50, orderParam=2):
|
||||
pdb.gimp_image_set_active_layer(timg, tdrawable)
|
||||
|
||||
# alpha to selection
|
||||
pdb.gimp_image_select_item(timg, CHANNEL_OP_REPLACE, tdrawable)
|
||||
pdb.gimp_selection_layer_alpha(tdrawable)
|
||||
# Want the transparent, not the opaque.
|
||||
pdb.gimp_selection_invert(timg)
|
||||
# Since transparency was probably anti-aliased (dithered with partial transpancy),
|
||||
@@ -56,12 +57,14 @@ def heal_transparency(timg, tdrawable, samplingRadiusParam=50, orderParam=2):
|
||||
|
||||
# Call heal selection (not the resynthesizer), which will create a proper corpus.
|
||||
# 0 = sample from all around
|
||||
pdb.python_fu_heal_selection(timg, tdrawable, samplingRadiusParam, 0, orderParam, run_mode=RUN_NONINTERACTIVE)
|
||||
pdb.python_fu_heal_selection(
|
||||
timg, tdrawable, samplingRadiusParam, 0, orderParam, run_mode=RUN_NONINTERACTIVE
|
||||
)
|
||||
|
||||
# Restore image to initial conditions of user, except for later cleanup.
|
||||
|
||||
# restore selection
|
||||
pdb.gimp_image_select_item(timg, CHANNEL_OP_REPLACE, org_selection)
|
||||
pdb.gimp_selection_load(org_selection)
|
||||
|
||||
# Clean up (comment out to debug)
|
||||
pdb.gimp_image_undo_group_end(timg)
|
||||
@@ -69,7 +72,9 @@ def heal_transparency(timg, tdrawable, samplingRadiusParam=50, orderParam=2):
|
||||
|
||||
register(
|
||||
"python_fu_heal_transparency",
|
||||
N_("Removes alpha channel by synthesis. Fill outward for edges, inward for holes."),
|
||||
N_(
|
||||
"Removes alpha channel by synthesis. Fill outward for edges, inward for holes."
|
||||
),
|
||||
"Requires separate resynthesizer plugin.",
|
||||
"Lloyd Konneker",
|
||||
"Copyright 2010 Lloyd Konneker",
|
||||
@@ -80,13 +85,18 @@ register(
|
||||
(PF_IMAGE, "image", "Input image", None),
|
||||
(PF_DRAWABLE, "drawable", "Input drawable", None),
|
||||
(PF_INT, "samplingRadiusParam", _("Context sampling width (pixels):"), 50),
|
||||
(PF_OPTION, "orderParam", _("Filling order:"), 2, [_("Random"),
|
||||
_("Inwards towards center"), _("Outwards from center") ])
|
||||
(
|
||||
PF_OPTION,
|
||||
"orderParam",
|
||||
_("Filling order:"),
|
||||
2,
|
||||
[_("Random"), _("Inwards towards center"), _("Outwards from center")],
|
||||
),
|
||||
],
|
||||
[],
|
||||
heal_transparency,
|
||||
menu="<Image>/Filters/Enhance",
|
||||
domain=("resynthesizer", gimp.locale_directory)
|
||||
)
|
||||
domain=("resynthesizer", gimp.locale_directory),
|
||||
)
|
||||
|
||||
main()
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''
|
||||
"""
|
||||
Gimp plugin.
|
||||
Transfer style (color and surface texture) from a source image to the active, target image.
|
||||
|
||||
Requires resynthesizer plug-in.
|
||||
|
||||
Author:
|
||||
Authors:
|
||||
lloyd konneker, lkk
|
||||
Gabriel Almir, avlye
|
||||
|
||||
Version:
|
||||
1.0 lkk 7/15/2010 Initial version. Released to Gimp Registry.
|
||||
1.1 lkk 8/1/2010 Unreleased
|
||||
1.2 lkk 8/10/2010
|
||||
1.3 avlye 01/10/2021
|
||||
|
||||
Change log:
|
||||
_________________
|
||||
@@ -22,6 +24,8 @@ _________________
|
||||
1.2
|
||||
Changes for new resynthesizer: no need to synchronize, remove alphas
|
||||
Fixed improper adjustment of contrast of source: only adjust source map.
|
||||
1.3
|
||||
Minor changes to improve developer experience
|
||||
|
||||
TODO
|
||||
a quality setting that changes the parameters to resynth
|
||||
@@ -105,18 +109,19 @@ synchronization of modes
|
||||
abstracting the settings
|
||||
contrast adjustment
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
from gimpfu import *
|
||||
from math import acos
|
||||
from math import acos, pi
|
||||
|
||||
gettext.install("resynthesizer", gimp.locale_directory, unicode=True)
|
||||
|
||||
# True if you want to display and retain working, temporary images
|
||||
debug = False
|
||||
|
||||
def display_debug_image(image) :
|
||||
if debug :
|
||||
|
||||
def display_debug_image(image):
|
||||
if debug:
|
||||
try:
|
||||
pdb.gimp_display_new(image)
|
||||
pdb.gimp_displays_flush()
|
||||
@@ -125,7 +130,7 @@ def display_debug_image(image) :
|
||||
|
||||
|
||||
def make_grayscale_map(image, drawable):
|
||||
'''
|
||||
"""
|
||||
Make a grayscale copy for a map.
|
||||
|
||||
Maps must be same size as their parent image.
|
||||
@@ -133,8 +138,8 @@ def make_grayscale_map(image, drawable):
|
||||
If image is already grayscale, return it without copying.
|
||||
|
||||
Maps don't need a selection, since the resynthesizer looks at parent drawables for the selection.
|
||||
'''
|
||||
if pdb.gimp_image_base_type(image) == GRAY :
|
||||
"""
|
||||
if pdb.gimp_image_base_type(image) == GRAY:
|
||||
return image, drawable
|
||||
|
||||
# Save selection, copy entire image, and restore
|
||||
@@ -142,8 +147,10 @@ def make_grayscale_map(image, drawable):
|
||||
pdb.gimp_selection_all(image) # copy requires selection
|
||||
pdb.gimp_edit_copy(drawable)
|
||||
if original_selection:
|
||||
pdb.gimp_image_select_item(image, CHANNEL_OP_REPLACE, original_selection) # restore selection in image
|
||||
pdb.gimp_image_remove_channel(image, original_selection) # cleanup the copied selection mask
|
||||
pdb.gimp_selection_load(original_selection) # restore selection in image
|
||||
pdb.gimp_image_remove_channel(
|
||||
image, original_selection
|
||||
) # cleanup the copied selection mask
|
||||
# !!! Note remove_channel not drawable_delete
|
||||
|
||||
# Make a copy, greyscale
|
||||
@@ -154,8 +161,8 @@ def make_grayscale_map(image, drawable):
|
||||
return temp_image, temp_drawable
|
||||
|
||||
|
||||
def synchronize_modes(target_image, source_image) :
|
||||
'''
|
||||
def synchronize_modes(target_image, source_image):
|
||||
"""
|
||||
User-friendliness:
|
||||
If mode of target is not equal to mode of source source, change modes.
|
||||
Resynthesizer requires target and source to be same mode.
|
||||
@@ -163,46 +170,25 @@ def synchronize_modes(target_image, source_image) :
|
||||
UI decision: make this quiet, presume user intends mode change.
|
||||
But don't permanently change mode of source.
|
||||
Always upgrade GRAY to RGB, not downgrade RGB to GRAY.
|
||||
'''
|
||||
"""
|
||||
target_mode = pdb.gimp_image_base_type(target_image)
|
||||
source_mode = pdb.gimp_image_base_type(source_image)
|
||||
if target_mode != source_mode :
|
||||
if target_mode != source_mode:
|
||||
# print("Map style: converted mode\n.")
|
||||
if target_mode == GRAY:
|
||||
pdb.gimp_image_convert_rgb(target_image)
|
||||
else : # target is RGB and source is GRAY
|
||||
else: # target is RGB and source is GRAY
|
||||
# Assert only convert a copy of source,
|
||||
# user NEVER intends original source be altered.
|
||||
pdb.gimp_image_convert_rgb(source_image)
|
||||
|
||||
'''
|
||||
Not used
|
||||
'''
|
||||
"""
|
||||
def synchronize_alphas(target_drawable, source_drawable) :
|
||||
'''
|
||||
User-friendliness:
|
||||
If source has alpha and target doesn't, remove or add alpha to source.
|
||||
Do this without user dialog since it is done on copies, and really, the alpha doesn't matter.
|
||||
'''
|
||||
if pdb.gimp_drawable_has_alpha(source_drawable) :
|
||||
if not pdb.gimp_drawable_has_alpha(target_drawable) :
|
||||
# Should never get here, since removed alpha from source_drawable copy earlier
|
||||
print "Adding alpha channel to target image since style source image has alpha."
|
||||
pdb.gimp_layer_add_alpha (target_drawable)
|
||||
else: # source has no alpha
|
||||
if pdb.gimp_drawable_has_alpha(target_drawable) :
|
||||
print "Map style: Adding alpha channel to style source image copy since target image has alpha."
|
||||
pdb.gimp_layer_add_alpha (source_drawable)
|
||||
"""
|
||||
|
||||
|
||||
def copy_selection_to_image(drawable) :
|
||||
'''
|
||||
def copy_selection_to_image(drawable):
|
||||
"""
|
||||
If image has a selection, copy selection to new image, and prepare it for resynthesizer,
|
||||
else return a copy of the entire source image.
|
||||
This is called for the source image, where it helps performance to reduce size and flatten.
|
||||
'''
|
||||
"""
|
||||
image = pdb.gimp_drawable_get_image(drawable)
|
||||
|
||||
# copy selection or whole image
|
||||
@@ -216,34 +202,52 @@ def copy_selection_to_image(drawable) :
|
||||
return image_copy, layer_copy
|
||||
|
||||
|
||||
def synchronize_contrast( drawable, source_drawable, percent_transfer) :
|
||||
'''
|
||||
def synchronize_contrast(drawable, source_drawable, percent_transfer):
|
||||
"""
|
||||
Adjust contrast of source, to match target.
|
||||
Adjustment depends inversely on percent_transfer.
|
||||
Very crude histogram matching.
|
||||
'''
|
||||
"""
|
||||
# histogram upper half: typical mean is 191 (3/4*255). Skew of mean towards 255 means high contrast.
|
||||
mean, deviation, median, pixels, count, percentile = pdb.gimp_histogram(drawable, HISTOGRAM_VALUE, 128, 255)
|
||||
source_mean, source_deviation, source_median, pixels, count, percentile = pdb.gimp_histogram(
|
||||
source_drawable, HISTOGRAM_VALUE, 128, 255)
|
||||
mean, deviation, median, pixels, count, percentile = pdb.gimp_histogram(
|
||||
drawable, HISTOGRAM_VALUE, 128, 255
|
||||
)
|
||||
(
|
||||
source_mean,
|
||||
source_deviation,
|
||||
source_median,
|
||||
pixels,
|
||||
count,
|
||||
percentile,
|
||||
) = pdb.gimp_histogram(source_drawable, HISTOGRAM_VALUE, 128, 255)
|
||||
|
||||
# if mean > source_mean: # target has more contrast than source
|
||||
# Adjust contrast of source.
|
||||
# Inversely proportional to percent transfer.
|
||||
# 2.5 is from experimentation with gimp_brightness_contrast which seems linear in its effect.
|
||||
contrast_control = (mean - source_mean) * 2.5 * (1 - (percent_transfer / 100))
|
||||
|
||||
# clamp to valid range (above formula is lazy, ad hoc)
|
||||
if contrast_control < -127: contrast_control = -127
|
||||
if contrast_control > 127: contrast_control = 127
|
||||
contrast_control = max(min(contrast_control, 127), -127)
|
||||
|
||||
pdb.gimp_brightness_contrast(source_drawable, 0, contrast_control)
|
||||
|
||||
# For experimentation, print new values
|
||||
source_mean, source_deviation, source_median, pixels, count, percentile = pdb.gimp_histogram(
|
||||
source_drawable, HISTOGRAM_VALUE, 128, 255)
|
||||
(
|
||||
source_mean,
|
||||
source_deviation,
|
||||
source_median,
|
||||
pixels,
|
||||
count,
|
||||
percentile,
|
||||
) = pdb.gimp_histogram(source_drawable, HISTOGRAM_VALUE, 128, 255)
|
||||
|
||||
# print "Map style: Source contrast changed by ", contrast_control
|
||||
# print "Map style: Target and source upper half histogram means", mean, source_mean
|
||||
|
||||
|
||||
def calculate_map_weight(percent_transfer) :
|
||||
'''
|
||||
def calculate_map_weight(percent_transfer):
|
||||
"""
|
||||
This is a GUI design discussion.
|
||||
Transform percent_transfer to map_weight parameter to resynthesizer.
|
||||
For resynthesizer:
|
||||
@@ -252,43 +256,43 @@ def calculate_map_weight(percent_transfer) :
|
||||
Transform from a linear percent GUI, because user more comfortable than with a ratio [.5, 0]
|
||||
which is backwards to the usual *less on the left*.
|
||||
By experiment, a sinusoid gives good results for linearizing the non-linear map_weight control.
|
||||
'''
|
||||
return acos((percent_transfer/100)*2 -1)/(2*3.14)
|
||||
"""
|
||||
return acos((percent_transfer / 100) * 2 - 1) / (2 * pi)
|
||||
|
||||
|
||||
def transfer_style(image, drawable, source_drawable, percent_transfer, map_mode ):
|
||||
'''
|
||||
def transfer_style(image, drawable, source_drawable, percent_transfer, map_mode):
|
||||
"""
|
||||
Main body of plugin to transfer style from one image to another.
|
||||
|
||||
!!! Note map_mode is type string, "if map_mode:" will not work.
|
||||
'''
|
||||
"""
|
||||
|
||||
pdb.gimp_image_undo_group_start(image)
|
||||
|
||||
# Get image of source drawable
|
||||
source_image = pdb.gimp_drawable_get_image(source_drawable)
|
||||
|
||||
'''
|
||||
"""
|
||||
User-friendliness.
|
||||
Note the drawable chooser widget in Pygimp does not allow us to prefilter INDEXED mode.
|
||||
So check here and give a warning.
|
||||
'''
|
||||
"""
|
||||
# These are the originals base types, and this plugin might change the base types
|
||||
original_source_base_type = pdb.gimp_image_base_type(source_image)
|
||||
original_target_base_type = pdb.gimp_image_base_type(image)
|
||||
|
||||
if original_source_base_type == INDEXED :
|
||||
pdb.gimp_message(_("The style source cannot be of mode INDEXED"));
|
||||
if original_source_base_type == INDEXED:
|
||||
pdb.gimp_message(_("The style source cannot be of mode INDEXED"))
|
||||
return
|
||||
|
||||
if image == source_image and drawable == source_drawable:
|
||||
is_source_copy = False
|
||||
'''
|
||||
"""
|
||||
If source is same as target,
|
||||
then the old resynthesizer required a selection (engine used inverse selection for corpus).
|
||||
New resynthesizer doesn't need a selection.
|
||||
If source same as target, effect is similar to a blur.
|
||||
'''
|
||||
"""
|
||||
# assert modes and alphas are same (since they are same layer!)
|
||||
else: # target layer is not the source layer (source could be a copy of target, but effect is none)
|
||||
# Copy source always, for performance, and for possible mode change.
|
||||
@@ -298,32 +302,36 @@ def transfer_style(image, drawable, source_drawable, percent_transfer, map_mode
|
||||
# Futz with modes if necessary.
|
||||
synchronize_modes(image, source_image)
|
||||
|
||||
'''
|
||||
"""
|
||||
Old resythesizer required both images to have alpha, or neither.
|
||||
synchronize_alphas( drawable, source_drawable)
|
||||
'''
|
||||
"""
|
||||
|
||||
'''
|
||||
"""
|
||||
TODO For performance, if there is a selection in target, it would be better to copy
|
||||
selection to a new layer, and later merge it back (since resynthesizer engine reads
|
||||
entire target into memory. Low priority since rarely does user make a selection in target.
|
||||
'''
|
||||
"""
|
||||
|
||||
'''
|
||||
"""
|
||||
!!! Note this plugin always sends maps to the resynthesizer,
|
||||
and the "percent transfer" setting is always effective.
|
||||
However, maps may not be separate,copied images unless converted to grayscale.
|
||||
'''
|
||||
"""
|
||||
|
||||
# Copy and reduce maps to grayscale: at the option of the user
|
||||
# !!! Or if the target was GRAY and source is RGB, in which case maps give a better result.
|
||||
# Note that if the target was GRAY, we already upgraded it to RGB.
|
||||
if map_mode == 1 or (original_source_base_type == RGB and original_target_base_type == GRAY) :
|
||||
if map_mode == 1 or (
|
||||
original_source_base_type == RGB and original_target_base_type == GRAY
|
||||
):
|
||||
# print "Map style: source mode: ", original_source_base_type, " target mode: ", original_target_base_type
|
||||
# print "Map style: Converting maps to grayscale"
|
||||
# Convert mode, but in new temp image and drawable
|
||||
target_map_image, target_map_drawable = make_grayscale_map(image, drawable)
|
||||
source_map_image, source_map_drawable = make_grayscale_map(source_image, source_drawable)
|
||||
source_map_image, source_map_drawable = make_grayscale_map(
|
||||
source_image, source_drawable
|
||||
)
|
||||
|
||||
target_map = target_map_drawable
|
||||
source_map = source_map_drawable
|
||||
@@ -333,14 +341,13 @@ def transfer_style(image, drawable, source_drawable, percent_transfer, map_mode
|
||||
# Hard to explain why, but experimentation shows result more like user expectation.
|
||||
# TODO This could be improved.
|
||||
# !!! Don't change the original source, only a temporary map we created
|
||||
synchronize_contrast( drawable, source_map, percent_transfer)
|
||||
else :
|
||||
synchronize_contrast(drawable, source_map, percent_transfer)
|
||||
else:
|
||||
# !!! Maps ARE the target and source, not copies
|
||||
source_map = source_drawable
|
||||
target_map = drawable
|
||||
|
||||
|
||||
'''
|
||||
"""
|
||||
Parameters to resynthesizer:
|
||||
|
||||
htile and vtile = 1 since it reduces artifacts around edge
|
||||
@@ -351,12 +358,25 @@ def transfer_style(image, drawable, source_drawable, percent_transfer, map_mode
|
||||
|
||||
9 neighbors (a 3x3 patch) and 200 tries for speed
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
map_weight = calculate_map_weight(percent_transfer)
|
||||
|
||||
# !!! This is for version of resynthesizer, with an uninverted selection
|
||||
pdb.plug_in_resynthesizer(image, drawable, 1, 1, 1, source_drawable.ID, source_map.ID, target_map.ID, map_weight, 0.117, 9, 200)
|
||||
pdb.plug_in_resynthesizer(
|
||||
image,
|
||||
drawable,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
source_drawable.ID,
|
||||
source_map.ID,
|
||||
target_map.ID,
|
||||
map_weight,
|
||||
0.117,
|
||||
9,
|
||||
200,
|
||||
)
|
||||
|
||||
# Clean up.
|
||||
# Delete working images: separate map images and copy of source image
|
||||
@@ -385,13 +405,18 @@ register(
|
||||
(PF_DRAWABLE, "drawable", "Input drawable", None),
|
||||
(PF_DRAWABLE, "source_drawable", _("Source of style:"), None),
|
||||
(PF_SLIDER, "percent_transfer", _("Percent transfer:"), 0, (10, 90, 10.0)),
|
||||
(PF_RADIO, "map_mode", _("Map by:"), 0, ((_("Color and brightness"), 0),(_("Brightness only"),1)))
|
||||
(
|
||||
PF_RADIO,
|
||||
"map_mode",
|
||||
_("Map by:"),
|
||||
0,
|
||||
((_("Color and brightness"), 0), (_("Brightness only"), 1)),
|
||||
),
|
||||
],
|
||||
[],
|
||||
transfer_style,
|
||||
menu="<Image>/Filters/Map",
|
||||
domain=("resynthesizer", gimp.locale_directory)
|
||||
)
|
||||
domain=("resynthesizer", gimp.locale_directory),
|
||||
)
|
||||
|
||||
main()
|
||||
|
||||
|
||||
@@ -11,12 +11,14 @@ Sometimes called rendering a texture.
|
||||
|
||||
Requires resynthesizer plug-in.
|
||||
|
||||
Author:
|
||||
Authors:
|
||||
lloyd konneker, lkk, bootch at nc.rr.com
|
||||
Gabriel Almir, avlye, avlye.me
|
||||
|
||||
Version:
|
||||
1.0 lkk 7/15/2010 Initial version
|
||||
1.1 lkk 4/10/2011 Fixed a bug with layer types impacting greyscale images.
|
||||
1.3 avlye 01/10/2020 Minor changes for improving developer experience
|
||||
|
||||
License:
|
||||
|
||||
@@ -69,6 +71,7 @@ def new_resized_image(image, resize_ratio):
|
||||
# Create new image resized by a ratio from *selection* in the old image
|
||||
|
||||
(is_selection, ulx, uly, lrx, lry) = pdb.gimp_selection_bounds(image)
|
||||
|
||||
if not is_selection :
|
||||
# Resynthesizer will use the entire source image as corpus.
|
||||
# Resize new image in proportion to entire source image.
|
||||
@@ -83,10 +86,12 @@ def new_resized_image(image, resize_ratio):
|
||||
new_basetype = pdb.gimp_image_base_type(image) # same as source
|
||||
new_layertype = pdb.gimp_drawable_type(pdb.gimp_image_get_active_layer(image))
|
||||
new_image = pdb.gimp_image_new(new_width, new_height, new_basetype)
|
||||
|
||||
# !!! Note that gimp_layer_new wants a layer type, not an image basetype
|
||||
new_drawable = pdb.gimp_layer_new(new_image, new_width, new_height,
|
||||
new_layertype, "Texture", 100, NORMAL_MODE)
|
||||
pdb.gimp_image_add_layer(new_image, new_drawable, 0)
|
||||
|
||||
return new_image, new_drawable
|
||||
|
||||
|
||||
@@ -95,6 +100,7 @@ def display_image(image):
|
||||
gimp.Display(image)
|
||||
except gimp.error:
|
||||
pass # If runmode is NONINTERACTIVE, expect gimp_display_new() to fail
|
||||
|
||||
gimp.displays_flush()
|
||||
|
||||
|
||||
@@ -117,6 +123,7 @@ def render_texture(image, drawable, resize_ratio=2, make_tile=0):
|
||||
'''
|
||||
new_image, new_drawable = new_resized_image(image, resize_ratio)
|
||||
pdb.gimp_image_undo_disable(new_image)
|
||||
|
||||
if not new_drawable:
|
||||
raise RuntimeError, "Failed create layer."
|
||||
|
||||
@@ -128,19 +135,19 @@ def render_texture(image, drawable, resize_ratio=2, make_tile=0):
|
||||
copy original into temp and crop it to the selection to save memory in resynthesizer
|
||||
'''
|
||||
temp_image = pdb.gimp_image_duplicate(image)
|
||||
|
||||
if not temp_image:
|
||||
raise RuntimeError, "Failed duplicate image"
|
||||
|
||||
# Get bounds, offset of selection
|
||||
(is_selection, ulx, uly, lrx, lry) = pdb.gimp_selection_bounds(image)
|
||||
if not is_selection :
|
||||
# No need to crop. Resynthesizer will use all if no selection.
|
||||
pass
|
||||
else :
|
||||
|
||||
if is_selection:
|
||||
pdb.gimp_image_crop(temp_image, lrx - ulx, lry - uly, ulx, uly)
|
||||
|
||||
# Don't flatten because it turns transparency to background (white usually)
|
||||
work_layer = pdb.gimp_image_get_active_layer(temp_image)
|
||||
|
||||
if not work_layer:
|
||||
raise RuntimeError, "Failed get active layer"
|
||||
|
||||
@@ -151,12 +158,7 @@ def render_texture(image, drawable, resize_ratio=2, make_tile=0):
|
||||
# That is what these settings mean in the resynthesizer:
|
||||
# wrap context probes in the target so that edges of target will be suitable for seamless tiling.
|
||||
# I.E. treat the target as a sphere when matching.
|
||||
if make_tile :
|
||||
htile = 1
|
||||
vtile = 1
|
||||
else :
|
||||
htile = 0
|
||||
vtile = 0
|
||||
tile = (1, 1) if make_tile else (0, 0)
|
||||
|
||||
# Call resynthesizer
|
||||
# use_border is moot since there is no context (outside the target) in the newImage.
|
||||
@@ -167,7 +169,7 @@ def render_texture(image, drawable, resize_ratio=2, make_tile=0):
|
||||
|
||||
# For version of resynthesizer with uninverted selection
|
||||
# !!! Pass -1 for ID of no layer, not None
|
||||
pdb.plug_in_resynthesizer(new_image, new_drawable, htile, vtile, 0, work_layer.ID, -1, -1, 0.0, 0.117, 9, 200)
|
||||
pdb.plug_in_resynthesizer(new_image, new_drawable, tile[0], tile[1], 0, work_layer.ID, -1, -1, 0.0, 0.117, 9, 200)
|
||||
|
||||
display_image(new_image)
|
||||
|
||||
@@ -201,4 +203,3 @@ register(
|
||||
)
|
||||
|
||||
main()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''
|
||||
"""
|
||||
Gimp plugin "Fill with pattern seamless..."
|
||||
Front end to the resynthesizer plugin to make a seamless fill.
|
||||
|
||||
@@ -28,31 +28,41 @@ GNU General Public License for more details.
|
||||
The GNU Public License is available at
|
||||
http://www.gnu.org/copyleft/gpl.html
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
from gimpfu import *
|
||||
|
||||
gettext.install("resynthesizer", gimp.locale_directory, unicode=True);
|
||||
gettext.install("resynthesizer", gimp.locale_directory, unicode=True)
|
||||
|
||||
debug = False
|
||||
|
||||
|
||||
def layer_from_pattern(image, pattern):
|
||||
'''
|
||||
"""
|
||||
Create a new image and layer having the same size as a pattern.
|
||||
'''
|
||||
"""
|
||||
new_basetype = pdb.gimp_image_base_type(image) # same as source
|
||||
new_layertype = pdb.gimp_drawable_type(pdb.gimp_image_get_active_layer(image))
|
||||
pattern_width, pattern_height, bpp = pdb.gimp_pattern_get_info(pattern)
|
||||
new_image = pdb.gimp_image_new(pattern_width, pattern_height, new_basetype)
|
||||
|
||||
# !!! Note that gimp_layer_new wants a layer type, not an image basetype
|
||||
new_drawable = pdb.gimp_layer_new(new_image, pattern_width, pattern_height,
|
||||
new_layertype, "Texture", 100, NORMAL_MODE)
|
||||
new_drawable = pdb.gimp_layer_new(
|
||||
new_image,
|
||||
pattern_width,
|
||||
pattern_height,
|
||||
new_layertype,
|
||||
"Texture",
|
||||
100,
|
||||
NORMAL_MODE,
|
||||
)
|
||||
pdb.gimp_image_add_layer(new_image, new_drawable, 0)
|
||||
|
||||
return new_image, new_drawable
|
||||
|
||||
|
||||
def guts(image, drawable, pattern):
|
||||
''' Crux of algorithm '''
|
||||
""" Crux of algorithm """
|
||||
|
||||
# Make drawble from pattern
|
||||
pattern_image, pattern_layer = layer_from_pattern(image, pattern)
|
||||
@@ -70,7 +80,9 @@ def guts(image, drawable, pattern):
|
||||
# -1, -1, 0: No maps and no map weight
|
||||
# DO pass pattern_layer.ID !!!
|
||||
# Resynthesizer is an engine, never interactive
|
||||
pdb.plug_in_resynthesizer(image, drawable, 0, 0, 0, pattern_layer.ID, -1, -1, 0, 0.05, 8, 300)
|
||||
pdb.plug_in_resynthesizer(
|
||||
image, drawable, 0, 0, 0, pattern_layer.ID, -1, -1, 0, 0.05, 8, 300
|
||||
)
|
||||
|
||||
# Clean up
|
||||
if not debug:
|
||||
@@ -79,10 +91,10 @@ def guts(image, drawable, pattern):
|
||||
|
||||
|
||||
def plugin_main(image, drawable, pattern):
|
||||
'''
|
||||
"""
|
||||
Main: the usual user-friendly precondition checking, postcondition cleanup.
|
||||
pattern is a string
|
||||
'''
|
||||
"""
|
||||
# User_friendly: if no selection, use entire image.
|
||||
# But the resynthesizer does that for us.
|
||||
|
||||
@@ -107,12 +119,12 @@ register(
|
||||
[
|
||||
(PF_IMAGE, "image", "Input image", None),
|
||||
(PF_DRAWABLE, "drawable", "Input drawable", None),
|
||||
(PF_PATTERN, "pattern", _("Pattern:"), 'Maple Leaves')
|
||||
(PF_PATTERN, "pattern", _("Pattern:"), "Maple Leaves"),
|
||||
],
|
||||
[],
|
||||
plugin_main,
|
||||
menu="<Image>/Edit",
|
||||
domain=("resynthesizer", gimp.locale_directory)
|
||||
)
|
||||
domain=("resynthesizer", gimp.locale_directory),
|
||||
)
|
||||
|
||||
main()
|
||||
|
||||
@@ -7,6 +7,7 @@ Requires resynthesizer plug_in (v2).
|
||||
Author:
|
||||
lloyd konneker (bootch at nc.rr.com)
|
||||
Based on smart_enlarge.scm 2000 by Paul Harrison.
|
||||
Gabriel Almir, avlye
|
||||
|
||||
Version:
|
||||
1.0 lloyd konneker lkk 2010 Initial version in python.
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''
|
||||
"""
|
||||
Gimp plugin "Uncrop"
|
||||
|
||||
Increase image/canvas size and synthesize outer band from edge of original.
|
||||
|
||||
Author:
|
||||
lloyd konneker, lkk
|
||||
Gabriel Almir, avlye
|
||||
|
||||
Version:
|
||||
1.0 lkk 5/15/2009 Initial version in scheme, released to Gimp Registry.
|
||||
1.1 lkk 9/21/2009 Translate to python.
|
||||
1.3 avlye 01/11/2020
|
||||
|
||||
License:
|
||||
|
||||
@@ -41,48 +43,51 @@ Refresh: just copy, no need to restart gimp if the pdb registration is unchange
|
||||
|
||||
IN: Nothing special. The selection is immaterial but is not preserved.
|
||||
OUT larger layer and image. All other layers not enlarged.
|
||||
'''
|
||||
"""
|
||||
|
||||
from gimpfu import *
|
||||
|
||||
gettext.install("resynthesizer", gimp.locale_directory, unicode=True)
|
||||
|
||||
|
||||
def resizeImageCentered(image, percentEnlarge):
|
||||
# resize and center image by percent (converted to pixel units)
|
||||
deltaFraction = (percentEnlarge / 100) + 1.0
|
||||
priorWidth = pdb.gimp_image_width(image)
|
||||
priorHeight = pdb.gimp_image_height(image)
|
||||
|
||||
deltaFraction = (percentEnlarge / 100) + 1.0
|
||||
deltaWidth = priorWidth * deltaFraction
|
||||
deltaHeight = priorHeight * deltaFraction
|
||||
centeredOffX = (deltaWidth - priorWidth)/ 2
|
||||
|
||||
centeredOffX = (deltaWidth - priorWidth) / 2
|
||||
centeredOffY = (deltaHeight - priorHeight) / 2
|
||||
|
||||
pdb.gimp_image_resize(image, deltaWidth, deltaHeight, centeredOffX, centeredOffY)
|
||||
#if not pdb.gimp_image_resize(image, deltaWidth, deltaHeight, centeredOffX, centeredOffY):
|
||||
# raise RuntimeError, "Failed resize"
|
||||
|
||||
|
||||
def shrinkSelectionByPercent(image, percent):
|
||||
# shrink selection by percent (converted to pixel units)
|
||||
deltaFraction = percent / 100
|
||||
# convert to pixel dimensions
|
||||
priorWidth = pdb.gimp_image_width(image)
|
||||
priorHeight = pdb.gimp_image_height(image)
|
||||
|
||||
deltaWidth = priorWidth * deltaFraction
|
||||
deltaHeight = priorHeight * deltaFraction
|
||||
# shrink selection by percent (converted to pixel units)
|
||||
deltaFraction = percent / 100
|
||||
|
||||
# !!! Note total shrink percentage is halved (width of band is percentage/2)
|
||||
maxDelta = max(deltaWidth, deltaHeight) / 2
|
||||
|
||||
pdb.gimp_selection_shrink(image, maxDelta)
|
||||
#if not pdb.gimp_selection_shrink(image, maxDelta):
|
||||
# raise RuntimeError, "Failed shrink selection"
|
||||
|
||||
|
||||
def uncrop(orgImage, drawable, percentEnlargeParam=10):
|
||||
'''
|
||||
"""
|
||||
Create frisket stencil selection in a temp image to pass as source (corpus) to plugin resynthesizer,
|
||||
which does the substantive work.
|
||||
'''
|
||||
"""
|
||||
|
||||
if not pdb.gimp_item_is_layer(drawable):
|
||||
if not pdb.gimp_drawable_is_layer(drawable):
|
||||
pdb.gimp_message(_("A layer must be active, not a channel."))
|
||||
return
|
||||
|
||||
@@ -90,43 +95,52 @@ def uncrop(orgImage, drawable, percentEnlargeParam=10):
|
||||
|
||||
# copy original into temp for later use
|
||||
tempImage = pdb.gimp_image_duplicate(orgImage)
|
||||
|
||||
if not tempImage:
|
||||
raise RuntimeError, "Failed duplicate image"
|
||||
|
||||
'''
|
||||
"""
|
||||
Prepare target: enlarge canvas and select the new, blank outer ring
|
||||
'''
|
||||
"""
|
||||
|
||||
# Save original bounds to later select outer band
|
||||
pdb.gimp_selection_all(orgImage)
|
||||
selectAllPrior = pdb.gimp_selection_save(orgImage)
|
||||
|
||||
# Resize image alone doesn't resize layer, so resize layer also
|
||||
resizeImageCentered(orgImage, percentEnlargeParam)
|
||||
pdb.gimp_layer_resize_to_image_size(drawable)
|
||||
pdb.gimp_image_select_item(orgImage, CHANNEL_OP_REPLACE, selectAllPrior)
|
||||
pdb.gimp_selection_load(selectAllPrior)
|
||||
|
||||
# select outer band, the new blank canvas.
|
||||
pdb.gimp_selection_invert(orgImage)
|
||||
|
||||
# Assert target image is ready.
|
||||
|
||||
'''
|
||||
"""
|
||||
Prepare source (corpus) layer, a band at edge of original, in a dupe.
|
||||
Note the width of corpus band is same as width of enlargement band.
|
||||
'''
|
||||
"""
|
||||
# Working with the original size.
|
||||
# Could be alpha channel transparency
|
||||
workLayer = pdb.gimp_image_get_active_layer(tempImage)
|
||||
|
||||
if not workLayer:
|
||||
raise RuntimeError, "Failed get active layer"
|
||||
|
||||
# Select outer band: select all, shrink
|
||||
pdb.gimp_selection_all(tempImage)
|
||||
shrinkSelectionByPercent(tempImage, percentEnlargeParam)
|
||||
pdb.gimp_selection_invert(tempImage) # invert interior selection into a frisket
|
||||
|
||||
# Note that v1 resynthesizer required an inverted selection !!
|
||||
# No need to crop corpus to save memory.
|
||||
|
||||
# Note that the API hasn't changed but use_border param now has more values.
|
||||
# !!! The crux: use_border param=5 means inside out direction
|
||||
pdb.plug_in_resynthesizer(orgImage, drawable, 0,0,5, workLayer.ID, -1, -1, 0.0, 0.117, 16, 500)
|
||||
pdb.plug_in_resynthesizer(
|
||||
orgImage, drawable, 0, 0, 5, workLayer.ID, -1, -1, 0.0, 0.117, 16, 500
|
||||
)
|
||||
|
||||
# Clean up.
|
||||
# Any errors now are moot.
|
||||
@@ -134,12 +148,15 @@ def uncrop(orgImage, drawable, percentEnlargeParam=10):
|
||||
pdb.gimp_image_remove_channel(orgImage, selectAllPrior)
|
||||
pdb.gimp_image_undo_group_end(orgImage)
|
||||
pdb.gimp_displays_flush()
|
||||
|
||||
gimp.delete(tempImage) # Comment out to debug corpus creation.
|
||||
|
||||
|
||||
register(
|
||||
"python_fu_uncrop",
|
||||
N_("Enlarge image by synthesizing a border that matches the edge, maintaining perspective. Works best for small enlargement of natural edges. Undo a Crop instead, if possible! "),
|
||||
N_(
|
||||
"Enlarge image by synthesizing a border that matches the edge, maintaining perspective. Works best for small enlargement of natural edges. Undo a Crop instead, if possible! "
|
||||
),
|
||||
"Requires separate resynthesizer plugin.",
|
||||
"Lloyd Konneker",
|
||||
"Copyright 2009 Lloyd Konneker",
|
||||
@@ -149,13 +166,12 @@ register(
|
||||
[
|
||||
(PF_IMAGE, "image", "Input image", None),
|
||||
(PF_DRAWABLE, "drawable", "Input drawable", None),
|
||||
(PF_SLIDER, "percentEnlargeParam", _("Percent enlargement"), 10, (0, 100, 1))
|
||||
(PF_SLIDER, "percentEnlargeParam", _("Percent enlargement"), 10, (0, 100, 1)),
|
||||
],
|
||||
[],
|
||||
uncrop,
|
||||
menu="<Image>/Filters/Enhance",
|
||||
domain=("resynthesizer", gimp.locale_directory)
|
||||
)
|
||||
domain=("resynthesizer", gimp.locale_directory),
|
||||
)
|
||||
|
||||
main()
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user