Optimizing border-like selection in python
This discussion is connected to the gimp-developer-list.gnome.org mailing list which is provided by the GIMP developers and not related to gimpusers.com.
This is a read-only list on gimpusers.com so this discussion thread is read-only, too.
Optimizing border-like selection in python
Hi,
My code needs to do a one-pixel-wide selection, at distance "x" from the current selection. This looks a lot like a border selection except that the border selection creates at best a two-pixel wide ribbon and I only want one (but if I'm wrong, please tell me how to :-)
So far my code goes like this:
# Selects pixels that are between x and x+1 pixels from # the original selection. Bumping the selection by one # each time doesn't work, a small circle degenerates into # a square with rounded corners instead of a big circle. def select_ribbon(self,image,selection,dist): pdb.gimp_selection_load(selection) pdb.gimp_selection_grow(image,dist+1) outer=pdb.gimp_selection_save(image) pdb.gimp_selection_load(selection) pdb.gimp_selection_grow(image,dist) inner=pdb.gimp_selection_save(image) pdb.gimp_channel_combine_masks(outer,inner,CHANNEL_OP_SUBTRACT,0,0) pdb.gimp_selection_load(outer) image.remove_channel(outer) image.remove_channel(inner)
That works, but can be slow (especially since it's at the core of a loop). Is there any better way? Or useless code to jettison?
Next improvement is to create a 3-pixels selection and feather it one pixel. Anything to be wary of?
-- Ofnuts
Optimizing border-like selection in python
On Fri, Sep 24, 2010 at 11:19 AM, Ofnuts wrote:
Hi,
My code needs to do a one-pixel-wide selection, at distance "x" from the current selection. This looks a lot like a border selection except that the border selection creates at best a two-pixel wide ribbon and I only want one (but if I'm wrong, please tell me how to :-)
So far my code goes like this:
# Selects pixels that are between x and x+1 pixels from # the original selection. Bumping the selection by one # each time doesn't work, a small circle degenerates into # a square with rounded corners instead of a big circle. def select_ribbon(self,image,selection,dist): pdb.gimp_selection_load(selection) pdb.gimp_selection_grow(image,dist+1) outer=pdb.gimp_selection_save(image) pdb.gimp_selection_load(selection) pdb.gimp_selection_grow(image,dist) inner=pdb.gimp_selection_save(image) pdb.gimp_channel_combine_masks(outer,inner,CHANNEL_OP_SUBTRACT,0,0) pdb.gimp_selection_load(outer) image.remove_channel(outer)
image.remove_channel(inner)That works, but can be slow (especially since it's at the core of a loop). Is there any better way? Or useless code to jettison?
Next improvement is to create a 3-pixels selection and feather it one pixel. Anything to be wary of?
Hmm..this _will_ be slow. :-)
You can speed it up by making a copy of your drawable to another
image, disable the undo system on this new image and perform your
cations above, before copying the results back to the original image -
but it is about it.
Maybe you can perform the whole loop in the copy with undo disabled -
but I don't know your intent.
js ->
--
Ofnuts
Optimizing border-like selection in python
On 24/09/2010 17:05, Joao S. O. Bueno wrote:
On Fri, Sep 24, 2010 at 11:19 AM, Ofnuts wrote:
Hi,
My code needs to do a one-pixel-wide selection, at distance "x" from the current selection. This looks a lot like a border selection except that the border selection creates at best a two-pixel wide ribbon and I only want one (but if I'm wrong, please tell me how to :-)
So far my code goes like this:
# Selects pixels that are between x and x+1 pixels from # the original selection. Bumping the selection by one # each time doesn't work, a small circle degenerates into # a square with rounded corners instead of a big circle. def select_ribbon(self,image,selection,dist): pdb.gimp_selection_load(selection) pdb.gimp_selection_grow(image,dist+1) outer=pdb.gimp_selection_save(image) pdb.gimp_selection_load(selection) pdb.gimp_selection_grow(image,dist) inner=pdb.gimp_selection_save(image) pdb.gimp_channel_combine_masks(outer,inner,CHANNEL_OP_SUBTRACT,0,0) pdb.gimp_selection_load(outer) image.remove_channel(outer) image.remove_channel(inner)
That works, but can be slow (especially since it's at the core of a loop). Is there any better way? Or useless code to jettison?
Next improvement is to create a 3-pixels selection and feather it one pixel. Anything to be wary of?
Hmm..this _will_ be slow. :-)
You can speed it up by making a copy of your drawable to another image, disable the undo system on this new image and perform your cations above, before copying the results back to the original image - but it is about it.
Maybe you can perform the whole loop in the copy with undo disabled - but I don't know your intent.
The whole plugin paints successive concentric uniform 1-pixel ribbons around the original selection (pixel values a obtained from another layer). On my laptop (T2400, 1.83GHz) it takes roughly 10 seconds to draw 20 concentric ribbons around a 500-pixels circular selection.
The whole thing runs within a undo_group_start()/image.undo_group_end() so I don't expect any more undos?
Optimizing border-like selection in python
On 24/09/2010 19:44, Ofnuts wrote:
The whole plugin paints successive concentric uniform 1-pixel ribbons around the original selection (pixel values a obtained from another layer). On my laptop (T2400, 1.83GHz) it takes roughly 10 seconds to draw 20 concentric ribbons around a 500-pixels circular selection.
The whole thing runs within a undo_group_start()/image.undo_group_end() so I don't expect any more undos?
By the way, when my plug-in runs, the "marching ants" go into a frenzy, as if each selection change caused a screen update (though the bucket-fills don't show until the whole thing is finished). Would disabling these updates speed things up? (if so how is it done?)
Optimizing border-like selection in python
You can't disable the border selection programatically. In such cases what I've done is duplicate the image without displaying it and perform all the selection work on the duplicate then copy and paste it in as a channel into the original image. Then channel to Selection and delete the channel.
-Rob A>
On 9/24/10, Ofnuts wrote:
On 24/09/2010 19:44, Ofnuts wrote:
The whole plugin paints successive concentric uniform 1-pixel ribbons around the original selection (pixel values a obtained from another layer). On my laptop (T2400, 1.83GHz) it takes roughly 10 seconds to draw 20 concentric ribbons around a 500-pixels circular selection.
The whole thing runs within a undo_group_start()/image.undo_group_end() so I don't expect any more undos?
By the way, when my plug-in runs, the "marching ants" go into a frenzy, as if each selection change caused a screen update (though the bucket-fills don't show until the whole thing is finished). Would disabling these updates speed things up? (if so how is it done?)
-- Ofnuts
Optimizing border-like selection in python
Calculating a new selection from the current selection would be faster (I think):
def create_border(image, selection_type='rectangle', size=1): is_selection, x1, y1, x2, y2 = pdb.gimp_selection_bounds(image)
if is_selection:
if size < 1:
size = 1
if selection_type =='rectangle':
pdb.gimp_rect_select(image, x1+size, y1+size,
(x2-x1)-(size*2), (y2-y1)-(size*2), CHANNEL_OP_SUBTRACT, False, 0)
elif selection_type == 'ellipse':
pdb.gimp_ellipse_select(image, x1+size, y1+size,
(x2-x1)-(size*2), (y2-y1)-(size*2), CHANNEL_OP_SUBTRACT, True, False, 0)
else:
gimp.message("Please make a selection...")
def test_plugin(image, drawable): image.undo_group_start()
create_border(image) # A rectangular selecion with default
settings...
#create_border(image, selection_type='ellipse', size=3) # Or
Ellipse with custom settings...
# Fill pdb.gimp_edit_fill(drawable, 0) pdb.gimp_selection_none(image)
image.undo_group_end()
On 09/24/2010 10:19 AM, Ofnuts wrote:
Hi,
My code needs to do a one-pixel-wide selection, at distance "x" from the current selection. This looks a lot like a border selection except that the border selection creates at best a two-pixel wide ribbon and I only want one (but if I'm wrong, please tell me how to :-)
So far my code goes like this:
# Selects pixels that are between x and x+1 pixels from # the original selection. Bumping the selection by one # each time doesn't work, a small circle degenerates into # a square with rounded corners instead of a big circle. def select_ribbon(self,image,selection,dist): pdb.gimp_selection_load(selection) pdb.gimp_selection_grow(image,dist+1) outer=pdb.gimp_selection_save(image) pdb.gimp_selection_load(selection) pdb.gimp_selection_grow(image,dist) inner=pdb.gimp_selection_save(image) pdb.gimp_channel_combine_masks(outer,inner,CHANNEL_OP_SUBTRACT,0,0) pdb.gimp_selection_load(outer) image.remove_channel(outer) image.remove_channel(inner)
That works, but can be slow (especially since it's at the core of a loop). Is there any better way? Or useless code to jettison?
Next improvement is to create a 3-pixels selection and feather it one pixel. Anything to be wary of?
-- Ofnuts
Optimizing border-like selection in python
Ahh... autoformatting... this should be easier to read... (sorry)
def create_border(image, selection_type='rectangle', size=1): is_selection, x1, y1, x2, y2 = pdb.gimp_selection_bounds(image)
if is_selection:
if size < 1:
size = 1
if selection_type =='rectangle':
pdb.gimp_rect_select(image,
x1+size,
y1+size,
(x2-x1)-(size*2),
(y2-y1)-(size*2),
CHANNEL_OP_SUBTRACT,
False,
0)
elif selection_type == 'ellipse':
pdb.gimp_ellipse_select(image,
x1+size,
y1+size,
(x2-x1)-(size*2),
(y2-y1)-(size*2),
CHANNEL_OP_SUBTRACT,
True,
False,
0)
else:
gimp.message("Please make a selection...")
def test_plugin(image, drawable): image.undo_group_start()
# A rectangular selecion with default settings... create_border(image)
# Ellipse with custom settings... Uncomment to activate #create_border(image, selection_type='ellipse', size=3)
# Fill pdb.gimp_edit_fill(drawable, 0) pdb.gimp_selection_none(image)
image.undo_group_end()
On 09/24/2010 06:18 PM, Jerry Baker wrote:
Calculating a new selection from the current selection would be faster (I think):
def create_border(image, selection_type='rectangle', size=1): is_selection, x1, y1, x2, y2 = pdb.gimp_selection_bounds(image)
if is_selection: if size< 1:
size = 1
if selection_type =='rectangle': pdb.gimp_rect_select(image, x1+size, y1+size, (x2-x1)-(size*2), (y2-y1)-(size*2), CHANNEL_OP_SUBTRACT, False, 0) elif selection_type == 'ellipse': pdb.gimp_ellipse_select(image, x1+size, y1+size, (x2-x1)-(size*2), (y2-y1)-(size*2), CHANNEL_OP_SUBTRACT, True, False, 0) else:
gimp.message("Please make a selection...")def test_plugin(image, drawable): image.undo_group_start()
create_border(image) # A rectangular selecion with default settings...
#create_border(image, selection_type='ellipse', size=3) # Or Ellipse with custom settings...# Fill pdb.gimp_edit_fill(drawable, 0) pdb.gimp_selection_none(image)
image.undo_group_end()
On 09/24/2010 10:19 AM, Ofnuts wrote:
Hi,
My code needs to do a one-pixel-wide selection, at distance "x" from the current selection. This looks a lot like a border selection except that the border selection creates at best a two-pixel wide ribbon and I only want one (but if I'm wrong, please tell me how to :-)
So far my code goes like this:
# Selects pixels that are between x and x+1 pixels from # the original selection. Bumping the selection by one # each time doesn't work, a small circle degenerates into # a square with rounded corners instead of a big circle. def select_ribbon(self,image,selection,dist): pdb.gimp_selection_load(selection) pdb.gimp_selection_grow(image,dist+1) outer=pdb.gimp_selection_save(image) pdb.gimp_selection_load(selection) pdb.gimp_selection_grow(image,dist) inner=pdb.gimp_selection_save(image) pdb.gimp_channel_combine_masks(outer,inner,CHANNEL_OP_SUBTRACT,0,0) pdb.gimp_selection_load(outer) image.remove_channel(outer) image.remove_channel(inner)
That works, but can be slow (especially since it's at the core of a loop). Is there any better way? Or useless code to jettison?
Next improvement is to create a 3-pixels selection and feather it one pixel. Anything to be wary of?
-- Ofnuts
Optimizing border-like selection in python
The code should work for any shape of selection (for instance after a text-to-selection). Actually the plugin this routine is part of hasn't got much merit on rectangular/circular selections...
On 25/09/2010 00:27, Jerry Baker wrote:
Ahh... autoformatting... this should be easier to read... (sorry)
def create_border(image, selection_type='rectangle', size=1): is_selection, x1, y1, x2, y2 = pdb.gimp_selection_bounds(image)
if is_selection: if size < 1:
size = 1
if selection_type =='rectangle':
pdb.gimp_rect_select(image,
x1+size,
y1+size,
(x2-x1)-(size*2),
(y2-y1)-(size*2),
CHANNEL_OP_SUBTRACT,
False,
0)
elif selection_type == 'ellipse':
pdb.gimp_ellipse_select(image,
x1+size,
y1+size,
(x2-x1)-(size*2),
(y2-y1)-(size*2),
CHANNEL_OP_SUBTRACT,
True,
False,
0)
else:
gimp.message("Please make a selection...")def test_plugin(image, drawable): image.undo_group_start()
# A rectangular selecion with default settings... create_border(image)
# Ellipse with custom settings... Uncomment to activate #create_border(image, selection_type='ellipse', size=3)
# Fill pdb.gimp_edit_fill(drawable, 0)
pdb.gimp_selection_none(image)image.undo_group_end()
On 09/24/2010 06:18 PM, Jerry Baker wrote:
Calculating a new selection from the current selection would be faster (I think):
def create_border(image, selection_type='rectangle', size=1): is_selection, x1, y1, x2, y2 = pdb.gimp_selection_bounds(image)
if is_selection: if size< 1:
size = 1
if selection_type =='rectangle':
pdb.gimp_rect_select(image, x1+size, y1+size, (x2-x1)-(size*2), (y2-y1)-(size*2), CHANNEL_OP_SUBTRACT, False, 0) elif selection_type == 'ellipse':
pdb.gimp_ellipse_select(image, x1+size, y1+size, (x2-x1)-(size*2), (y2-y1)-(size*2), CHANNEL_OP_SUBTRACT, True, False, 0) else:
gimp.message("Please make a selection...")def test_plugin(image, drawable): image.undo_group_start()
create_border(image) # A rectangular selecion with default settings...
#create_border(image, selection_type='ellipse', size=3) # Or Ellipse with custom settings...# Fill pdb.gimp_edit_fill(drawable, 0)
pdb.gimp_selection_none(image)image.undo_group_end()
Optimizing border-like selection in python
You'll have to keep an eye on the parameter count. The ellipse select requires one more parameter than the rectangle one. But yes, some of them should work...
On 09/24/2010 06:37 PM, Ofnuts wrote:
The code should work for any shape of selection (for instance after a text-to-selection). Actually the plugin this routine is part of hasn't got much merit on rectangular/circular selections...
On 25/09/2010 00:27, Jerry Baker wrote:
Ahh... autoformatting... this should be easier to read... (sorry)
def create_border(image, selection_type='rectangle', size=1): is_selection, x1, y1, x2, y2 = pdb.gimp_selection_bounds(image)
if is_selection: if size< 1:
size = 1
if selection_type =='rectangle':
pdb.gimp_rect_select(image,
x1+size,
y1+size,
(x2-x1)-(size*2),
(y2-y1)-(size*2),
CHANNEL_OP_SUBTRACT,
False,
0)
elif selection_type == 'ellipse':
pdb.gimp_ellipse_select(image,
x1+size,
y1+size,
(x2-x1)-(size*2),
(y2-y1)-(size*2),
CHANNEL_OP_SUBTRACT,
True,
False,
0)
else:
gimp.message("Please make a selection...")def test_plugin(image, drawable): image.undo_group_start()
# A rectangular selecion with default settings... create_border(image)
# Ellipse with custom settings... Uncomment to activate #create_border(image, selection_type='ellipse', size=3)
# Fill pdb.gimp_edit_fill(drawable, 0)
pdb.gimp_selection_none(image)image.undo_group_end()
On 09/24/2010 06:18 PM, Jerry Baker wrote:
Calculating a new selection from the current selection would be faster (I think):
def create_border(image, selection_type='rectangle', size=1): is_selection, x1, y1, x2, y2 = pdb.gimp_selection_bounds(image)
if is_selection: if size< 1:
size = 1
if selection_type =='rectangle':
pdb.gimp_rect_select(image, x1+size, y1+size, (x2-x1)-(size*2), (y2-y1)-(size*2), CHANNEL_OP_SUBTRACT, False, 0) elif selection_type == 'ellipse':
pdb.gimp_ellipse_select(image, x1+size, y1+size, (x2-x1)-(size*2), (y2-y1)-(size*2), CHANNEL_OP_SUBTRACT, True, False, 0) else:
gimp.message("Please make a selection...")def test_plugin(image, drawable): image.undo_group_start()
create_border(image) # A rectangular selecion with default settings...
#create_border(image, selection_type='ellipse', size=3) # Or Ellipse with custom settings...# Fill pdb.gimp_edit_fill(drawable, 0)
pdb.gimp_selection_none(image)image.undo_group_end()
Optimizing border-like selection in python
On 24/09/2010 17:05, Joao S. O. Bueno wrote:
On Fri, Sep 24, 2010 at 11:19 AM, Ofnuts wrote:
Hi,
My code needs to do a one-pixel-wide selection, at distance "x" from the current selection. This looks a lot like a border selection except that the border selection creates at best a two-pixel wide ribbon and I only want one (but if I'm wrong, please tell me how to :-)
So far my code goes like this:
# Selects pixels that are between x and x+1 pixels from # the original selection. Bumping the selection by one # each time doesn't work, a small circle degenerates into # a square with rounded corners instead of a big circle. def select_ribbon(self,image,selection,dist): pdb.gimp_selection_load(selection) pdb.gimp_selection_grow(image,dist+1) outer=pdb.gimp_selection_save(image) pdb.gimp_selection_load(selection) pdb.gimp_selection_grow(image,dist) inner=pdb.gimp_selection_save(image) pdb.gimp_channel_combine_masks(outer,inner,CHANNEL_OP_SUBTRACT,0,0) pdb.gimp_selection_load(outer) image.remove_channel(outer) image.remove_channel(inner)
That works, but can be slow (especially since it's at the core of a loop). Is there any better way? Or useless code to jettison?
Next improvement is to create a 3-pixels selection and feather it one pixel. Anything to be wary of?
Hmm..this _will_ be slow. :-)
You can speed it up by making a copy of your drawable to another image, disable the undo system on this new image and perform your cations above, before copying the results back to the original image - but it is about it.
Maybe you can perform the whole loop in the copy with undo disabled - but I don't know your intent.
Disabling undo on the main image (just for tests) doesn't show much speed gain (from 2'03" to 1'56" in my test). It's only better memory-wise.
Optimizing border-like selection in python
On Mon, Sep 27, 2010 at 3:45 AM, Ofnuts wrote:
On 24/09/2010 17:05, Joao S. O. Bueno wrote:
On Fri, Sep 24, 2010 at 11:19 AM, Ofnuts wrote:
Hi,
My code needs to do a one-pixel-wide selection, at distance "x" from the current selection. This looks a lot like a border selection except that the border selection creates at best a two-pixel wide ribbon and I only want one (but if I'm wrong, please tell me how to :-)
So far my code goes like this:
# Selects pixels that are between x and x+1 pixels from # the original selection. Bumping the selection by one # each time doesn't work, a small circle degenerates into # a square with rounded corners instead of a big circle. def select_ribbon(self,image,selection,dist): pdb.gimp_selection_load(selection) pdb.gimp_selection_grow(image,dist+1) outer=pdb.gimp_selection_save(image) pdb.gimp_selection_load(selection) pdb.gimp_selection_grow(image,dist) inner=pdb.gimp_selection_save(image) pdb.gimp_channel_combine_masks(outer,inner,CHANNEL_OP_SUBTRACT,0,0) pdb.gimp_selection_load(outer) image.remove_channel(outer) image.remove_channel(inner)
That works, but can be slow (especially since it's at the core of a loop). Is there any better way? Or useless code to jettison?
Next improvement is to create a 3-pixels selection and feather it one pixel. Anything to be wary of?
Hmm..this _will_ be slow. :-)
You can speed it up by making a copy of your drawable to another image, disable the undo system on this new image and perform your cations above, before copying the results back to the original image - but it is about it.
Maybe you can perform the whole loop in the copy with undo disabled - but I don't know your intent.Disabling undo on the main image (just for tests) doesn't show much speed gain (from 2'03" to 1'56" in my test). It's only better memory-wise.
Hmm..maybe soem of the spped-up I experience has tod o with the new image I create on BG not being displayed - it will certainly help on this due to the marching ants that are used midway that don't need to show up.
js ->
Optimizing border-like selection in python
On Mon, 2010-09-27 at 08:58 -0300, Joao S. O. Bueno wrote:
Hmm..maybe soem of the spped-up I experience has tod o with the new image I create on BG not being displayed - it will certainly help on this due to the marching ants that are used midway that don't need to show up.
Shouldn't make much of a difference. The change to the selection only invalidates the view and queues a redraw. The pending redraw shouldn't slow things down considerably.
Sven
Optimizing border-like selection in python
Quoting Ofnuts :
My code needs to do a one-pixel-wide selection, at distance "x" from the current selection. This looks a lot like a border selection except that the border selection creates at best a two-pixel wide ribbon and I only want one (but if I'm wrong, please tell me how to :-)
So far my code goes like this:
# Selects pixels that are between x and x+1 pixels from # the original selection. Bumping the selection by one # each time doesn't work, a small circle degenerates into # a square with rounded corners instead of a big circle. def select_ribbon(self,image,selection,dist): pdb.gimp_selection_load(selection) pdb.gimp_selection_grow(image,dist+1) outer=pdb.gimp_selection_save(image) pdb.gimp_selection_load(selection) pdb.gimp_selection_grow(image,dist) inner=pdb.gimp_selection_save(image) pdb.gimp_channel_combine_masks(outer,inner,CHANNEL_OP_SUBTRACT,0,0) pdb.gimp_selection_load(outer) image.remove_channel(outer) image.remove_channel(inner)
That works, but can be slow (especially since it's at the core of a loop). Is there any better way? Or useless code to jettison?
There is no need to create (and later delete) the 'outer' channel. Just use the selection itself. In Script-fu, this would be done as follows:
(gimp-selection-load selection)
(gimp-selection-grow image dist)
(set! inner (car (gimp-selection-save image)))
(gimp-selection-load selection)
(gimp-selection-grow image (+ dist 1))
(gimp-channel-combine-masks (car (gimp-image-get-selection image))
inner
CHANNEL-OP-SUBTRACT 0 0)
(gimp-image-remove-channel image inner)
Optimizing border-like selection in python
On 27/09/2010 18:47, saulgoode@flashingtwelve.brickfilms.com wrote:
Quoting Ofnuts:
My code needs to do a one-pixel-wide selection, at distance "x" from the current selection. This looks a lot like a border selection except that the border selection creates at best a two-pixel wide ribbon and I only want one (but if I'm wrong, please tell me how to :-)
So far my code goes like this:
# Selects pixels that are between x and x+1 pixels from # the original selection. Bumping the selection by one # each time doesn't work, a small circle degenerates into # a square with rounded corners instead of a big circle. def select_ribbon(self,image,selection,dist): pdb.gimp_selection_load(selection) pdb.gimp_selection_grow(image,dist+1) outer=pdb.gimp_selection_save(image) pdb.gimp_selection_load(selection) pdb.gimp_selection_grow(image,dist) inner=pdb.gimp_selection_save(image) pdb.gimp_channel_combine_masks(outer,inner,CHANNEL_OP_SUBTRACT,0,0) pdb.gimp_selection_load(outer) image.remove_channel(outer) image.remove_channel(inner)
That works, but can be slow (especially since it's at the core of a loop). Is there any better way? Or useless code to jettison?
There is no need to create (and later delete) the 'outer' channel. Just use the selection itself. In Script-fu, this would be done as follows:
(gimp-selection-load selection) (gimp-selection-grow image dist) (set! inner (car (gimp-selection-save image))) (gimp-selection-load selection)
(gimp-selection-grow image (+ dist 1)) (gimp-channel-combine-masks (car (gimp-image-get-selection image)) inner CHANNEL-OP-SUBTRACT 0 0) (gimp-image-remove-channel image inner)
Implemented your code. It runs 10 to 20% faster, and uses only half the memory... So you have been duly credited in the V0.2 history. Thx.