My problem is that I would like to threshold an HSV image, thanks to OpenCV inRange function.
To find my range of hue, I use ImageJ.
I uploaded my picture after I changed it from RGB to HSV, and threshold it manually.
My HSV image, converted from RGB image is:
I obtained these values from imageJ:
H (62;100) S (0;255) V (0;255)
And this threshold result:
But when I try to obtain the same result with a Python script using OpenCV, I obtain this:
Here is my script:
import cv2
image_color = cv2.imread(image_color)
image_hsv_convert = cv2.cvtColor(image_color, cv2.COLOR_BGR2HSV)
cv2.imwrite(folder_dest, image_hsv_convert)
H_low = (62/255)*180
H_high = (100/255)*180
HUE_MIN = (H_low,0,0)
HUE_MAX = (H_high,255,255)
frame_threshed = cv2.inRange(image_hsv_convert, HUE_MIN, HUE_MAX)
cv2.imwrite(folder_dest2, frame_threshed)
I know the H values in OpenCV go from "0 to 179", so I've converted ImageJ values into OpenCV values. What's the problem here ? Is ImageJ showing me a wrong result ? Or is my code wrong ?
In ImageJ, HSV (threshold tool) values all range from 0 to 255. But In OpenCV, S and V range from 0 to 255, while H only ranges from 0 to 180. In ImageMagick, S and V range from 0 to 255, but H ranges from 0 to 360. In GIMP, S and V range from 0 to 100 and H ranges from 0 to 360. So one must understand which range is being used by each tool for HSV.
Thus different scaling of the ranges, especially, for H in different tools account for different threshold results.
See also https://en.wikipedia.org/wiki/HSL_and_HSV
The OpenCV imwrite() function can only write BRG images. When you pass a matrix with an image saved in HSV format, it interprets the HSV image as a BGR image. If you convert the HSV matrices to BGR and then write the BGR ones this should work.
eg
frame_threshed_bgr = cv2.cvtColor(frame_threshed, cv2.COLOR_HSV2BGR)
cv2.imrwite(folder_dest2,frame_threshed_bgr)
Related
I'm experimenting with saturation adjustments in OpenCV. A standard approach to the problem is to convert input image from BGR(A) to HSV colour space and simply adjust the S channel like so:
# Convert from BGR to HSV
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
# We want to increase saturation by 50
value = 50
# Grab saturation channel
saturation = hsv[..., 1]
# Increase saturation by a given value
saturation = cv2.add(saturation, value)
# Clip resulting values to fit within 0 - 255 range
np.clip(saturation, 0, 255)
# Put back adjusted channel into the HSV image
hsv[..., 1] = saturation
# Convert back from HSV to BGR
cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
Below is the input image I am working with and the result of the above operations. You can clearly see that something is terribly off with low frequency areas as well with highlights.
Perhaps there is another approach to solve the problem without producing such blockiness?
P.S
The blockiness is not a result of JPEG compression as the artefact blocks do not fit into the standard JPEGs 8x8 coding units. Also, I've confirmed the problem persists on the lossless PNG as both input and output format.
Before:
After:
I suggest another approach using black and white version of the picture, it worked best for me:
Just sum the picture with the b/w version with coefficient that give 1 in sum. Alpha is the saturation parameter: alpha = 0 gives b/w picture, higher alpha gives more saturated picture. C++ code example:
addWeighted(image, alpha, black_and_white(image), 1 - alpha, 0.0, res);
I am trying to use the CIELAB and CIELUV color space for doing some computer vision work. I used OpenCV to convert from RGB to the spaces
img = cv2.imread(PATH)
img_Lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
img_Luv = cv2.cvtColor(img, cv2.COLOR_BGR2LUV)
From what I have read, LUV has pixel values of L from 0 to 100 and U & V are -100 to 100. Same thing is given in this research paper I am referencing
https://drive.google.com/file/d/13_Xd8RfDZyQRGuF2-_b6lFx0v2wMhu6d/view?usp=sharing
But OpenCV is giving pixel values as large as 236 and no negative values on converting to LUV. Similar is case for LAB. A and B ranges from -128 to 127, from what I have read, but OpenCV gives all +ve values. Why is that so? Is the range I am considering and mentioned in the paper wrong? If that is the case, what is the actual range?
I want a way or steps to unify the brightness of 2 images or in other words make their brightness the same but without assigning them. I know how to get the brightness of an image using PIL, the code is found below:
from PIL import Image
imag = Image.open("test.png")
# Convert the image te RGB if it is a .gif for example
imag = imag.convert('RGB')
# coordinates of the pixel
X, Y = 0, 0
# Get RGB
pixelRGB = imag.getpixel((X, Y))
R, G, B = pixelRGB
brightness = sum([R, G, B]) / 3 ##0 is dark (black) and 255 is bright (white)
print(brightness)
Does anyone have an idea of how to make 2 images having the same brightness. Thank you
You can use the mean/standard deviation color transfer technique in Python/OpenCV as described at https://www.pyimagesearch.com/2014/06/30/super-fast-color-transfer-images/. But to force it so as not to modify the color and only adjust the brightness/contrast, convert your image to HSV. Process only the V channel using the method described in that reference. Then combine the new V and old S and H channels and convert that back to BRG.
I am working with OpenCV to convert an image from RGB to HSV. Ultimately, I have a Matlab routine that I am trying to implement in Python, so I am double checking my conversions against the values obtained in Matlab. I've got some questions about the conversion of RGB to HSV using OpenCV. For example, take these two different ways of converting the image.
img = cv2.imread(img_path)
hsv1 = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
hsv2 = cv2.cvtColor(np.float32(img), cv2.COLOR_BGR2HSV)
Using Matlab, the max value of the hue matrix is found as:
>> I = imread(img_path);
>> I_hsv = rgb2hsv(I);
>> max(max(I_hsv(:,:,1)))
ans =
0.9979
Using the HSV matrices obtained using OpenCV:
In [1]: cv2.minMaxLoc(hsv1[:,:,0]/180.)
Out[1]: (0.0, 0.9944444444444445, (72, 0), (1013, 399))
In [2]: cv2.minMaxLoc(hsv2[:,:,0]/360.)
Out[2]: (0.0, 0.9979166388511658, (72, 0), (456, 768))
So I have two questions.
1) Why when I convert to float32 does the hue matrix have a maximum value of 360, instead of 180?
2) Why does the conversion to float32 make the value of the hue matrix closer to that computed using Matlab?
#fmw42 is correct in that the opencv spec calls for H to be divided by 2 when the output type is uint8 providing a range of H from 0 to 180. The range of H will be 0 to 360 if the output type is float32.
This stuffing of values is not ideal for consistency or comparing results when data may be in different formats, but at least it is documented.
From color_conversions v4.7.0:
RGB ↔ HSV
...
On output 0≤V≤1, 0≤S≤1, 0≤H≤360 .
The values are then converted to the destination data type:
8-bit images: V←255V,S←255S,H←H/2(to fit to 0 to 255)
16-bit images: (currently not supported) V<−65535V,S<−65535S,H<−H
32-bit images: H, S, and V are left as is
I'm trying to implement binary image filter (to get monochrome binary image) using python & PyQT5, and, to retrieve new pixel colors I use the following method:
def _new_pixel_colors(self, x, y):
color = QColor(self.pixmap.pixel(x, y))
result = qRgb(0, 0, 0) if all(c < 127 for c in color.getRgb()[:3]) else qRgb(255, 255, 255)
return result
Could It be a correct sample of binary filter for RGB image? I mean, is that a sufficient condition to check whether the pixel is brighter or darker then (127,127,127) Gray color?
And please, do not provide any solutions with opencv, pillow, etc. I'm only asking about the algorithm itself.
I would at least compare against intensity i=R+G+B ...
For ROI like masks you can use any thresholding techniques (adaptive thresholding is the best) but if your resulting image is not a ROI mask and should resemble the visual features of the original image then the best conversion I know of is to use Dithering.
The Idea behind BW dithering is to convert gray scales into BW patterns preserwing the shading. The result is often noisy but preserves much much more visual details. Here simple naive C++ dithering (sorry not a Python coder):
picture pic0,pic1;
// pic0 - source img
// pic1 - output img
int x,y,i;
color c;
// resize output to source image size clear with black
pic1=pic0; pic1.clear(0);
// dithering
i=0;
for (y=0;y<pic0.ys;y++)
for (x=0;x<pic0.xs;x++)
{
// get source pixel color (AARRGGBB)
c=pic0.p[y][x];
// add to leftovers
i+=WORD(c.db[picture::_r]); // _r,_g,_b are just constants 0,1,2
i+=WORD(c.db[picture::_g]);
i+=WORD(c.db[picture::_b]);
// threshold white intensity is 255+255+255=765
if (i>=384){ i-=765; c.dd=0x00FFFFFF; } else c.dd=0;
// copy to destination image
pic1.p[y][x]=c;
}
So its the same as in the link above but using just black and white. i is the accumulated intensity to be placed on the image. xs,ys is the resolution and c.db[] is color channel access.
If I apply this on colored image like this:
The result looks like this:
As you can see all the details where preserved but a noisy patterns emerge ... For printing purposes was sometimes the resolution of the image multiplied to enhance the quality. If you change the naive 2 nested for loops with a better pattern (like 16x16 squares etc) then the noise will be conserved near its source limiting artifacts. There are also approaches that use pseudo random patterns (put the leftover i near its source pixel in random location) that is even better ...
But for a BW dithering even naive approach is enough as the artifacts are just one pixel in size. For colored dithering the artifacts could create unwanted horizontal line patterns of several pixels in size (depends on used palette mis match the worse palette the bigger artifacts...)
PS just for comparison to other answer threshold outputs this is the same image dithered:
Image thresholding is the class of algorithms you're looking for - a binary threshold would set pixels to 0 or 1, yes.
Depending on the desired output, consider converting your image first to other color spaces, in particular HSL, with the luminance channel. Using (127, 127, 127) as a threshold does not uniformly take brightness into account because each channel of RGB is the saturation of R, G, or B; consider this image:
from PIL import Image
import colorsys
def threshold_pixel(r, g, b):
h, l, s = colorsys.rgb_to_hls(r / 255., g / 255., b / 255.)
return 1 if l > .36 else 0
# return 1 if r > 127 and g > 127 and b > 127 else 0
def hlsify(img):
pixels = img.load()
width, height = img.size
# Create a new blank monochrome image.
output_img = Image.new('1', (width, height), 0)
output_pixels = output_img.load()
for i in range(width):
for j in range(height):
output_pixels[i, j] = threshold_pixel(*pixels[i, j])
return output_img
binarified_img = hlsify(Image.open('./sample_img.jpg'))
binarified_img.show()
binarified_img.save('./out.jpg')
There is lots of discussion on other StackExchange sites on this topic, e.g.
Binarize image data
How do you binarize a colored image?
how can I get good binary image using Otsu method for this image?