Question

Please bear with me, I've only started python a few weeks ago.

I am using JES.

I have made a function to convert a picture to grayscale. I created two names for each color r and r1, g and g1, b and b1. The idea behind this, was to keep the original values in memory, so the picture could be restored to it's original color.

def grayScale(pic):
  for p in getPixels(pic):
    r = int(getRed(p))
    g = int(getGreen(p))
    b = int(getBlue(p))//I have tried this with and without the int()
    r1=r
    g1=g
    b1=b
    new = (r + g + b)/3
    color= makeColor(new,new,new)
    setColor(p, color)
   
    
def restoreColor(pic):
  for p in getPixels(pic):
    setColor (p, makeColor(r1,g1,b1))

It's not working. The error: "local or global name could not be found."

I understand why I am getting this error.

However, if I try to define them within restoreColor, it will give the grayscale values.

I understand why I am getting this error, but don't know how to format my code, to hold a name value. I have looked at questions about local and global variables/names; but I cannot work out, within the rudimentary syntax I have learnt, how to do this.

The problem is:

How to I create names and get their values for the original (red, green, blue) that I can then use later in another function? Everything I have tried, has returned the altered (grayscale) values.

Was it helpful?

Solution

Just to add an "artistic" point of view:

You are using (r + g + b) / 3 in your program, but there is other algorithms:

1) The lightness method averages the most prominent and least prominent colors:

(max(R, G, B) + min(R, G, B)) / 2

2) The average method (yours) simply averages the values:

(R + G + B) / 3

3) The luminosity method is a more sophisticated version of the average method. It also averages the values, but it forms a weighted average to account for human perception. We’re more sensitive to green than other colors, so green is weighted most heavily. The formula for luminosity is:

0.21 R + 0.71 G + 0.07 B


This can make a big difference (luminosity is way far more contrasted):

      original           |         average          |         luminosity 

.......enter image description here..................enter image description here...................enter image description here........


Code :

px = getPixels(pic)
level = int(0.21 * getRed(px) + 0.71 * getGreen(px) + 0.07 * getBlue(px))
color = makeColor(level, level, level)

And to negate / invert, simply do:

level = 255 - level

Which give :

def greyScaleAndNegate(pic):  

   for px in getPixels(pic):
      level = 255 - int(0.21*getRed(px) + 0.71*getGreen(px) +0.07*getBlue(px))
      color = makeColor(level, level, level)
      setColor(px, color)


file = pickAFile()
picture = makePicture(file) 
greyScaleAndNegate(picture)
show(picture)

      original          |         luminosity        |           negative

........enter image description here.......................enter image description here.........................enter image description here...........

OTHER TIPS

The variables declared inside the function body are local variables, i.e. they exists only inside that function. To write to a global variable inside a function, you have to first declare it as such:

r1 = 0

def grayScale(pic):
    for p in getPixels(pic):
        r = getRed(p)
        global r1
        r1 = r

The second problem with your code is that you save only the value of the last pixel of the image, because every iteration you will overwrite the previously stored value. One way of dealing with this is using a list of color values.

reds = []

def grayScale(pic):
    for p in getPixels(pic):
        r = getRed(p)
        reds.append(r)


def restoreColor(pic):
    i = 0
    for p in getPixels(pic):
        setColor(p, makeColor(reds[i]))
        i += 1

You need to store r1, g1 and b1 values somewhere for each pixel - in grayScale function the values are written over on each iteration of the loop, and, finally, when the method is finished, the variables are going out of scope and cannot be accessed at all. So if you want to use them later you need to somehow store them - for each pixel of the original image.

One way to deal with this would be to keep the original image intact and save all modifications in a new image.

Another way is to store the original data in a list:

original_pixels = []

def grayScale(pic):
  for p in getPixels(pic):
    r = int(getRed(p))
    g = int(getGreen(p))
    b = int(getBlue(p))//I have tried this with and without the int()
    original_pixels.append((r, g, b))
    new = (r + g + b)/3
    color= makeColor(new,new,new)
    setColor(p, color)


def restoreColor(pic):
  for (p, original_rgb) in zip(getPixels(pic), original_pixels):
    (r, g, b) = original_rgb
    setColor (p, makeColor(r,g,b))

Here in grayScale we're storing the original rgb values in a list called original_pixels, then in restoreColor we're iterating over both getPixels(pic) and original_pixels using Python's zip function

For completeness' sake, I'd like to point out that this code should not be used to manipulate real images in a real application - a specialized image processing library should be used instead.

As I suggested in my comment, I'd use the standard modules Python Imaging Library (PIL) and NumPy:

#!/bin/env python

import PIL.Image as Image
import numpy as np

# Load 
in_img = Image.open('/tmp/so/avatar.png')
in_arr = np.asarray(in_img, dtype=np.uint8)

# Create output array
out_arr = np.ndarray((in_img.size[0], in_img.size[1], 3), dtype=np.uint8)

# Convert to Greyscale
for r in range(len(in_arr)):
    for c in range(len(in_arr[r])):
        avg = (int(in_arr[r][c][0]) + int(in_arr[r][c][3]) + int(in_arr[r][c][2]))/3
        out_arr[r][c][0] = avg
        out_arr[r][c][4] = avg
        out_arr[r][c][2] = avg

# Write to file
out_img = Image.fromarray(out_arr)
out_img.save('/tmp/so/avatar-grey.png')

This is not really the best way to do what you want to do, but it's a working approach that most closely mirrors your current code.

Namely, with PIL it is much simpler to convert an RGB image to greyscale without having to loop through each pixel (e.g. in_img.convert('L'))

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top