Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Searching for python script islands to layers
#1
Hi, I'm searching for a plugin that converts a layer's non-transparent items/islands to multiple layers with each layer an item/island.
Please help, I am non-programmer and tried very simple python script but am very novice.
Actions/steps should be:
1. make sure tranparency is already mask layer, else make mask layer with right-click Layer > "Add Layer Mask..." > "Layer's alpha channel"

2. make duplicate of current layer as backup named temp_layer.

3. select temp_layer

4. go to every pixel in every row in temp_layer, if pixel's transparency is 0 (mask layer pixel is black) than do nothing
if transparency is not 0 (mask layer pixel is not black) select "fuzzy select tool" on position of that pixel with settings "Threshhold" at 254.9 and checked off "antialiasing", "feather edges", "select transparent areas" and "sample merged".

5. duplicate temp_layer and call it item### or island### for example island001.

6. crop to selection in island001 layer (Layer > Crop to Selection).

7. go back to temp_layer and make selection fully transparent in mask layer by filling selection with black color

8. update temp_layer and repeat from step 3 or 4 to 8 untill there are no more islands in temp layer and temp layer is fully transparent (mask layer is fully black).

As I said I'm no programmer so I hope someone will make this or point me to script that already does this, I haven't found one on the internet, only for photoshop but I don't have photoshop.
Here's link for photoshop version: https://graphicdesign.stackexchange.com/...yers-batch

Thanks in advance
Reply
#2
I think there is a G'MIC filter for that. Investigating now.

EDIT: yes its under Arrays & Tiles -> Extract Objects
Reply
#3
Thank you, I didn't know this existed in G'MIC, but it doesn't work with masked layers, which is very important to me.
As a python-fu exercise, how would you do the steps in the first post?
Reply
#4
See ofn-extract-objects here: https://sourceforge.net/projects/gimp-to...s/scripts/

Basically it makes an alpha-selection, and converts it to a path. That path has exactly as many strokes as there are "islands" in the selection, so the script breaks the multi-stroke path into as many single-stroke paths, and iterates them to make a selection restricted to one islands, and  copies it to a new layer.

For a crash course on paths: https://www.gimp-forum.net/Thread-Paths-Basics
Reply
#5
(01-22-2018, 03:56 PM)mich_lloid Wrote: Thank you, I didn't know this existed in G'MIC, but it doesn't work with masked layers, which is very important to me.
As a python-fu exercise, how would you do the steps in the first post?

Why is a layer mask so important? Is it just because it is part of that long complicated PS script?

What is the desired end result other than each item/island on its own layer? Do you want to keep as a stack of layers or do you eventually want each as a separate image.

I can think of other ways for each of those options
Reply
#6
(01-22-2018, 04:26 PM)rich2005 Wrote:
(01-22-2018, 03:56 PM)mich_lloid Wrote: Thank you, I didn't know this existed in G'MIC, but it doesn't work with masked layers, which is very important to me.
As a python-fu exercise, how would you do the steps in the first post?

Why is a layer mask so important? Is it just because it is part of that long complicated PS script?

What is the desired end result other than each item/island on its own layer? Do you want to keep as a stack of layers or do you eventually want each as a separate image.

I can think of other ways for each of those options

Because G'MIC's version destroys colors of transparent regions while Ofnuts's version does not.

I paint transparent regions around objects/islands or use G'MIC's solidify to give neighboring transparent pixels of the objects/islands the same color as the border of the islands which is very important so that sprites don't "bleed" or create artifacts in 2d/3d applications for html games or godot engine games or any other game engine.
Reply
#7
(01-22-2018, 05:14 PM)mich_lloid Wrote: Because G'MIC's version destroys colors of transparent regions while Ofnuts's version does not.

Should I take this as a hint that the plugin worked for your purpose?
Reply
#8
Not 100%, there's a lot of seemingly empty layers, probably because sprites have some holes in them and are counted as objects/islands.

Anyways, this is my first python script ever so bear with me, in python-fu console:


