I am trying to process images from Unity3D WebCamTexture graphics format(ARGB32) using OpenCV Python. But I am having trouble interpreting the image on the Open CV side. The image is all Blue (possibly due to ARGB)
try:
while(True):
data = sock.recv(480 * 640 * 4)
if(len(data) == 480 * 640 * 4):
image = numpy.fromstring(data, numpy.uint8).reshape( 480, 640, 4 )
#imageNoAlpha = image[:,:,0:2]
cv2.imshow('Image', image) #further do image processing
key = cv2.waitKey(1) & 0xFF
if key == ord("q"):
break
finally:
sock.close()
The reason is because of the order of the channels. I think the sender read image as a RGB image and you show it as a BGR image or vice versa.
Change the order of R and B channels will solve the problem:
image = image[..., [0,3,2,1]] # swap 3 and 1 represent for B and R
You will meet this problem frequently if you work with PIL.Image and OpenCV. The PIL.Image will read the image as RGB and cv2 will read as BGR, that's why all the red points in your image become blue.
OpenCV uses BGR (BGRA when including alpha) ordering when working with color images [1][2], this applies to images read/written with imread(), imwrite(); images acquired with VideoCapture; drawing functions ellipse(), rectangle(); and so on. This convention is self-consistent within the library, if you read an image with imread() and show it with imshow(), the correct colors will appear.
OpenCV is the only library I know that uses this ordering, e.g. PIL and Matplotlib both use RGB. If you want to convert from one color space to another use cvtColor(), example:
# Convert RGB to BGR.
new_image = cvtColor(image, cv2.COLOR_RGB2BGR)
See the ColorConversionCodes enum for all supported conversion pairs. Unfortunately there is no ARGB to BGR, but you can always manually manipulate the NumPy array anyway:
# Reverse channels ARGB to BGRA.
image_bgra = image[..., ::-1]
# Convert ARGB to BGR.
image_bgr = image[..., [3, 2, 1]]
There is also a mixChannels() function and a bunch other array manipulation utilities but most of these are redundant in OpenCV Python since images are backed by NumPy arrays so it's easier to just use the NumPy counterparts instead.
OpenCV uses BGR for seemingly historical reasons: Why OpenCV Using BGR Colour Space Instead of RGB.
References:
[1] OpenCV: Mat - The Basic Image Container (Search for 'BGR' under Storing methods.)
[2] OpenCV: How to scan images, lookup tables and time measurement with OpenCV
Image from [2] showing BGR layout in memory.
IMAGE_WIDTH = 640
IMAGE_HEIGHT = 480
IMAGE_SIZE = IMAGE_HEIGHT * IMAGE_WIDTH * 4
try:
while(True):
data = sock.recv(IMAGE_SIZE)
dataLen = len(data)
if(dataLen == IMAGE_SIZE):
image = numpy.fromstring(data, numpy.uint8).reshape(IMAGE_HEIGHT, IMAGE_WIDTH, 4)
imageDisp = cv2.cvtColor(image, cv2.COLOR_RGBA2BGR)
cv2.imshow('Image', imageDisp)
key = cv2.waitKey(1) & 0xFF
if key == ord("q"):
break
finally:
sock.close()
Edited as per the suggestions from comment
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'm trying to process some images in OpenCV. Specifically, swapping color panes using the following functions.
def green_ble_swap(image)
im_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
im_copy = np.copy(im_rgb)
blue = im_copy[:,:,2].copy()
green = im_copy[:,:,1].copy()
im_copy[:,:,2] = green
im_copy[:,:,1] = blue
return im_copy
However I get the following error.
> Unsupported depth of input image:
> 'VDepth::contains(depth)'
> where
> 'depth' is 4 (CV_32S)
Not sure whats the error here.
You're encountering the error because you're trying to perform a 3-channel operation on a 4-channel image. Specifically, the error comes from trying to convert a BGR image to RGB when the input image has a transparent channel. The correct method would be to do cv2.COLOR_BGRA2RGB instead of cv2.COLOR_BGR2RGB. You can swap the blue and green channels in-place using cv2.split() to obtain the BGR channels (for 3-channel image) and BGRA channels for (4-channel image) then swap the channels using Numpy indexing. You also need to use the cv2.IMREAD_UNCHANGED flag when loading the image or the alpha channel will be dropped. Example:
Input -> Output
import cv2
import numpy as np
def green_blue_swap(image):
# 3-channel image (no transparency)
if image.shape[2] == 3:
b,g,r = cv2.split(image)
image[:,:,0] = g
image[:,:,1] = b
# 4-channel image (with transparency)
elif image.shape[2] == 4:
b,g,r,a = cv2.split(image)
image[:,:,0] = g
image[:,:,1] = b
return image
# Load image
image = cv2.imread('1.png', cv2.IMREAD_UNCHANGED)
cv2.imshow('image', image)
# Swap channels
swapped = green_blue_swap(image)
cv2.imshow('swapped', swapped)
cv2.waitKey()
I have recorded some data as npy file. And I tried to diplay the image (data[0]) to check if it makes sense with the following code
import numpy as np
import cv2
train_data = np.load('c:/data/train_data.npy')
for data in train_data:
output = data[1]
# only take the height, width and channels of the 4 dimensional array
image = data[0][0, :, :, :]
# image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
cv2.imshow('test', image)
print('output {}'.format(output))
if cv2.waitKey(25) & 0xFF == ord('q'):
cv2.destroyAllWindows()
break
But if I display the images without the line image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) the images seem to be BGR based. If I comment this line into the code the images are displayed correctly.
My question: Does this observation imply that the image array is already in BGR format? Or does this imply that cv2.imshow() does by
default interprete the array as BGR array?
Matplotlib and Numpy read images into RGB and processes them as RGB. OpenCV reads images into BGR and processes them as BGR. Either system recognizes a range of input types, has ways to convert between color spaces of almost any type, and offers support of a variety of image processing tasks.
This gives three different ways to load an image (plt.imread(), ndimage.imread() and cv2.imread()), two systems for processing the data (Numpy and CV2), and two ways to display the image (plt.imshow() and cv2.imshow()), and really, there is a third way to display the image using pyplot, if you want to treat the image as numerical data in 2-d plus another dimension for each color.
Here is some simple code to demonstrate some of this.
#!/usr/bin/python
import matplotlib.pyplot as plt
from scipy.ndimage import imread
import numpy as np
import cv2
img = imread('index.jpg')
print( "img data type: %s shape %s"%( type(img), str( img.shape) ) )
plt.imshow( img )
plt.title( 'pyplot as read' )
plt.savefig( 'index.plt.raw.jpg' )
cv2.imshow('cv2, read by numpy', img)
cv2.imwrite('index.cv2.raw.jpg',img)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
cv2.imshow('after conversion', img)
cv2.imwrite('index.cv2.bgr2rgb.jpg',img)
This generates the following line of text, and the following three example image files.
img data type: <type 'numpy.ndarray'> shape (225, 225, 3)
The correct image has red as the upper circle. We read the image into a numpy array, using ndimage.imread(), and show it with Pyplot's imshow() and get the correct image. We then show it with cv2.imshow() and we see that the red channel is interpreted as the blue channel and vice versa. Then we convert the colorspace and we see that cv2.imshow() now interprets the result correctly.
plt.imshow(), as read by ndimage():
cv2.imshow(), the image as read by ndimage:
cv2.imshow(), after converting from RGB to BGR:
I am new to image processing. I program in Python3 and uses the OpenCV image processing library.I want to adjust the following attributes.
Brightness
Contrast
Vibrance
Hue
Saturation
Lightness
For 4, 5, 6. I am using the following code to convert to HSV space.
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)
h += value # 4
s += value # 5
v += value # 6
final_hsv = cv2.merge((h, s, v))
img = cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
The only tutorial I found for 1 and 2 is here. The tutorial uses C++, but I program in Python. Also, I do not know how to adjust 3. vibrance. I would very much appreciate the help, thanks!.
Thanks to #MarkSetchell for providing the link.
In short, the answers uses numpy only and the formula can be presented as in below.
new_image = (old_image) × (contrast/127 + 1) - contrast + brightness
Here contrast and brightness are integers in the range [-127,127]. The scalar 127 is used for this range.
Also, below is the code I used.
brightness = 50
contrast = 30
img = np.int16(img)
img = img * (contrast/127+1) - contrast + brightness
img = np.clip(img, 0, 255)
img = np.uint8(img)
a simple way for brightness adjustment, proper for both color and monochrome images is
img = cv2.imread('your path',0)
brt = 40
img[img < 255-brt] += brt
cv2.imshow('img'+ img)
where brt could be a positive number for increase brightness or a negative for darkness.
The following links for a before and after of an image processed in this code, when the brt = 40 :
input image
output image
I am not sure if this would help, but for changing Brightness, Contrast I personally switch the image to PIL.Image and use PIL.ImageEnhance which comes in handy when using the ratios or percentages.
image = PIL.Image.open("path_to_image")
#increasing the brightness 20%
new_image = PIL.ImageEnhance.Brightness(image).enhance(1.2)
#increasing the contrast 20%
new_image = PIL.ImageEnhance.Contrast(image).enhance(1.2)
I still have not found a clean way for Vibrance. For more on ImageEnahance, I'd suggest to read the official doc - https://pillow.readthedocs.io/en/stable/reference/ImageEnhance.html
For Conversion, I use this ..
NOTE - OpenCV uses BGR and PIL uses RGB channels. So, can get messy if not converted properly.
#convert pil.image to opencv (numpy.ndarray)
#need numpy library for this
cv_image = numpy.array(pil_image)
#convert opencv to pil.image
image = cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB)
pil_image = Image.fromarray(image)
Here is one way to do the vibrance in Python/OpenCV.
Convert to HSV. Then create a sigmoid function LUT.
(The sigmoid function increases linearly from the origin, but then tapers off to flat.)
See https://en.wikipedia.org/wiki/Sigmoid_function
Apply the LUT to S channel.
Convert back to BGR.
Input:
import cv2
import numpy as np
# read image
img = cv2.imread('yellow_building.jpg')
# convert image to hsv colorspace as floats
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)
print(np.amax(s), np.amin(s), s.dtype)
# set vibrance
vibrance=1.4
# create 256 element non-linear LUT for sigmoidal function
# see https://en.wikipedia.org/wiki/Sigmoid_function
xval = np.arange(0, 256)
lut = (255*np.tanh(vibrance*xval/255)/np.tanh(1)+0.5).astype(np.uint8)
# apply lut to saturation channel
new_s = cv2.LUT(s,lut)
# combine new_s with original h and v channels
new_hsv = cv2.merge([h,new_s,v])
# convert back to BGR
result = cv2.cvtColor(new_hsv, cv2.COLOR_HSV2BGR)
# save output image
cv2.imwrite('yellow_building_vibrance.jpg', result)
# display images
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:
Hi I am currently working on trying to convert a gray scale image to its original color format using Open CV in python.
import cv2
img = cv2.imread('bw.jpg')
img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
cv2.imwrite('gray_image.png',gray_image)
executing this produces an error:
error: (-215) scn == 1 && (dcn == 3 || dcn == 4) in function cv::cvtColor
Code in Python Imaging Library are also welcome.
Any help will be appreciated.
Thank you
I am assuming that you are trying to convert a single channel image to 3 channel grayscale image. You are reading the image as img = cv2.imread('bw.jpg'), by default if you do not pass any param to cv2.imread(), then it reads a 3 channel image, irrespective of the original number of channels in the image. You may simply remove the line cv2.cvtColor(img, cv2.COLOR_GRAY2RGB), as the img is already a 3 channel image with only grayscale information.
However if you are into this delusion that OpenCV has functionality of filling RGB colors to your grayscale image, then you are probably using wrong library. You can checkout other Open Source projects like this, which colorise your image using Deep Learning.
See inline comment where mistake was made.
import cv2
img = cv2.imread('bw.jpg')
x = img.shape
# check for color or gray-scale image type.
if x[3] == 3:
print 'Got color image'
# variable "gray_image" linked to result.
gray_image = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
cv2.imwrite('gray_image.png',gray_image) # varname no longer img > gray_image.
else:
print 'Got black/white, single channel image.'
url = 'https://github.com//gustavla//autocolorize'
print "Using ZdaR's posted solution from %s" % (url)