No conversion from RGB to YUV - python

I fail to find an easy-to-use function in any Python library (preferrably PIL) for conversion from RGB to YUV.
Since I have to convert many images, I don't want to implement it myself (would be expensive without LUTs and so on).
When I do the intuitive:
from PIL import Image
img = Image.open('test.jpeg')
img_yuv = img.convert('YUV')
I get an error:
ValueError: conversion from RGB to YUV not supported
Do you know why this is the case?
Is there any efficieint implementation of that in python and maybe even PIL?
I am no computer vision expert but I thought this ocnversion is standard in most of the libraries...
Thanks,
Roman

You can try this:
import cv2
img_yuv = cv2.cvtColor(img, cv2.COLOR_BGR2YUV)

you could try 'YCbCr' instead of 'YUV', i.e.
from PIL import Image
img = Image.open('test.jpeg')
img_yuv = img.convert('YCbCr')

I know it may be late, but scikit-image has the function rgb2yuv
from PIL import Image
from skimage.color import rgb2yuv
img = Image.open('test.jpeg')
img_yuv = rgb2yuv(img)

If you don't want to install any additional package, you can just take a look at skimage source code. The following snippet is taken from that github page with some minor changes:
# Conversion matrix from rgb to yuv, transpose matrix is used to convert from yuv to rgb
yuv_from_rgb = np.array([[ 0.299 , 0.587 , 0.114 ],
[-0.14714119, -0.28886916, 0.43601035 ],
[ 0.61497538, -0.51496512, -0.10001026 ]])
# Optional. The next two line can be ignored if the image is already in normalized numpy array.
# convert image array to numpy array and normalize it from 0-255 to 0-1 range
new_img = np.asanyarray(your_img)
new_img = dtype.img_as_float(new_img)
# do conversion
yuv_img = new_img.dot(yuv_from_rgb.T.copy())

Related

ValueError: cannot reshape array of size 27648000 into shape (2400,1280,3)

here I have a small project on which I block for weeks
I Have a display is 3840x2400 monochrome pixels. Nevertheless, it is driven like 1280(RGB)x2400, whereas each RGB subpixel maps to one monochrome pixel.
Therefore, in order to display real 3840x2400 one has to map 3 consecutive pixels of the monochrome image to one pseudo-RGB pixel. This yields a 1280x2400 wide image, where each RGB subpixel corresponds to one real monochrome pixel.
I try to do this in python3.9 with numpy and PIL
The code below:
from PIL import Image
import numpy as np
def TransTo1224(SourcePngFileName, DestPngFileName):
#trans png file from 3840x2400 to 1280X2400(RGB)
print('~~~~~~~')
print(SourcePngFileName)
imgSrc = Image.open(SourcePngFileName)
dataSrc = np.array(imgSrc)
dataDest = dataSrc.reshape(2400,1280,3)
imgDest = Image.fromarray(dataDest, 'RGB')
imgDest.save(DestPngFileName)
TransTo1224("./source/test1.png","./output/test1.png")
I have a error:
dataDest = dataSrc.reshape(2400,1280,3)
ValueError: cannot reshape array of size 27648000 into shape (2400,1280,3)
I don't understand my mistake, if someone can help me, thank you in advance.
try this
dataDest = vv.reshape(2400,1280,3,-1)
or
dataDest = vv.reshape(2400,1280,3,3)
using dataDest = dataSrc.reshape(2400,1280,3) it wont work
ok i solved my problem it came indeed from my input image, the code works with some images but not the one i want to remap. besides i didn't understand where this multiple of 3 came from the
3840x2400x3 = 27648000.
Well my problem came from the mode of the image which was in RGB.
it was enough for me to convert this mode in "L", luminance before making my reshape
from PIL import Image
import numpy as np
def TransTo1224(SourcePngFileName, DestPngFileName):
#trans png file from 3840x2400 to 1280X2400(RGB)
print('~~~~~~~')
print(SourcePngFileName)
imgSrc = Image.open(SourcePngFileName)
imgSrc = imgSrc.convert('L') # <-----
dataSrc = np.array(imgSrc)
dataDest = dataSrc.reshape(2400,1280,3)
imgDest = Image.fromarray(dataDest, 'RGB')
imgDest.save(DestPngFileName)
TransTo1224("./source/test1.png","./output/test1.png")
Thank you all for helping me

