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.
Related
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)
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
Assume we are reading and loading an image using OpenCV from a specific location on our drive and then we read some pixels values and colors, and lets assume that this is a scanned image.
Usually if we open scanned image we will notice some differences between the printed image (before scanning) and the image if we open it and see it on the display screen.
The question is:
The values of the pixels colors that we get from OpenCV. Are they according to our display screen color space or we get exactly the same colors we have in the scanned image (printed version) ??
I am not sure, what you want to do or achieve, here's one thing to mention about color profiles.
The most common color profile for cameras, screens and printers is sRGB, which is a limited color spectrum which does not include the whole RGB range (because the cheap hardware can't visualize it anyways).
Some cameras (and probably scanners) allow to use different color profiles like AdobeRGB, which increases the color space and "allows" more colors.
The problem is, if you capture (e.g. scan) an image in AdobeRGB color profile, but the system (browser/screen/printer) interprets it as sRGB, you'll probably get washed out colors, just because of wrong interpretation (like you'll get blue faces in your image, if you interpret BGR images as RGB images).
OpenCV and many browsers, printers, etc. always interpret images as sRGB images, according to http://fotovideotec.de/adobe_rgb/
As long as you don't change the extension of the image file, the pixel values don't change because they're stored in memory and your display or printer are just the way you want to see the image and often you don't get the same thing because it depends on the technology and different filters applied to you image before they're displayed or printed..
The pixel values are the ones you read in with
imgread
It depends on the flags you set for it. The original image may have a greater bit-depth (depending on your scanner) than the one you loaded.
Also the real file extension is determined from the first bytes of the file, not by the file name extension.
So it may not be the pixel value of the scanned image if the bit-depths differ.
Please have a look at the imgread documentation.
I am trying to use an image editor (such as MS paint or paint.net) to draw bounding boxes with a fixed color (such as pure red with RGB = 255, 0, 0) on images, and later load the images in python (such as opencv imread) by looking for pixels with this RGB value (or BGR value in 0, 0, 255) so that I can create labels for object detection purposes.
However, after the image is saved and loaded, I don't see any pixels with such RGB or BGR values. Instead, these pixels are within a range of values which may be far from what I specified.
I also tried to use something like this for experiment purpose:
cv2.rectangle(img_arr, (10, 10), (60, 60), (0, 0, 255), thickness=1)
Right after this statement, I do see pixels with values (0, 0, 255). However, when I run cv2.imwrite and then cv2.imread like this:
cv2.imwrite(full_path_name, img_arr)
and later:
img_arr = cv2.imread(full_path_name)
I noticed in this new img_arr, there is no any pixels with these BGR values any more. What is the problem?
Back to original problem of labeling images for object detection, I don't want to use any tools for labeling as most of them are detecting mouse motions, however, my task of object detection is to detect text areas, which requires very accurate bounding boxes so that the later stages of image segmentation and character recognition won't be too hard. Therefore, I prefer a static way so that the bounding boxes can be adjusted to be accurate and even be reviewed. When they are final, we create labels. Will this idea even work?
Thank you very much!
Be careful when using JPEG as intermediate storage for image processing tasks since it is a lossy format and values may differ when you subsequently read them back.
Consider maybe using lossless PNG format for intermediate storage. Or use NetPBM PGM (greyscale) or PPM (colour) format for a particularly simple format to read and write - though be aware it cannot persist metadata, such as copyrights or EXIF data.
How can I replace a colour across multiple images with another in python? I have a folder with 400 sprite animations. I would like to change the block coloured shadow (111,79,51) with one which has alpha transparencies. I could easily do the batch converting using:
img = glob.glob(filepath\*.bmp)
however I dont know how I could change the pixel colours. If it makes any difference, the images are all 96x96 and i dont care how long the process is. I am using python 3.2.2 so I cant really use PIL (I think)
BMP is a windows file format, so you will need PIL or something like it; or you can roll your own reader/writer. The basic modules won't help as far as I'm aware. You can read PPM and GIF using Tk (PhotoImage()) which is part of the standard distribution and use get() and put() on that image to change pixel values. See references online, because it's not straight-forward - the pixels come from get() as 3-tuple integers, but need to go back to put() as space-separated hex text!
Are your images in indexed mode (8 bit per pixel with a palette),or "truecolor" 32bpp images? If they are in indexed modes, it would not be hard to simply mark the palette entry for that color to be transparent across all files.
Otherwise, you will really have to process all pixel data. It also could be done by writting a Python script for GIMP - but that would require Python-2 nonetheless.