import math import time import datetime import os from os import remove from PIL import Image from multiprocessing import Pool import multiprocessing from copy import deepcopy import argparse # print functions for control from debug_level parameter def print_start(bool, string): if bool == True: print(str(string)) def print_lvl_0(debug_level, string): print(str(string)) def print_lvl_1(debug_level, string): if debug_level > 0: print(str(string)) def print_lvl_2(debug_level, string): if debug_level > 1: print(str(string)) def print_lvl_3(debug_level, string): if debug_level > 2: print(str(string)) def print_lvl_4(debug_level, string): if debug_level > 3: print(str(string)) def print_lvl_5(debug_level, string): if debug_level > 4: print(str(string)) def print_lvl_6(debug_level, string): if debug_level > 5: print(str(string)) def print_lvl_7(debug_level, string): if debug_level > 6: print(str(string)) # centralized way to turn off messages at the start of the script show_starting_messages = False # starting message print_start(show_starting_messages, '-'*79) print_start(show_starting_messages, 'Starting...') # path definition diskPath = ''# only fill if you want it to work in some absolute path inputFolder = diskPath + 'input/' outputFolder = diskPath + 'output/' tempFolder = outputFolder + 'temp/'#don't change this # make sure the output folders exist os.makedirs(outputFolder, exist_ok = True) os.makedirs(tempFolder, exist_ok = True) # printing path just to check print_start(show_starting_messages, 'inputFolder is ' + inputFolder) print_start(show_starting_messages, 'outputFolder is ' + outputFolder) # starting timer tt = time.time() # format the time. I forgot how this works but it does. startedTime = datetime.datetime.fromtimestamp(tt).strftime('%H:%M:%S') # open palette images # ----------------------------------------------------------------------------------------------------------------- # image for palette data palette_img_indexed = Image.open(inputFolder + 'palette_key.png') palette_data = deepcopy(palette_img_indexed.palette) print_start(show_starting_messages, 'Opening and loading palette: ' + 'openttd-palette-dos-RGBA.png') # image for colour comparing palette_img = Image.open(inputFolder + 'openttd-palette-dos-RGBA.png') # creating palette list of RGB pixels p=[] for b in range(0,palette_img.height): for a in range(0, palette_img.width): p.append(palette_img.getpixel((a,b))) print_start(show_starting_messages, 'Palette loaded.') # ---------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------- # D E F I N I N G O F F U N C T I O N S # ---------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------- def rgb2palette(args): # extract the arguments from the big args list thread_id = args[0] input_image = args[1] x_start = args[2] x_end = args[3] allowed_colour_types = args[4] disallowed_colour_types = args[5] allowed_colour_indexes = args[6] disallowed_colour_indexes = args[7] alpha_ignore = args[8] alpha_offset_2 = args[9] alpha_offset_1 = args[10] red_weight = args[11] green_weight = args[12] blue_weight = args[13] arg_colour_shift = args[14] arg_debug_level = args[15] offset_list = args[16] arg_individual_temp = args[17] print('individual_temp - rgb2palette = ' + str(arg_individual_temp)) print_lvl_5(arg_debug_level, 'rgb2palette args: ' + str(args)) # create allowed colours list colours_to_filter = [] for coltype in allowed_colour_types: if coltype == 'ALL': for n in range(1, 80): colours_to_filter.append(n) for n in range(88, 197): colours_to_filter.append(n) for n in range(205, 215): colours_to_filter.append(n) if coltype == 'GRAYSCALE': for n in range(1, 16): colours_to_filter.append(n) if coltype == 'METAL': for n in range(16, 24): colours_to_filter.append(n) if coltype == 'LIME_GREEN': for n in range(24, 32): colours_to_filter.append(n) if coltype == 'BEIGE': for n in range(32, 40): colours_to_filter.append(n) if coltype == 'DARK_PINK': for n in range(40, 48): colours_to_filter.append(n) if coltype == 'YELLOW': for n in range(50, 53): colours_to_filter.append(n) if coltype == 'DARK_BEIGE': for n in range(53, 60): colours_to_filter.append(n) if coltype == 'YELLOW': for n in range(60, 70): colours_to_filter.append(n) if coltype == 'BROWN_1': for n in range(70, 80): colours_to_filter.append(n) if coltype == 'CC2': for n in range(80, 88): colours_to_filter.append(n) if coltype == 'DARK_GREEN': for n in range(88, 96): colours_to_filter.append(n) if coltype == 'PALE_GREEN': for n in range(96, 104): colours_to_filter.append(n) if coltype == 'BROWN_2': for n in range(104, 112): colours_to_filter.append(n) if coltype == 'BROWN_3': for n in range(112, 122): colours_to_filter.append(n) if coltype == 'BROWN_4': for n in range(122, 128): colours_to_filter.append(n) if coltype == 'MAUVE': for n in range(128, 136): colours_to_filter.append(n) if coltype == 'PURPLE': for n in range(136, 144): colours_to_filter.append(n) if coltype == 'BLUE': for n in range(144, 154): colours_to_filter.append(n) if coltype == 'LIGHT_BLUE': for n in range(154, 162): colours_to_filter.append(n) if coltype == 'PINK': for n in range(162, 170): colours_to_filter.append(n) if coltype == 'LIGHT_PURPLE': for n in range(170, 178): colours_to_filter.append(n) if coltype == 'RED_1': for n in range(178, 185): colours_to_filter.append(n) if coltype == 'RED_2': for n in range(185, 192): colours_to_filter.append(n) if coltype == 'ORANGE': for n in range(192, 198): colours_to_filter.append(n) if coltype == 'CC1': for n in range(198, 206): colours_to_filter.append(n) if coltype == 'GREEN': for n in range(206, 210): colours_to_filter.append(n) if coltype =='CYAN': for n in range(210, 215): colours_to_filter.append(n) if coltype == 'COLA': for n in range(227, 232): colours_to_filter.append(n) if coltype == 'FIRE': for n in range(232, 239): colours_to_filter.append(n) if coltype == 'LED_RED': for n in range(239, 241): colours_to_filter.append(n) if coltype == 'LED_YELLOW': for n in range(241, 245): colours_to_filter.append(n) if coltype == 'WATER': for n in range(245, 255): colours_to_filter.append(n) if coltype == 'WHITE': for n in range(255, 256): colours_to_filter.append(n) # create disallowed colours list disallowed_colours = [] for coltype in disallowed_colour_types: if coltype == 'ALL': for n in range(1, 80): disallowed_colours.append(n) for n in range(88, 197): disallowed_colours.append(n) for n in range(205, 215): disallowed_colours.append(n) if coltype == 'GRAYSCALE': for n in range(1, 16): disallowed_colours.append(n) if coltype == 'METAL': for n in range(16, 24): disallowed_colours.append(n) if coltype == 'LIME_GREEN': for n in range(24, 32): disallowed_colours.append(n) if coltype == 'BEIGE': for n in range(32, 40): disallowed_colours.append(n) if coltype == 'DARK_PINK': for n in range(40, 48): disallowed_colours.append(n) if coltype == 'YELLOW': for n in range(50, 53): disallowed_colours.append(n) if coltype == 'DARK_BEIGE': for n in range(53, 60): disallowed_colours.append(n) if coltype == 'YELLOW': for n in range(60, 70): disallowed_colours.append(n) if coltype == 'BROWN_1': for n in range(70, 80): disallowed_colours.append(n) if coltype == 'CC2': for n in range(80, 88): disallowed_colours.append(n) if coltype == 'DARK_GREEN': for n in range(88, 96): disallowed_colours.append(n) if coltype == 'PALE_GREEN': for n in range(96, 104): disallowed_colours.append(n) if coltype == 'BROWN_2': for n in range(104, 112): disallowed_colours.append(n) if coltype == 'BROWN_3': for n in range(112, 122): disallowed_colours.append(n) if coltype == 'BROWN_4': for n in range(122, 128): disallowed_colours.append(n) if coltype == 'MAUVE': for n in range(128, 136): disallowed_colours.append(n) if coltype == 'PURPLE': for n in range(136, 144): disallowed_colours.append(n) if coltype == 'BLUE': for n in range(144, 154): disallowed_colours.append(n) if coltype == 'LIGHT_BLUE': for n in range(154, 162): disallowed_colours.append(n) if coltype == 'PINK': for n in range(162, 170): disallowed_colours.append(n) if coltype == 'LIGHT_PURPLE': for n in range(170, 178): disallowed_colours.append(n) if coltype == 'RED_1': for n in range(178, 185): disallowed_colours.append(n) if coltype == 'RED_2': for n in range(185, 192): disallowed_colours.append(n) if coltype == 'ORANGE': for n in range(192, 198): disallowed_colours.append(n) if coltype == 'CC1': for n in range(198, 206): disallowed_colours.append(n) if coltype == 'GREEN': for n in range(206, 210): disallowed_colours.append(n) if coltype =='CYAN': for n in range(210, 215): disallowed_colours.append(n) if coltype == 'COLA': for n in range(227, 232): disallowed_colours.append(n) if coltype == 'FIRE': for n in range(232, 239): disallowed_colours.append(n) if coltype == 'LED_RED': for n in range(239, 241): disallowed_colours.append(n) if coltype == 'LED_YELLOW': for n in range(241, 245): disallowed_colours.append(n) if coltype == 'WATER': for n in range(245, 255): disallowed_colours.append(n) if coltype == 'WHITE': for n in range(255, 256): disallowed_colours.append(n) # add individual disallowed colours for n in disallowed_colour_indexes: disallowed_colours.append(n) # move non-disallowed colours to a new list filtered_colours_to_filter = [] for c in colours_to_filter: skip = 0 for d in disallowed_colours: if c == d: skip = 1 if skip == 0: filtered_colours_to_filter.append(c) # add individual allowed colours for n in allowed_colour_indexes: filtered_colours_to_filter.append(n) print_lvl_4(arg_debug_level, 'Colours to filter: ' + str(filtered_colours_to_filter)) # open input image i = Image.open(inputFolder + input_image + '.png') print_lvl_2(arg_debug_level, 'Thread ' + str(thread_id) + ' Opening: ' + input_image + '.png') # create new empty indexed image for output imageOutput = Image.new('L', (i.width,i.height), color = 0) # go through the input image - y axis for y in range (0, i.height): # timestamp for debug ts = time.time() timeStamp = datetime.datetime.fromtimestamp(ts).strftime('%H:%M:%S') if y%32 == 0: print_lvl_5(arg_debug_level, 'Thread ' + str(thread_id) + ' ' + timeStamp + ' - ' + input_image + ' row {}'.format(y)) # go through the input image - x axis, limited by threaded strips for x in range (x_start, x_end): # defining winner variables just so they exist winnerDistance = 100000000 winnerID = 0 # loading pixel from image and separating RGBA pixelNumber = x + (y * i.width) pix = i.getpixel((x,y)) pixRed = pix[0] pixGreen = pix[1] pixBlue = pix[2] pixAlpha = pix[3] # check Alpha in pixel, and output alpha/color offset if pixAlpha < alpha_ignore: finalAlpha = 0 colorOffset = 0 if pixAlpha >= alpha_ignore and pixAlpha < 178: finalAlpha = 255 colorOffset = 2 if pixAlpha >= alpha_offset_1 and pixAlpha < alpha_offset_2: finalAlpha = 255 colorOffset = 1 if pixAlpha >= alpha_offset_2: finalAlpha = 255 colorOffset = 0 # if alpha above 50%, do colour comparing to palette if pixAlpha >= alpha_ignore: # go through all of the specified colours to filter for colour_id in filtered_colours_to_filter: rgb1 = p[colour_id] r1 = rgb1[0] g1 = rgb1[1] b1 = rgb1[2] l1 = (r1*red_weight + g1*green_weight + b1*blue_weight) / 255000 l2 = (pixRed*red_weight + pixGreen*green_weight + pixBlue*blue_weight) / 255000 dL = l1-l2 dR = (r1-pixRed)/255 dG = (g1-pixGreen)/255 dB = (b1-pixBlue)/255 distance = (dR*dR*red_weight*0.001 + dG*dG*blue_weight*0.001 + dB*dB*blue_weight*0.001)*0.75 + dL*dL # if it's better match than the previous tries, save it as the new winner if distance < winnerDistance: winnerDistance = distance winnerID = colour_id # final color changed by colorOffset finalID = offset_list[winnerID][colorOffset] #winnerID - colorOffset # finalAlpha taken from the if output above palette colour comparing # making sure that disallowed colours don't appear even by color offset. # if the disallowed colour appears with color offset = 2, try with color offset = 1. # if the problem persists, remove color offset if colorOffset == 2: for g in disallowed_colours: if g == finalID: colorOffset = 1 finalID = offset_list[winnerID][colorOffset] if colorOffset == 1: for g in disallowed_colours: if g == finalID: colorOffset = 0 finalID = offset_list[winnerID][colorOffset] # argument to ignore colorOffset if arg_colour_shift == False: finalID = winnerID # put the final pixel into the output picture imageOutput.putpixel((x,y),(finalID)) # crop the output image based on the threaded strip values # because indexed images don't have alpha, this makes it easier to combine them later cropped_imageOutput = imageOutput.crop((x_start, 0,x_end,i.height)) # put the palette data into the output cropped_imageOutput.putpalette(palette_data) # save the output image to temp folder (with individual parameter or without) if arg_individual_temp == True: cropped_imageOutput.save(outputFolder + 'temp/' + str(input_image) + '_' + str(thread_id) + '_8bpp.png') elif arg_individual_temp == False: cropped_imageOutput.save(outputFolder + 'temp/' + 'temp' + '_' + str(thread_id) + '_8bpp.png') # close the images # we won't need i anymore, and output will need to be loaded again later i.close() cropped_imageOutput.close() # function for combining the outputs of different threads def combineResults(args, thread_count, palette_data, arg_debug_level, arg_individual_temp, arg_auto_clean_temp): print('individual_temp = ' + str(arg_individual_temp)) # extract arguments from the big args list thread_id = args[0] combine_input_image = args[1] x_start = args[2] x_end = args[3] # print arguments for debug print_lvl_5(arg_debug_level, 'combineResults args: ' + str(args)) # get the final output resolution based on input image image_for_resolution = Image.open(inputFolder + str(combine_input_image[1]) + '.png') # create new empty input image with indexed colour final_image = Image.new('L', (image_for_resolution.width, image_for_resolution.height), color = 0) # put palette in the final output image final_image.putpalette(palette_data) # print the height of the output print_lvl_3(arg_debug_level, 'img_resolution.height = ' + str(image_for_resolution.height)) # args is a list of commands for the combiner for combine_order in args: # take the image we want to paste into the final output image if arg_individual_temp == True: image_to_paste = Image.open(outputFolder + 'temp/' + str(combine_order[1]) + '_' + str(combine_order[0]) + '_8bpp.png') elif arg_individual_temp == False: image_to_paste = Image.open(outputFolder + 'temp/' + 'temp' + '_' + str(combine_order[0]) + '_8bpp.png') # print which strip we are pasting print_lvl_3(arg_debug_level, 'Combine strip with x_start = ' + str(combine_order[2])) # paste the strip into the correct position of x_start final_image.paste(image_to_paste, box = (combine_order[2], 0)) # put the palette data in there again just to make sure (not sure which one is necessary, it works now) final_image.putpalette(palette_data) # save the final output image final_image.save(outputFolder + str(combine_input_image[1]) + '_8bpp.png') # optionally remove the temp files if arg_auto_clean_temp == True: for combine_order in args: if arg_individual_temp == True: os.remove(outputFolder + 'temp/' + str(combine_order[1]) + '_' + str(combine_order[0]) + '_8bpp.png') elif arg_individual_temp == False: os.remove(outputFolder + 'temp/' + 'temp' + '_' + str(combine_order[0]) + '_8bpp.png') # function for easy debug of constructing the long and rather unreadable offset list def check_list_count(arg_debug_level, list_to_check, n): #define some variable w = 0 for stuff in list_to_check: # add +1 to w for every item in the list w += 1 # print amount of items in the list(w), and n for easy comparison. They should be equal. print_lvl_5(arg_debug_level, 'Offset list item count: ' + str(w) + ' , last n is: ' + str(n)) # function to append only sub-lists instead of appending the whole list def append_offset_list(offset_list, temp_list): for t in temp_list: offset_list.append(t) # function to make a template for 6-index colours for index offset list def add_6_index_list(offset_list, n): temp_list = [ [ n, n+1, n+2],#1 [ n+1, n+2, n+3],#2 [ n+2, n+3, n+3],#3-- [ n+3, n+3, n+2],#4-- [ n+4, n+3, n+2],#5 [ n+5, n+4, n+3]# 6 ] append_offset_list(offset_list,temp_list) # function to make a template for 8-index colours for index offset list def add_8_index_list(offset_list, n): temp_list = [ [ n, n+1, n+2],#1 [ n+1, n+2, n+3],#2 [ n+2, n+3, n+4],#3 [ n+3, n+4, n+4],#4-- [ n+4, n+4, n+3],#5-- [ n+5, n+4, n+3],#6 [ n+6, n+5, n+4],#7 [ n+7, n+6, n+5]# 8 ] append_offset_list(offset_list,temp_list) # function to make a template for 10-index colours for index offset list def add_10_index_list(offset_list, n): temp_list = [ [ n, n+1, n+2],#1 [ n+1, n+2, n+3],#2 [ n+2, n+3, n+4],#3 [ n+3, n+4, n+5],#4 [ n+4, n+5, n+5],#5-- [ n+5, n+5, n+4],#6-- [ n+6, n+5, n+4],#7 [ n+7, n+6, n+5],#8 [ n+8, n+7, n+6],#9 [ n+9, n+8, n+7]#10 ] append_offset_list(offset_list,temp_list) def run(): # define thread count thread_count = options['thread_count'] # define what should the queue for rgb2palette include job_list = [ [ options['input_name'], options['allowed_colour_types'],#allowed colour types (string list) options['disallowed_colour_types'],#disallowed colour types (string list) options['allowed_colour_indexes'],#allowed colour indexes (number list) options['disallowed_colour_indexes'],#disallowed colour indexes (number list) options['alpha_ignore'], options['alpha_offset_2'], options['alpha_offset_1'], options['red_weight'],#red weight (number, default = 1) options['green_weight'],#green weight (number, default = 1) options['blue_weight'],#blue weight (number, default = 1) options['colour_shift'], options['debug_level'], options['individual_temp'] ] ] # job_list-related stuff to be continued later down after creating offset list... #-------------------------------------------------------------------------------- #creating offset list ----------------------------------------------------------- #-------------------------------------------------------------------------------- offset_list = [] #TRANSPARENCY BLUE n = 0 check_list_count(options['debug_level'], offset_list, n) temp_list = [ [n,n,n] ] append_offset_list(offset_list,temp_list) #GRAYSCALE n = 1 check_list_count(options['debug_level'], offset_list, n) temp_list = [ [ n, n+1, n+2],#1 [ n+1, n+2, n+3],#2 [ n+2, n+3, n+4],#3 [ n+3, n+4, n+5],#4 [ n+4, n+5, n+6],#5 [ n+5, n+6, n+7],#6 [ n+6, n+7, n+7],#7-- [ n+7, n+8, n+8],#8-- [ n+8, n+8, n+7],#9-- [ n+9, n+8, n+7],#10 [n+10, n+9, n+8],#11 [n+11, n+10, n+9],#12 [n+12, n+11, n+10],#13 [n+13, n+12, n+11],#14 [n+14, n+13, n+12],#15 ] append_offset_list(offset_list, temp_list) #METAL n = 16 check_list_count(options['debug_level'], offset_list, n) add_8_index_list(offset_list, n) #LIME_GREEN n = 24 check_list_count(options['debug_level'], offset_list, n) add_8_index_list(offset_list, n) #BEIGE n = 32 check_list_count(options['debug_level'], offset_list, n) add_8_index_list(offset_list, n) #DARK_PINK n = 40 check_list_count(options['debug_level'], offset_list, n) add_10_index_list(offset_list, n) #YELLOW n = 50 check_list_count(options['debug_level'], offset_list, n) temp_list = [ [ n, 189, 188],#1 [ n+1, n+0, 188],#2 [ n+2, n+1, n+0]# 3 ] append_offset_list(offset_list, temp_list) #DARK_BEIGE n = 53 check_list_count(options['debug_level'], offset_list, n) temp_list = [ [ n, n+1, n+2],#1 [ n+1, n+2, n+3],#2 [ n+2, n+3, n+3],#3-- [ n+3, n+3, n+4],#4-- [ n+4, n+3, n+3],#5-- [ n+5, n+4, n+3],#6 [ n+6, n+5, n+4]# 7 ] append_offset_list(offset_list, temp_list) #YELLOW n = 60 check_list_count(options['debug_level'], offset_list, n) add_10_index_list(offset_list, n) #BROWN_1 n = 70 check_list_count(options['debug_level'], offset_list, n) add_10_index_list(offset_list, n) #CC2, n = 80 check_list_count(options['debug_level'], offset_list, n) add_8_index_list(offset_list, n) #DARK_GREEN n = 88 check_list_count(options['debug_level'], offset_list, n) add_8_index_list(offset_list, n) #PALE_GREEN n = 96 check_list_count(options['debug_level'], offset_list, n) add_8_index_list(offset_list, n) #BROWN_2 n = 104 check_list_count(options['debug_level'], offset_list, n) add_8_index_list(offset_list, n) #BROWN_3 n = 112 check_list_count(options['debug_level'], offset_list, n) add_10_index_list(offset_list, n) #BROWN_4 n = 122 check_list_count(options['debug_level'], offset_list, n) add_6_index_list(offset_list, n) #MAUVE n = 128 check_list_count(options['debug_level'], offset_list, n) add_8_index_list(offset_list, n) #PURPLE n = 136 check_list_count(options['debug_level'], offset_list, n) add_8_index_list(offset_list, n) #BLUE n = 144 check_list_count(options['debug_level'], offset_list, n) add_10_index_list(offset_list, n) #LIGHT_BLUE n = 154 check_list_count(options['debug_level'], offset_list, n) add_8_index_list(offset_list, n) #PINK n = 162 check_list_count(options['debug_level'], offset_list, n) add_8_index_list(offset_list, n) #LIGHT_PURPLE n = 170 check_list_count(options['debug_level'], offset_list, n) add_8_index_list(offset_list, n) #RED n = 178 check_list_count(options['debug_level'], offset_list, n) temp_list = [ [ n, n+1, n+2],#1 [ n+1, n+2, n+3],#2 [ n+2, n+3, n+4],#3 [ n+3, n+4, n+5],#4 [ n+4, n+5, n+6],#5 [ n+5, n+6, n+7],#6 [ n+6, n+7, n+7],#7-- [ n+7, n+6, n+6],#8-- [ n+8, n+7, n+6],#9 [ n+9, n+8, n+7],#10 [n+10, n+9, n+8],#11 [n+11, n+10, n+9],#12 [n+12, n+11, n+10],#13 [n+13, n+12, n+11]#14 ] append_offset_list(offset_list, temp_list) #ORANGE n = 192 check_list_count(options['debug_level'], offset_list, n) temp_list = [ [ n, 64, 63],#1 [ n+1, n+0, 64],#2 [ n+2, n+1, n+0],#3 [ n+3, n+2, n+1],#4 [ n+4, n+3, n+2],#5 [ n+5, n+4, n+3]#6 ] append_offset_list(offset_list, temp_list) #CC1 n = 198 check_list_count(options['debug_level'], offset_list, n) add_8_index_list(offset_list, n) #GREEN n = 206 check_list_count(options['debug_level'], offset_list, n) temp_list = [ [ n, 93, 92],#1 [ n+1, n+0, 93],#2 [ n+2, n+1, n+0],#3 [ n+3, n+2, n+1]# 4 ] append_offset_list(offset_list, temp_list) #CYAN n = 210 check_list_count(options['debug_level'], offset_list, n) temp_list = [ [ n, n+1, n+2],#1 [n+1, n+2, n+2],#2 [n+2, n+3, n+3],#3 [n+3, n+2, n+2],#4 [n+4, n+3, n+2]# 5 ] append_offset_list(offset_list, temp_list) #ALPHAPINK & ACT n = 215 check_list_count(options['debug_level'], offset_list, n) temp_list = [ [ n+0, n+0, n+0],#1 - index 215 [ n+1, n+1, n+1],#2 - index 216 [ n+2, n+2, n+2],#3 - index 217 [ n+3, n+3, n+3],#4 - index 218 [ n+4, n+4, n+4],#5 - index 219 [ n+5, n+5, n+5],#6 - index 220 [ n+6, n+6, n+6],#7 - index 221 [ n+7, n+7, n+7],#8 - index 222 [ n+8, n+8, n+8],#9 - index 223 [ n+9, n+9, n+9],#10 - index 224 [ n+10, n+10, n+10],#11 - index 225 [ n+11, n+11, n+11],#12 - index 226 [ n+12, n+12, n+12],#13 - index 227 [ n+13, n+13, n+13],#14 - index 228 [ n+14, n+14, n+14],#15 - index 229 [ n+15, n+15, n+15],#16 - index 230 [ n+16, n+16, n+16],#17 - index 231 [ n+17, n+17, n+17],#18 - index 232 [ n+18, n+18, n+18],#19 - index 233 [ n+19, n+19, n+19],#20 - index 234 [ n+20, n+20, n+20],#21 - index 235 [ n+21, n+21, n+21],#22 - index 236 [ n+22, n+22, n+22],#23 - index 237 [ n+23, n+23, n+23],#24 - index 238 [ n+24, n+24, n+24],#25 - index 239 [ n+25, n+25, n+25],#26 - index 240 [ n+26, n+26, n+26],#27 - index 241 [ n+27, n+27, n+27],#28 - index 242 [ n+28, n+28, n+28],#29 - index 243 [ n+29, n+29, n+29],#30 - index 244 [ n+30, n+30, n+30],#31 - index 245 [ n+31, n+31, n+31],#32 - index 246 [ n+32, n+32, n+32],#33 - index 247 [ n+33, n+33, n+33],#34 - index 248 [ n+34, n+34, n+34],#35 - index 249 [ n+35, n+35, n+35],#36 - index 250 [ n+36, n+36, n+36],#37 - index 251 [ n+37, n+37, n+37],#38 - index 252 [ n+38, n+38, n+38],#39 - index 253 [ n+39, n+39, n+39],#40 - index 254 [ n+40, n+40, n+40] #41 - index 255 ] append_offset_list(offset_list, temp_list) # final check n = 256 check_list_count(options['debug_level'], offset_list, n) # continuation of job_list-related stuff from before creating offset list... for job in job_list: # defining lists just so they exist all_jobs = [] job_chunks = [] queue = [] # variable for adding a pixel to some threads in case full image result can't be divided by thread count extra = 0 # open image chunk_image = Image.open(inputFolder + job[0] + '.png') # get their average size, rounded down chunk_average_size = math.floor(chunk_image.width/thread_count) # get the remaining pixel columns after rounded down division chunk_modulo_size = chunk_image.width % thread_count # get x-axis starts and ends of strips for thread in range(0, thread_count): start = chunk_average_size * thread end = (chunk_average_size * (thread+1)) # add extra pixels in case full image width can't be divided by thread count start += extra if thread < chunk_modulo_size: extra += 1 end += extra # define a list to append into a big chunk of jobs for 1 task # (not utilized anymore but can be if parameters are overridden # and everything is launched from python with changing the options from here) job_chunk_list = [ thread, #0 - threadID job[0], #1 - input_name start, #2 - x_start end, #3 - x_end job[1], #4 - allowed_colour_types job[2], #5 - disallowed_colour_types job[3], #6 - allowed_colour_indexes job[4], #7 - disallowed_colour_indexes job[5], #8 - alpha_ignore job[6], #9 - alpha_offset_1 job[7], #10- alpha_offset_2 job[8], #11- red_weight job[9], #12- green_weight job[10],#13- blue_weight job[11],#14- colour_shift job[12],#15- debug_level offset_list,#16 options['individual_temp']#17 ] # append the job chunk list in to job chunks job_chunks.append(job_chunk_list) # append the list of job pieces into queue queue.append( [thread, job[0], start, end, job[1], job[2], job[3], job[4], job[5], job[6], job[7], job[8], job[9], job[10], job[11], job[12], offset_list, options['individual_temp'] ]) # append the job chunks into all jobs all_jobs.append(job_chunks) # go through all jobs and print what they are about to do for a_job in all_jobs: print_lvl_5(options['debug_level'], '-'*32) thread_id = 0 for b_thread in a_job: if thread_id == 0: thread_id += 1 print_lvl_5(options['debug_level'], 'Job: ' + str(b_thread[0])) print_lvl_5(options['debug_level'], ' '*10 + 'Start, ' + 'End') print_lvl_5(options['debug_level'], 'Thread ' + str(thread_id) + ': ' + str(b_thread[1]) + ', ' + str(b_thread[2]))# # print the whole queue (debug level 7, this thing is LONG) print_lvl_7(options['debug_level'], (queue)) # ---------------------------------------------------------------------------------------------------------------- # M U L T I T H R E A D E D P R O C E S S S T A R T # ---------------------------------------------------------------------------------------------------------------- # pool used on rgb2palette pool = multiprocessing.Pool(processes = thread_count) pool.map(rgb2palette, queue) pool.close() pool.join() # ---------------------------------------------------------------------------------------------------------------- # M U L T I T H R E A D E D P R O C E S S E N D # ---------------------------------------------------------------------------------------------------------------- # combine the results of the individual threads combineResults(queue, thread_count, palette_data, options['debug_level'], options['individual_temp'], options['auto_clean_temp']) # finished time for timestamp tx = time.time() # format the time. I forgot how this works but it does. finishedTime = datetime.datetime.fromtimestamp(tx).strftime('%H:%M:%S') # elapsed time for timestamp te = tx - tt # format the elapsed time. I forgot how this works but it does. elapsedTime = str(datetime.timedelta(seconds=int(te))) # print the time it took to do all this mess print('Started: ' + startedTime) print('Finished: ' + finishedTime) print('-'*18) print('Elapsed: ' + elapsedTime) if __name__ == '__main__': # define arguments/parameters parser = argparse.ArgumentParser(description = 'Process some arguments.') parser.add_argument('-t','--thread_count', help='Number of theads to run, Default: 16', type = int, required = False) parser.add_argument('-n','--input_name', help='File to process. Without .png extension. File can only be RGBA (not RGB)', required = True) parser.add_argument('-e','--allowed_colour_types', help='Allowed colour types (list of strings), Default: "ALL"', nargs = '+', required = False) parser.add_argument('-f','--disallowed_colour_types', help='Disallowed colour types (list of strings), Default: nothing', nargs = '+', required = False) parser.add_argument('-i','--allowed_colour_indexes', help='Allowed colour indexes (list of numbers), Default: nothing', nargs = '+', type = int, required = False) parser.add_argument('-y','--disallowed_colour_indexes', help='Disallowed colour indexes (list of numbers), Default: nothing', nargs = '+', type = int, required = False) parser.add_argument('-a','--alpha_ignore', help='Threshold of ignoring transparency. Default: 128', type = int, required = False) parser.add_argument('-o','--alpha_offset_1', help='Threshold of transparency to colour shift by 1 index. Default: 178', type = int, required = False) parser.add_argument('-p','--alpha_offset_2', help='Threshold of transparency to colour shift by 2 indexes. Default: 230', type = int, required = False) parser.add_argument('-r','--red_weight', help='Weight of red input for colour comparing. Default: 1', type = float, required = False) parser.add_argument('-g','--green_weight', help='Weight of green input for colour comparing. Default: 1', type = float, required = False) parser.add_argument('-b','--blue_weight', help='Weight of blue input for colour comparing. Default: 1', type = float, required = False) parser.add_argument('-c','--colour_shift', help='For semi-transparent pixels, shifts index to attempt to compensate alpha. Default: True', type = bool, required = False) parser.add_argument('-d', '--debug_level', help='Amount of info shown in console. Default: 1, Min: 1, Max: 7', type = int, required = False) parser.add_argument('-z', '--individual_temp', help='Filenames in temp folder are unique for each input filename, to allow running on multiple inputs at the same time. (Temp folder can get larger)', type = bool, required = False) parser.add_argument('-x', '--auto_clean_temp', help='Automatically remove temp files after results are combined.', type = bool, required = False) options = vars(parser.parse_args()) # defaults for parameters default_values = [ ('thread_count', 16), ('allowed_colour_types', ['ALL']), ('disallowed_colour_types', []), ('allowed_colour_indexes', []), ('disallowed_colour_indexes', []), ('alpha_ignore', 128), ('alpha_offset_1', 178), ('alpha_offset_2', 230), ('red_weight', 1), ('green_weight', 1), ('blue_weight', 1), ('colour_shift', False), ('debug_level', 1), ('individual_temp', False), ('auto_clean_temp', False), ] for name, def_value in default_values: if not options[name]: options[name] = def_value run()