It is currently Sat Sep 26, 2026 4:20 pm


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: 4557
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.

_________________
Image
Visualize your and others dreams coming true.


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: 4812
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: 4557
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.

_________________
Image
Visualize your and others dreams coming true.


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: 4812
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: 4557
Location: Canada
ah that's too bad I wished GIMP would just include numpy automatically.

_________________
Image
Visualize your and others dreams coming true.


Top
Post new topic Reply to topic  [ 5 posts ] 

All times are UTC - 5 hours [ DST ]



* Login  



Powered by phpBB3 © phpBB Group