How to determine amount of gaussian noise in image - opencv

I have multiple colored images of gauges. I apply adaptive Gaussian thresholding to make the filter the image so that the ticks and needle are more prominent.
For the above thresholding I used cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 21, 2). (21,2) seemed to process images best in general.
However, when the gauge images are too dark adaptThresh(21,2) produces much noise
Increasing the kernel sizes (adaptiveThreshold parameters) filters the salt and pepper noise and produces an image I want.
I want to be able to determine how much Gaussian (salt and pepper) noise in the image so that if there is too much noise, my algorithm will increase the filter size. What is a good metric to measure the amount of noise in this case?

A popular way to do this is considering the "derivatives" of the picture, or in other words, how much the picture "varies". A popular way to do that is using the Total-Variation. There are many ways to define it in the discrete domain, but in the end they boild down to a (weighted) sum of the absolute differences of neighbouring pixels. This means if you have a small total variation if the image comprises large areas (with "short" boundaries) of uniform brightness, and you'll have a big value for noisy or high frequency images.
So a simple way to measure the noise is just measuring the total variation and if that is above a certain threshold you could try to increase the filter size.

Get a clean background image. you can use OpenCV to reduce the low-level image info.
code example
img = cv2.imread("/home/xx/Pictures/test.png",cv2.IMREAD_GRAYSCALE)
# filter kernel
kernel = np.ones((5, 5), np.float32) / 25
dst = cv2.filter2D(img, -1, kernel)
img = img - dst/20
img = cv2.adaptiveThreshold(img.astype(np.uint8), 255,
cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 21, 2)
cv2.imshow('1',img)
cv2.waitKey()

Related

What does this subtract_gaussian_blur function that uses the GaussianBlur function in OpenCV do?

I came across this Kaggle kernel that has the following function.
def subtract_gaussian_blur(img):
gb_img = cv2.GaussianBlur(img, (0, 0), 5)
return cv2.addWeighted(img, 4, gb_img, -4, 128)
That converts this RGB image.
Into the following image.
I can see the effect is that it somewhat sharpens the image and turns it into a more grayscale image (not actually grayscale since the image is still RGB) but I'm not actually sure I fully understand what is happening in the function even after reading the OpenCV docs on GaussianBlur and addWeighted.
Also, does this particular image transformation have a specific name that I can do further reading into?
The main step I can see is cv2.addWeighted(img, 4, gb_img, -4, 128). The underlying equation for addWeighted is dst(I)=saturate(src1(I)∗alpha+src2(I)∗beta+gamma). In the example here, alpha is 4, beta -4, and gamma 128.
My understanding of how that works is it first performs a gaussian blur to make a denoised version of the image. However as well as removing noise, Gaussian Blurring can also "smear" edges, which is important later. It then subtracts the denoised version from the original, and adds 128 to each pixel colour channel.
In regions where the original pixel is identical to the filtered pixel, this will result in a uniform grey region. In areas where the original and filtered pixels differ a lot, you will end up either with a lighter or darker region depending on whether the intensity of the original or filtered pixel is higher. The differences will be most pronounced around edges in the original image, because those will be strongly "smeared" by the gaussian blur.
The result isn't fully greyscale as addWeighted() is applied to each colour channel of the pixels separately. Areas where the RGB values of the pre and post blur images differ in an unbalanced way (ie the difference between the two red channels is much bigger than between the blue or green channels) there will be a degree of colour rather than just grey.

Calculate histogram of gradients (HOG) of a fixed length regardless of image size

