4

I can't seem to figure out how to take my grayscale function and change it to give me false color. I know I need to break each color (R,G,B) into ranges and then assign colors based on the range for each color. Does anyone have any idea how this can work?

def grayscale(pic):
    (width,height) = pic.size
    for y in range (height):
        for x in range(width):
            pix = cp.getpixel((x,y))
            (r, g, b) = pix
            avg = (r + g + b)//3
            newPix = (avg, avg, avg)
            cp.putpixel((x,y),newPix)
    return cp
martineau
  • 119,623
  • 25
  • 170
  • 301
Nick Merz
  • 51
  • 1
  • 2
  • What is `cp`? What sort of effect do you want at the end — can you give an example? Are [this](http://stackoverflow.com/questions/16546862/color-over-grayscale-image?rq=1) or [this](http://stackoverflow.com/questions/7521058/colorize-grayscale-image?rq=1) related/helpful (even though not Python)? – Matt Hall Jul 20 '15 at 01:10
  • 1
    Sounds like what you really want to do is convert a color image to a false-color one (which somehow involves finding the average of the three components of the original color of each pixel). What false color scheme do you want to use? Describe the mapping of rgb -> rgb color. – martineau Jul 20 '15 at 02:09
  • 1
    It sounds like you want to give each unique intensity of your image a unique colour. What you are actually doing is simply computing the rough luminance of each RGB pixel which isn't the right approach. Also, what you have described in your problem statement is **uniform quantization** which converts an image with a full colour palette to a smaller one. Your question title and problem statement are conflicting... consider editing to ensure consistency. – rayryeng Jul 20 '15 at 04:38
  • I apologize it is my first time using this site. Cp = "Image".copy(). What I need to do is out of 255, I need to break that into chunks and assign each chunk (ex: 0-31 = red) a color scheme. Therefore the pixels within that range will turn to that color. – Nick Merz Jul 20 '15 at 15:17
  • OK, that's a little better description. How many chunks/colors will there be and how will they specified? – martineau Jul 21 '15 at 11:35

2 Answers2

6

Since you never answered my final follow-on question in the comments, I've made a few guesses and implemented something to illustrate how it might be done using only the PIL (or pillow) module.

In a nutshell, the code converts the image a grayscale, divides up the resulting 0% to 100% luminosity (intensity) pixel range into equally sized sub-ranges, and then assigns a color from a palette to each of them.

from PIL import Image
from PIL.ImageColor import getcolor, getrgb
from PIL.ImageOps import grayscale

try:
    xrange
except NameError:  # Python 3.
    xrange = range

def falsecolor(src, colors):
    if Image.isStringType(src):  # File path?
        src = Image.open(src)
    if src.mode not in ['L', 'RGB', 'RGBA']:
        raise TypeError('Unsupported source image mode: {}'.format(src.mode))
    src.load()

    # Create look-up-tables (luts) to map luminosity ranges to components
    # of the colors given in the color palette.
    num_colors = len(colors)
    palette = [colors[int(i/256.*num_colors)] for i in xrange(256)]
    luts = (tuple(c[0] for c in palette) +
            tuple(c[1] for c in palette) +
            tuple(c[2] for c in palette))

    # Create grayscale version of image of necessary.
    l = src if Image.getmodebands(src.mode) == 1 else grayscale(src)

    # Convert grayscale to an equivalent RGB mode image.
    if Image.getmodebands(src.mode) < 4:  # Non-alpha image?
        merge_args = ('RGB', (l, l, l))  # RGB version of grayscale.

    else:  # Include copy of src image's alpha layer.
        a = Image.new('L', src.size)
        a.putdata(src.getdata(3))
        luts += tuple(xrange(256))  # Add a 1:1 mapping for alpha values.
        merge_args = ('RGBA', (l, l, l, a))  # RGBA version of grayscale.

    # Merge all the grayscale bands back together and apply the luts to it.
    return Image.merge(*merge_args).point(luts)

if __name__ == '__main__':
    R, G, B = (255,   0,   0), (  0, 255,   0), (  0,   0, 255)
    C, M, Y = (  0, 255, 255), (255,   0, 255), (255, 255,   0)
    filename = 'test_image.png'

    # Convert image into falsecolor one with 4 colors and display it.
    falsecolor(filename, [B, R, G, Y]).show()

Below is a composite showing an RGB test image, the intermediate internal 256-level grayscale image, and the final result of changing that into a false color one comprised of only four colors (each representing a range of 64 levels of intensity):

before, intermediate, and after images

Here's another composite, only this time showing the conversion of an image that's already grayscale into the same palette of 4 false colors.

example of grayscale to false color image conversion

Is something like this what you're wanting to do?

martineau
  • 119,623
  • 25
  • 170
  • 301
1

It looks like that all you want to do is to determine the average brightness per pixel, and make each pixel grey. I would use either native Image functionality for that, or if you want to manipulate individual pixels, at least use numpy for that instead of a nested for loop. Example:

from PIL import Image, ImageDraw
import numpy as np

def makeGrey():
    W = 800
    H = 600
    img = Image.new('RGB', (W, H))
    draw = ImageDraw.Draw(img)

    draw.rectangle((0, H * 0 / 3, W, H * 1 / 3), fill=(174, 28, 40), outline=None)
    draw.rectangle((0, H * 1 / 3, W, H * 2 / 3), fill=(255, 255, 255), outline=None)
    draw.rectangle((0, H * 2 / 3, W, H * 3 / 3), fill=(33, 70, 139), outline=None)

    img.show()

    pixels = np.array(img.getdata())
    avg_pixels = np.sum(pixels, axis=1) / 3
    grey_img = avg_pixels.reshape([H, W])

    img2 = Image.fromarray(grey_img)
    img2.show()

if __name__ == '__main__':
    makeGrey()
physicalattraction
  • 6,485
  • 10
  • 63
  • 122