Why 16bit to 8bit conversion produces striped image?

I am testing a segmentation algorithm on several VHSR satellite images, which originally comes in 16bit format, but when I convert them to 8bit images, the produced images are showing striped appearance.
I've been trying different python libraries (skimage, cv2, scipy) getting similar results.
1) The original 16-bit image it is a 4 band image (NIR,B,G,R), so you need to choose the right bands to create a true color image, RGB image (4,3,2 bands). thanks in advance. It can be downloaded from this link:
16bit image
2) I use this code to convert each pixel value, from a 16-bit integer now fitting within 8-bit range:
from scipy.misc import bytescale
SS = io.imread('Imag16bit.tif')
SS = bytescale(SS)
SS = np.asarray(SS)
plt.imshow(SS)
This is my result of above code:
bytescale works for me. I think the asarray step messes up something.
import cv2
from skimage import io
from scipy.misc import bytescale
image = io.imread('SkySat_16bit.tif')
cv2.imshow('Original', image)
print(image.dtype)
image = bytescale(image)
print(image.dtype)
cv2.imshow('Converted', image)
cv2.waitKey(0)
I think this is a way to do it:
#!/usr/local/bin/python3
from PIL import Image
from tifffile import imsave, imread
# Load image
im = imread('SkySat_16bit.tif')
# Extract Red, Green and Blue bands into separate 8-bit arrays
R = (im[:,:,3]/256).astype(np.uint8)
G = (im[:,:,2]/256).astype(np.uint8)
B = (im[:,:,1]/256).astype(np.uint8)
# Combine bands into RGB array
RGB = np.dstack((R,G,B))
# Save to disk
Image.fromarray(RGB).save('result.png')
You may want to adjust the contrast a bit, and check I selected the correct bands.

convert image from CV_64F to CV_8U

I want to convert an image of type CV_64FC1 to CV_8UC1 in Python using OpenCV.
In C++, using convertTo function, we can easily convert image type using following code snippet:
image.convertTo(image, CV_8UC1);
I have searched on Internet but unable to find any solution without errors. Any function in Python OpenCV to convert this?
You can convert it to a Numpy array.
import numpy as np
# Convert source image to unsigned 8 bit integer Numpy array
arr = np.uint8(image)
# Width and height
h, w = arr.shape
It seems OpenCV Python APIs accept Numpy arrays as well. I've not tested it though. Please test it and let me know the result.
For those getting a black screen or lots of noise, you'll want to normalize your image first before converting to 8-bit. This is done with numpy directly as OpenCV uses numpy arrays for its images.
Before normalization, the image's range is from 4267.0 to -4407.0 in my case.
Now to normalize:
# img is a numpy array/cv2 image
img = img - img.min() # Now between 0 and 8674
img = img / img.max() * 255
Now that the image is between 0 and 255, we can convert to a 8-bit integer.
new_img = np.uint8(img)
This can also be done by img.astype(np.uint8).
I faced similar issue and when I trying to convert the image 64F to CV_U8 I would end up with a black screen.
This link will help you understand the datatypes and conversion. Below is the code that worked for me.
from skimage import img_as_ubyte
cv_image = img_as_ubyte(any_skimage_image)

Trouble with Canny Edge Detector - Returning black image