Code:
def crop_selection_to_new_layer(image):
   img = image
   prev_layer = img.active_layer
   newlayer = pdb.gimp_layer_copy( img.active_layer, pdb.gimp_drawable_has_alpha( img.active_layer ) )
   pdb.gimp_image_insert_layer(img, newlayer, None, -1)
   bounds = pdb.gimp_selection_bounds(img)
   pdb.gimp_layer_resize(img.active_layer, bounds[3] - bounds[1], bounds[4] - bounds[2], -bounds[1], -bounds[2])
   img.active_layer = prev_layer

#crop selection to first image in list
crop_selection_to_new_layer( gimp.image_list()[0] )


I select a sprite, then run last line, then select next sprite, then run last line again etc...
This gives accurate results but tedious and time consumming so I need help with:
-step 4 of first post: how do I go over each pixel and select pixel with non-zero mask layer with "fuzzy select tool" with said parameters.
-step 7 and 8 of first post
Reply
#9
4) "num_channels, pixel = pdb.gimp_drawable_get_pixel(drawable, x_coord, y_coord)" will give you all four channels (so includes alpha). You can get all the pixels in an array using pixels regions. See this for an example (no, you don't need to use numpy).

7) The mask is a drawable and you can get it with "mask = pdb.gimp_layer_get_mask(layer)". Once you have it, you can fill the selection with "pdb.gimp_edit_fill(drawable, fill_type)"

8) "mean, std_dev, median, pixels, count, percentile = pdb.gimp_histogram(drawable, channel, start_range, end_range)" will tell you a lot of things about the contents of the selection.

However:
  • You are going to loop on the pixels. and even on a small 512x512 sheet this is 262K iterations... Of course you can optimize here and there, but you will still loop a lot.
  • By using just the bounding box, you will get pieces of objects that have overlapping bounding boxes (think of LT for instance).
The algorithm with paths is quick because the scanning of pixels is done by the native Gimp code. It can be improved by checking for near-empty selections:
  • compute an approximate area (in pixels) of the path stroke (using the path anchors as the summits of a polygon)
  • use the gimp_histogram() call to have an alpha-weighted count of the pixels in the stroke
  • compute the ratio of selected pixels/approximate area
  • if this is below some threshold (50%... depending on the relative size of holes in objects) then ignore it
Reply
#10
Wow, that's a lot to take in, I'm really a novice at this, a lot of studying to do...
Meanwhile I made a small script that works by going every 10 pixels, use color picker, if alpha is bigger than 0, use fuzzy select tool etc.
I also have to apply the mask layer for this script to work, thankfully the color data of the transparent pixels is preserved.

Code:
img = gimp.image_list()[0]
layer = img.active_layer
#sample every 10 pixels
sample = 10
for i in range(img.height/sample):
#for i in range(img.width/sample):
    for j in range(img.width/sample):
    #for j in range(img.height/sample):
        alphacheck = pdb.gimp_color_picker(img, layer, j*sample, i*sample, 0,0,0)
        #alphacheck = pdb.gimp_color_picker(img, layer, i*sample, j*sample, 0,0,0)
        if alphacheck[-1] > 0:
            #pdb.gimp_fuzzy_select(layer, i*sample, j*sample, 254.9 , 2,0,0, 0,0)
            pdb.gimp_fuzzy_select(layer, j*sample, i*sample, 254.9 , 2,0,0, 0,0)
            prev_layer = img.active_layer
            newlayer = pdb.gimp_layer_copy( img.active_layer, pdb.gimp_drawable_has_alpha( img.active_layer ) )
            pdb.gimp_image_insert_layer(img, newlayer, None, -1)
            bounds = pdb.gimp_selection_bounds(img)
            pdb.gimp_layer_resize(img.active_layer, bounds[3] - bounds[1], bounds[4] - bounds[2], -bounds[1], -bounds[2])
            img.active_layer = prev_layer
            pdb.gimp_edit_clear(prev_layer)


It works better than your version as it takes into account holes in sprites, but the downside the script is kinda slow.
Further questions:
-How do i make selection go from upper right to lower left in my loop, now it goes diagonally if you know what I mean. The selection went vertically instead of horizontally, i had to switch image.height loop with image.width, very counter intuative.
-How do I rename copied layers to something like <original_layer>### for example layer001, layer002, etc.
Reply


Forum Jump: