Fill each object by average color behind their layer

Community Beginner ,
Sep 11, 2022 Sep 11, 2022

Copy link to clipboard

Copied

Does anyone know of any script that can fill multiple selected object with the average color behind their layer? Also, it can work even if it’s not the average color, just a color of a pixel from inside each and every object.

 

Something like this https://i.imgur.com/wE3h5u5.jpg

 

I need it to create a special mosaic, but the tiles are random in shape and size and are already drawn on top of the initial picture and there are over 100k objects/tiles that are needed to be filled.

 

I’ll apreciate any help with this!

TOPICS
Feature request , Scripting , Tools

Views

456

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advisor ,
Sep 11, 2022 Sep 11, 2022

Copy link to clipboard

Copied

Illustrator scripting cannot access individual pixels. I don't think even Photoshop scripting can access individual pixels (although a workaround may be possible). 

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Sep 11, 2022 Sep 11, 2022

Copy link to clipboard

Copied

In photoshop is possible with blur - average color, but in my case it works for only one object at a time

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Sep 11, 2022 Sep 11, 2022

Copy link to clipboard

Copied

You could probably invoke the eyedroper tool and sample the colors at specific points, but any script based on that would probably be slow as hog due the convoluted loop. Likewise, you could possibly invoke image trace and generate a swatch palette and operate based on that, but that won't be a cakewalk, either. That and of course color math based on such abstract criteria would be complicated. There is simply no single definitive formula that defines what the "average" of two or more colors is. Color math is not linear. If you already have drawn the shapes, determining the sample point positions would at least be simple (center of the shape or some pattern based on its anchor points), but you still have to deal with the rest. If you're willing to experiment around an excursion to Inkscape might be worth a consideration. People use its Voronoi pattern extension to generate colored mosaics all the time (easy to find a tutorial) and that may provide a base color for each cell.

 

Mylenium

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Contributor ,
Sep 11, 2022 Sep 11, 2022

Copy link to clipboard

Copied

I assume you’ve already looked around to see if there’s a Photoshop mosaic filter that can take a predefined mosaic pattern, and didn’t find one. Or there is some reason why your random mosaic pattern has to be rendered as paths, not pixels.

 

If you want to approach it as an Illustrator problem, Object > Create Object Mosaic… will convert your source bitmap image to a grid mosaic. Make the grid cells a good bit smaller than your random mosaic’s tiles; maybe 9-to-1 or 16-to-1. You can then use a script to iterate through each path in your random mosaic, find the grid tile(s) it overlaps, and set its fill color to a color taken from those. From what you describe I suspect that will be the easiest solution to implement as the whole job can be done without going outside of AI. Crude, but you say the color doesn’t need to be an exact average so probably good enough.

 

The alternative would be to use a bit of bitmap image processing to determine your tile colors. For each random path in your mosaic, export a black/white mask image of the same dimensions as your bitmap (you can script that task in AI), then (the math-y image processing part) apply that mask to the source bitmap to discard all pixels except those it overlaps, then average those remaining pixels’ color values to get the color to apply to that mosaic tile back in AI. This will give you the exact average color, though is obviously more work. I would suggest rummaging on npm and/or processing.org to see if you can find a suitable bitmap processing library to do the gruntwork for you: the algorithm is trivial enough on paper; it’s reading the bitmap image file formats into arrays of numbers to work on that’s probably the chore.

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Sep 12, 2022 Sep 12, 2022

Copy link to clipboard

Copied

LATEST

I have the code in python, but it's made for gimp. Maybe someone could change it to adobe illustrator.

from gimpfu import *
#from array import array
#import sys

