As I'm lead to believe, OpenCV reads images in BGR colorspace ordering and we usually have to convert it back to RGB like this:
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
But when I try to simply read an image and show it, the coloring seems fine (without the need to convert BGR to RGB):
img_bgr = cv2.imread(image_path)
cv2.imshow('BGR Image',img_bgr)
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
cv2.imshow('RGB Image',img_rgb )
cv2.waitkey(0)
So is imshow() changing the ordering within the function automatically (from BGR to RGB) or the ordering has been BGR all along?
BGR and RGB are not color spaces, they are just conventions for the order of the different color channels. cv2.cvtColor(img, cv2.COLOR_BGR2RGB) doesn't do any computations (like a conversion to say HSV would), it just switches around the order. Any ordering would be valid - in reality, the three values (red, green and blue) are stacked to form one pixel. You can arrange them any way you like, as long as you tell the display what order you gave it.
OpenCV imread, imwrite and imshow indeed all work with the BGR order, so there is no need to change the order when you read an image with cv2.imread and then want to show it with cv2.imshow.
While BGR is used consistently throughout OpenCV, most other image processing libraries use the RGB ordering. If you want to use matplotlib's imshow but read the image with OpenCV, you would need to convert from BGR to RGB.
screen = cv2.cvtColor(screen, cv2.COLOR_RGB2BGR)
this one line code changes rgb to bgr
for matplotlib we need to change BGR to RGB:
img = cv2.imread("image_name")
img = img[...,::-1]
plt.imshow(img)
opencv_image_with_bgr_channels = cv2.imread('path/to/color_image.jpg')
matplotlib_compatible_image_with_rgb_channels = opencv_image_with_bgr_channels[:,:, ::-1]
This converts BGR to RGB Channels Image by reversing the channels.
If you do not need to use any other Image processing library (example Matplotlib's imshow), there is no need to do color scale conversion. Below code is an example, where the color scale conversion is done but when the image is loaded, it is still loaded in BGR. This conversion is not needed as the image is displayed using cv2.imshow().
import cv2
# read the image #
image = cv2.imread('<<Image Path>>')
image_rgb = cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
# write a function to draw circles on the image #
def draw_circle(event,x,y,flags,params):
if event == cv2.EVENT_RBUTTONDOWN:
cv2.circle(img=image_rgb,center=(x,y),radius=100,color=(0,0,255),thickness=10)
# Open CV callbacks #
cv2.namedWindow(winname='ImageWindow')
cv2.setMouseCallback('ImageWindow',draw_circle)
# display the image till the user hits the ESC key #
while True:
cv2.imshow('ImageWindow',image_rgb)
if cv2.waitKey(20) & 0xFF == 27:
break
cv2.destroyAllWindows()
Alternatively, you can use imutils.opencv2matplotlib() function, which does not need BGR to RGB conversion.
Related
I am having an issue where I'm using Pillow to recolor an image that has a lot of soft gradients but it seems to not completely color the most translucent part of these gradients, with the recolored image having a gradient that is not as smooth. Is there a way to fix this issue? Example Images and current code below.
enter image description here
Original Gradient: 1: https://i.stack.imgur.com/VFi75.png
enter image description here
Recolored Gradient: 1: https://i.stack.imgur.com/e5iNa.png
Here is the Original transparent PNG of the image
import random
import Owl_Attributes
from PIL import Image, ImageColor
# I create the image here and convert the color code to RGBA
RGB_im = image_base_accent3.convert("RGBA")
datas = RGB_im.getdata()
newData = []
for item in datas:
if item[0] == 208 and item[1] == 231 and item[2] == 161:
newData.append((255, 0, 0, item[3]))
else:
newData.append(item)
RGB_im.putdata(newData)
RGB_im.save('Owl_project_pictures\_final_RGB.png')
First, a couple of things to consider:
Inspect your images before you start work. Yours has an alpha channel that is pretty much pointless and irrelevant so I would discard that to save space and processing time.
Using for loops over Python lists of pixels is slow, inefficient, and error-prone in Python. Try to use built-in functions based on C code, or to use vectorised functions like Numpy.
On to your image. There are a whole load of shades and gradations of tone in your image and dealing with one separately through if statements is going to be difficult. I would suggest you want to use HSV colourspace instead.
I think you want the basic result to be a very saturated red with the lightness dictated by the lightness of the original image.
So, I would make an image with:
Hue=0 (see lower part of this diagram), and
Saturation=255 (i.e. fully saturated), and
Value (i.e. brightness) of the original image.
In code that might look like this:
#!/usr/bin/env python3
# ImageMagick command-line "equivalent"
# magick -size 599x452 xc:black xc:white \( VFi75.png -colorspace gray +level 0,60% \) +combine HSL result.png
from PIL import Image
# Load image and create HSV version
im = Image.open('VFi75.png')
HSV = im.convert('HSV')
# Split into separate channels for processing, discarding Hue and Saturation
_, _, V = HSV.split()
# Synthesize Hue channel, same size as input image, filled with 0, to make Red
H = Image.new('L', (im.width, im.height), 0)
# Synthesize Saturation channel, same size as input image, filled with 255, to make fully saturated
S = Image.new('L', (im.width, im.height), 255)
# Recombine synthesized H, S and V (based on original image brightness) back into a recombined image
RGB = Image.merge('HSV', (H,S,V)).convert('RGB')
# Save processed result
RGB.save('result.png')
If you wanted to make it lime green, you would change the Hue angle like this:
# Synthesize Hue channel, same size as input image, filled with 120, to make Lime Green
H = Image.new('L', (im.width, im.height), 120)
If you wanted to make it less saturated, you would change the saturation like this:
# Synthesize Saturation channel, same size as input image, filled with 64, to make less saturated
S = Image.new('L', (im.width, im.height), 64)
I am just starting to learn LabVIEW. I want to get a threshold from my image in a python function and display the image in LabVIEW. But when the function returns the image, it gives an error in LabVIEW. I am sending the relevant code in Python and the LabVIEW program as an attachment.
Thanks
import numpy as np
import cv2
def thershold(data):
gray = np.array(data,dtype=np.uint8)
ret, thresh1 = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)
return np.array( thresh1, dtype=np.float64)
if __name__ == '__main__':
data = cv2.imread('C:/Users/user00/Desktop/LabView/1120220711_151148.tiff', 1)
thresh = thershold(data)
cv2.imshow('thresh1',thresh)
cv2.waitKey(0)
As the commenters on your post have suggested, it appears that the python code and LabVIEW code are expecting different types. When you perform the test just in Python the code adapts as required to show the image but the types need to match when passing between the two environments.
As per the OP's comment below, we need to pass a grayscale image and return an RGB image.
The grayscale image is easier as it is a 2D array of uint8 types. We can convert a Grayscale IMAQ image into the correct array type using IMAQ ImageToArray.vi.
When it comes to passing an RGB image back to LabVIEW we need to know the following:
In OpenCV an RGB image is a 2-dimensional image with multiple "channels". Each channel represents one of the colours and the OpenCV convention is to store the channels in the Blue-Green-Red channel order
In LabVIEW IMAQ RGB images are represented as a 2-dimensional image of unsigned 32-bit integers. The most significant byte is the Alpha channel which IMAQ cannot handle but is still stored. The next byte is the Red Channel, then the Green Channel and finally the least significant byte is the Blue Channel
We have two options - we can either format the image data before passing it from the Python side or we can take the Python image data as-is and transform it to the format LabVIEW/IMAQ needs in LabVIEW.
In The example code below I choose the latter (because I have more experience manipulating data in LabVIEW). Once theRGB image data is an array of U32 integers we can use the IMAQ ArrayToColorImage.vi to write the data to the IMAQ image.
The associated Python code is
import numpy as np
import cv2
def threshold(data):
gray = np.array(data,dtype=np.uint8)
# perform threshold operation
ret, thresh1 = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)
#
# create an RGB image to demonstrate output
#
height = 200
width = 300
rgb = np.zeros((height,width,3), np.uint8)
# create RGB verticle stripes
# note cv2 channels are arranged BGR
# red stripe
rgb[:,0:width//3] = (0,0,255)
# green stripe
rgb[:,width//3:2*width//3] = (0,255,0)
# blue stripe
rgb[:,2*width//3:width] = (255,0,0)
# return rgb 3d-array
return rgb
Note - the labVIEW code is attached as a VI snippet so you should be able to drag it into a fresh LabVIEW Block-Diagram
Alternatively all the code is in this github gist
As I'm lead to believe, OpenCV reads images in BGR colorspace ordering and we usually have to convert it back to RGB like this:
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
But when I try to simply read an image and show it, the coloring seems fine (without the need to convert BGR to RGB):
img_bgr = cv2.imread(image_path)
cv2.imshow('BGR Image',img_bgr)
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
cv2.imshow('RGB Image',img_rgb )
cv2.waitkey(0)
So is imshow() changing the ordering within the function automatically (from BGR to RGB) or the ordering has been BGR all along?
BGR and RGB are not color spaces, they are just conventions for the order of the different color channels. cv2.cvtColor(img, cv2.COLOR_BGR2RGB) doesn't do any computations (like a conversion to say HSV would), it just switches around the order. Any ordering would be valid - in reality, the three values (red, green and blue) are stacked to form one pixel. You can arrange them any way you like, as long as you tell the display what order you gave it.
OpenCV imread, imwrite and imshow indeed all work with the BGR order, so there is no need to change the order when you read an image with cv2.imread and then want to show it with cv2.imshow.
While BGR is used consistently throughout OpenCV, most other image processing libraries use the RGB ordering. If you want to use matplotlib's imshow but read the image with OpenCV, you would need to convert from BGR to RGB.
screen = cv2.cvtColor(screen, cv2.COLOR_RGB2BGR)
this one line code changes rgb to bgr
for matplotlib we need to change BGR to RGB:
img = cv2.imread("image_name")
img = img[...,::-1]
plt.imshow(img)
opencv_image_with_bgr_channels = cv2.imread('path/to/color_image.jpg')
matplotlib_compatible_image_with_rgb_channels = opencv_image_with_bgr_channels[:,:, ::-1]
This converts BGR to RGB Channels Image by reversing the channels.
If you do not need to use any other Image processing library (example Matplotlib's imshow), there is no need to do color scale conversion. Below code is an example, where the color scale conversion is done but when the image is loaded, it is still loaded in BGR. This conversion is not needed as the image is displayed using cv2.imshow().
import cv2
# read the image #
image = cv2.imread('<<Image Path>>')
image_rgb = cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
# write a function to draw circles on the image #
def draw_circle(event,x,y,flags,params):
if event == cv2.EVENT_RBUTTONDOWN:
cv2.circle(img=image_rgb,center=(x,y),radius=100,color=(0,0,255),thickness=10)
# Open CV callbacks #
cv2.namedWindow(winname='ImageWindow')
cv2.setMouseCallback('ImageWindow',draw_circle)
# display the image till the user hits the ESC key #
while True:
cv2.imshow('ImageWindow',image_rgb)
if cv2.waitKey(20) & 0xFF == 27:
break
cv2.destroyAllWindows()
Alternatively, you can use imutils.opencv2matplotlib() function, which does not need BGR to RGB conversion.
Observe the following image:
Observe the following Python code:
import cv2
img = cv2.imread("rainbow.png", cv2.IMREAD_COLOR)
img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # convert it to hsv
img = cv2.cvtColor(img, cv2.COLOR_HSV2BGR) # convert back to BGR
cv2.imwrite("out.png", img)
Here's the output image:
If you can't see it, there's a clear loss of visual fidelity in the image here. For comparison's sake, here's the original next to the output image zoomed in around the yellows:
What's going on here? Is there any way to prevent these blocky artifacts from appearing? I need to convert to the HSL color space to rotate the hue, but I can't do that if I'm going to get these kinds of artifacts.
As a note, the output image does not have the artifacts when I don't do the two conversions; the conversions themselves are indeed the cause.
Back at a computer now - try like this:
#!/usr/bin/env python3
import numpy as np
import cv2
img = cv2.imread("rainbow.png", cv2.IMREAD_COLOR)
img = img.astype(np.float32)/255 # go to 32-bit float on 0..1
img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # convert it to hsv
img = cv2.cvtColor(img, cv2.COLOR_HSV2BGR) # convert back to BGR
cv2.imwrite("output.png", (img*255).astype(np.uint8))
I think the problem is that when you use unsigned 8-bit representation, the Hue gets "squished" from a range of 0..360 into a range of 0..180, in 2 degree increments in order to stay within 8-bit unsigned range of 0..255 causing steps between nearby values. A solution is to move to 32-bit floats and scale to the range 0..1.
In the program given below I am adding alpha channel to a 3 channel image to control its opacity. But no matter what value of alpha channel I give there is no effect on image! Anyone could explain me why?
import numpy as np
import cv2
image = cv2.imread('image.jpg')
print image
b_channel,g_channel,r_channel = cv2.split(image)
a_channel = np.ones(b_channel.shape, dtype=b_channel.dtype)*10
image = cv2.merge((b_channel,g_channel,r_channel,a_channel))
print image
cv2.imshow('img',image)
cv2.waitKey(0)
cv2.destroyAllWindows()
I can see in the terminal that alpha channel is added and its value changes as I change it in the program, but there is no effect on the opacity of the image itself!
I am new to OpenCV so I might be missing something simple. Thanks for help!
Alpha is a channel that is used to control the opacity of an image. An alpha channel typically doesn't do anything unless you perform an action on it. It doesn't make an image transparent on its own.
Alpha is usually used to either remove unimportant areas of an image or to combine one image with another image. In the first case the image is usually simply multiplied by its alpha. This is sometimes referred to premultiplying. In this case the dark areas of the alpha channel darken the RGB and the bright areas leave the RGB untouched.
R = R*A
G = G*A
B = B*A
Here is a version of your code that might do what you want (Note- I converted to 32-bit because it's easier to use alpha channels when they are ranged from 0 to 1):
import numpy as np
import cv2
i = cv2.imread('image.jpg')
img = np.array(i, dtype=np.float)
img /= 255.0
cv2.imshow('img',img)
cv2.waitKey(0)
#pre-multiplication
a_channel = np.ones(img.shape, dtype=np.float)/2.0
image = img*a_channel
cv2.imshow('img',image)
cv2.waitKey(0)
cv2.destroyAllWindows()
The second case is used when trying to overlay an image over another image. This is a compositing operation that is often referred to as an "over" merge or a "blend" merge. In this case there is a foreground image "A" and a background image "B" and an alpha channel which could be included in the RGB images or on its own. In this case you can place A over B using:
output = (A * alpha) + (B * (1-alpha))
Actually, the answer is simple. OpenCV's imshow() function ignores the alpha channel.
If you want to see the effect of your alpha channel, save your image in PNG format (because that supports alpha channel) and display in a different viewer.
I also wrote a decorator/enhancement for imshow() here that helps visualise transparent images.