I'm trying to run the canny edge detector on this image:
With this code:
def edges(img):
from skimage import feature
img = Image.open(img)
img.convert('L')
array = np.array(img)
out = feature.canny(array, sigma=1, )
return Image.fromarray(out,'L')
edges('Q_3.jpg').save('Q_3_edges.jpg')
But I'm just getting a black image back. Any ideas what I could be doing wrong? I tried sigma of 1 and of 3.
I have the same situation and this helps for me. Before use the Canny filter, just convert your elements of image array to float32 type:
array = np.array(img)
array = array.astype('float32')
out = feature.canny(array, sigma=1, )
Your images need to be in the correct range for the relevant dtype, as discussed in the user manual here: http://scikit-image.org/docs/stable/user_guide/data_types.html
This should be automatically handled if you use the scikit-image image I/O functions:
from skimage import io
img = io.imread('Q_3.jpg')
So the issue was with the canny function returning and array of type boolean.
Oddly, setting the Image.fromarray mode to '1' didn't help. Instead this was the only way I could get it working; converting the output array to grayscale:
def edges(img):
from skimage import feature
img = Image.open(img)
img.convert('L')
array = np.array(img)
out = np.uint8(feature.canny(array, sigma=1, ) * 255)
return Image.fromarray(out,mode='L')
The problem happens when the image is loaded as float (i.e. in the range 0-1). The loader does that for some types of images. You can check the type of the loaded image by:
print(img.dtype)
If the output is something like float64 (i.e. not uint8), then your image is in the range 0-1.
Canny expects an image in the range 0-255. Therefore, the solution is as easy as:
from skimage import img_as_ubyte
img = io.imread("an_image.jpg")
img = img_as_ubyte(img)
Hope this helps,
The problem happens when the image is saved. You can save image with other library like matplotlib:
import numpy as np
import matplotlib.pyplot as plt
from skimage import feature
from skimage import io
def edges(img):
img = io.imread(img)
array = np.array(img)
out = feature.canny(array, sigma=1, )
return out
plt.imsave("canny.jpg", edges("input.jpg"), cmap="Greys")

how to save an array as a grayscale image with matplotlib/numpy?

I am trying to save a numpy array of dimensions 128x128 pixels into a grayscale image.
I simply thought that the pyplot.imsave function would do the job but it's not, it somehow converts my array into an RGB image.
I tried to force the colormap to Gray during conversion but eventhough the saved image appears in grayscale, it still has a 128x128x4 dimension.
Here is a code sample I wrote to show the behaviour :
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mplimg
from matplotlib import cm
x_tot = 10e-3
nx = 128
x = np.arange(-x_tot/2, x_tot/2, x_tot/nx)
[X, Y] = np.meshgrid(x,x)
R = np.sqrt(X**2 + Y**2)
diam = 5e-3
I = np.exp(-2*(2*R/diam)**4)
plt.figure()
plt.imshow(I, extent = [-x_tot/2, x_tot/2, -x_tot/2, x_tot/2])
print I.shape
plt.imsave('image.png', I)
I2 = plt.imread('image.png')
print I2.shape
mplimg.imsave('image2.png',np.uint8(I), cmap = cm.gray)
testImg = plt.imread('image2.png')
print testImg.shape
In both cases the results of the "print" function are (128,128,4).
Can anyone explain why the imsave function is creating those dimensions eventhough my input array is of a luminance type?
And of course, does anyone have a solution to save the array into a standard grayscale format?
Thanks!
With PIL it should work like this
from PIL import Image
I8 = (((I - I.min()) / (I.max() - I.min())) * 255.9).astype(np.uint8)
img = Image.fromarray(I8)
img.save("file.png")
There is also an alternative of using imageio. It provides an easy and convenient API and it is bundled with Anaconda. It can save grayscale images as a single color channel file.
Quoting the documentation
>>> import imageio
>>> im = imageio.imread('imageio:astronaut.png')
>>> im.shape # im is a numpy array
(512, 512, 3)
>>> imageio.imwrite('astronaut-gray.jpg', im[:, :, 0])
I didn't want to use PIL in my code and as noted in the question I ran into the same problem with pyplot, where even in grayscale, the file is saved in MxNx3 matrix.
Since the actual image on disk wasn't important to me, I ended up writing the matrix as is and reading it back "as-is" using numpy's save and load methods:
np.save("filename", image_matrix)
And:
np.load("filename.npy")
There is also a possibility to use scikit-image, then there is no need to convert numpy array into a PIL object.
from skimage import io
io.imsave('output.tiff', I.astype(np.uint16))

Categories

Resources