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
Related
so when I Hstack the noise to images in the frame, all of the images will be shown broken.
What's the problem with the noise?
import numpy as np
import cv2
image = cv2.imread("sunset.jpg")
img = np.float64(image)
noise = np.random.randn(*img.shape) * 80 # 80% noise
noisy_img = img + noise
noisy_img = np.uint8(np.clip(noisy_img, 0, 255))
# cv2.imshow("res:", np.hstack([image, noisy_img]))
cv2.imshow("res:", np.hstack([image, noisy_img, noise]))
cv2.waitKey()
cv2.destroyAllWindows()
There is a fundamental problem: the np.uint8 type.
Uint8 can only store 2^8=256 values in the range [0,255]. If your original image has high pixel values, those pixel can saturate and get clipped at 255 when you use
np.clip()
I would recommend reducing the noise intensity.
You must normalize the noise data to integer values (int) as follows:
cv2.imshow("res:", np.hstack((image, noisy_img, np.uint8(noise))))
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
In OpenCV when I convert JPG image (8 bit per channel) to gray scale, either using cv.cvtColor(img, cv.COLOR_BGR2GRAY) or simply reading it as a grayscale immediately: cv.imread(path + 'image.JPG', cv.IMREAD_GRAYSCALE), the grayscale image is only an 8 bit image.
Is there a way of getting a 16 bit gray scale image?
I know I can square the values of the grayscale image and get 16 bits that way, but I want a true 16 bit of colour information (not 8 bits scaled up).
What you could do is create your custom function to convert BGR in uint16 to a GRAYSCALE in uint16. Fore example as follows:
def bgr2gray(img):
weights = [0.11, 0.59, 0.3]
return np.uint16(np.dot(img, weight))
Where the weights are the standard weights used to convert from RGB/BGR to grayscale (https://www.tutorialspoint.com/dip/grayscale_to_rgb_conversion.htm#:~:text=Since%20its%20an%20RGB%20image,Its%20done%20in%20this%20way.&text=If%20you%20have%20an%20color,into%20grayscale%20using%20average%20method.).
Then you would apply this function to the BGR image that you previously converted to uint16. However, this should in general not give you more information than converting the 8-bit BGR image to a 8-bit grayscale image. It would be different if the original BGR image was 16-bit.
To convert from uint8 to uint16, you should use the following formula:
img16 = np.uint16(img8)*256
You can convert you 8 bit/pixel gray image to a 16 bits per pixel but you have to note that the 16 bpp image will not transport more information than the original one.
If you want to keep the original dynamic (0->255) do:
img16 = np.uint16(img8)
If you want to extend the dynamic (make sense for further processing that require more than 8 bpp prevision ) do:
cv.convertScaleAbs(img8,img16,alpha=(65535/255))
Scale the BGR coefficients by 256 before applying them to the image:
import numpy as np
import cv2
# Create a small BGR image with unique pixel values
test_img = np.array([[(b, g, r)
for b in range(0, 256, 8)
for g in range(2, 256, 8)
for r in range(4, 256, 8)]], dtype=np.uint8)
coefficients = np.uint16(256 * np.array((.114, .587, .299)))
test_img.dot(coefficients)
This does preserve additional information:
>>> len(np.unique(cv2.cvtColor(test_img, cv2.COLOR_BGR2GRAY)))
249
>>> len(np.unique(test_img.dot(coefficients)))
7034
I have a black and white image with (224,224) shape, but I want to have (224,224,3), so I need to expand the dim, but not with empty value, so np.expand_dims or np.atleast_3d couldn't help me. How can I do this correctly? Thanks.
What I use:
from PIL import Image
img = Image.open('data/'+link)
rsize = img.resize((224,224))
rsizeArr = np.asarray(rsize)
When we use numpy.dstack(), we don't have to expand the dimension manually, it will take care of that work and stack it along the third axis which is what we want.
In [4]: grayscale = np.random.random_sample((224,224))
# make it RGB by stacking the grayscale image along depth dimension 3 times
In [5]: rgb = np.dstack([grayscale]*3)
In [6]: rgb.shape
Out[6]: (224, 224, 3)
For your specific case, it should be:
rsize_rgb = np.dstack([rsize]*3)
For whatever reason, if you still want to expand the dimension of your grayscale image by 1 and then make it as RGB image, then you can use numpy.concatenate() as in:
In [9]: rgb = np.concatenate([grayscale[..., np.newaxis]]*3, axis=2)
In [10]: rgb.shape
Out[10]: (224, 224, 3)
For your specific case, it would then be:
rsize_rgb = np.concatenate([rsize[..., np.newaxis]]*3, axis=2)
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)