Does OpenCV cv.InRange function work only for RGB images?Can I do thresholding of grayscale image using this function?
I got an error,Following is my code:
import cv2
image=cv2.imread("disparitySGB.jpg")
thresh=cv2.inRange(image,190,255);
It gives the following error:
thresh=cv2.inRange(image,190,255); TypeError: unknown is not a
numpy array
I tried fixing it by:
thresh=cv2.inRange(image,numpy.array(190),numpy.array(255));
Now there is no error but it produces black image.
For a gray-valued image which has shape (M, N) in numpy and size MxN with one single channel in OpenCV, then cv2.inRange takes scalar bounds:
gray = cv2.imread(filename, cv2.CV_LOAD_IMAGE_GRAYSCALE)
gray_filtered = cv2.inRange(gray, 190, 255)
But for RGB-images which have shape (M, N, 3) in numpy and size MxN with three channels in OpenCV you need to have the bounds match the "channel size".
rgb = cv2.imread(filename, cv2.CV_LOAD_IMAGE_COLOR)
rgb_filtered = cv2.inRange(gray, (190, 190, 190), (255, 255, 255))
This is explained in the documentation, although not very clearly.
cv2.inRange(src, lowerb, upperb[, dst]) → dst
Takes src as array and lowerand upper as array or a scalar, this means you can use it to Threshold Grayscale images. You just have to use scalars for upper and lower.
Example:
myResult = cv2.InRange(myGrayscale, 50, 100)
You just need to 'import numpy as np' and your original code should work fine.
Your cv2.imread is reading a RGB image. To read in grayscale it is
image = cv2.imread("disparitySGB.jpg", 0)
Related
How can I divide this shape into a single unit (eg 1x1) like gridding?
Is there a way, either opencv or algorithm or python?
I used that shape as an example, but in reality I want to split a rectangle or a square (unit cube of arbitrary size, not 1x1 in the example).
I used crop method to crop certain grid.
cropped = img[y:y+h,x:x+h]
import numpy as np
import cv2 as cv
img = np.zeros((800, 800, 3), dtype="uint8")
img.fill(255)
cv.rectangle(img,(100,100), (350,600),(0,0,0),2)
cv.rectangle(img, (600,100), (350,350), (0,0,0), 2)
cropped = img[100:100+250, 350:350+250]
cv.imshow('grid',img)
cv.imshow('white',cropped)
cv.waitKey(0)
cv.destroyAllWindows()
I have an aerial image:
I was able to get a binary image of the riverbed of the river part:
After applying a distance transform and some segmentation techniques I was able to get a binary image of the mean riverline:
My question is: how to overlay the white pixels from the riverline so that they're on "top" of the original image?
Here´s an example:
This is a very simple way to solve your problem. But it works.
import cv2
original = cv2.imread('original.png') # Orignal image
mask = cv2.imread('line.png') # binary mask image
result = original.copy()
for i in range(original.shape[0]):
for j in range(original.shape[1]):
result[i, j] = [255, 255, 255] if mask[i, j][0] == 255 else result[i, j]
cv2.imwrite('result.png', result) # saves modified image to result.png
Result
Let's assume your images are numpy arrays called img and mask. Let's also assume that img has shape (M, N, 3), while mask has shape (M, N). Finally, let's assume that img is off dtype np.uint8 while mask is of type np.bool_. If the last assumption isn't true, start with
mask = mask.astype(bool)
Now you can set your river channel to 255 directly:
img[mask, :] = 255
If img were a single grayscale image without a third dimension, as in your last example, you would just remove the : from the index expression above. In fact, you could write it to work for any number of dimensions with
img[mask, ...] = 255
I need to get the minimum RGB value of a circle. How can I do like the average RGB value method (cv2.mean) appliying a mask? To get the average RGB value of a circle I'm doing:
circle_img = np.zeros((circle_img.shape[0],circle_img.shape[1]), np.uint8)
cv2.circle(circle_img,(x_center,y_center),radio,(255,255,255),-1)
datos_rgb = cv2.mean(color_img, mask=circle_img)
Where color_img is the original image.
To get the minimum RGB value I'm doing:
masked_data = cv2.bitwise_and(color_img, color_img, mask=circle_img)
rgb_min = masked_data.reshape((masked_data.shape[0]*masked_data.shape[1], 3)).min(axis=0)
Where masked_data is the second image (masked circle).
But I'm getting all time the value [0,0,0] because of the background I think... I need to do like the average (cv2.mean) apliying the mask to ignore the black background. There is no pure black in the original image, so it is not possible to get the value [0,0,0]
To get the maximum RGB value it works perfectly doing:
masked_data = cv2.bitwise_and(color_img, color_img, mask=circle_img)
rgb_max = masked_data.reshape((masked_data.shape[0]*masked_data.shape[1], 3)).max(axis=0)
Because the black color [0,0,0] it does not affect here.
This is the original image.
This is the masked circle.
You may try using only numpy methods to get the results for all required calculations, rather than using OpenCV for some and numpy for others, and in some cases numpy can out-perform OpenCV in terms of execution time. You may use numpys' min, max and mean as:
import cv2
import numpy as np
img = cv2.imread("./assets/11yeJ.jpg")
mask = np.zeros((img.shape[0],img.shape[1]), np.uint8)
cv2.circle(mask, (493, 338), 30, (255, 255, 255), -1)
# Get the indices of mask where value == 255, which may be later used to slice the array.
img_mask = img[np.where(mask == 255)]
img_avg = np.mean(img_mask, axis=0)
img_min = np.min(img_mask, axis=0)
img_max = np.max(img_mask, axis=0)
I am doing some tests in opencv, looking at blurring and the result in discrete cosine transform (increasing kernel size with each loop). I want to display the image and the dct result in the same frame so I can compare.
When I display them in separate frames, they look good. But when I use np.hpstack or np.concatenate to display the blurred image and the dct, the grayscale image becomes really thresholded. I can see a bit of black with kernel size 1, but after that it pretty much goes white after that in my loop.
import cv2
import numpy as np
img = cv2.imread('lena.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
for i in xrange(1,31,2):
median_blur = cv2.medianBlur(img,i)
string = 'median_blur : kernel size - '+str(i)
imf = np.float32(median_blur)/255.0 # float conversion/scale
dst = cv2.dct(imf) # the dct
img2 = np.uint8(dst)*255.0 # convert back
cv2.putText(median_blur,string,(20,20),cv2.FONT_HERSHEY_COMPLEX_SMALL,1,(0,0,0))
vis = np.hstack([median_blur,img2])
cv2.imshow('Blur',median_blur)
cv2.imshow('dct',img2)
cv2.imshow('together', vis)
cv2.waitKey(500)
I think it must have something to do with median_blur and img2 being of different dimensions but I am confused because they display ok by themselves. Sorry but my screenshot cut off a bit of the 'together' window. I am using greyscale because the dct only works on one channel at a time as far as I know.
The dct result was remaining a float64 because it was multiplied by 255.0 not 255.
The issue was diagnosed by looking at the dimensions and data type of the numpy arrays:
print median_blur.shape, median_blur.dtype
print img2.shape, img2.dtype
print vis.shape, vis.dtype
Initially I had:
(512, 512) uint8
(512, 512) float64
(512, 1024) float64
Then after changing img2 = np.uint8(dst)*255.0 to img2 = np.uint8(dst)*255 I got:
(512, 512) uint8
(512, 512) uint8
(512, 1024) uint8
EDIT:
one last thing. I did NOT want to use the divide by 255 and *255 altogether!! eliminating them gave the dct i wanted. now you can beautifully see the effect of the different blurring filters on the dct.
imf = np.float32(blur) # float conversion (NO scale)
dst = cv2.dct(imf) # the dct
img2 = np.uint8(dst) # convert back to unsigned 8bit image
Taking an image as input, how can I get the rgb matrix corresponding to it?
I checked out the numpy.asarray function. Does that give me the rgb matrix or some other matrix?
Note that this answer is outdated as of 2018; scipy has deprecated imread, and you should switch to imageio.imread. See this transition doc about differences between the two. The code below should work with no changes if you just import the new library in place of the old, but I haven’t tested it.
The simplest answer is to use the NumPy and SciPy wrappers around PIL. There's a great tutorial, but the basic idea is:
from scipy import misc
arr = misc.imread('lena.png') # 640x480x3 array
arr[20, 30] # 3-vector for a pixel
arr[20, 30, 1] # green value for a pixel
For a 640x480 RGB image, this will give you a 640x480x3 array of uint8.
Or you can just open the file with PIL (or, rather, Pillow; if you're still using PIL, this may not work, or may be very slow) and pass it straight to NumPy:
import numpy as np
from PIL import Image
img = Image.open('lena.png')
arr = np.array(img) # 640x480x4 array
arr[20, 30] # 4-vector, just like above
This will give you a 640x480x4 array of type uint8 (the 4th is alpha; PIL always loads PNG files as RGBA, even if they have no transparency; see img.getbands() if you're every unsure).
If you don't want to use NumPy at all, PIL's own PixelArray type is a more limited array:
arr = img.load()
arr[20, 30] # tuple of 4 ints
This gives you a 640x480 PixelAccess array of RGBA 4-tuples.
Or you can just call getpixel on the image:
img.getpixel(20, 30) # tuple of 4 ints
I have a feeling I'm not doing exactly what you wanted here, so please specify if this is totally off. You could open the image like this and get an array of pixels:
import Image
im = Image.open('Lenna.png')
pixels = list(im.getdata())
This will get you a flat list of RGB data that looks like
[(226, 137, 125), (226, 137, 125), (223, 137, 133), (223, 136, 128),
(226, 138, 120), (226, 129, 116), (228, 138, 123), (227, 134, 124),
(227, 140, 127), (225, 136, 119), (228, 135, 126), (225, 134, 121),...
Now this will be all pixels in a flat array, if you want a two dimensional array then some additional code would be needed for that. Not sure if there is a direct function for it in PIL.
I tried imageio.imread and it worked great, but a minute later stumbled upon a function in matplotlib which worked exactly the same, getting a numpy n by m by 3 array:
from matplotlib import pyplot as plt
image = plt.imread(path)
You can do that with Pillow, the getdata method gives you a flat array of the pixels, you can then build a matrix from that using the size of the image.
from PIL import Image
def getPixels(filename):
img = Image.open(filename, 'r')
w, h = img.size
pix = list(img.getdata())
return [pix[n:n+w] for n in range(0, w*h, w)]
Also to add, if you or anyone else is using opencv.
imgc=cv2.imread(file)
or to read in as grayscale
imgc=cv2.imread(file,0)
If you will be doing some comparison between the images you may want to think about turning the array of pixels into histograms to normalise the data.
hist = np.histogram(img.flatten(),256,[0,256])[0]
The above line firstly flattens your img array so you do lose the dimensionality of your image. It then produces bins from 0 to 256 (for the grayscale image) and adds the counts from the img to these bins and returns them as hist which can then be plotted. For example, if the 100 bin has a value of 20 it means that 20 pixels in your image had a value of 100.
Hope this adds another possiblity to think about or to anyone looking to get started in opencv.