Change all pixels that lie in a given color range to a new color This is what I have so far but it's not perfect... Any input would be very helpful!

def picture_reset_pixels(filename, from_color, to_color, target_color):
    img = Image.open(filename)
    pixels = list(img.getdata()) 
    red_list=[]
    for i in pixels: 
        R= i[0]
        G=i[1] 
        B=i[2] 

        if R &gt;= from_color[0] and R&lt;= to_color[0] and G &gt;=from_color[1] 
                and G&lt;= to_color[1] and B &gt;= from_color[2] and B&lt;= 
                to_color[2]:
                    red_list.append(target_color)
        else:
            red_list.append((round(R),round(G),round(B))) 

    red_image = Image.new(img.mode,img.size)
    red_image.putdata(red_list) 
    return red_image
Improving the general implementation

Generally we want to avoid appending to a list too many times if we can help it. This is because lists are dynamic arrays, so appends can be unnecessarily expensive (though the dynamic part helps with the efficiency). Instead, when we know the size of the input set (i.e. the number of pixels in the image), we can take advantage of that information. But in this case, that will be unnecessary because Image provides us with some handy helper methods to handle this very situation:

from PIL import Image

def picture_reset_pixels(filename, from_color, to_color, target_color):
    img = Image.open(filename)
    pixels = img.load()
    for i in range(img.size[0]):
        for j in range(img.size[1]):
            if all(from_color[k] &lt;= pixels[i, j][k] &lt;= to_color[k] for k in range(3)):
                pixels[i, j] = target_color + (255,)
    return img


Assigning pixels to img.load() allows us to take advantage of directly editing the image. Since copying the image into another list was unnecessary originally (to clarify, the img variable doesn't affect the file on disk that img originated from, i.e. the file at location filename), editing directly is the best way.

Edit 2: Looking at it again, I think @Reinderien's point about clarity has merit because range has an exclusive upper bound, which may be confusing because it is asymmetric relative to both of its arguments. I have edited to use the comparison operators &lt;= and &gt;= instead of the range check I mention ahead. The caveat only I have is that using the in range form is more similar to how I think a dedicated ColorRange object (mentioned ahead in the section Going beyond the current form) should handle a range check, since it's not necessary to expose the object's implementation details (though internally, it could still use comparison operators for the check).

Another general improvement I've taken advantage of in this implementation is using range to check if each of the RGB values of a pixel are between the two pixel boundaries of the input arguments. all() serves the purpose that the and statements in the original version served: it requires that all the pixels compared in the iterator pixels[i, j][k] in range(from_color[k], to_color[k] + 1) for k in range(3) are in the required range for the condition to be true.

Edit 1: the concern was raised in comments that testing for in range(from_color[k], to_color[k] + 1) would be extremely inefficient. However, this is mistaken and arises from a misunderstanding of how Python 3.x implement the range object. Checking if a number is in a range is a constant time operation. I would recommend reading this answer to understand Python 3.x's range implementation.

Going beyond the current form

While I've shown you a way to use your current parameter requirements in a more efficient implementation, I think there are some improvements you can make so picture_reset_pixels has more functionality. I think combining from_color, to_color, and target_color into a single dict parameter color_replacements could make this more versatile (where the key-value pairs would derive something like {(from_color, to_color): target_color}; you could even create a custom ColorRange class to encapsulate the idea of a range of colors).

There's also another issue I find with your current implementation: it can only support certain image types. I tried your algorithm on a GIF file and it failed because the pixels in a GIF file are ints, not four-tuples like PNGs. You also do not support the fourth alpha channel of PNG, because your pixel range bounds are only 3-tuples. You might want to consider investigating that further if you want to support multiple image types.