4

I have an image represented by a numpy.array matrix nxm of triples (r,g,b) and I want to convert it into grayscale, , using my own function.

My attempts fail converting the matrix nxmx3 to a matrix of single values nxm, meaning that starting from an array [r,g,b] I get [gray, gray, gray] but I need gray.

i.e. Initial colour channel : [150 246 98]. After converting to gray : [134 134 134]. What I need : 134

How can I achieve that?

My code:

def grayConversion(image):
    height, width, channel = image.shape
    for i in range(0, height):
        for j in range(0, width):
            blueComponent = image[i][j][0]
            greenComponent = image[i][j][1]
            redComponent = image[i][j][2]
            grayValue = 0.07 * blueComponent + 0.72 * greenComponent + 0.21 * redComponent
            image[i][j] = grayValue
    cv2.imshow("GrayScale",image)
    return image
9
  • please use vectorization instead of nested for loops. Commented Jul 11, 2018 at 12:45
  • Can you please elaborate ? @Quickbeam2k1 Commented Jul 11, 2018 at 12:52
  • I'm not sure if I understand your quetion right. What's exactly the problem with the opencv conversion? Do you want to implement the opencv conversion yourself? Commented Jul 11, 2018 at 13:01
  • There is no any problem with opencv conversion. Yes, I wanted a 2-dimensional array of image implementing my own code. Commented Jul 11, 2018 at 13:03
  • thesamiroli if my answer isn't correct could you please expand on what am I missing? Thanks! Commented Jul 11, 2018 at 13:13

3 Answers 3

5

Here is a working code:

def grayConversion(image):
    grayValue = 0.07 * image[:,:,2] + 0.72 * image[:,:,1] + 0.21 * image[:,:,0]
    gray_img = grayValue.astype(np.uint8)
    return gray_img

orig = cv2.imread(r'C:\Users\Jackson\Desktop\drum.png', 1)
g = grayConversion(orig)

cv2.imshow("Original", orig)
cv2.imshow("GrayScale", g)
cv2.waitKey(0)
cv2.destroyAllWindows()
Sign up to request clarification or add additional context in comments.

Comments

2

You can use a dot product:

gray_image = image.dot([0.07, 0.72, 0.21])

Or even just do the whole operation manually:

b = image[..., 0]
g = image[..., 1]
r = image[..., 2]
gray_image = 0.21 * r + 0.72 * g + 0.07 * b

Don't forget to convert back to 0-255:

gray_image = np.min(gray_image, 255).astype(np.uint8)

2 Comments

The image is still a 3-dimensional array.
You mean gray_image.ndim == 3? It's 2 on my machine
2

Solution using apply_along_axis

A solution can be achieved by using apply_along_axis:

import numpy as np
def grayscale(colors):
    """Return grayscale of given color."""
    r, g, b = colors
    return 0.21 * r + 0.72 * g + 0.07 * b

image = np.random.uniform(255, size=(10,10,3))
result = np.apply_along_axis(grayscale, 2, image)

Examples

10x10 image

We can now proceed to visualise the results:

from matplotlib import pyplot as plt
plt.subplot(1,2,1)
plt.imshow(image)
plt.subplot(1,2,2)
plt.imshow(result, cmap='gray')

Example results

Textual example (2x2 image)

To visualise the actual results in text I will use a smaller array, just a 2x2 image:

image = np.random.uniform(250, size=(2,2,3))

The content is:

array([[[205.02229826, 109.56089703, 163.74868594],
    [ 11.13557763, 160.98463727, 195.0294515 ]],

   [[218.15273335,  84.94373737, 197.70228018],
    [ 75.8992683 , 224.49258788, 146.74468294]]])

Let's convert it to grayscale, using our custom function:

result = np.apply_along_axis(grayscale, 2, image)

And the output of the conversion is:

array([[127.62263079, 157.64461409],
   [117.94766108, 197.76399547]])

We can visualise this simple example too, using the same code as above:

Smaller example

Further suggestions

If you want to apply your own custom function, then apply_along_axis is the way to go, but you should consider using purer numpy approaches such as the one suggested by Eric or, if possible, just load the black and white image using cv2 option:

cv2.imread('smalltext.jpg',0)

6 Comments

so, the result variable should be a grayscale image right ? or am i missing something because when I try to display it, it only shows white pixels everywhere.
Yes, but he asked for using his own custom function.
Your comment gives the misleading impression that this is better because it avoids a python loop - but a python loop is exactly what apply_along_axis does.
Eric Comment removed, you are right.
@thesamiroli I believe so. What does it happen when using the cv2 grayscale loading cv2.imread('smalltext.jpg',0)?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.