I have a number between 0 and 1, and I would like to convert it into the corresponding RGB components in a blue scale.
So, when the number is 0, I would like to obtain [255,255,255] (i.e. white), while if the number is 1 I would like to obtain [0,0,255] (blue).
Do you know a formula or how this can be implemented with Python?
It would be nice to know also how to convert the 0-1 number to other color scales, for example green (in the sense that 0 corresponds again to [255,255,255], while 1 corresponds to [0,255,0]).
Thanks in advance for any help you can provide!
def green(brightness):
brightness = round(255 * brightness) # convert from 0.0-1.0 to 0-255
return [255 - brightness, 255, 255 - brightness]
def blue(brightness):
brightness = round(255 * brightness) # convert from 0.0-1.0 to 0-255
return [255 - brightness, 255 - brightness, 255]
# etc., green(1.0) -> [0, 255, 0]
That is a strange "scale", since it goes from white to blue, rather than from black ([0, 0, 0]) to blue.
Anyway, it's just a linear interpolation:
def make_blue(alpha):
invalpha = 1 - alpha
scaled = int(255 * invalpha)
return [scaled, scaled, 255]
How to extending this to other color components should be obvious.
See also this answer for the general question of blending between two colors. In your case, one of the colors is white, the other one is blue for this case, but you also wondered about red and green.
Use the HSL color space to get the correct value. Define the correct color with the Hue, for blue take for example the vale '0.6'. The Saturation can be the max value of 1, and take your number to control the Lightness. 0 means black, 0.5 is the color, and 1 is white. So we only use the range of 0.5 to 1. After you have define your correct HSL color, convert it to the RGB color space.
Putting it all togehter
import colorsys
colorsys.hls_to_rgb(0.6, 1, YOURNUMBER * 0.5 + 0.5)
Related
for y in range(image.getHeight()):
for x in range(image.getWidth()):
def GetDistance(tuple1,tuple2):
tuple1=(r,g,b)
tuple2=(r2,g2,b2)
import math
math.sqrt(((r-r2)**2)+((g-g2)**2)+((b-b2)**2))
return
I am trying to compare to pixel colors in an image. For the colors that are the closest to red I want to change to blue, the closest to blue I want to change to green, and the color in the image that are closest to green I want to change to red. How can I get the color pixels I am looking for in the image?
Personally, I wouldn't approach this using for loops as they are slow, inefficient and error-prone in Python. However, I have implemented it with for loops so it matches your way of thinking and is easier to understand. If you look through some of my other answers you will find better ways of doing it.
I note a couple of things:
you don't need to take the square root of the distances, you can simply compare the squared distances. If a**2 > b**2, it follows a>b, as long as there are no negatives involved which will always be the case here because both a and b are themselves the sums of squares
as your output image will consist of just 3 colours, it can best be stored as a palette image - see here. So I did that.
#!/usr/bin/env python3
from PIL import Image
def closestColour(px):
"""Returns palette index for whichever colour (out of R, G and B) is nearest"""
# Unpack the tuple into its constituent parts
R, G, B = thispx
# Calculate distance to solid Red, solid Green and solid Blue corners of colour cube
# No need for sqrt(), if a**2 < b**2 we know a>b as long as no negative numbers are involved and there aren't because a amd b are sums of squares
RdistSquared = (255-R)**2 + G**2 + B**2
GdistSquared = R**2 + (255-G)**2 + B**2
BdistSquared = R**2 + G**2 + (255-B)**2
# Find index of minimum value in list
values = [RdistSquared, GdistSquared, BdistSquared]
mi = values.index(min(values))
# Map red to blue, blue to green and green to red
lkup = [2, 0, 1]
return lkup[mi]
# Open input image and load pixels
im = Image.open('billiards.jpg')
px = im.load()
# List of output pixels
outpx = []
for y in range(im.height):
for x in range(im.width):
thispx = px[x,y]
outpx.append(closestColour(thispx))
# Create palettised output image, same size as original
out = Image.new('P', im.size)
# Stuff in the list of pixels
out.putdata(outpx)
# Stuff in the palette, index 0 = Red, index 1 = Green, index 2 = Blue
out.putpalette([255,0,0, 0,255,0, 0,0,255])
out.save('result.png')
That transforms this:
Photo by Sandesh Sharma on Unsplash
into this:
By the way, there is no real need to write any Python to do this, you can just do it in the Terminal with ImageMagick, like this:
# Make a small palette of the colours we want to remap to
magick xc:red xc:lime xc:blue +append PNG8:rgb-palette.png
# Remap to the new palette, without dithering and then re-order colour channels
magick billiards.jpg +dither -remap rgb-palette.png -channel-fx ',1,2,0' result.png
I have this image:
And I'm trying to write a function in Python that will return True if the image contains blue pixels, or False otherwise.
That image is just an example. I will have others were the blue colour can be slightly different. But they will always be blue letters over a black background.
So far I have this:
def contains_blue(img):
# Convert the image to HSV colour space
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# Define a range for blue color
hsv_l = np.array([100, 150, 0])
hsv_h = np.array([140, 255, 255])
# Find blue pixels in the image
#
# cv2.inRange will create a mask (binary array) where the 1 values
# are blue pixels and 0 values are any other colour out of the blue
# range defined by hsv_l and hsv_h
return 1 in cv2.inRange(hsv, hsv_l, hsv_h)
The function always returns False because no 1 values are found in the array returned by cv2.inRange. Maybe the range defined by hsv_l and hsv_h is not good? I took it from here: OpenCV & Python -- Can't detect blue objects
Any help is appreciated. Thanks.
You could have just used np.any() instead. It will return True if any one pixel has a value of 255.
So instead of
return 1 in cv2.inRange(hsv, hsv_l, hsv_h),
you can just add the following:
return np.any(cv2.inRange(hsv, hsv_l, hsv_h))
Update:
As #AKX mentioned in the comments you could rather try out the following:
return cv2.inRange(hsv, hsv_l, hsv_h).any()
The problem is that you are not reading the documentation of inRange :D
Which tells the following:
That is, dst (I) is set to 255 (all 1 -bits) if src (I) is within the
specified 1D, 2D, 3D, ... box and 0 otherwise.
and you check for 1
# cv2.inRange will create a mask (binary array) where the 1 values
# are blue pixels and 0 values are any other colour out of the blue
# range defined by hsv_l and hsv_h
return 1 in cv2.inRange(hsv, hsv_l, hsv_h)
So the solution is to change it to:
return 255 in cv2.inRange(hsv, hsv_l, hsv_h)
I tested it with your image and returns true, also with a black and white image (BGR though) and returns false.
In my opinion the blue ranges you have chosen are a little far to the violet side... You may use a hsv colorpicker like this one http://colorizer.org/ and select the ranges you will like. Just rememeber OpenCV uses H -> Hue / 2 and S and V are like percentages (0-100) and you just divide them by 100 (0-1.) and multiply them by 255.
This question already has answers here:
Finding red color in image using Python & OpenCV
(3 answers)
Closed 10 months ago.
I am trying to make a program where I detect red. However sometimes it is darker than usual so I can't just use one value.
What is a good range for detecting different shades of red?
I am currently using the range 128, 0, 0 - 255, 60, 60 but sometimes it doesn't even detect a red object I put in front of it.
RGBis not a good color space for specific color detection. HSV will be a good choice.
For RED, you can choose the HSV range (0,50,20) ~ (5,255,255) and (175,50,20)~(180,255,255)using the following colormap. Of course, the RED range is not that precise, but it is just ok.
The code taken from my another answer: Detect whether a pixel is red or not
#!/usr/bin/python3
# 2018.07.08 10:39:15 CST
# 2018.07.08 11:09:44 CST
import cv2
import numpy as np
## Read and merge
img = cv2.imread("ColorChecker.png")
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
## Gen lower mask (0-5) and upper mask (175-180) of RED
mask1 = cv2.inRange(img_hsv, (0,50,20), (5,255,255))
mask2 = cv2.inRange(img_hsv, (175,50,20), (180,255,255))
## Merge the mask and crop the red regions
mask = cv2.bitwise_or(mask1, mask2 )
croped = cv2.bitwise_and(img, img, mask=mask)
## Display
cv2.imshow("mask", mask)
cv2.imshow("croped", croped)
cv2.waitKey()
Related answers:
Choosing the correct upper and lower HSV boundaries for color detection with`cv::inRange` (OpenCV)
How to define a threshold value to detect only green colour objects in an image :Opencv
How to detect two different colors using `cv2.inRange` in Python-OpenCV?
Detect whether a pixel is red or not
Of course, for the specific question, maybe other color space is also OK.
How to read utility meter needle with opencv?
You could check that the red component is the maximum and others are both clearly lower:
def red(r, g, b):
threshold = max(r, g, b)
return (
threshold > 8 # stay away from black
and r == threshold # red is biggest component
and g < threshold*0.5 # green is much smaller
and b < threshold*0.5 # so is b
)
This can be implemented very efficiently using numpy.
The "right way" would be doing a full conversion to HSV and check there, but it's going to be slower and somewhat trickier (hue is an angle so you cannot just take the absolute value of the difference, moreover colors like (255, 254, 254) are going to be qualified as "red" even if they're considered white for a human).
Note also that human visual system tends to compensate for average, so something could be seen as "blue" even if indeed the biggest component is red, but everything in the image is red, so that "doesn't count" for our brain.
In the image below if you ask a human what color is the part in the circle area most would say "blue" while indeed the biggest component is red:
Please, use HSV or HSL (hue, saturation, luminance) instead of RGB, in HSV the red color can be easily detected using the value of hue within some threshold.
Red Color means Red value is higher than Blue and Green.
So you can check the differences between Red and Blue, Red and Green.
You can simply split RGB into individual channels and apply threshold like this.
b,g,r = cv2.split(img_rgb)
rg = r - g
rb = r - b
rg = np.clip(rg, 0, 255)
rb = np.clip(rb, 0, 255)
mask1 = cv2.inRange(rg, 50, 255)
mask2 = cv2.inRange(rb, 50, 255)
mask = cv2.bitwise_and(mask1, mask2)
Hope it can be a solution for your problem.
Thank you.
I need a suitable color-space to detect orange color above very similar colors as red and yellow.I have already tried some color-spaces as :RGB HSV & YUV but object i want to detect , changes its position which means the environmental light changes over time & this is my main problem .
HSV is a good color space for color detection.
This is a hsv colormap for reference:
The x-axis represents Hue in [0,180), the y-axis1 represents Saturation in [0,255], the y-axis2 represents S = 255, while keep V = 255.
To find a color, usually just look up for the range of H and S, and set v in range(20, 255).
For example:
detect orange
Details from my another answer: Choosing the correct upper and lower HSV boundaries for color detection with`cv::inRange` (OpenCV)
To find the orange color, we look up for the map, and find the best range: H :[10, 25], S: [100, 255], and V: [20, 255]. So the mask is cv2.inRange(hsv,(10, 100, 20), (25, 255, 255) )
#!/usr/bin/python3
# 2018.01.21 20:46:41 CST
import cv2
img = cv2.imread("test.jpg")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv,(10, 100, 20), (25, 255, 255) )
cv2.imshow("orange", mask);cv2.waitKey();cv2.destroyAllWindows()
The result:
detect green / yellow/ blue
How to define a threshold value to detect only green colour objects in an image :Opencv
detect two different colors
How to detect two different colors using `cv2.inRange` in Python-OpenCV?
Randomly choosing a color system might not be the best approach.
A more systematic approach could be by looking at a color histogram such as below, which shows all image pixels in the RGB cube.
Then you populate this histogram with orange color samples taken from various images, in such a way to cover all the "oranges" you are thinking of.
This will delimit a region in RGB space and the shape of the region will tell you the most suitable color system, knowing how the other color systems map to to the cube. For example, HLS can be represented as a bicone or bipyramid with it axs along the main diagonal of the cube.
Admittedly, this is a difficult task.
I'm using opencv and numpy to process some satellite images.
I need to differentiate what is "land" from what is "green" (crops and vegetation).
My question is: How can I decide which values are close to green in the RGB format?
What I'm doing so far is:
img = cv2.imread('image1.jpg',1)
mat = np.asarray(img)
for elemento in mat:
for pixel in elemento:
if pixel[1] > 200: # If the level of green is higher than 200, I change it to black
pixel[0] = 0
pixel[1] = 0
pixel[2] = 0
else: # If the level of G is lower than 200 I change it to white.
pixel[0] = 255
pixel[1] = 255
pixel[2] = 255
This code works, but isn't really useful. I need a more precise manner to decide which RGB values correspond to green and which ones does not.
How can I achieve this?
You could use InRange function to find colors in specific range, because you will not be able to find green color from satelites just with one or few values of pixels. InRange function will help you to find a range of set colors (you should set the range of green colors) and return an image with the coordinates of those green pixels ir the original image. I've answered a similar quiestion HERE with examples and code (although it is not python, you should understand the methods and easily implement it in your OpenCV project), you should find everything you need there.