It is currently Tue Jul 02, 2024 12:20 am


All times are UTC - 5 hours [ DST ]



Post new topic Reply to topic  [ 5 posts ] 
Author Message
 Post subject: GIMP's fast pixel operations
PostPosted: Tue Jan 02, 2024 8:52 am  (#1) 
Offline
Script Coder
User avatar

Joined: May 07, 2014
Posts: 4001
Location: Canada
I googled this because I know it existed before and did it probably from this same page: Fast Pixel Ops in GIMP-Python (https://shallowsky.com/blog/gimp/pygimp-pixel-ops.html)
But in the example there's a missing part which is updating the destination layer after the work has been done which is found at (https://github.com/akkana/gimp-plugins/ ... rclayer.py) by the same author which was linked in 1st post as well but I didn't know this when working with it so spent some time fighting.
But it's this piece of code:
    destDrawable.flush()
    destDrawable.merge_shadow(True)
    destDrawable.update(0, 0, newWidth,newHeight)


So together I created a test py that uses the above technique for fast pixel operations (code below):
Have an image with 2 layers, select the layer you want to copy or flip 2 directions, vertical and horizontal
Plug-in will allow you to choose a destination layer to have result shown on using fast pixel operations.
That's it. it's SUPER FAST like about 1 second to do on a large 2000 x 2000 or so image.
#!/usr/bin/env python
# test-fast-pixel.py
# fast-pixel test based on instructions shown on this webpage: https://shallowsky.com/blog/gimp/pygimp-pixel-ops.html
# Created by TT
# Just a test
#
# Comments directed to http://gimpchat.com or http://gimp-forum.net or http://gimpscripts.com
#
# License: GPLv3
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY# without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# To view a copy of the GNU General Public License
# visit: http://www.gnu.org/licenses/gpl.html
#
#
# ------------
#| Change Log |
# ------------
# Rel 1: Initial release.
import math
import random
from gimpfu import *
from array import array #fast pixel operations need this


def test_command(image,layer,dest):
    #[ ... setting up ... ] # initialize the regions and get their contents into arrays:
    srcWidth = layer.width
    srcHeight = layer.height
    srcRgn = layer.get_pixel_rgn(0, 0, srcWidth, srcHeight,False, False)
    src_pixels = array("B", srcRgn[0:srcWidth, 0:srcHeight])
    newWidth = dest.width
    newHeight = dest.height
    dstRgn = dest.get_pixel_rgn(0, 0, newWidth, newHeight,True, True)
    p_size = len(srcRgn[0,0])
    dest_pixels = array("B", "\x00" * (newWidth * newHeight * p_size))
    #[ ... then inside the loop over x and y ... ]
    pdb.gimp_message(p_size)
    x = 0; y = 0;
    src_pos = (x + srcWidth * y) * p_size
    newval = src_pixels[src_pos: src_pos + p_size]

    pdb.gimp_message(newval[0])
    for y in range(0,srcHeight):
        pdb.gimp_progress_update(float(y)/srcHeight)
        for x in range(0,srcWidth):
            #The below 2 lines are like newval = getpixel(x,y)
            src_pos = (x + srcWidth * y) * p_size
            newval = src_pixels[src_pos: src_pos + p_size]
           
            newx = newWidth - x - 1
            newy = newHeight - y - 1
            #The below 2 lines are like setpixel(x,y,newvalue)
            dest_pos = (newx + newWidth * newy) * p_size
            dest_pixels[dest_pos : dest_pos + p_size] = newval

    #[ ... when the loop is all finished ... ]
    # Copy the whole array back to the pixel region:
    pdb.gimp_message(len(dstRgn[0:newWidth, 0:newHeight]))
    pdb.gimp_message(len(dest_pixels.tostring()))
    dstRgn[0:newWidth, 0:newHeight] = dest_pixels.tostring()
    #need this to update layer
    dest.flush()
    dest.merge_shadow(True)
    dest.update(0, 0, newWidth,newHeight)
   
register(
    "python_fu_a_fast_pixel_test",
    "Fast Pixel Test",
    "Fast Pixel Test",
    "TT",
    "TT",
    "2024.1.1",
    "A Fast Pixel Test",
    "",      # Alternately use RGB, RGB*, GRAY*, INDEXED etc.
    [
    #INPUT BEGINS
    (PF_IMAGE, "image", "IMAGE:", None), # should be type gimp.image, but None works
    (PF_DRAWABLE, "layer", "Source Layer:", None),
    (PF_DRAWABLE, "dest", "Dest Layer:", None),
    #INPUT ENDS
    ],
    [],
    test_command,
    menu="<Image>/Python-Fu")

main()
# Below is all the example input types for INPUTS for the plug-in which can be cut and pasted into #INPUT BEGINS section and edited to taste
#           (PF_INT, "p0", "_INT:", 0), # PF_INT8, PF_INT16, PF_INT32  similar but no difference in Python.
#           (PF_FLOAT, "p02", "_FLOAT:", 3.141),
#           (PF_STRING, "p03", "_STRING:", "foo"),  # alias PF_VALUE
#           (PF_TEXT, "p04", "TEXT:", "bar"),
#           # PF_VALUE
#           # Pick one from set of choices
#           (PF_OPTION,"p1",   "OPTION:", 0, ["0th","1st","2nd"]), # initially 0th is choice
#           (PF_RADIO, "p16", "RADIO:", 0, (("0th", 1),("1st",0))), # note bool indicates initial setting of buttons
#           # PF_RADIO is usually called a radio button group.
#           # SLIDER, ADJUSTMENT types require the extra parameter of the form (min, max, step).
#           (PF_TOGGLE, "p2",   "TOGGLE:", 1), # initially True, checked.  Alias PF_BOOL
#           # PF_TOGGLE is usually called a checkbox.
#           (PF_SLIDER, "p3", "SLIDER:", 0, (0, 100, 10)),
#           (PF_SPINNER, "p4", "SPINNER:", 21, (1, 1000, 50)),  # alias PF_ADJUSTMENT
#           # Pickers ie combo boxes ie choosers from lists of existing Gimp objects
#           (PF_COLOR, "p14", "_COLOR:", (100, 21, 40) ), # extra param is RGB triple
#           # PF_COLOUR is an alias by aussie PyGimp author lol
#           (PF_IMAGE, "p15", "IMAGE:", None), # should be type gimp.image, but None works
#           (PF_FONT, "p17", "FONT:", 0),
#           (PF_FILE, "p18", "FILE:", 0),
#           (PF_BRUSH, "p19", "BRUSH:", 0),
#           (PF_PATTERN, "p20", "PATTERN:", 0),
#           (PF_GRADIENT, "p21", "GRADIENT:", 0),
#           (PF_PALETTE, "p22", "PALETTE:", 0),
#           (PF_LAYER, "p23", "LAYER:", None),
#           (PF_CHANNEL, "p24", "CHANNEL:", None),  # ??? Usually empty, I don't know why.
#           (PF_DRAWABLE, "p25", "DRAWABLE:", None),
#           # Mostly undocumented, but work
#           (PF_VECTORS, "p26", "VECTORS:", None),
#           (PF_FILENAME, "p27", "FILENAME:", 0),
#           (PF_DIRNAME, "p28", "DIRNAME:", 0)
#           # PF_REGION might work but probably of little use.  See gimpfu.py.

_________________
TinT


Share on Facebook Share on Twitter Share on Orkut Share on Digg Share on MySpace Share on Delicious Share on Technorati
Top
 Post subject: Re: GIMP's fast pixel operations
PostPosted: Tue Jan 02, 2024 1:25 pm  (#2) 
Offline
Script Coder
User avatar

Joined: Oct 25, 2010
Posts: 4752
trandoductin wrote:
I googled this because I know it existed before and did it probably from this same page: Fast Pixel Ops in GIMP-Python (https://shallowsky.com/blog/gimp/pygimp-pixel-ops.html)
But in the example there's a missing part which is updating the destination layer after the work has been done which is found at (https://github.com/akkana/gimp-plugins/ ... rclayer.py) by the same author which was linked in 1st post as well but I didn't know this when working with it so spent some time fighting.
But it's this piece of code:
    destDrawable.flush()
    destDrawable.merge_shadow(True)
    destDrawable.update(0, 0, newWidth,newHeight)


So together I created a test py that uses the above technique for fast pixel operations (code below):
Have an image with 2 layers, select the layer you want to copy or flip 2 directions, vertical and horizontal
Plug-in will allow you to choose a destination layer to have result shown on using fast pixel operations.
That's it. it's SUPER FAST like about 1 second to do on a large 2000 x 2000 or so image.
#!/usr/bin/env python
# test-fast-pixel.py
# fast-pixel test based on instructions shown on this webpage: https://shallowsky.com/blog/gimp/pygimp-pixel-ops.html
# Created by TT
# Just a test
#
# Comments directed to http://gimpchat.com or http://gimp-forum.net or http://gimpscripts.com
#
# License: GPLv3
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY# without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# To view a copy of the GNU General Public License
# visit: http://www.gnu.org/licenses/gpl.html
#
#
# ------------
#| Change Log |
# ------------
# Rel 1: Initial release.
import math
import random
from gimpfu import *
from array import array #fast pixel operations need this


def test_command(image,layer,dest):
    #[ ... setting up ... ] # initialize the regions and get their contents into arrays:
    srcWidth = layer.width
    srcHeight = layer.height
    srcRgn = layer.get_pixel_rgn(0, 0, srcWidth, srcHeight,False, False)
    src_pixels = array("B", srcRgn[0:srcWidth, 0:srcHeight])
    newWidth = dest.width
    newHeight = dest.height
    dstRgn = dest.get_pixel_rgn(0, 0, newWidth, newHeight,True, True)
    p_size = len(srcRgn[0,0])
    dest_pixels = array("B", "\x00" * (newWidth * newHeight * p_size))
    #[ ... then inside the loop over x and y ... ]
    pdb.gimp_message(p_size)
    x = 0; y = 0;
    src_pos = (x + srcWidth * y) * p_size
    newval = src_pixels[src_pos: src_pos + p_size]

    pdb.gimp_message(newval[0])
    for y in range(0,srcHeight):
        pdb.gimp_progress_update(float(y)/srcHeight)
        for x in range(0,srcWidth):
            #The below 2 lines are like newval = getpixel(x,y)
            src_pos = (x + srcWidth * y) * p_size
            newval = src_pixels[src_pos: src_pos + p_size]
           
            newx = newWidth - x - 1
            newy = newHeight - y - 1
            #The below 2 lines are like setpixel(x,y,newvalue)
            dest_pos = (newx + newWidth * newy) * p_size
            dest_pixels[dest_pos : dest_pos + p_size] = newval

    #[ ... when the loop is all finished ... ]
    # Copy the whole array back to the pixel region:
    pdb.gimp_message(len(dstRgn[0:newWidth, 0:newHeight]))
    pdb.gimp_message(len(dest_pixels.tostring()))
    dstRgn[0:newWidth, 0:newHeight] = dest_pixels.tostring()
    #need this to update layer
    dest.flush()
    dest.merge_shadow(True)
    dest.update(0, 0, newWidth,newHeight)
   
register(
    "python_fu_a_fast_pixel_test",
    "Fast Pixel Test",
    "Fast Pixel Test",
    "TT",
    "TT",
    "2024.1.1",
    "A Fast Pixel Test",
    "",      # Alternately use RGB, RGB*, GRAY*, INDEXED etc.
    [
    #INPUT BEGINS
    (PF_IMAGE, "image", "IMAGE:", None), # should be type gimp.image, but None works
    (PF_DRAWABLE, "layer", "Source Layer:", None),
    (PF_DRAWABLE, "dest", "Dest Layer:", None),
    #INPUT ENDS
    ],
    [],
    test_command,
    menu="<Image>/Python-Fu")

main()
# Below is all the example input types for INPUTS for the plug-in which can be cut and pasted into #INPUT BEGINS section and edited to taste
#           (PF_INT, "p0", "_INT:", 0), # PF_INT8, PF_INT16, PF_INT32  similar but no difference in Python.
#           (PF_FLOAT, "p02", "_FLOAT:", 3.141),
#           (PF_STRING, "p03", "_STRING:", "foo"),  # alias PF_VALUE
#           (PF_TEXT, "p04", "TEXT:", "bar"),
#           # PF_VALUE
#           # Pick one from set of choices
#           (PF_OPTION,"p1",   "OPTION:", 0, ["0th","1st","2nd"]), # initially 0th is choice
#           (PF_RADIO, "p16", "RADIO:", 0, (("0th", 1),("1st",0))), # note bool indicates initial setting of buttons
#           # PF_RADIO is usually called a radio button group.
#           # SLIDER, ADJUSTMENT types require the extra parameter of the form (min, max, step).
#           (PF_TOGGLE, "p2",   "TOGGLE:", 1), # initially True, checked.  Alias PF_BOOL
#           # PF_TOGGLE is usually called a checkbox.
#           (PF_SLIDER, "p3", "SLIDER:", 0, (0, 100, 10)),
#           (PF_SPINNER, "p4", "SPINNER:", 21, (1, 1000, 50)),  # alias PF_ADJUSTMENT
#           # Pickers ie combo boxes ie choosers from lists of existing Gimp objects
#           (PF_COLOR, "p14", "_COLOR:", (100, 21, 40) ), # extra param is RGB triple
#           # PF_COLOUR is an alias by aussie PyGimp author lol
#           (PF_IMAGE, "p15", "IMAGE:", None), # should be type gimp.image, but None works
#           (PF_FONT, "p17", "FONT:", 0),
#           (PF_FILE, "p18", "FILE:", 0),
#           (PF_BRUSH, "p19", "BRUSH:", 0),
#           (PF_PATTERN, "p20", "PATTERN:", 0),
#           (PF_GRADIENT, "p21", "GRADIENT:", 0),
#           (PF_PALETTE, "p22", "PALETTE:", 0),
#           (PF_LAYER, "p23", "LAYER:", None),
#           (PF_CHANNEL, "p24", "CHANNEL:", None),  # ??? Usually empty, I don't know why.
#           (PF_DRAWABLE, "p25", "DRAWABLE:", None),
#           # Mostly undocumented, but work
#           (PF_VECTORS, "p26", "VECTORS:", None),
#           (PF_FILENAME, "p27", "FILENAME:", 0),
#           (PF_DIRNAME, "p28", "DIRNAME:", 0)
#           # PF_REGION might work but probably of little use.  See gimpfu.py.


And it you use numpy to manipulate your arrays it is even faster.

_________________
Image


Top
 Post subject: Re: GIMP's fast pixel operations
PostPosted: Tue Jan 02, 2024 1:48 pm  (#3) 
Offline
Script Coder
User avatar

Joined: May 07, 2014
Posts: 4001
Location: Canada
ofnuts wrote:
And it you use numpy to manipulate your arrays it is even faster.

What? really? but numpy doesn't come standard in GIMP python.
I don't know if it comes in newer GIMP.
If it does would you please have sample code like above blog like just important chunks. Because if I can use numpy in GIMP python without having to tell users having to install numpy then I would definitely want to know how! Please.

_________________
TinT


Top
 Post subject: Re: GIMP's fast pixel operations
PostPosted: Tue Jan 02, 2024 8:42 pm  (#4) 
Offline
Script Coder
User avatar

Joined: Oct 25, 2010
Posts: 4752
trandoductin wrote:
ofnuts wrote:
And it you use numpy to manipulate your arrays it is even faster.

What? really? but numpy doesn't come standard in GIMP python.
I don't know if it comes in newer GIMP.
If it does would you please have sample code like above blog like just important chunks. Because if I can use numpy in GIMP python without having to tell users having to install numpy then I would definitely want to know how! Please.


Alas, you have to install numpy. But it can be really fast because operations on arrays are direct C code (when it is not contracted to the graphics card). I wrote experimental median & average filters (across layers) than run faster that thein GMIC equivalents.

_________________
Image


Top
 Post subject: Re: GIMP's fast pixel operations
PostPosted: Tue Jan 02, 2024 9:54 pm  (#5) 
Offline
Script Coder
User avatar

Joined: May 07, 2014
Posts: 4001
Location: Canada
ah that's too bad I wished GIMP would just include numpy automatically.

_________________
TinT


Top
Post new topic Reply to topic  [ 5 posts ] 

All times are UTC - 5 hours [ DST ]


   Similar Topics   Replies 
No new posts Attachment(s) Line/pixel size doesnot correspond with pixel resolution

3

No new posts Attachment(s) Fast Mask (Rel 2)

14

No new posts Attachment(s) Is there anyway to add blend modes inbetween operations in Bimp?

2

No new posts Attachment(s) GEGL operations exposed to Python-Fu - WIP 72 ops wrapped

96

No new posts I'll make a filter based on other people's GEGL chain operations

1



* Login  



Powered by phpBB3 © phpBB Group