#!/usr/bin/env python # # ============================================================================== # GraduatedFilter.py V4 - (c) Berthold Hinz 2009 # ============================================================================== # # GRADUATED FILTER FOR THE GIMP # # ============================================================================== # This script is the result of joint effort and inspiring discussion in the # forum of www.meetthegimp.org. It was initiated by Luigi and includes smart # suggestians and ideas of him and the forum members Rolf, di98jgu and # mramshaw. # # ============================================================================== # 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, version 3 of the License. # # 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. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # ============================================================================== from gimpfu import * def GFilters(img, drw, filter, f_colour, softness, type, orientation): ## CONSTANTS col_code={2:(0,255,255),3:(0,0,255),4:(210,105,30),5:(255,0,255),6:(0,128,0),7:(0,255,0),8:(128,0,0),9:(203,78,97),10:(0,0,128),11:(128,128,0),12:(255,165,0),13:(128,0,128),14:(255,0,0),15:(70,130,180),16:(237,140,25),17:(0,128,128),18:(111,78,55),19:(255,255,0)} col_name={2:"aqua",3:"blue",4:"chocolate",5:"fuchsia",6:"green",7:"lime",8:"maroon",9:"mauve",10:"navy",11:"olive",12:"orange",13:"purple",14:"red",15:"steelblue",16:"sunset",17:"teal",18:"tobacco",19:"yellow"} fgc = gimp.get_foreground() bgc = gimp.get_background() w=img.width h=img.height pdb.gimp_image_undo_group_start(img) ## CHECK REQUIREMENTS numlayers=len(img.layers) maskexists=pdb.gimp_layer_get_mask(img.layers[0]) if maskexists<>None: m="There's already a mask for the lighter layer." + "\n"+"\n"+"The gradient can't be added."+"\n" + "Please remove the layer mask and run the script again! (Shortcut: Ctrl+F)" gimp.message(m) if numlayers<>2: m="Invalid Number of layers!" + "\n"+"\n"+"Exactly 2 layers are necessary to use the Graduated Filters."+"\n" + "Please open two differently exposed images as layers (shortcut: CTRL+ALT+O) and run the script again! (Ctrl+F)" gimp.message(m) else: ## SORT LAYERS ACCORDING LIGHTNESS drw=img.layers[0] (mean, std_dev, median, pixels, count, percentile) = pdb.gimp_histogram (drw, HISTOGRAM_VALUE,0,255) meanL0=mean drw=img.layers[1] (mean, std_dev, median, pixels, count, percentile) = pdb.gimp_histogram (drw, HISTOGRAM_VALUE,0,255) meanL1=mean if meanL1>meanL0: pdb.gimp_image_raise_layer_to_top(img,drw) img.layers[0].name="lighter image" img.layers[1].name="darker image" ## APPPLY FILTER if maskexists==None: if filter==0: f_colour=(0,0,0) elif filter==1: lname="custom colour"+" (adjust opacity)" else: f_colour=col_code[filter] lname=col_name[filter]+" (adjust opacity)" if filter>0: if pdb.gimp_drawable_is_rgb(img.layers[0])==False: pdb.gimp_image_convert_rgb(img) m="Image conversion." + "\n" +"\n" + "Since you added colour to your grayscale image it was converted to RGB-mode." gimp.message(m) if type=="horizon": guide_mode,guide_y,guide_m = guide_det(img) if guide_mode==1: horizon=guide_y gnd_name="manual horizon:" elif guide_mode==2: gimp.message(guide_m) else: horizon=a_horizon(img,img.layers[1],h,w,3.0,0.5) gnd_name="auto horizon:" gnd_name=gnd_name+str(horizon) + "; softness:" + str(softness) if orientation=="flipped": gnd_name=gnd_name+"; flipped" gnd_name=gnd_name+" (adjust opacity)" ## CALL FUNCTION TO CREATE FILTER if type=="horizon": make_gradient(img,img.layers[0],horizon,softness,orientation,filter,f_colour) if type=="lightness": make_lfilter(img,img.layers[0],softness,filter,f_colour) gnd_name="lightness mode; softness:" + str(softness) + " (adjust opacity)" l=0 if filter>0: l=1 img.layers[0].name=lname img.layers[l].name=gnd_name ## RESET gimp.set_foreground(fgc) gimp.set_background(bgc) pdb.gimp_image_undo_group_end(img) ## AUTODETECTION HORIZON def a_horizon(img,drw,h,w,resolution,prop_width): hor=0 steps=int(h/resolution) sel_h=resolution sel_w=w*prop_width sel_x=w*(1-prop_width)/2 sel_y=0 last_mv=0.0 curr_mv=0.0 diff_max=0.0 for i in xrange(steps-6): sel_y=(i+3)*sel_h pdb.gimp_rect_select(img, sel_x, sel_y, sel_w, sel_h, 2, False, 0) (mean, std_dev, median, pixels, count, percentile) = pdb.gimp_histogram (drw, HISTOGRAM_VALUE,0,255) if i>0: last_mv=curr_mv curr_mv=mean diff=abs(curr_mv-last_mv) if diff>diff_max and last_mv>0: diff_max=diff if i>1: hor=sel_y+resolution if hor<5 or hor>h-5: hor=h m="No horizon found!" + "\n"+"\n"+"Gradient will extend across the full height of the image. If this is not desired, undo the changes (Ctrl+Z), set the horizon manually by guide and run the script again! (Ctrl+F)" gimp.message(m) else: pdb.gimp_image_add_hguide(img,hor) pdb.gimp_selection_none(img) return hor ## SEARCH FOR MANUALLY SET HORIZON def guide_det(img): guide_mode=0 guide_y=0 guide_m="" guide_num_1 = pdb.gimp_image_find_next_guide(img, 0) if guide_num_1<>0: guide_or=pdb.gimp_image_get_guide_orientation(img,guide_num_1) if guide_or<>0: guide_m="No horizon defined!" + "\n" + "\n" + "Apparently you intended to define the horizon by a horizontal guide (ruler) but the guide that was found is vertical." + "\n" + "Take care to have just one horizontal guide and run the script again! (Shortcut: Ctrl+F)" guide_mode=2 else: guide_y=pdb.gimp_image_get_guide_position(img,guide_num_1) guide_mode=1 guide_num = pdb.gimp_image_find_next_guide(img, guide_num_1) if guide_num<>0: guide_m="Horizon not clearly defined!" + "\n" + "\n" + "You decided to define the horizon by a guide (ruler) but more than one guide was found." + "\n" + "Take car to have just one guide and run the script again! (Shortcut: Ctrl+F)" guide_y=0 guide_mode=2 return guide_mode, guide_y, guide_m ## MAKE "LIGHTNESS FILTER" def make_lfilter(img,drw,softness,filter,f_colour): blurr=int(img.width/5.0)+softness gnd_mask = pdb.gimp_layer_create_mask(drw, 5) pdb.gimp_layer_add_mask(drw,gnd_mask) drw=gnd_mask pdb.gimp_invert(drw) pdb.gimp_levels(drw,0,0,128,0.1+softness/100.0*9.9,0,255) pdb.plug_in_gauss(img,drw,blurr,blurr,0) if filter>0: add_colour(img,gnd_mask,f_colour) ## MAKE GRADIENT def make_gradient(img,drw,horizon,softness,orientation,filter,f_colour): h=img.height gimp.set_foreground(255,255,255) gimp.set_background(0,0,0) x1=0 x2=0 y1=horizon if orientation=="standard": hplain=int(horizon-softness/100.0*horizon) y2=hplain else: hplain=h-int((h-horizon)-(100-softness)/100.0*(h-horizon)) y2=y1+h-hplain mask = pdb.gimp_layer_create_mask(drw, ADD_WHITE_MASK) pdb.gimp_layer_add_mask(drw,mask) drw=mask pdb.gimp_edit_blend(drw, FG_BG_RGB_MODE, NORMAL_MODE, GRADIENT_LINEAR, 100, 0, REPEAT_NONE, False, False, 0, 0, True, x1, y1, x2, y2) if filter>0: add_colour(img,mask,f_colour) ## ADD COLOUR LAYER + LAYER MASK def add_colour(img,mask,f_colour): pdb.gimp_edit_copy(mask) layer_colour = gimp.Layer(img, "", img.width, img.height, RGB_IMAGE, 50, OVERLAY_MODE) img.add_layer(layer_colour, 0) pdb.gimp_edit_paste(layer_colour, 0) pdb.gimp_floating_sel_anchor(img.layers[0]) drw=layer_colour pdb.gimp_invert(drw) col_mask = pdb.gimp_layer_create_mask(drw, 5) pdb.gimp_layer_add_mask(drw,col_mask) gimp.set_foreground(f_colour) drw=layer_colour pdb.gimp_edit_bucket_fill(drw, 0, 0, 100, 255, False, 0, 0) register( "GRADUATED-FILTERS", "Digitally create a Graduated Filter / Lightness Mask\n -- product of forum.meetthegimp.org --\n\nGiven two layers, simulate a horizontal Graduated Filter.\n\nThe horizon to be used for the start of the filter can be specified by a guide line; if not specified, it will be determined by this plug-in.\n\nThere is also a lightness mode which creates a layer mask based upon pixel lightness.\n\n", "Graduated Filters", "Berthold Hinz", "public domain", "2009", "/Filters/Photo/Graduated Filters", "RGB*, GRAY*", [(PF_OPTION, "filter", ("Filter"), 0,["Neutral (GND)","Custom colour","Aqua","Blue","Chocolate","Fuchsia","Green","Lime","Maroon","Mauve","Navy","Olive","Orange","Purple","Red","Steelblue","Sunset","Teal","Tobacco","Yellow"]), (PF_COLOUR,"f_colour"," Custom colour selection ", (170,190,255)), (PF_SLIDER,"softness","Softness of Filter/Mask (%)",100,(1,100,10)), (PF_RADIO, "type", ("Pixel darkening depends on "), "horizon", (("Distance from horizon", "horizon"),("Lightness", "lightness"))), (PF_RADIO,"orientation",("Orientation"+"\n"+"(ignored in Lightness mode) "),"standard",(("Standard (filtering top)", "standard"), ("Flipped (filtering bottom)", "flipped"))), ], [], GFilters) main()