-2

I'm supposed to write a method that converts an RGB image to Grayscale by using the "average method" where I take the average of the 3 colors (not the weighted method or luminosity method). I then must display the original RGB image and grayscale image next to each other (concatenated). The language I'm writing in is Python. This is what my code looks like currently.

import numpy as np
import cv2

def getRed(redVal):
    return '#%02x%02x%02x' % (redVal, 0, 0)

def getGreen(greenVal):
    return '#%02x%02x%02x' % (0, greenVal, 0)

def getBlue(blueVal):
    return '#%02x%02x%02x' % (0, 0, blueVal)

# Grayscale = (R + G + B / 3)
# For each pixel,
# 1- Get pixels red, green, and blue
# 2- Calculate the average value
# 3- Set each of red, green, and blue values to average value

def average_method(img):
    for p in img:
        red = p.getRed()
        green = p.getGreen()
        blue = p.getBlue()
        average = (red + green + blue) / 3
        p.setRed(average)
        p.setGreen(average)
        p.setBlue(average)

def main():
    img1 = cv2.imread('html/images/sun.jpeg')
    img1 = cv2.resize(img1, (0, 0), None, .50, .50)
    img2 = average_method(img1)
    img2 = np.stack(3 * [img2], axis=2)
    numpy_concat = np.concatenate((img1, img2), 1)
    cv2.imshow('Numpy Concat', numpy_concat)
    cv2.waitKey(0)
    cv2.destroyAllWindows

if __name__ =="__main__":
    main()

The portion that is commented within the average_method function is the steps that I must follow.

When I try to run the code, I get

  File "test.py", line 38, in <module>
    main()
  File "test.py", line 30, in main
    img2 = average_method(img1)
  File "test.py", line 15, in average_method
    red = p.getRed()
AttributeError: 'numpy.ndarray' object has no attribute 'getRed' 

I thought that defining the functions for getRed, getGreen, and getBlue up above would mean they'd become recognizable in my average_method function (I got those functions from online so I hope they're right). I'm also not sure what it has to do with numpy.ndarray. If anyone could help me fill in this average_method function with code that follows the commented steps correctly, I would really appreciate it.

EDIT::: New code looks like this:

import cv2
import numpy as np

def average_method(img):
    for p in img:
        gray = sum(p)/3
        for i in range(3):
            p[i] = gray

def main():
    img1 = cv2.imread('html/images/sun.jpeg')
    img1 = cv2.resize(img1, (0, 0), None, .50, .50)
    img2 = average_method(img1)
    img2 = np.stack(3 * [img2], axis=2)
    numpy_concat = np.concatenate((img1, img2), 1)
    cv2.imshow('Numpy Concat', numpy_concat)
    cv2.waitKey(0)
    cv2.destroyAllWindows

if __name__ =="__main__":
    main()

I now get the error

  File "test.py", line 50, in <module>
    main()
  File "test.py", line 43, in main
    img2 = np.stack(3 * [img2], axis=2)
  File "<__array_function__ internals>", line 5, in stack
  File "C:\Users\myname\AppData\Local\Programs\Python\Python38-32\lib\site-packages\numpy\core\shape_base.py", line 430, in stack
    axis = normalize_axis_index(axis, result_ndim)
numpy.AxisError: axis 2 is out of bounds for array of dimension 1

I have that line "img2 = np.stack(3 * [img2], axis=2)" since I was previously told on Stack Overflow I need it due to my img2 now being a greyscale (single-channel) image, when img1 is still color (three-channel). This line apparently fixes that. But now it seems like there is something wrong with that?

coda
  • 125
  • 1
  • 3
  • 11
  • `img` is a matrix (or `np.ndarray`). `p` is a row of that matrix (also of type `np.ndarray`). Matrix rows don't have a method `getRed()`. Instead, you need to do `getRed(p)`. – Pranav Hosangadi Oct 26 '20 at 21:59

1 Answers1

1

