#!/usr/bin/python # This Gimp plug-in changes colors in an image based on the HSV difference # between two colors. This is particularly useful for applying color themes # to existing images. # Version 1.1, no active selection now supported # Version 1.0, first release 3/6 2008 # It does not currently support INDEXED mode, undo or partial selections # Copyright (C) 2008 Amplex A/S (lars@amplex.dk) # # 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 2 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. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # This plug-in is based on code by Ling Xu and Emanuele Zattin with most # of the actual code replaced by now. Used with permission. import math from gimpfu import * #from Numeric import * # Returns the amount of selectedness this pixel has. 0 == not selected, # 255 = fully selected def selectedness(drawable, x, y): bounds = pdb.gimp_selection_bounds(drawable.image) if bounds[0]: sel = drawable.image.selection if (sel != None): tile = sel.get_tile2(False, x, y) return ord(tile[x % 64, y % 64]) return 255 # Get the pixel at x,y anywhere in the drawable. Slow. def getpixel(drawable, x, y): tile = drawable.get_tile2(False, x, y) x_offset = x % 64 y_offset = y % 64 pixel = tile[x_offset, y_offset] values = [] for i in range(len(pixel)): values.append(ord(pixel[i])) if len(values) == 1: values.append(255) return values # Put a pixel value into a drawable at x, y. Slow. def putpixel(drawable, x, y, value): tile = drawable.get_tile2(False, x, y) x_offset = x % 64 y_offset = y % 64 pixel = "" for i in range(len(value)): pixel += chr(value[i]) tile[x_offset, y_offset] = pixel # Convert RGB to HSV def rgbToHsv(rgb): r = rgb[0] g = rgb[1] b = rgb[2] H = 0.0 minRGB = min(min(r, g), b) maxRGB = max(max(r, g), b) * 1.0 delta = ( maxRGB - minRGB ) * 1.0 br = maxRGB if (maxRGB <> 0.0): s = 255.0 * delta / maxRGB else: s = 0.0 if (s <> 0.0): if r == maxRGB: h = (g - b) / delta else: if g == maxRGB: h = 2.0 + (b - r) / delta else: if b == maxRGB: h = 4.0 + (r - g) / delta else: h = -1.0 h = h * 60 if h < 0.0: h = h + 360.0 return [h, s * 100 / 255.0, br * 100 / 255] # Convert HSV to RGB def hsvToRgb(hsv): h = hsv[0] / 360 s = hsv[1] / 100 v = hsv[2] / 100 if ((s - 0.0) < 0.000): return [int(v * 255), int(v * 255), int(v * 255)] else: var_h = h * 6 var_i = math.floor(var_h) var_1 = v * (1 - s) var_2 = v * (1 - s * (var_h - var_i)) var_3 = v * (1 - s * (1 - (var_h - var_i))) if (var_i == 0): var_r = v var_g = var_3 var_b = var_1 else: if (var_i == 1): var_r = var_2 var_g = v var_b = var_1 else: if (var_i == 2): var_r = var_1 var_g = v var_b = var_3 else: if (var_i == 3): var_r = var_1 var_g = var_2 var_b = v else: if (var_i == 4): var_r = var_3 var_g = var_1 var_b = v else: var_r = v var_g = var_1 var_b = var_2 return [int(var_r * 255), int(var_g * 255), int(var_b * 255)] def color_remap(image, drawable, source, target): image.disable_undo() layer = image.active_layer height = layer.height width = layer.width gimp.tile_cache_ntiles(2 * (width + 63) / 64) gimp.progress_init("Mapping colors...") sourceHsv = rgbToHsv(source) targetHsv = rgbToHsv(target) hDiff = sourceHsv[0] - targetHsv[0] if (sourceHsv[1] > 0): sDiff = targetHsv[1] / sourceHsv[1] if (targetHsv[1] > sourceHsv[1]): sDiff2 = (100 - targetHsv[1]) / (100 - sourceHsv[1]) else: sDiff = -1 if (sourceHsv[2] > 0): vDiff = targetHsv[2] / sourceHsv[2] if (targetHsv[2] > sourceHsv[2]): vDiff2 = (100 - targetHsv[2]) / (100 - sourceHsv[2]) else: vDiff = -1 for i in range(width): gimp.progress_update( (1.0 * i) / width); for j in range(height): sel = selectedness(layer, i, j) if (sel == 0): continue pixel = getpixel(layer, i, j) hsv = rgbToHsv(pixel) hsv[0] = (hsv[0] - hDiff) % 360 if (sDiff != -1): if ((sDiff > 1) & (hsv[1] > sourceHsv[1])): hsv[1] = (100 - (100 - hsv[1]) * sDiff2) else: hsv[1] = hsv[1] * sDiff if (vDiff != -1): if ((vDiff > 1) & (hsv[2] > sourceHsv[2])): hsv[2] = (100 - (100 - hsv[2]) * vDiff2) else: hsv[2] = hsv[2] * vDiff finalPixel = hsvToRgb(hsv) if (len(pixel) > 3): finalPixel.append(pixel[3]) putpixel(layer, i, j, finalPixel) layer.update(0, 0, width, height) image.enable_undo() register( "RelativeColorRemap", "Remap colors in this layer relative to a given color", "Given a source and target color, transform all colors proportionally to the difference between the two in HSV space", "Lars Clausen", "Lars Clausen, based on code from Ling Xu, Emanuele Zattin", "2008", "/Colors/Map/Relative", "RGB*, GRAY*", [ (PF_COLOUR, "source", "Source colour", (128,128,128)), (PF_COLOUR, "target", "Target colour", (255,128,0)) ], [], color_remap) main()