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)
Related
I am trying to resize a grayscale image into a numpy array like so:
return np.array(image.getdata()).reshape((im_height, im_width, 3)).astype(np.uint8)
and getting this error:
ValueError: cannot reshape array of size 1909760 into shape
(1024,1865,3)
I've read that the product of an images columns and rows (1024 x 1865) is supposed to equal the size of the array being reshaped - (1909760) which it does. I've also tried the same code on images with three channels and it works.
If you're using the PIL module for your image, you could try converting it to an RGB before getting the data. Something like this should work:
image = image.convert("RGB")
return np.array(image.getdata()).reshape((im_height, im_width, 3)).astype(np.uint8)
This works because when you convert from a grayscale to an RGB, PIL automatically sets each pixel to have three values, an R, G, and B.
Do not use .getdata(). That's pointless and a waste of effort. What'll happen is that a python list of integers is constructed as an intermediate. Directly converting to a numpy array is much more efficient.
Just use this:
# image = Image.open(...)
image_array = np.array(image)
Secondly you need to handle the conversion from grayscale to RGB, which you seem to want. Your PIL image appears to be grayscale, yet you want a numpy array with three channels (third dimension sized 3). You can either use PIL to convert, or you can use OpenCV.
PIL: image = image.convert("RGB") before converting to numpy (thanks Timmy Diehl, I don't use PIL that often)
OpenCV: image_array = cv.cvtColor(image_array, cv.COLOR_GRAY2BGR) after converting to numpy
Also note the order of color channels. PIL prefers RGB. OpenCV prefers BGR. What you need depends on what you'll do with the numpy array.
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
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.
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())
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))