Gimp-Forum.net

Full Version: Python plugin: point in selection?
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Is there any way to find out, in a Python plugin, if a given point (x,y) is in the selection or not?
From one of my scripts:

Code:
def isSelectedPoint(point,image):
   x,y=point.pixelX,point.pixelY
   return 0<=x<image.width and 0<=y<image.height and image.selection.get_pixel(x,y)[0] > 0

Instead of testing >0 you may want to try >127.

In my Point class, pixelX and pixelY are pseudo attributes:
Code:
    @property
    def pixelX(self):
        return int(math.floor(self.x))

    @property
    def pixelY(self):
        return int(math.floor(self.y))
Thank you. That also lead me to find it in pdb too:

value = pdb.gimp_selection_value(image, x, y)

In my Gimp it is 127. Is this limit going to change in the future? If so, then the plugin should check the version.
(01-23-2021, 03:02 PM)Ottia Tuota Wrote: [ -> ]Thank you. That also lead me to find it in pdb too:

value = pdb.gimp_selection_value(image, x, y)

In my Gimp it is 127. Is this limit going to change in the future? If so, then the plugin should check the version.

There are places where things are 0-255 integers, and others where they are 0.-1. floats. As parameters some calls accept either.
Testing if a point is in the selection appeared to be slower than desired. Then I wanted to compare the two solutions. I made the following test code:

Code:
def selected1(x,y,image): # Ofnuts
  return image.selection.get_pixel(x,y)[0] > 127

def selected2(x,y,image): # pdb
   return pdb.gimp_selection_value(image, x, y) > 127

def test(image, N):
   x = 500
   y = 500
   for i in range(N):
       test1 = selected1(x,y,image) # Ofnuts
   for i in range(N):
       test2 = selected2(x,y,image) # pdb

import cProfile
image = gimp.image_list()[0]
command = 'test(image, 100000)'
cProfile.runctx(command, None, locals(), sort='tottime')


I ran it in Gimp's Python console. The results:

Code:
        300005 function calls in 62.485 seconds

  Ordered by: internal time

  ncalls  tottime  percall  cumtime  percall filename:lineno(function)
  100000   39.456    0.000   39.456    0.000 <input>:1(selected2)
  100000   16.192    0.000   22.855    0.000 <input>:1(selected1)
  100000    6.663    0.000    6.663    0.000 {method 'get_pixel' of 'gimp.Drawable' objects}
       1    0.173    0.173   62.485   62.485 <input>:1(test)
       2    0.001    0.001    0.001    0.001 {range}
       1    0.000    0.000   62.485   62.485 <string>:1(<module>)
       1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


So, using a call to pdb (pdb.gimp_selection_value(image, x, y)) takes about 73% more time than your method (image.selection.get_pixel(x,y)[0]). The numbers are 39.456 and 22.855. There must be an explanation?

(Note: I did this on an old and slow machine.)
(01-25-2021, 12:10 PM)Ottia Tuota Wrote: [ -> ]Testing if a point is in the selection appeared to be slower than desired. Then I wanted to compare the two solutions. I made the following test code:

Code:
def selected1(x,y,image): # Ofnuts
  return image.selection.get_pixel(x,y)[0] > 127

def selected2(x,y,image): # pdb
   return pdb.gimp_selection_value(image, x, y) > 127

def test(image, N):
   x = 500
   y = 500
   for i in range(N):
       test1 = selected1(x,y,image) # Ofnuts
   for i in range(N):
       test2 = selected2(x,y,image) # pdb

import cProfile
image = gimp.image_list()[0]
command = 'test(image, 100000)'
cProfile.runctx(command, None, locals(), sort='tottime')


I ran it in Gimp's Python console. The results:

Code:
        300005 function calls in 62.485 seconds

  Ordered by: internal time

  ncalls  tottime  percall  cumtime  percall filename:lineno(function)
  100000   39.456    0.000   39.456    0.000 <input>:1(selected2)
  100000   16.192    0.000   22.855    0.000 <input>:1(selected1)
  100000    6.663    0.000    6.663    0.000 {method 'get_pixel' of 'gimp.Drawable' objects}
       1    0.173    0.173   62.485   62.485 <input>:1(test)
       2    0.001    0.001    0.001    0.001 {range}
       1    0.000    0.000   62.485   62.485 <string>:1(<module>)
       1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


So, using a call to pdb (pdb.gimp_selection_value(image, x, y)) takes about 73% more time than your method (image.selection.get_pixel(x,y)[0]). The numbers are 39.456 and 22.855. There must be an explanation?

(Note: I did this on an old and slow machine.)

Likely due overhead in the Gimp API and the Python API layer. When you do a get_pixel() a drawable you remove all the code to obtain the drawable via the image.

If you are in a hurry, you can use a pixel_region. A pixel region is a native python vector/list the the pixel channels as bytes. There is some overhead to create one, but then access is near instantaneous. But I wouldn't consider that if you are dealing with paths that are mostly "linear" objects, usually around a thousand), it makes more sense when you are actually dealing with all the pixels ("area objects", often above a million). And then for efficiency you can convert the pixel_region into a numpy array.
Thanks. That makes sense.

I am working with paths, hence I leave learning about pixel regions to some other time. And the code I am working on right now needs much work anyhow to make it faster.