When converting an image into an array and viceversa, are there extra considerations one must take into account?

∥☆過路亽.° 提交于 2021-02-11 06:22:57

问题


I wrote this code to switch the red and blue values in the RGB array from a given image:

from PIL import Image 
import numpy as np 

image = Image.open("image.jpg")
RGBarr = np.asarray(image)

newRGB = np.full_like(RGBarr, 1)

red = RGBarr[..., 0]
green = RGBarr[...,1]
blue = RGBarr[..., 2]

newRGB[..., 0] = blue 
newRGB[..., 1] = green
newRGB[..., 2] = red

inv_image = Image.fromarray(newRGB, 'RGB')

inv_image.save('inv_image.png')

inv_image.show() 

I tried it with multiple images, and it works almost every time. However, in some cases I get the following error:

raise ValueError("not enough image data")
ValueError: not enough image data

That can be fixed if I do not specify the mode in Image.fromarray(obj, mode), but even doing that I am not sure if the result I obtain is the "correct" one.

Is there a way to determine what mode should be used for a certain image?

I hope this is not a dumb question, but I am sort of new in this image processing business.


回答1:


The error occurs, when you try to read images which are not RGB like grayscale images or RGBA images. To keep the rest of your code valid, the easiest way would be to enforce RGB input by using:

image = Image.open("image.jpg").convert('RGB')

Then, possible grayscale or RGBA images are converted to RGB, and can be processed as regular RGB images.

As you found out yourself,

inv_image = Image.fromarray(newRGB)

also works, but the processing from the rest of your code then isn't correct anymore (no proper slicing of the desired dimensions/axes). That would require further work on your code to also respect grayscale or RGBA images.

Hope that helps!


EDIT: To incorporate furas' idea to get rid of NumPy, here's a PIL only way of swapping the channels. Notice: You still need the enforced RGB input.

from PIL import Image

image = Image.open('image.jpg').convert('RGB')

r, g, b = image.split()

inv_image = Image.merge('RGB', (b, g, r))

inv_image.save('inv_image.png')

inv_image.show()



回答2:


If you want to re-order RGB channels to BGR with Numpy, it is much simpler to do this:

BGR = RGB[...,::-1]

which just addresses the last index (i.e. the channels) in reverse. It has the benefit of being O(1) which means it takes the same amount of time regardless of the size of the array. On my Mac, it takes 180ns to do BGR->RGB with 10x10 image and just the same with a 10,000x10,000 image.


In general, you may want some other ordering rather than straight reversal, so if you want BGR->BRG, you can do:

BRG = BGR[...,(0,2,1)]

Or, if you want to make a 3-channel greyscale image by repeating the Green channel three times (because the green is usually the least noisy - see Wikipedia Bayer array article), you can simply do this:

RGBgrey = BGR[...,(1,1,1)]

If you want to get rid of Numpy, you can do it straight in PIL/Pillow using a matrix multiplication:

# Open image
im = Image.open('image.jpg')

# Define matrix to re-order RGB->BGR
Matrix = ( 0, 0, 1, 0,  
           0, 1, 0, 0,  
           1, 0, 0, 0)

# BGR -> RGB
BGR = im.convert("RGB", Matrix)

You can understand the matrix like this:

newR = 0*oldR + 0*oldG + 1*oldB + 0 offset
newG = 0*oldR + 1*oldG + 0*oldB + 0 offset
newB = 1*oldR + 0*oldG + 0*oldB + 0 offset

Input

Result



来源:https://stackoverflow.com/questions/58872621/when-converting-an-image-into-an-array-and-viceversa-are-there-extra-considerat

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!