Cleaning the Image
To a human eye, the two images with which we are working are almost identical. Both images show the same Arduino on the same surface and in roughly the same position, with the exception of the missing microprocessor in the first image. However, to a computer, the two images look very different. This is because the flash of the camera that took both pictures created a lot of noise.
In order to find the human-visable differences in the image, we will first have to find a way to smooth both images and reduce the noise. This blog entry will document the attempts researched.
2D Convolution [1]
This method makes use of a Low Pass Filter through the OpenCV function cv2.filter2D(). This function convolves a kernel with an image. We will use the following kernel:
The function will place the kernel over a square of 25 pixels and get the average value for all pixels in the square. It then assigns this value to the pixel in the center of the kernel. It repeats this process for all pixels.
PCB1 |
 |
PCB2 |
 |
Code
def convolution2D(img):
kernel = np.ones((5,5),np.float32)/25
dst = cv2.filter2D(img,-1,kernel)
imgs = np.hstack((img, dst))
cv2.imshow(window, imgs)
cv2.imwrite(outputPath + window + fileExtension, imgs)
convolution2D(pcb1, "convolution1")
convolution2D(pcb2, "convolution2")
Image Blurring [1]
2D Convolultion introduces the idea of blurring an image using kernels. There are some pre-written image blurring functions in OpenCV based off of this idea. We will test them out here with a 5x5 kernel.
Averaging
Averaging makes use of a normalized box filter, i.e. all elements are set to 1. This method takes the average of all pixels within the kernel and assigns the result to the kernel’s central pixel. Very similar to the previous example execept we can do it in one line this time.
PCB1 |
 |
PCB2 |
 |
Code
def averaging(img, window):
blur = cv2.blur(img,(5,5))
imgs = np.hstack((img, blur))
cv2.imshow(window, imgs)
cv2.imwrite(outputPath + window + fileExtension, imgs)
averaging(pcb1, "averaging1")
averaging(pcb2, "averaging2")
Gaussian Blurring
Instead of a box filter, Gaussian Blurring uses a Gaussian kernel. This kernel contains small pixel values on the outside that get higher towards the centre. Similar to this example:
Using a kernel like this should help remove noise while maintaining the integrity of the image.
PCB1 |
 |
PCB2 |
 |
Code
def gaussianBlur(img, window):
blur = cv2.GaussianBlur(img,(5,5),0)
imgs = np.hstack((img, blur))
cv2.imshow(window, imgs)
cv2.imwrite(outputPath + window + fileExtension, imgs)
gaussianBlur(pcb1, "gaussianBlur1")
gaussianBlur(pcb2, "gaussianBlur2")
Bilateral Filtering
Bilateral Filtering aims fix the issues with Gaussian Blurring, specifically not caring if the pixel is a part of an edge or not. It uses a Gaussian kernel of space so it only compares nearby pixels, but it also makes use of a Gaussian function for intensity to ensure that only pixels of similar intensities are blurred, thus preserving edges.
PCB1 |
 |
PCB2 |
 |
Code
def bilateralFilter(img, window):
blur = cv2.bilateralFilter(img,9,75,75)
imgs = np.hstack((img, blur))
cv2.imshow(window, imgs)
cv2.imwrite(outputPath + window + fileExtension, imgs)
bilateralFilter(pcb1, "bilateralFilter1")
bilateralFilter(pcb2, "bilateralFilter2")
Denoising [2]
Image blurring works well for removing noise, however, OpenCV contains functions specifically for denoising which we will also explore
Non-local Means Denoising
This is very advanced technique. It is done by finding small windows in the image that look similar, taking a pixel inside one of those windows, averaging all windows and replacing the selected pixel with the result of this averaging.
PCB1 |
 |
PCB2 |
 |
Code
def fastN1MeansDenoising(img, window):
dst = cv2.fastNlMeansDenoisingColored(img,None,10,10,7,21)
imgs = np.hstack((img, dst))
cv2.imshow(window, imgs)
cv2.imwrite(outputPath + window + fileExtension, imgs)
fastN1MeansDenoising(pcb1, "Denoised1")
fastN1MeansDenoising(pcb2, "Denoised2")
Choosing a technique
Non-local Means Denoising seems to be very effective so I have advised my team to continue using it. However, because it is so far advanced, it might be worth further exploring the inner workings of this function to see if there is elements that we can tweak in order to further aid us in our goal. Perhaps even combining this function with Bilateral Filtering to keep the edges in mind when denoising.
References:
[1] ‘Smoothing Images’, 2014, [Online]. Available: https://docs.opencv.org/3.1.0/d4/d13/tutorial_py_filtering.html. [Accessed: 2017-10-27]
[2] ‘Image Denoising’, 2014, [Online]. Available: https://docs.opencv.org/3.2.0/d5/d69/tutorial_py_non_local_means.html. [Accessed: 2017-10-27]