Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Alternative to Drawable.get_tile() for Gimp 3?
#6
(03-25-2025, 11:00 PM)joeyeroq Wrote: sprites2layers gimp 3
Code:
def sprites2layers(image, layer, sample, minimal_dimensions):
    '''
    "Separates objects on one layer to their own layer. Handy for Spritesheets"
    Parameters:
    image -- The current image.
    layer -- The layer of the image that is selected.
    sample -- sample every nth pixel of layer tile.
    minimal_dimensions -- minimal dimension of object to be placed in "objects" layer group.
    '''
    
    # Start timer.
    time_start = time.time()
    
    # Check image type (RGB/Greyscale or Indexed) and if layer has alpha, if not stop this script with a warning.
    no_alpha_warning='Warning\nThis layer has no alpha, please add an alpha channel for this script to work.'
    image_base_type = image.get_base_type()
    match image_base_type:
        case 0:
            image_mode = 'RGB'
            if layer.get_bpp() != 4:
                Gimp.message(no_alpha_warning)
                return
        case 1:
            image_mode = 'Greyscale'
            if layer.get_bpp() != 2:
                Gimp.message(no_alpha_warning)
                return
        case 2:
            image_mode = 'Indexed'
            if layer.get_bpp() != 2:
                Gimp.message(no_alpha_warning)
                return
    
    layer_copy = layer.copy()
    image.insert_layer(layer_copy, layer.get_parent(), 0)
    
    # Context setters for pdb.gimp_image_select_contiguous_color().
    Gimp.context_set_antialias(0)
    Gimp.context_set_feather(0)
    Gimp.context_set_sample_merged(0)
    Gimp.context_set_sample_criterion(0)
    Gimp.context_set_sample_threshold(1)
    Gimp.context_set_sample_transparent(0)
    
    # Counters for objects in layer groups.
    counter_good = 1
    counter_bad = 1
    
    # Create an 'off-screen' copy of layer_copy (not added to the canvas).
    layer_offscreen = layer_copy.copy()
    
    # Loop over pixels.
    for pixel_y in range(0, layer_copy.get_height(), sample):
        for pixel_x in range(0, layer_copy.get_width(), sample):
            pixel = layer_copy.get_pixel(pixel_x, pixel_y).get_rgba()
            # Split components of imag_mode to get the alpha
            # RGBA image
            if image_mode == 'RGB':
                R,G,B,A = pixel
            # Greyscale or Indexed image
            if image_mode == 'Greyscale' or image_mode == 'Indexed':
                I,A = pixel
            # If pixel is not completely transparent select it and neighbouring non-transparent pixels.
            if A > 0:
                image.select_contiguous_color(2, layer_copy, pixel_x, pixel_y)
                # Create a layer for an object and assign it to 'small objects' layer group or
                # 'objects layer group' based on criteria "min_dimensions"
                object_layer = layer_copy.copy()
                _, x1, y1, x2, y2 = layer_copy.mask_bounds()
                
                # 'small objects' layer group
                if x2 - x1 < minimal_dimensions and y2 - y1 < minimal_dimensions:
                    if counter_bad == 1 and (image_mode == 'RGB' or image_mode == 'Greyscale'):
                        layer_group_bad = Gimp.GroupLayer.new(image, 'small objects')
                        image.insert_layer(layer_group_bad, layer.get_parent(), 0)
                    object_layer.set_name("trash {:04d} ({:d}, {:d}, {:d}, {:d})".format(counter_bad, x1, y1, x2 - x1, y2 - y1))
                    counter_bad += 1
                    #if image_mode == 'RGB' or image_mode == 'Greyscale':
                    #    image.active_layer = layer_group_bad
                    # Add object to layer_group_bad at the last position.
                    #pdb.gimp_image_insert_layer(image, object_layer, layer_group_bad, len(layer_group_bad.layers))
                    image.insert_layer(object_layer, layer_group_bad, len(layer_group_bad.get_children()))
                # 'objects' layer group.
                else:
                    if counter_good == 1 and (image_mode == 'RGB' or image_mode == 'Greyscale'):
                        layer_group_good = Gimp.GroupLayer.new(image, 'objects')
                        image.insert_layer(layer_group_good, layer.get_parent(), 0)
                    object_layer.set_name("object {:04d} ({:d}, {:d}, {:d}, {:d})".format(counter_good, x1, y1, x2 - x1, y2 - y1))
                    counter_good += 1
                    #if image_mode == 'RGB' or image_mode == 'Greyscale':
                    #    image.active_layer = layer_group_good
                    # Add object to layer_group_good at the last position.
                    #pdb.gimp_image_insert_layer(image, object_layer, layer_group_good, len(layer_group_good.layers))
                    image.insert_layer(object_layer, layer_group_good, len(layer_group_good.get_children()))
                
                # "Auto crop" object layer.
                object_layer.resize(x2 - x1, y2 - y1, -x1, -y1)
                # Remove/erase selection from layer_copy
                #image.active_layer = layer_copy
                layer_copy.edit_clear()
                
                # Update tile, by updating layer_offscreen, by copying layer_copy
                layer_offscreen = layer_copy.copy()
                #Tile = layer_offscreen.get_tile(False, tile_row, tile_col)
    
    # Remove layer_copy and layer_offscreen from canvas and memory.
    image.remove_layer(layer_copy)
    del(layer_copy)
    del(layer_offscreen)
    
    # Reset context settings to their default values.
    Gimp.context_set_defaults()
    
    # End timer and display number of objects, small objects and timer seconds up to milliseconds in error console
    time_end = time.time()
    Gimp.message('INFO:\n-{:d} objects found\n'.format(counter_good - 1) +
                 '-{:d} small objects found\n'.format(counter_bad - 1) +
                 '-time taken: {:.3f} seconds'.format(time_end - time_start))

Also in GIMP 3 python console:

Code:
import time
image = Gimp.get_images()[0]
layer = image.get_layers()[0]
sprites2layers(image, layer, 1, 10)


I did a quick test port to Gimp 3 but had to take GimpTile code out and just used a Python loop over pixels. The other difference is that I used Python console and not as plugin, but I don't think that would significantly change speed.
results:
GIMP 2.10, 2.710 seconds.
GIMP 3.0.0-1, 109.061 seconds!
I thought it was 10x slower but its much more than that! Any solution to the GimpTile problem?

Ah, so this looks more like my ofn-extract-objects script. It uses a radically different approach:
  • alpha to selection
  • Select > To path. At that point each stroke of the path is the outline of a sprite(*). In other words I outsource the alpha discrimination to native Gimp code.
  • From each stroke I can get a a bounding box and then crop around the sprite.
Also, even in Gimp2, I wouldn't have used tiles but "pixel regions".

(*) technically, if the sprite is hollow, I can get strokes within strokes, but there are ways to determine if a stroke is nested within another.
Reply


Messages In This Thread
RE: Alternative to Drawable.get_tile() for Gimp 3? - by Ofnuts - 03-26-2025, 08:51 AM

Forum Jump: