MLShax
MLShax

Reputation: 13

How to further optimize this python script for a custom image filter?

I'm attempting to create a custom image filter which takes in an image and generates an image which uses every RGB color by finding the (approximate) closest unused color. I've optimized it as much as I can think of for now, but I estimate it will still take over 40 hours to complete one (4096x4096) image as is, hoping someone else will have ideas. This is the current function which finds the (approximate) closest available color given the next pixel:

def getColor(pixelVal, colorArray):
    
    for distance in range(256):
        if distance < 10:
            distanceArray = colorArray[max(pixelVal[0]-distance,0):min(pixelVal[0]+distance+1,255), max(pixelVal[1]-distance,0):min(pixelVal[1]+distance+1,255), max(pixelVal[2]-distance,0):min(pixelVal[2]+distance+1,255)]

            available = distanceArray[distanceArray[..., 3] == 1]
            if (len(available) > 0):
                return available[0]
        else:
            pixelRMin = max(pixelVal[0]-distance,0)
            pixelRMax = min(pixelVal[0]+distance+1,255)
            pixelGMin = max(pixelVal[1]-distance,0)
            pixelGMax = min(pixelVal[1]+distance+1,255)
            pixelBMin = max(pixelVal[2]-distance,0)
            pixelBMax = min(pixelVal[2]+distance+1,255)
            
            array1 = colorArray[pixelRMin:pixelRMax, pixelGMin:pixelGMax, pixelBMin:pixelBMin+1]
            available = array1[array1[..., 3] == 1]
            if (len(available) > 0):
                return available[0]
            
            array2 = colorArray[pixelRMin:pixelRMax, pixelGMin:pixelGMax, pixelBMax-1:pixelBMax]
            available = array2[array2[..., 3] == 1]
            if (len(available) > 0):
                return available[0]
            
            array3 = colorArray[pixelRMin:pixelRMax, pixelGMin:pixelGMin+1, pixelBMin:pixelBMax]
            available = array3[array3[..., 3] == 1]
            if (len(available) > 0):
                return available[0]
            
            array4 = colorArray[pixelRMin:pixelRMax, pixelGMax-1:pixelGMax, pixelBMin:pixelBMax]
            available = array4[array4[..., 3] == 1]
            if (len(available) > 0):
                return available[0]
            
            array5 = colorArray[pixelRMin:pixelRMin+1, pixelGMin:pixelGMax, pixelBMin:pixelBMax]
            available = array5[array5[..., 3] == 1]
            if (len(available) > 0):
                return available[0]
            
            array6 = colorArray[pixelRMax-1:pixelRMax, pixelGMin:pixelGMax, pixelBMin:pixelBMax]
            available = array6[array6[..., 3] == 1]
            if (len(available) > 0):
                return available[0]

It searches an expanding cube for a color that has not been used yet from a 4d array (R, G, B, and available boolean), eventually switching to only search the shell of the cube (at a distance of 10 which after some testing seems to be optimal).

Here are the results for the first 10%:

Enter image description here

Thanks!

Edit: Full Example:

from PIL import Image
import numpy as np
import time

start = "top" #center, bottom, left, right
direction = "rows" #columns, spiral
method = "pixel" #color (pixel does each pixel in sequence, color does each distance in sequence)

def getColorArray():
    colorArray = np.zeros((256, 256, 256, 4), dtype=int)
    for r in range(256):
        for g in range(256):
            for b in range(256):
                colorArray[r, g, b] = (r, g, b, 1)
    return colorArray
    
def getColor(pixelVal, colorArray):
    
    for distance in range(256):
        if distance < 10:
            distanceArray = colorArray[max(pixelVal[0]-distance,0):min(pixelVal[0]+distance+1,255), max(pixelVal[1]-distance,0):min(pixelVal[1]+distance+1,255), max(pixelVal[2]-distance,0):min(pixelVal[2]+distance+1,255)]

            available = distanceArray[distanceArray[..., 3] == 1]
            if (len(available) > 0):
                return available[0]
        else:
            pixelRMin = max(pixelVal[0]-distance,0)
            pixelRMax = min(pixelVal[0]+distance+1,255)
            pixelGMin = max(pixelVal[1]-distance,0)
            pixelGMax = min(pixelVal[1]+distance+1,255)
            pixelBMin = max(pixelVal[2]-distance,0)
            pixelBMax = min(pixelVal[2]+distance+1,255)
            
            array1 = colorArray[pixelRMin:pixelRMax, pixelGMin:pixelGMax, pixelBMin:pixelBMin+1]
            available = array1[array1[..., 3] == 1]
            if (len(available) > 0):
                return available[0]
            
            array2 = colorArray[pixelRMin:pixelRMax, pixelGMin:pixelGMax, pixelBMax-1:pixelBMax]
            available = array2[array2[..., 3] == 1]
            if (len(available) > 0):
                return available[0]
            
            array3 = colorArray[pixelRMin:pixelRMax, pixelGMin:pixelGMin+1, pixelBMin:pixelBMax]
            available = array3[array3[..., 3] == 1]
            if (len(available) > 0):
                return available[0]
            
            array4 = colorArray[pixelRMin:pixelRMax, pixelGMax-1:pixelGMax, pixelBMin:pixelBMax]
            available = array4[array4[..., 3] == 1]
            if (len(available) > 0):
                return available[0]
            
            array5 = colorArray[pixelRMin:pixelRMin+1, pixelGMin:pixelGMax, pixelBMin:pixelBMax]
            available = array5[array5[..., 3] == 1]
            if (len(available) > 0):
                return available[0]
            
            array6 = colorArray[pixelRMax-1:pixelRMax, pixelGMin:pixelGMax, pixelBMin:pixelBMax]
            available = array6[array6[..., 3] == 1]
            if (len(available) > 0):
                return available[0]

    
def genImage(inputPath, outputPath):
    inputImage = Image.open(inputPath)
    inputArray = np.array(inputImage)
    
    colorArray = getColorArray()
    
    outputArray = np.zeros_like(inputArray)
    
    num = 0
    pct = 0
    start = time.time()
    last = time.time()
    print(len(colorArray[colorArray[..., 3] == 1]))
    for i in range(inputArray.shape[0]):
        for j in range(inputArray.shape[1]):
            pixelVal = tuple(inputArray[i, j])
            
            color = getColor(pixelVal, colorArray)
            if (color is None):
                print(pixelVal)
                print(len(colorArray[colorArray[..., 3] == 1]))
            
            outputArray[i, j] = (color[0], color[1], color[2])
            colorArray[color[0], color[1], color[2]] = (color[0], color[1], color[2], 0)
            
        if (num > 41):
            pct += 1
            now = time.time()
            print(str(pct) + "%: " + str(round((now - start), 2)) + " seconds total, " + str(round((now - last), 2)) + " seconds for this percent")
            last = now
            
            outputImage = Image.fromarray(outputArray)
            outputImage.save(outputPath)
            
            num = 0
        num += 1
    
    outputImage = Image.fromarray(outputArray)
    outputImage.save(outputPath)
            
inputPath = "input_image.jpg"
outputPath = "output_image.jpg"
genImage(inputPath, outputPath)

Upvotes: 0

Views: 158

Answers (0)

Related Questions