Image processing: Make pixels value in an image to either 0 or 255 - machine-learning

Satellite mask image
For image segmentation, I want to label pixel value 0 and 255 only. But my mask dataset contains other pixels value as well. So how can I perform image processing to threshold the value to only 0 and 255.
I only need python code for that.
I tried thresholding but the result was even worse. So expecting better solution for it.

Related

Visualizing RGB bands of RGBN image

I have an RGBN band .tif satellite image of PlanetScope which I would like to preprocess for a neural network. When I view the image in QGIS I get a nice RGB image, however when importing as a numpy array the image is very light. Some information on the image:
Type of the image : <class 'numpy.ndarray'>
Shape of the image : (7327, 7327, 5)
Image Height 7327
Image Width 7327
Image Shape (7327, 7327, 5)
Dimension of Image 3
Image size 268424645
Maximum RGB value in this image 65535
Minimum RGB value in this image 1
The image is uint16 type. The last band (pic[:,:,5]) only shows a singular value (65535) in all instances. Hence, I think this band should be removed leaving the RGBN bands, of which the information is as follows:
Type of the image : <class 'numpy.ndarray'>
Shape of the image : (7327, 7327, 4)
Image Height 7327
Image Width 7327
Image Shape (7327, 7327, 4)
Dimension of Image 3
Image size 214739716
Maximum RGB value in this image 19382
Minimum RGB value in this image 1
The maximum value (19382) of the RGBN image seems pretty low knowing that the range of uint16 images is 0-65535. Subsequently the function 'skimage.io.imshow(image)' shows a nearly white image. I do not understand why QGIS is able to show the image properly in real color but python does not.
The image is loaded by means of pic = skimage.io.imread("planetscope_20180502_43.tif")
I have tried scaling the image with img_scaled = pic / pic.max() and converting it to uint8 before viewing the image with img_as_ubyte(pic) without success. I view the image with skimage.io.imshow(pic).
If necessary the image can be downloaded here. I incorporate the image because somehow it seems not possible to import the image using certain packages (Tifffile for example does not work on this tif file).
The max values of the RGB channels are lower than that of the N channel:
>>> pic.max(axis=(0,1))
array([10300, 7776, 11530, 19382, 65535], dtype=uint16)
But look at the mean values of the RGB channels: they are much smaller than max/2:
>>> pic.mean(axis=(0,1))
array([ 439.14001492, 593.17588875, 542.4638124 , 3604.6826063 ,
65535. ])
You have a high dynamic range (HDR) image here and want to compress its high range to 8 bits for displaying. A linear scaling with the maximum value won't do as the highest peaks are an order of magnitude higher than the average image values. Plotting the histogram of the RGB values:
If you do a linear scaling with some factor that's a bit above the mean and just disregard clipping the rest (now overexposed) values you can display it to see you have valid data:
rgb = pic[..., :3].astype(np.float32) / 2000
rgb = np.clip(rgb, 0.0, 1.0)
But to get a proper image, you will need to look into what the camera response of your data is, and how these HDR images are usually compressed into 8 bits for displaying (I'm not familiar with satellite imaging).
Thank you w-m, I was able to built on that and figured it out. Since w-m already did a neat job to elaborate on the problem, I will just leave the code here that I wrote to resolve the issue:
for i in range(0,4):
min_ = int(np.percentile(image[:,:,i],2))
max_ = int(np.percentile(image[:,:,i],98))
np.maximum(image[:,:,i])
np.minimum(image[:,:,i])
image[:,:,i] = np.interp(image[:,:,i], image[:,:,i].min(), image[:,:,i].max(), (0,255))
image_8bit_scaled = skimage.img_as_ubyte(image)

Erosion/Dilation for binary and grayscale images

I am trying to work out the difference between Erosion and Dilation for binary and grayscale images.
As far as I know, this is erosion/dilation for binary images...
Erosion: If every pixel corresponding to an SE index that has 1 is a 1, output a 1. Otherwise 0.
Dilation: If at least one pixel corresponding to an SE index that has 1 is a 1, output a 1. Otherwise 0.
My question is, how does this work for 16-bit (0, 65535) grayscale images?
So what we have to do is to create an structual Element, that could be for example:
The formula says for dilation says:
image http://utam.gg.utah.edu/tomo03/03_mid/HTML/img642.png
and for erosion:
image http://utam.gg.utah.edu/tomo03/03_mid/HTML/img643.png
that means with have to take the maximum or minumum of each kernel values in the image and add 10 to it. If we have for example:
it goes to using dilation:
How you can see you just look at pixel position x,y take the center and add 10 to it. Then you check the neighbors if the computed value is the maximum. If it is a new maximum the pixel value get replaced, when not the pixel value stays. Hope it is clear for erosion you just take the minimum.

OpenCV - Saturated pixels

Just like the title of this topic, how can I determine in OpenCV if a particular pixel of an image (either grayscale or color) is saturated (for instance, excessively bright)?
Thank you in advance.
By definition, saturated pixels are those associated with an intensity (i.e. either the grayscale value or one of the color component) equal to 255. If you prefer, you can also use a threshold smaller than 255, such as 240 or any other value.
Unfortunately, using only the image, you cannot easily distinguish pixels which are much too bright from pixels which are just a little too bright.

Poisson blending rgb out of range

I am trying to blend two images using Poisson Blending technique. I have written the program and solved the system of linear equations separately for each r,g,b channel. After solving the equation rgb values are going out of bound, each value greater than 255. If I clamp each value to 255, the resulting image becomes white as all three channes are 255 now. My question is that can the rgb values be greater than 255 after solving poisson equation ? How can I have a proper blended image in this case ?
I think you need to change your scale for color values. According to the formula given in most of the online sites (set of equations), they consider the color value to be in the 0 to 1 range. Convert your 0 - 255 scale to floating point values between 0 - 1 and see.

How to get threshold value from histogram?

I'm writing an Android app in OpenCV to detect blobs. One task is to threshold the image to differentiate the foreground objects from the background (see image).
It works fine as long as the image is known and I can manually pass a threshold value to threshold()--in this particular image say, 200. But assuming that the image is not known with the only knowledge that there would be a dark solid background and lighter foreground objects how can I dynamically figure out the threshold value?
I've come across the histogram where I can compute the intensity distribution of the grayscale image. But I couldn't find a method to analyze the histogram and choose the value where the objects of interest (lighter) lies. That is; I want to differ the obviously dark background spikes from the lighter foreground spikes--in this case above 200, but in another case could be say, 100 if the objects are grayish.
If all your images are like this, or can be brought to this style, i think cv2.THRESHOLD_OTSU, ie otsu's tresholding algorithm is a good shot.
Below is a sample using Python in command terminal :
>>> import cv2
>>> import numpy as np
>>> img2 = cv2.imread('D:\Abid_Rahman_K\work_space\sofeggs.jpg',0)
>>> ret,thresh = cv2.threshold(img2,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
>>> ret
122.0
ret is the threshold value which is automatically calculated. We just pass '0' as threshold value for this.
I got 124 in GIMP ( which is comparable to result we got). And it also removes the noise. See result below:
If you say that the background is dark (black) and the foreground is lighter, then I recommend to use the YUV color space (or any other YXX like YCrCb, etc.), because the first component of such color spaces is luminance (or lightning).
So after the Y channel is extracted (via the extractChennel function) we need to analyse the histogram of this channel (image):
See the first (left) hump? It represents dark areas (the background in your situation) on your image. So our aim now is to find a segment (on abscissa, it's red part in the image) that contains this hump. Obviously the left point of this segment is zero. The right point is the first point where:
the (local) maximum of histogram is from the left of the point
the value of histogram is less than some small epsilon (you can set it to 10)
I drew a green vertical line to show the location of the right point of the segment in this histogram.
And that's it! This right point of the segment is the needed threshold. Here's the result (epsilon is 10 and the calculated threshold is 50):
I think that it's not a problem for you to delete the noise in the image above.
The following is a C++ implementation of Abid's answer that works with OpenCV 3.x:
// Convert the source image to a 1 channel grayscale:
Mat gray;
cvtColor(src, gray, CV_BGR2GRAY);
// Apply the threshold function with the CV_THRESH_OTSU setting as well
// You can skip having it return the value, but I include it for showing the
// results from OTSU
double thresholdValue = threshold(gray, gray, 0, 255, CV_THRESH_BINARY+CV_THRESH_OTSU);
// Present the threshold value
printf("Threshold value: %f\n", thresholdValue);
Running this against the original image, I get the following:
OpenCV calculated a threshold value of 122 for it, close to the value Abid found in his answer.
Just to verify, I altered the original image as seen here:
And produced the following, with a new threshold value of 178:

Resources