I'm training a HOG + SVM model, and my training data comes in various sizes and aspect ratios. The SVM model can't be trained on variable sized lists, so I'm looking to calculate a histogram of gradients that is the same length regardless of image size.
Is there a clever way to do that? Or is it better to resize the images or pad them?
What people usually do in such case is one of the follow two things:
Resize all images (or image patches) to a fixed size and extract the HOG features from those.
Use the "Bag of Words/Features" method and don't resize the images.
The first method 1. is quite simple but it has some problems which method 2. tries to solve.
First, think of what a hog descriptor does. It divides an image into cells of a fixed length, calculates the gradients cell-wise to generate cell-wise histograms(based on voting). At the end, you'll have a concatenated histogram of all the cells and that's your descriptor.
So there is a problem with it, because the object (that you want to detect) has to cover the images in similar manner. Otherwise your descriptor would look different depending on the location of the object inside the image.
Method 2. works as follows:
Extract the HOG features from both positive and negative images in your training set.
Use an clustering algorithm like k-means to define a fixed amount of k centroids.
For each image in your dataset, extract the HOG features and compare them element-wise to the centroids to create a frequency histogram.
Use the frequency histograms for the training of your SVM and use it for the classification phase. This way, the location doesn't matter and you'll always have a fixed sized of inputs. You'll also benefit from the reduction of dimensions.
You can normalize the images to a given target shape using cv2.resize(), divide image into number of blocks you want and calculate the histogram of orientations along with the magnitudes. Below is a simple implementation of the same.
img = cv2.imread(filename,0)
img = cv2.resize(img,(16,16)) #resize the image
gx = cv2.Sobel(img, cv2.CV_32F, 1, 0) #horizontal gradinets
gy = cv2.Sobel(img, cv2.CV_32F, 0, 1) # vertical gradients
mag, ang = cv2.cartToPolar(gx, gy)
bin_n = 16 # Number of bins
# quantizing binvalues in (0-16)
bins = np.int32(bin_n*ang/(2*np.pi))
# divide to 4 sub-squares
s = 8 #block size
bin_cells = bins[:s,:s],bins[s:,:s],bins[:s,s:],bins[s:,s:]
mag_cells = mag[:s,:s], mag[s:,:s], mag[:s,s:], mag[s:,s:]
hists = [np.bincount(b.ravel(), m.ravel(), bin_n) for b, m in zip(bin_cells,mag_cells)]
hist = np.hstack(hists) #histogram feature data to be fed to SVM model
Hope that helps!

Difference between contrast stretching and histogram equalization

I would like to know the difference between contrast stretching and histogram equalization.
I have tried both using OpenCV and observed the results, but I still have not understood the main differences between the two techniques. Insights would be of much needed help.
Lets Define Contrast first,
Contrast is a measure of the “range” of an image; i.e. how spread its intensities are. It has many formal definitions one famous is Michelson’s:
He says contrast = ( Imax - Imin )/( Imax + I min )
Contrast is strongly tied to an image’s overall visual quality.
Ideally, we’d like images to use the entire range of values available
to them.
Contrast Stretching and Histogram Equalisation have the same goal: making the images to use entire range of values available to them.
But they use different techniques.
Contrast Stretching works like mapping
it maps minimum intensity in the image to the minimum value in the range( 84 ==> 0 in the example above )
With the same way, it maps maximum intensity in the image to the maximum value in the range( 153 ==> 255 in the example above )
This is why Contrast Stretching is un-reliable, if there exist only two pixels have 0 and 255 intensity, it is totally useless.
However a better approach is Histogram Equalisation which uses probability distribution. You can learn the steps here
I came across the following points after some reading.
Contrast stretching is all about increasing the difference between the maximum intensity value in an image and the minimum one. All the rest of the intensity values are spread out between this range.
Histogram equalization is about modifying the intensity values of all the pixels in the image such that the histogram is "flattened" (in reality, the histogram can't be exactly flattened, there would be some peaks and some valleys, but that's a practical problem).
In contrast stretching, there exists a one-to-one relationship of the intensity values between the source image and the target image i.e., the original image can be restored from the contrast-stretched image.
However, once histogram equalization is performed, there is no way of getting back the original image.
In Histogram equalization, you want to flatten the histogram into a uniform distribution.
In contrast stretching, you manipulate the entire range of intensity values. Like what you do in Normalization.
Contrast stretching is a linear normalization that stretches an arbitrary interval of the intensities of an image and fits the interval to an another arbitrary interval (usually the target interval is the possible minimum and maximum of the image, like 0 and 255).
Histogram equalization is a nonlinear normalization that stretches the area of histogram with high abundance intensities and compresses the area with low abundance intensities.
I think that contrast stretching broadens the histogram of the image intensity levels, so the intensity around the range of input may be mapped to the full intensity range.
Histogram equalization, on the other hand, maps all of the pixels to the full range according to the cumulative distribution function or probability.
Contrast is the difference between maximum and minimum pixel intensity.
Both methods are used to enhance contrast, more precisely, adjusting image intensities to enhance contrast.
During histogram equalization the overall shape of the histogram
changes, whereas in contrast stretching the overall shape of
histogram remains same.

