Python OpenCV: How to convert YCbCr back to RGB? - python

In OpenCV (Python), to convert RGB to YCbCr we use:
imgYCC = cv2.cvtColor(img, cv2.COLOR_BGR2YCR_CB)
What if i want to come back to RGB?

Check the docs for color conversions. You can see all of the available color conversion codes here: Conversion Color Codes.
For the colorspaces available, you can generally transform both ways---COLOR_BGR2YCrCb (i.e. BGR-to-YCrCb) and COLOR_YCrCb2BGR (i.e. YCrCb-to-BGR). Also, OpenCV uses BGR ordering, not RGB ordering. Regardless, to answer the specific question at hand, simply convert back using the opposite order of the colorspaces:
img_bgr = cv2.cvtColor(imgYCC, cv2.COLOR_YCrCb2BGR)
Note: cv2.COLOR_YCrCb2BGR is equivalent to cv2.COLOR_YCR_CB2BGR, I just find the first variant easier to read. Since these transformations (on uint8 images especially), there's some rounding going on so you won't necessarily get the exact same image going back and fourth. But you shouldn't be more than like 1 off at a few of the locations.

Related

Converting image from RGB to LUV and back results in different image

I'm currently trying to start with an original RGB image, convert it to LUV, perform some operations (namely, rotate the hues), then rotate it back to RGB for display purposes. However, I'm encountering a vexing issue where the RGB-to-LUV conversion (and vice versa) seems to be changing the image. Specifically, if I begin with an LUV image, convert it to RGB, and then change it back to LUV, without changing anything else, the original image is different. This has happened for both the Python (cv2) and Matlab (open source) implementations of the color conversion algorithms, as well as my own hand-coded ones based on. Here is an example:
luv1 = np.array([[[100,6.12,0]]]).astype('float32')
rgb1 = cv2.cvtColor(luv1,cv2.COLOR_Luv2RGB)
luv2 = cv2.cvtColor(rgb1,cv2.COLOR_RGB2Luv)
print(luv2)
[[[99.36293 1.3064307 -1.0494182]]]
As you can see, the LUV coordinates have changed from the input. Is this because certain LUV coordinates have no direct match in RGB space?
Yes, remove the astype('uint8') bit in your code, and the difference should disappear if the conversion is implemented correctly.
You can see the equations for the conversion in Wikipedia. There is nothing there that is irreversible, the conversions are perfect inverses of each other.
However, this conversion contains a 3rd power, which does stretch some values significantly. The rounding of the conversion to an integer can introduce a significant shift of color.
Also, the Luv domain is highly irregular and it might not be easy to verify that Luv values will lead to a valued RGB value. Your statement "I've verified that luv1 has entries that all fall in the allowable input ranges" makes me believe that you think the Luv domain is a box. It is not. The ranges for u and v change with L. One good exercise is to start with a sampling of the RGB cube, and map those to Luv, then plot those points to see the shape of the Luv domain. Wikipedia has an example of what this could look like for the sRGB gamut.
The OpenCV cvtColor function will clamp RGB values to the [0,1] range (if of type float32), leading to irreversible changes of color if the input is out of gamut.
Here is an example that shows that the conversion is reversible. I start with RGB values because these are easy to verify as valid:
import numpy as np
import cv2
rgb1 = np.array([[[1.0,1.0,1.0],[0.5,1.0,0.5],[0.0,0.5,0.5],[0.0,0.0,0.0]]], 'float32')
luv1 = cv2.cvtColor(rgb1, cv2.COLOR_RGB2Luv)
rgb2 = cv2.cvtColor(luv1, cv2.COLOR_Luv2RGB)
np.max(np.abs(rgb2-rgb1))
This returns 2.8897537e-06, which is numerical precision for 32-bit floats.

How to resize and image in python while preserving specific grayscale values?

I have a .png image that contains three grayscale values. It contains black (0), white (255) and gray (128) blobs. I want to resize this image to a smaller size while preserving only these three grayscale values.
Currently, I am using scipy.misc.imresize to do it but I noticed that when I reduce the size, the edges get blurred and now contains more than 3 grayscale values.
Does anyone know how to do this in python?
From the docs for imresize, note the interp keyword argument:
interp : str, optional
Interpolation to use for re-sizing
(‘nearest’, ‘lanczos’, ‘bilinear’, ‘bicubic’ or ‘cubic’).
The default is bilinear filtering; switch to nearest and it will instead use the exact color of the nearest existing pixel, which will preserve your precise grayscale values rather than trying to linearly interpolate between them.
I believe that PIL.Image.resize does exactly what you want. Take a look at the docs.
Basically what you need is:
from PIL import Image
im = Image.open('old.png')
# The Image.NEAREST is the default, I'm just being explicit
im = im.resize((im.size[0]/2, im.size[1]/2), Image.NEAREST)
im.save('new.png')
Actually you can pretty much do that with the scipy.misc.imresize
Take a look at its docs.
The interp parameter is what you need. If you set it to nearest the image colors won't be affected.

image not displayed correctly when scaled with a decimal

I am using OpenCV to read and display an image. I am trying to do a scalar multiplication but it is being displayed very differently for two similar approaches:
img = cv2.imread('C:/Python27/user_scripts/images/g1.jpg', -1)
cv2.imshow('img_scaled1', 0.5*img)
cv2.waitKey(0)
cv2.imshow('img_scaled2', img/2)
cv2.waitKey(0)
In the 1st case, hardly anything is displayed. 2nd case works fine.
It seems to me that imshow() does not support numpy array of floats.
I want to use the first method. Can somebody help?
There are lot of pitfall when using images. This one seems like a type issue.
imshowaccept uint8 arrays in range(0,256) (256 excluded), and float arrays in range(0.0,1.0). When doing a=a*.5, you have a float array out of range, so no warranty on the result.
A solution is to cast the array in the uint8 type by:
imshow((a*.5).astype(np.uint8))
or
imshow((a*.5).astype('uint8'))

Python OpenCV Colorspace Differences Between LAB and Grayscale

It was my understanding that when converting an image from BGR to LAB, that the L-component was supposed to represent the grayscale component of the image. However, when I convert from BGR to Grayscale, the expected values don't match. For example,
img1 = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
print img1[0][0]
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
print img2[0][0]
The first pixel in my image in LAB produces [168 133 162] while the second produces 159. I was under the impression that they should be equivalent somehow (which is reinforced by the fact that there is no COLOR_LAB2GRAY constant).
Can someone clarify and explain why this is the case? Is my understanding of LAB incorrect, or am I just misusing something in my code?
If they are indeed different, then which is the better one to use? The rest of my application is manipulating images in the LAB model, so I am tempted to use the L-component as my grayscale baseline, but it some areas look lighter than they should be.... unlike in the BGR2GRAY scenario. Thoughts?
gray = 0.299R + 0.587G + 0.114B
But the conversion from RGB to the L channel of LAB differs. (which is a non-linear function)
The exact conversion can be found here.
And the non-linearity of LAB conversion explains the last part of your question.

Not able to display/Convert Image

I am new to Python and Opencv.
I am using the following code.
import Image
import ImageChops
im1 = Image.open("img1.png")
im2 = Image.open("img2.png")
diff = ImageChops.difference(im2, im1)
When I do cv.ShowImage, it asks me to convert it. I am trying all kinds of convert but there is always an error.
The only way I can see the image is by doing the following.
diff.save("final","JPEG")
Is there there another way I can convert to an IplImage or CvMat?
cv.SaveImage(diff, cv.LoadImage(diff)) might work, using the opencv function.
EDIT: In sight of the comment below, I think trying
cv.SaveImage(diff, cv.LoadImage(diff))
cv.ShowImage('box name', diff)
might work.
The difference image contains negative pixel values, so I don't think cv.ShowImage can display it 'as is'.
The range of possible pixel values after subtraction is -255 to 255. You might want to normalize pixel values first, by
new_value = (old_value + 255)/2
I don't use OpenCV on Python, so I cannot post code for the above.

Categories

Resources