I'm trying to save a filtered/hybrid image after manipulating it with OpenCV in Jupyter Notebook on a mac. Everything goes fine until I try to save the image. I have tried saving it as .jpg, .bmp. and .png. I've tried each of those and multiplying the image by 255 as well as not multiplying. The resulting saved file is either completely black or completely white depending on whether I multiplied by 255.
im1 = cv2.imread(im1_file, cv2.IMREAD_GRAYSCALE)
im1 = cv2.cvtColor(im1, cv2.COLOR_BGR2GRAY) / 255.0
# filtering and cropping operations
cv2.imwrite('hybrid_image.jpg', cropped_image*255)
I also tried 'converting' it back into a bgr image. But it gives an error of 'depth' is 6 (CV_64F)
cropped_image_255 = cropped_image*255.0
convertedimage = cv2.cvtColor(cropped_image_255, cv2.COLOR_GRAY2BGR)
cv2.imwrite('hybrid_image.bmp', convertedimage)
The fix for your problem is converting the Image type to unsigned 8-bit integer before saving.
You can replace the following line:
cv2.imwrite('hybrid_image.jpg', cropped_image*255)
with
cropped_image = cropped_image * 255 # Still (can be) floats
cv2.imwrite('hybrid_image.jpg', cropped_image.astype(np.uint8))
Please make sure that before converting to np.uint8 your image doesn't have any numbers above 255. Or else, OpenCV will clamp them to white pixel (255 value).
Related
I've converted some images from RGB to Grayscale for ML purpose.
However the shape of the converted grayscale image is still 3, the same as the color image.
The code for the Conversion:
from PIL import Image
img = Image.open('path/to/color/image')
imgGray = img.convert('L')
imgGray.save('path/to/grayscale/image')
The code to check the shape of the images:
import cv2
im_color = cv2.imread('path/to/color/image')
print(im_color.shape)
im_gray2 = cv2.imread('path/to/grayscale/image')
print(im_gray2.shape)
You did
im_gray2 = cv2.imread('path/to/grayscale/image')
OpenCV does not inspect colorness of image - it does assume image is color and desired output is BGR 8-bit format. You need to inform OpenCV you want output to be grayscale (2D intensity array) as follows
im_gray2 = cv2.imread('path/to/grayscale/image', cv2.IMREAD_GRAYSCALE)
If you want to know more about reading images read OpenCV: Getting Started with Images
cv.imread, without any flags, will always convert any image content to BGR, 8 bits per channel.
If you want any image file, grayscale or color, to be read as grayscale, you can pass the cv.IMREAD_GRAYSCALE flag.
If you want to read the file as it really is, then you need to use cv.IMREAD_UNCHANGED.
im_color = cv2.imread('path/to/color/image', cv2.IMREAD_UNCHANGED)
print(im_color.shape)
im_gray2 = cv2.imread('path/to/grayscale/image', cv2.IMREAD_UNCHANGED)
print(im_gray2.shape)
I am doing a denoising work and I'm not very familiar with Python. I applied BM3D to get the denoised picture and I also have the original one.
Now I want to get the noise by doing this:
tmp = img - img_denoised
But it turns out to be a very strange black and white figure like this:
So how can I get a proper noise picture? What I wish to get is image like this:
Edit:
Got an image from the Internet and done the same processing.
after processing:
Edit again:
Providing a simple example:
import cv2
img = cv2.imread("path of the original image")
img_denoised = cv2.imread("path of the denoised image")
tmp = img - img_denoised
cv2.imwrite("test_noise.jpg",tmp)
In your example code, both img and img_denoised are uint8 NumPy arrays. When operating on these arrays, the output is of the same type. These operations are modulo 256. When the result of an operation exceeds 255, it wraps around back to 0, and when the result is negative, it wraps around back to 255. For example:
np.array([5], np.uint8) - np.array([10], np.uint8)
return array([251], dtype=uint8). Instead of -5, which cannot be represented in a uint8 value, we get 256 - 5 = 251.
The subtraction img - img_denoised results in some values just above zero, which look black, and some values just below zero, which will be stored as values near 255 and look white.
We can solve this in different ways. One is to force the operation to happen with floating-point values:
tmp = img.astype(float) - img_denoised.astype(float)
We now have an array of floats, about half of them negative. But a JPEG file can only store uint8 values, and casting our float values to uint8 will get us back where we started. So we need to shift the origin (the zero value) to a middle-gray (typically 128):
tmp = img.astype(float) - img_denoised.astype(float)
tmp += 128
cv2.imwrite("test_noise.jpg", tmp.astype(np.uint8))
This is very fiddly, but it works. I prefer using a library that takes care of data types for me, so I don't have to think about them when I don't want to. DIPlib is such a library (disclaimer: I'm an author):
import diplib as dip
img = dip.ImageRead("7yJS3.png")
img_denoised = dip.ImageRead("xjQIy.png")
tmp = img - img_denoised
tmp += 128
dip.ImageWrite(tmp, "test_noise.jpg")
In DIPlib, arithmetic operations automatically promote the images to a floating-point type, unless we explicitly prevent it. Saving as JPEG silently casts the image to uint8 (this is where errors will happen if the pixel values are outside the range of the uint8 type).
With limited information it is hard to pin-point the problem. Please provide input images and more code.
Looks like the result image is a binary bitmap, only white or black, no gray. Your tmp image's pixel format is probably incorrect, which might be due to your img and img_denoised are not having the same pixel format, or both are wrong. Try display your input images to see if they look normal.
Your img and img_denoised should be the same pixel format, maybe 8-bit gray scale, or 24-bit RGB, and after img-img_denoised, the result should still have the same pixel format.
It could also due to it's unsigned, try to make it signed, or + 128 to all pixels and see what happened.
What is your image data type/range? 0-1 or 0-255?
if your image is 0-1 float32, the noise image will have a data range of [-1, 1], around half of the pixels is below 0, and when displayed by cv2.imshow() as "black".
Try
noise = origin - clean
noise = (noise + 1) * 0.5
I have a greyscale image that, as a numpy array, has a maximal value of 91, but if it is first converted from grayscale to RGB, its maximal value (across all channels) is 255. What formula is being used here? When viewing the images using im.show() they look identical. I checked the PIL source code for 'convert' (link) but it doesn't explicitly state how a greyscale image is converted to RGB.
I run the following:
im = PIL.Image.open(path_to_greyscale_image)
im_max_grey = max(np.asarray(im).flatten())
im = im.convert('RGB')
im_max_rgb = max(np.asarray(im).flatten())
I want to convert any image to grayscale, but I don't understand the difference between these implementations.
image = cv2.imread('lenna.jpg')
gray = cv2.cvtColor(image, cv2.IMREAD_GRAYSCALE)
gray1 = rgb2gray(image)
gray2 = cv2.imread('lenna.jpg', cv2.IMREAD_GRAYSCALE)
image1 = Image.open('lenna.jpg', 'r')
gray3 = image1.convert('L')
When I plot them, I get them in blue scale, green scale, green scale and gray respectively. When I should use each one?
You've encountered a spot where Python's type system isn't protecting you in the way that C++ would.
cv2.IMREAD_GRAYSCALE and cv2.COLOR_BGR2GRAY are values from different enumerations. The former, whose numerical value is 0, applies to cv2.imread(). The latter, whose numerical value is 6, applies to cv2.cvtColor(). C++ would have told you that cv2.IMREAD_GRAYSCALE can't be passed to cv2.cvtColor(). Python quietly accepts the corresponding int value.
Thus, you think you're asking cv2 to convert a color image to gray, but by passing cv2.IMREAD_GRAYSCALE, cv2.cvtColor() sees the value 0, and thinks you're passing cv2.COLOR_BGR2BGRA. Instead of a grayscale image, you get the original image with an alpha channel added.
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
is what you need instead.
The other issue you're seeing, assuming you're using a Jupyter notebook, is that cv2 layers color planes in BGR order instead of RGB. To display them properly, first do
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
and then display the result.
The images that are not gray are the still 3d arrays, that is to say they still somehow retain color information, the reason you are seeing blue and green is because in those 3d arrays the red and green channels in the first case and the blue & red channels in the second have been reduced to 0 leaving only the blue and green that you see.
In order to read the image as grayscale you would use
img_gray=cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
This will yield a 2d array with values between 0 and 255 corresponding to how bright the pixel should be instead of how bright each of the 3 color channels of the pixel should be.
I'm trying to open an RGB picture, convert it to grayscale, then represent it as a list of floats scaled from 0 to 1. At last, I want to convert it back again to an Image. However, in the code below, something in my conversion procedure fails, as img.show() (the original image) displays correctly while img2.show() display an all black picture. What am I missing?
import numpy as np
from PIL import Image
ocr_img_path = "./ocr-test.jpg"
# Open image, convert to grayscale
img = Image.open(ocr_img_path).convert("L")
# Convert to list
img_data = img.getdata()
img_as_list = np.asarray(img_data, dtype=float) / 255
img_as_list = img_as_list.reshape(img.size)
# Convert back to image
img_mul = img_as_list * 255
img_ints = np.rint(img_mul)
img2 = Image.new("L", img_as_list.shape)
img2.putdata(img_ints.astype(int))
img.show()
img2.show()
The image used
The solution is to flatten the array before putting it into the image. I think PIL interprets multidimensional arrays as different color bands.
img2.putdata(img_ints.astype(int).flatten())
For a more efficient way of loading images, check out
https://blog.eduardovalle.com/2015/08/25/input-images-theano/
but use image.tobytes() (Pillow) instead of image.tostring() (PIL).
.