# How to remove noise from this image without causing the crack to 'shrink'?

I am currently embarking on an image processing project to detect cracks on surfaces using OpenCV and Visual Studio via C++. I have tried writing my program and at the moment, I am stuck because I have difficulties striking a balance between noise in an image and preventing the 'crack' from shrinking due to the dilate() operation. I am completely new to OpenCV and have only coded in C before, so I hope you will be patient and explain things slowly. :)

I am currently working on the photograph below, taken from Google Images, which has the parent URL: http://www.newindianexpress.com/states/kerala/article1330634.ece

I shall type my code below:

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/photo/photo.hpp"
#include "highgui.h"
#include <cv.h>
#include <iostream>
#include <stdlib.h>
#include <stdio.h>

using namespace cv;
using namespace std;

int main( int argc, char** argv ){

//Declarations
double alpha; /**< Simple contrast control */ //unused
int beta;  /**< Simple brightness control */ //unused
float minVal;
float maxVal;
Mat bw_img;
Mat contrast1;
Mat gaussian_img;
Mat filtered_img;
Mat weighted_img;

//Loads image from file and put image matrix into "original_photo"
Mat original_img = imread ("crack.jpg", 1);

//Convert color-to-gray image
cvtColor( original_img, bw_img, CV_BGR2GRAY );
//imshow("bw_img", bw_img);

//Gaussian filtering
int i = 7;
GaussianBlur(bw_img,gaussian_img,Size(i,i),0,0);
imshow("gaussian_img", gaussian_img);

//HPF kernel
Mat kernel = (Mat_<float>(3,3) <<
0,  -1, 0,
-1, 5, -1,
0,  -1, 0);

filter2D(gaussian_img, filtered_img,-1, kernel, Point(-1,-1),0);

//Extract high-frequency features, e.g. edges  show up.
imshow("weighted_img",weighted_img);

//Construct structuring element
Mat struc_elem = getStructuringElement(MORPH_RECT,Size(5,5));

//Perform erosion first
Mat eroded_img;
imshow("eroded_img",eroded_img);

//Perform dilation next
Mat dilate_img;
dilate(eroded_img,dilate_img,struc_elem);
imshow("dilate_img",dilate_img);

double otsu_thresh_val = threshold(dilate_img, dilate_img, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
double high_thresh_val  = otsu_thresh_val,lower_thresh_val = otsu_thresh_val * 0.5;

Mat cannyOP;
Canny( dilate_img, cannyOP, lower_thresh_val, high_thresh_val );
imshow("cannyOP",cannyOP);

//wait for long long time
int c = waitKey(20000000000000);

//if you get impatient, end program
if (c == 27)
{
return 0;
}
}


The images I got so far are rather noisy, with a lot of black specks after doing erosion followed by dilation. I shall post the photos later as I don't have enough reputation to post more links. Sorry.

I have tried to increase the blockSize and C values in adaptiveThreshold() function, from the current 51/20 to 101/50. The noises were mostly removed, but the crack feature got reduced in size. This image shall be posted later and referred to as Image 2.

I have also tried to maintain the adaptiveThreshold() figures for blockSize and C, and tried to perform a dilation followed by an erosion. The final image was less noisy, though there were still black specks, but the crack became slightly discontinuous. This image shall be posted later and referred to as Image 3.

May I ask, how should I remove noise via either adaptiveThreshold() function or the erode() and dilate() functions without causing the crack to become thinner or wider?

This is because if the crack is thinner, the crack will 'disappear' totally in the final output image.

Thank you! :)

• As I am unable to post more images here, I shall upload them onto a separate image hosting site. s32.postimg.org/efo9jqd6t/several_images.jpg This image is generated from my block of code above, before any further edits. – galaxy_twirl May 19 '16 at 5:50
• This is the image after changing the blockSize and C of adaptiveThreshold() function to 101 and 50 respectively. s32.postimg.org/o47omqc79/boo.jpg – galaxy_twirl May 19 '16 at 5:55
• This is the image after performing dilation followed by erosion, after the first round of erosion-then-dilation. (Image 3) s32.postimg.org/j296uaddx/boo.jpg – galaxy_twirl May 19 '16 at 5:57
• You should add images in the original question itself by editing the question. – Dipan Mehta Jun 21 '17 at 12:25

As first preprocessing step use edge-aware smoothing methods before converting your image to binary. These methods do not modify the sharp boundaries that are larger than a parametric size. Following search reveals many methods:

I am not quite sure if the method will work in your case, but it seems there is an edge aware smoothing filter in OpenCV:

http://docs.opencv.org/3.1.0/d3/d14/tutorial_ximgproc_disparity_filtering.html

Your problem is much less that of noise - rather that of clutter. (I may be completely wrong!)

What you need to do is isolate the crank portion from everything else. This is essentially segmentation job. You may either do it based on geometry or you may also try to create better image capture system that helps filter only rod from everything else.

Once you can do this, identifying crack can be done in many ways. One is to identify edges, and then analyze edges with transforms such as hough transform to see if that edge is significant.

Another way to do can be following:

• Assumption: that picture only has crank-shaft and known background (which should not be same as color of of the crack). Generally speaking the edge is identifiable as drop or change in intensity.

• Assumption: Crankshaft can be any color; and intensity can roughly vary based on lighting condition so we don't want to hard code specific value. However, we are assuming crankshaft to be generally smooth (plain texture) and not arbitrary designs on it.

• Assumption: Crack can be anywhere, horizontal or vertical or slant at any position. Or it may not exist as well. Algorithm should be able to identify position and depth of the crack

• Solution: plot average intensity of every row, every column and across diagonals. Wherever there is an edge the number of pixels accumulated being 'black' will produce a drop in this running average. So a pure horizontal crack will be caught by drop in intensity across rows. And drop in column intensity will capture vertical ones. The slant cracks will spread its impact in horizontal and vertical ones but will be captured by diagonal averages.

$row\_avg(i)= 1/W*\sum_{j=0}^{j=W} pixel[i][j] ...$ where W is width

Apply similar formula for col_avg.

The spike in row_avg or col_avg will also give you width/depth and location of edges/cracks.

It should be able to do reasonably good job for multiple cracks as well.

The only limitation as I see it if the cracks are too small or some arbitrarily non-aligned shapes.

DISCLAIMER: This is not any specific well known algorithm, but I am explaining based on my personal experience with row and column operators like this.