Simple way to check if an image bitmap is blur

I am looking for a "very" simple way to check if an image bitmap is blur. I do not need accurate and complicate algorithm which involves fft, wavelet, etc. Just a very simple idea even if it is not accurate.
I've thought to compute the average euclidian distance between pixel (x,y) and pixel (x+1,y) considering their RGB components and then using a threshold but it works very bad. Any other idea?
Don't calculate the average differences between adjacent pixels.
Even when a photograph is perfectly in focus, it can still contain large areas of uniform colour, like the sky for example. These will push down the average difference and mask the details you're interested in. What you really want to find is the maximum difference value.
Also, to speed things up, I wouldn't bother checking every pixel in the image. You should get reasonable results by checking along a grid of horizontal and vertical lines spaced, say, 10 pixels apart.
Here are the results of some tests with PHP's GD graphics functions using an image from Wikimedia Commons (Bokeh_Ipomea.jpg). The Sharpness values are simply the maximum pixel difference values as a percentage of 255 (I only looked in the green channel; you should probably convert to greyscale first). The numbers underneath show how long it took to process the image.
If you want them, here are the source images I used:
original
slightly blurred
blurred
Update:
There's a problem with this algorithm in that it relies on the image having a fairly high level of contrast as well as sharp focused edges. It can be improved by finding the maximum pixel difference (maxdiff), and finding the overall range of pixel values in a small area centred on this location (range). The sharpness is then calculated as follows:
sharpness = (maxdiff / (offset + range)) * (1.0 + offset / 255) * 100%
where offset is a parameter that reduces the effects of very small edges so that background noise does not affect the results significantly. (I used a value of 15.)
This produces fairly good results. Anything with a sharpness of less than 40% is probably out of focus. Here's are some examples (the locations of the maximum pixel difference and the 9×9 local search areas are also shown for reference):
(source)
(source)
(source)
(source)
The results still aren't perfect, though. Subjects that are inherently blurry will always result in a low sharpness value:
(source)
Bokeh effects can produce sharp edges from point sources of light, even when they are completely out of focus:
(source)
You commented that you want to be able to reject user-submitted photos that are out of focus. Since this technique isn't perfect, I would suggest that you instead notify the user if an image appears blurry instead of rejecting it altogether.
I suppose that, philosophically speaking, all natural images are blurry...How blurry and to which amount, is something that depends upon your application. Broadly speaking, the blurriness or sharpness of images can be measured in various ways. As a first easy attempt I would check for the energy of the image, defined as the normalised summation of the squared pixel values:
1 2
E = --- Σ I, where I the image and N the number of pixels (defined for grayscale)
N
First you may apply a Laplacian of Gaussian (LoG) filter to detect the "energetic" areas of the image and then check the energy. The blurry image should show considerably lower energy.
See an example in MATLAB using a typical grayscale lena image:
This is the original image
This is the blurry image, blurred with gaussian noise
This is the LoG image of the original
And this is the LoG image of the blurry one
If you just compute the energy of the two LoG images you get:
E = 1265 E = 88
or bl
which is a huge amount of difference...
Then you just have to select a threshold to judge which amount of energy is good for your application...
calculate the average L1-distance of adjacent pixels:
N1=1/(2*N_pixel) * sum( abs(p(x,y)-p(x-1,y)) + abs(p(x,y)-p(x,y-1)) )
then the average L2 distance:
N2= 1/(2*N_pixel) * sum( (p(x,y)-p(x-1,y))^2 + (p(x,y)-p(x,y-1))^2 )
then the ratio N2 / (N1*N1) is a measure of blurriness. This is for grayscale images, for color you do this for each channel separately.