def tt_average_fill_it(img,src_layer,work_layer,brushit):
	#Apply Average Color from Source if there is selection
	non_empty,x1,y1,x2,y2 = pdb.gimp_selection_bounds(img)
	if non_empty:
		#pdb.gimp_message("grabbing colors")
		r, _, _, _, _, _ = pdb.gimp_drawable_histogram(src_layer,HISTOGRAM_RED,0,1)
		g, _, _, _, _, _ = pdb.gimp_drawable_histogram(src_layer,HISTOGRAM_GREEN,0,1)
		b, _, _, _, _, _ = pdb.gimp_drawable_histogram(src_layer,HISTOGRAM_BLUE,0,1)
		pdb.gimp_context_set_foreground((int(r),int(g),int(b)))
		#pdb.gimp_message("grabbing colors done")
		pdb.gimp_context_set_background((int(r),int(g),int(b)))
		#pdb.gimp_edit_fill(work_layer,FOREGROUND_FILL)
		if brushit == 1:
			#pdb.gimp_edit_bucket_fill_full(work_layer,BUCKET_FILL_FG,LAYER_MODE_NORMAL,100,15.0,FALSE,FALSE,SELECT_CRITERION_COMPOSITE,x1,y1)
			#pdb.gimp_message("gimp_pencil")
			pdb.gimp_edit_blend(work_layer,BLEND_FG_BG_RGB,LAYER_MODE_NORMAL,GRADIENT_LINEAR,100,0,REPEAT_NONE,FALSE,FALSE,3,0.2,FALSE,x1,y1,x2,y2)
			#pdb.gimp_pencil(work_layer,2,[(x2+x1)/2,(y2+y1)/2])
			#pdb.gimp_message('gimp_pencil done')
		else:	
			pdb.gimp_drawable_edit_fill(work_layer,FILL_FOREGROUND)
def python_tt_draw_average(image, layer, grow, brushit, dodge):
	#pdb.gimp_image_undo_group_start(image)
	pdb.gimp_context_push()
	
	source_layer = pdb.gimp_image_get_layer_by_name(image,"Source")
	non_empty,x1,y1,x2,y2 = pdb.gimp_selection_bounds(image)
	if not non_empty:
		pdb.gimp_selection_all(image)
	
	#save selection to intersect with each grown area later
	saved_selection = pdb.gimp_selection_save(image)
	
	#deal with multi_selections, turns it into a path then get it's strokes
	pdb.plug_in_sel2path(image,layer)
	vectors = image.vectors[0]
	num_strokes,stroke_ids = pdb.gimp_vectors_get_strokes(vectors)
	
	if brushit == 1: #if brush it we select brush and set brushsize
		pdb.gimp_context_set_brush("2. Hardness 100")
		pdb.gimp_context_set_opacity(100)
		#pdb.gimp_context_set_brush_size(brushsize)
		pdb.gimp_context_get_gradient('FG to BG (Hardedge)')

	#loop through all strokes, select each stroke and do average color on each stroke
	for stroke_id in stroke_ids:
		temp_vectors = pdb.gimp_vectors_new(image,"Temp")
		pdb.gimp_image_insert_vectors(image,temp_vectors,None,0)
		
		_type, _num_points, _control_points, _closed = pdb.gimp_vectors_stroke_get_points(vectors,stroke_id)
		new_stroke_id = pdb.gimp_vectors_stroke_new_from_points(temp_vectors,_type,_num_points,_control_points,_closed)
		
		pdb.gimp_image_select_item(image,CHANNEL_OP_REPLACE,temp_vectors)
		#grow it then insect with original selection to get exact select area to pixels accuracy
		pdb.gimp_selection_grow(image,grow)
		pdb.gimp_image_select_item(image,CHANNEL_OP_INTERSECT,saved_selection)
		#Try to Subtract [Alpha To Selection of current layer] before applying average color
		if dodge == 1:
			pdb.gimp_image_select_item(image,CHANNEL_OP_SUBTRACT,layer)
		tt_average_fill_it(image,source_layer,layer,brushit)
		#we're done, remove it
		pdb.gimp_image_remove_vectors(image,temp_vectors)

	pdb.gimp_image_remove_vectors(image,vectors)
	pdb.gimp_image_remove_channel(image,saved_selection)

	#UNCOMMENT BELOW WHEN DONE
	pdb.gimp_context_pop()
	#pdb.gimp_image_undo_group_end(image)
	
	pdb.gimp_displays_flush()
    #return

register(
	"python_fu_tt_draw_average",                           
	"Fills selection of active layer with average color of selection on layer named 'Source'",
	"Fills selection of active layer with average color of selection on layer named 'Source'",
	"Tin Tran",
	"Tin Tran",
	"July 2017",
	"<Image>/Python-Fu/Fill Average Color..",             #Menu path
	"RGB*, GRAY*", 
	[
	(PF_SPINNER, "grow", "Pixels to grow each area before intersecting with original selection:", 2, (0, 100, 1)),
	(PF_TOGGLE, "brushit", "Edit-Blend instead of Edit-Fill (Edit-blend is faster):", 1),
	#(PF_SPINNER, "brushsize", "Brush Size:", 500, (100, 10000, 1)),
	(PF_TOGGLE, "dodge", "Dodge filled areas (Subtract alpha to selection's selection):", 0),
	],
	[],
	python_tt_draw_average)

main()

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines