I would like to smoothly convert an RGB image to greyscale as a function of some continuous parameter. I have seen plenty of posts on how to convert 3-channel to 1-channel, but that would not work for me, I would like the output to still be 3-channels. Is this possible?
I would like to have a function
f(image, parameter)
that does more or less the following: if paramater is zero, the function returns the original image, and if the parameter is one it returns a greyscale image. Therefore, I would have the ability to smoothly tune the color between on and off via parameter.
If there already is a coded solution, in Python is strongly preferred.
Thanks!
It's quite easy to do with PIL/Pillow.
from PIL import Image
im = Image.open(r'c:\temp\temp.jpg')
gray = im.copy().convert('L').convert('RGB')
im2 = Image.blend(im, gray, 0.75)
Related
I learned that OpenCV color order is BGR while that of Matpotlib's Pyplot is RGB. So I started experimenting with reading and displaying an image using both libraries. Here is the Image I experimented with:
It's just a Black and white image with red color in some parts. Now, when I used pyplot.imshow() to display the image copy read by OpenCV, the tie's and the shoes' color changes to blue. The same happened when I used cv2.imshow() to display a copy of the image read by pyplot.imread(). However, the color remains the same when I use cv2.imshow() to display the image copy read by cv2.imread() and use plt.imshow() to display a copy read by plt.imread().
I am just curious and would like to know about the things that go behind the scenes when such operations are performed. Can Anyone help me with that?
Assume you have a vector like this: [0, 0, 255].
You know have two different color encodings: RGB and BGR. So, in the first case you have Blue, in the second system you have Red.
Now, Let's call RGB_Reader and BGR_Reader two systems to open the number and display it.
If I open the image with BGR_Reader, I have [0, 0, 255]. I pass it on to RGB_Reader, still is [0, 0, 255]. I see Blue. When I pass it around, I would pass [0, 0, 255]. I open it again with RGB_Reader, it is blue, again.
The same happens the other way around.
Does it make sense to you? The vector doesn't change, but the way it is decoded does.
Now introduce another thing, called jpg_encoder. That one is telling people where to put Blue, Red and Green, and will probably re-order things.
That's basically dependent upon the color convention. OpenCV follows BGR convention, which means that it interprets a triplet (0, 150, 255) as B, G and R values respectively. And all other libraries follow the more obvious RGB convention. The reason for OpenCV to follow BGR convention is legacy I guess(since 1991, maybe).
I would recommend you to use OpenCV methods only such as cv2.imread(), cv2.imshow() or cv2.imwrite(), etc. to perform any operation on image(s). Because writing code in this way you will never have to worry about the underlying BGR or RGB stuff, everything will just work fine.
The problem would arise when you want to use OpenCV with matplotlib or pillow etc. In those cases you need to take extra care while passing on your image matrix to respective libraries. Since OpenCV holds the data in BGR format, while matplotlib or pillow would be expecting RGB format, so you explicitly need to convert the color order using cv2.cvtColor(img, cv2.COLOR_BGR2RGB), or you may use numpy slicing as well to swap the first and third channel as well.
You may consult this answer for a demo code which converts OpenCV images to PIL(another python image processing module) format images.
I have a uint16 3-dim numpy array reppresenting an RGB image, the array is created from a TIF image.
The problem is that when I import the original image in QGIS for example is displayed correctly, but if I try to display within python (with plt.imshow) the result is different (in this case more green):
QGIS image:
Plot image:
I think it is somehow related to the way matplotlib manages uint16 but even if I try to divide by 255 and convert to uint8 I can't get good results.
Going by your comment, the image isn't encoded using an RGB colour space, since the R, G and B channels have a value range of [0-255] assuming 8 bits per channel.
I'm not sure exactly which colour space the image is using, but TIFF files generally use CMYK which is optimised for printing.
Other common colour spaces to try include YCbCr (YUV) and HSL, however there are lots of variations of these that have been created over the years as display hardware and video streaming technologies have advanced.
To convert the entire image to an RGB colour space, I'd recommend the opencv-python pip package. The package is well documented, but as an example, here's how you would convert a numpy array img from YUV to RGB:
img_bgr = cv.cvtColor(img, cv.COLOR_YUV2RGB)
When using plt.imshow there's the colormap parameter you can play with, try adding cmap="gray" so for example
plt.imshow(image, cmap="gray")
source:
https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.imshow.html
If I try to normalize the image I get good results:
for every channel:
image[i,:,:] = image[i,:,:] / image[i,:,:].max()
However, some images appear darker than others:
different images
I am new to OpenCV and trying to impliment the basic optical flow example here: https://docs.opencv.org/3.3.1/d7/d8b/tutorial_py_lucas_kanade.html
I only want goodFeaturesToTrack() to analyze a small static circular part of my webcam input. The goodFeaturesToTrack function takes a mask argument, to which I am trying to pass in a 1-channel .png image of the same size as the input with cv2.imread('mask.png',0), but with no luck (it returns a mask.empty() error).
I feel like I am going about this in the wrong way, perhaps. Am I missing something obvious here? Or would it be easier for me to draw a circle within OpenCV itself?
For future searchers, here is how I got it working:
maskimage = cv2.imread('your_mask_file.png',0) #1-channel image, white area to be active
mask = np.uint8(maskimage)
Then the mask can simply be passed in as a variable.
I have some traffic camera images, and I want to extract only the pixels on the road. I have used remote sensing software before where one could specify an operation like
img1 * img2 = img3
where img1 is the original image and img2 is a straight black-and-white mask. Essentially, the white parts of the image would evaluate to
img1 * 1 = img3
and the black parts would evaluate to
img1 * 0 = img3
And so one could take a slice of the image and let all of the non-important areas go to black.
Is there a way to do this using PIL? I can't find anything similar to image algebra like I'm used to seeing. I have experimented with the blend function but that just fades them together. I've read up a bit on numpy and it seems like it might be capable of it but I'd like to know for sure that there is no straightforward way of doing it in PIL before I go diving in.
Thank you.
The Image.composite method can do what you want. The first image should be a constant value representing the masked-off areas, and the second should be the original image - the third is the mask.
You can use the PIL library to mask the images. Add in the alpha parameter to img2, As you can't just paste this image over img1. Otherwise, you won't see what is underneath, you need to add an alpha value.
img2.putalpha(128) #if you put 0 it will be completly transparent, keep image opaque
Then you can mask both the images
img1.paste(im=img2, box=(0, 0), mask=img2)
I have a hundred 10x10 px images, and I want to combine them into a big 100x100 image. I'm using the Image library to first create a blank image and then paste in the smaller images:
blank = Image.new('P',(100,100))
blank.paste(im,box)
The smaller images are in color, but the resulting image turns out in all grayscale. Is there a fix or workaround for this?
It's probably something to do with using a palette type image (mode P). Is there a specific reason you are doing this? If not, try passing 'RGB' as the first argument.