#!/usr/bin/env python from gimpfu import * import glob import inspect import os import re import sys class Tronify: def __init__(self): # Method names of the form "f_42_55..." are used as image filters and # act upon a range of frame indices e.g. 42-55. Construct a list of # these filters in the form of [("f_42_55...", 42, 55), ...] p = re.compile(r"^f_(\d+)_(\d+).*$") self.filters = [(r.group(0), int(r.group(1)), int(r.group(2))) \ for r in [p.search(f) for f in dir(Tronify)] if r] self.scale = 1 self.testMode = False self.blue = (70, 172, 255) self.darkBlue = (47, 115, 237) self.red = (255, 0, 0) self.white = (255, 255, 255) self.ringsColor = self.blue self.ringsFeather = 0 self.ringsThreshold = 70 # self.chromaOut = False self.chromaOut = (0, 0, 0) def animate(self, initial, final, f1 = 0, f2 = 0): # linear if f1 == 0: f1, f2 = self.frameRange() def _animate(y1, y2): m = (y2 - y1) / float(f2 - f1) b = y1 - m * f1 return int(m * self.i + b) if type(initial) is tuple: return tuple(map(_animate, initial, final)) return _animate(initial, final) def animatePeak(self, initial, final, f1 = 0, f2 = 0): # linear peak at midpoint /\ if f1 == 0: f1, f2 = self.frameRange() if f2 - f1 < 2: return final fMid = (f1 + f2) / 2.0 def _animatePeak(y1, y2): if f1 <= self.i <= fMid: m = (y2 - y1) / float(fMid - f1) b = y1 - m * f1 else: m = (y1 - y2) / float(f2 - fMid) b = y2 - m * fMid return int(m * self.i + b) if type(initial) is tuple: return tuple(map(_animatePeak, initial, final)) return _animatePeak(initial, final) def frameRange(self): p = re.compile(r"^f_(\d+)_(\d+).*$") r = p.search(inspect.stack()[2][3]) if not r: r = p.search(inspect.stack()[3][3]) if not r: raise ValueError("Couldn't infer frame range.") return (int(r.group(1)), int(r.group(2))) def tronifyDir(self, threadId, numThreads, framesDir, testMode): self.testMode = testMode if testMode: print(filter(lambda f: f[0], self.filters)) # Determine frames (and file extensions) to be processed. framesList = [re.match(r"frame-(\d+)\.(\w{3})", os.path.basename(f)).groups() for f in sorted(glob.glob(framesDir + "/frame-*[0-9].???")) if not os.path.isfile(f[0:-4] + ".done." + f[-3:])] segmentSize = len(framesList) // numThreads # e.g. 100 // 6 = 16 remainder = len(framesList) % numThreads # e.g 100 % 6 = 4 # We want e.g. 17, 17, 17, 17, 16, 16 segmentSize += 1 segmentStart = (threadId - 1) * segmentSize + 1 segmentEnd = segmentStart + segmentSize - 1 if threadId > remainder: segmentStart -= (threadId - 1) - remainder segmentEnd -= threadId - remainder # print("Thread " + str(threadId) + " has indices " + str(segmentStart) + "-" + str(segmentEnd)) for i in range(segmentStart, segmentEnd + 1): n = framesList[i - 1][0] extension = framesList[i - 1][1] infile = os.path.join(framesDir, "frame-" + n + "." + extension) outfile = os.path.join(framesDir, "frame-" + n + ".done." + extension) image = pdb.gimp_file_load(infile, infile) drawable = pdb.gimp_image_get_active_layer(image) self.tronifyImage(image, drawable, int(n)) pdb.gimp_file_save(image, drawable, outfile, outfile) pdb.gimp_image_delete(image) if testMode: break def tronifyImage(self, image, drawable, i): self.i = i # save instance variables state = vars(self).copy() del state["filters"] # apply filters and tronify filters = [r[0] for r in self.filters if r[1] <= i <= r[2]] for f in filters: getattr(self, f)() self.__tronifyImage(image, drawable) # restore instance variables for k,v in state.iteritems(): setattr(self, k, v) def __tronifyImage(self, image, drawable): s = self.scale # create channel: disc pdb.gimp_image_select_item(image, CHANNEL_OP_REPLACE, drawable) # all but alpha channelDisc = pdb.gimp_selection_save(image) # create channel: white pdb.gimp_context_set_antialias(True) pdb.gimp_context_set_sample_criterion(SELECT_CRITERION_V) pdb.gimp_context_set_sample_threshold_int(self.ringsThreshold) pdb.gimp_image_select_color(image, CHANNEL_OP_SUBTRACT, drawable, (0, 0, 0)) channelWhite = pdb.gimp_selection_save(image) # create channel: rings pdb.gimp_image_select_item(image, CHANNEL_OP_REPLACE, channelDisc) pdb.gimp_image_select_item(image, CHANNEL_OP_SUBTRACT, channelWhite) channelRings = pdb.gimp_selection_save(image) # white: add grainy noise and gray-ify pdb.gimp_image_select_item(image, CHANNEL_OP_REPLACE, channelWhite) pdb.plug_in_hsv_noise(image, drawable, 4, 0, s*64, s*64) pdb.gimp_colorize(drawable, 199, 11, -56) # rings: colorarization pdb.gimp_image_select_item(image, CHANNEL_OP_REPLACE, channelRings) pdb.gimp_context_set_foreground(self.ringsColor) pdb.gimp_edit_fill(drawable, 0) pdb.script_fu_drop_shadow(image, drawable, 0, s*1, s*3.0, (0, 0, 0), 100, 0) pdb.gimp_selection_feather(image, s*self.ringsFeather) pdb.gimp_edit_fill(drawable, 0) pdb.gimp_edit_fill(drawable, 0) # apply chroma key mask if self.chromaOut is not False: pdb.gimp_context_set_background(self.chromaOut) pdb.gimp_layer_flatten(drawable) def f_001_138(self): self.ringsColor = self.darkBlue self.ringsFeather = 0 def f_036_048(self): self.ringsColor = self.animate(self.darkBlue, self.white) self.ringsFeather = self.animate(20, 80) self.ringsThreshold = self.animate(self.ringsThreshold, 90) def f_049_063(self): self.ringsColor = self.animate(self.white, self.darkBlue) self.ringsFeather = self.animate(80, 20) self.ringsThreshold = self.animate(90, self.ringsThreshold) def f_082_092(self): self.ringsColor = self.animate(self.darkBlue, self.white) self.ringsFeather = self.animate(20, 80) self.ringsThreshold = self.animate(self.ringsThreshold, 90) def f_093_109(self): self.ringsColor = self.animate(self.white, self.darkBlue) self.ringsFeather = self.animate(80, 20) self.ringsThreshold = self.animate(90, self.ringsThreshold) from gimpfu import * def tronify_dir(outPipe, threadId, numThreads, framesDir, testMode): sys.stdout = open(outPipe, "w", 0) pngCompression = 1 # 0-9 pdb.file_png_set_defaults(0, pngCompression, 1, 0, 0, 1, 1, 0, 0) t = Tronify() t.tronifyDir(threadId, numThreads, framesDir, testMode) sys.stdout.close() register( "python_fu_tronify_dir", "TRONify", "Create TRON-like effects on series of images.", "David Fleming", "David Fleming", "2024", "TRONify Images...", "", [ # (PF_IMAGE, "image", "Input image", None), # (PF_DRAWABLE, "drawable", "Input layer", None), (PF_STRING, "outPipe", "Output Pipe", ""), (PF_INT, "threadId", "Thread ID", 1), (PF_INT, "numThreads", "Number of Threads", 1), (PF_STRING, "framesDir", "Frames Directory", ""), (PF_BOOL, "testMode", "Test Mode", False), ], [], tronify_dir, menu="/Filters") main()