Is There a Algorithm or Library that can Detect Motion Blur in Images?

Anyone know of an algorithm that can return fuzzy true/false to if a image has motion blur / camera shake in a image?
Ideally it would be particular to motion blur, as lots of the images in the set might have blurred (Bokeh) backgrounds.
A language preference would be C, Perl, Shell Utility, or Python, but I'm open to anything really.
With my current knowledge of Math / Programming, I don't think I really have a hope of writing such an algorithm, only using one that takes some parameters...
The Discrete wavelet transform is a useful tool in such detection. Here is a paper from Carnegie Mellon School of Computer Science on detecting and quantifying blur in images by using the DWT. For a binary decision, you threshold the amount to a desired level and everything above that has blur.
There are multiple ways to do this, maybe one of the imaging gurus here has a much better answer. Anyways...
My first shot would be to have a frequency analysis of the image (read: 2d fourier transform). Then define a threshold for the high frequencies (i.e. fast changes from one pixel to the next) for true/false. Motion blur filters the high frequencies out. Your mileage may vary, e.g. a completely black picture will have no high frequencies although it is not blurred. Depending on the lens and aperture used, parts of the image may be blurry because they are in the background. I don't think there's a one-size-fits-all solution here.
To detect blurriness, you can convolve grayscale image with laplacian kernel and calculate the variance. Focused images should have high variance and blur images should have lower variance. Below is the code to do so :
def is_blur(image) :
"""
This function convolves a grayscale image with
laplacian kernel and calculates its variance.
"""
thresold = #Some value you need to decide
#Laplacian kernel
laplacian_kernel = np.array([[0,1,0],[1,-4,1],[0,1,0]])
laplacian_kernel = tf.expand_dims(laplacian_kernel, -1)
laplacian_kernel = tf.expand_dims(laplacian_kernel, -1)
laplacian_kernel = tf.cast(laplacian_kernel, tf.float32)
#Convolving image with laplacian kernel
new_img = tf.nn.conv2d(image, laplacian_kernel, strides=[1, 1, 1, 1],
padding="SAME")
#Calculating variance
img_var = tf.math.reduce_variance(new_img)
if img_var < thresold :
return True
else :
return False
This function takes a grayscale image as input and it should be a 4-dim tensor as tf.nn.conv2d accepts a 4-d tensor. Below is the code to load image :
image_string = tf.io.read_file(ImagePath)
#decoding image
image = tf.image.decode_png(image_string, channels=3)
#Converting image to grayscale
image = tf.image.rgb_to_grayscale(image)
# This will convert to float values in [0, 1]
image = tf.image.convert_image_dtype(image, tf.float32)
#Reshaping image since conv2d accepts a 4-d tensor.
image = tf.reshape(image, shape=[1, image.shape[0], image.shape[1], 1])
The thresold should be set very carefully. If set too low, it can declare a blur image as focused and if set too high, then it will miss classify a focused image as blurred. A better way is to calculate the blurriness of each image and then decide by plotting a distplot.
Hope this helps :)
You can also use the Richardson-Lucy Algorithm. It's mostly used for blind de-convolution, but as you know that it's motion blur you need to remove, the RL Algorithms should take fewer iterations to compute a workable reconstruction.

Resources