In Java, the for loop you highlighted is called an "enhanced for loop". Python doesn't have these because Python for loops pog (in terms of concision).

The Python equivalent of the line in question would be:

for p in img:

No need to state object class or anything like that.

EDIT: After OP changed question

The problem now is that you're not calling the functions correctly. p is an array containing the RGB values for that pixel. To call the function as you defined above do:

for p in img:
    red = getRed(p[0])
    green = getGreen(p[1])
    blue = getBlue(p[2])
    average = (red + green + blue) / 3
    p[0] = average
    p[1] = average
    p[2] = average

Remember when you moved the code to Python, you seem to no longer be working in Object Oriented Programming! Pixels don't come with methods that you can call like that anymore.

However, as pointed out by Guimoute in the comments, the code can be much simpler if you get rid of the get[Color] functions and do the following:

for p in img:
    gray = sum(p)/3
    for i in range(3):
        p[i] = gray
General Poxter
  • 554
  • 2
  • 8
  • 1
    Hold up. OP edited the question into a completely different one. – General Poxter Oct 26 '20 at 21:39
  • Oh thanks! I went ahead and edited that portion of the code. I also edited the bottom of the question to show that I got a new error now. It's saying "AttributeError: 'numpy.ndarray' object has no attribute 'getRed'". I wrote more of my thoughts on it in the question. If you happen to know how to correct this, please let me know. – coda Oct 26 '20 at 21:41
  • Oh sorry, I went off of what you said and changed it a bit to show my progress and to fully show the new error. – coda Oct 26 '20 at 21:42
  • No it's all good. I was just scared for a moment because I thought I responded to the wrong person! I edited my answer to address the issue you have now. – General Poxter Oct 26 '20 at 21:44
  • 1
    It seems that the functions are not even needed (they return strings) and `grey = sum(p)/3; for n in range(3): p[n] = grey` should work. – Guimoute Oct 26 '20 at 21:48
  • That is true. I have fooled. – General Poxter Oct 26 '20 at 21:49
  • When I keep the get[Color], and add in the longer block of code that you added, I get this error: ~~~ File "test.py", line 30, in main img2 = average_method(img1) ~~~ File "test.py", line 18, in average_method red = getRed(p[0]) ~~~ File "test.py", line 5, in getRed return '#%02x%02x%02x' % (redVal, 0, 0) ~~~ TypeError: %x format: an integer is required, not numpy.ndarray. Something wrong with my functions? :/ – coda Oct 26 '20 at 22:09
  • Then, I also tried taking out the get[Color] functions, then only having "for p in img: p = np.repeat(sum(p)/3, 3)" in my average_method. I get the error "axis = normalize_axis_index(axis, result_ndim) ~~~ numpy.AxisError: axis 2 is out of bounds for array of dimension 1" .......... However, in my case, my focus is to 1- Get pixels red, green, and blue. 2- Calculate the average value. 3- Set each of red, green, and blue values to average value. (Just like how you did in your longer block of code) – coda Oct 26 '20 at 22:15
  • Sorry, I did another dumb. The new code should work now. – General Poxter Oct 26 '20 at 23:13
  • I tried the new one now (with gray = sum(p)/3 etc). I'll edit my question to show what I have and show a new error occuring. – coda Oct 27 '20 at 00:33
  • This new error is not related to the modified for loop. Instead it is caused by setting img2 to the output of average_method, which doesn't return anything. average_method should modify the contents of the img you pass as the argument (in this case img1), so there's no need to set a new img variable to average_method. – General Poxter Oct 27 '20 at 00:51
  • I must display the original image and the grayscale image side by side so I am not too sure how to do as you said. I have it set to img2 so that I can concatenate them later (shown in main method), and display them both. I did try to do a "return gray" at the bottom of the average_method but I still get the same error message. – coda Oct 27 '20 at 01:05
  • If that's the case, then just initialize an empty image in the average_method and then add gray pixels one by one in the for loop and return the final image. – General Poxter Oct 27 '20 at 01:06