Canny edge detector with a single threshold value - matlab

I'm trying to port some Matlab code to C++.
I've come across this line:
edges = edge(gray,'canny',0.1);
The output for the sample image is a completely black image. I want to reproduce the same behaviour using cv::Canny. What values should I use for low threshold and high threshold?
Sample:
Output:

In the line above you have not defined a threshold, probably it takes zero then, thus delivering a black picture. Also, you use a sigma of 0.1 which means virtually no Gauss Blur in the first Canny step. Within Matlab you can get an optimized threhold by:
[~, th] = edge(gray,'canny');
and then apply the optimized threshold th multiplied by some factor f (from my experience f should be between 1-3), you have to try out:
edges=edge(gray,'canny',f*th,'both', sigma);
sigma is sqrt(2) by default (you used 0.1 above). Following remarks:
Matlab calculated the optimized threshold as a percentile of the distribution of intensity gradients (you can see the construction of edge() if you enter "edit edge", if I remember correctly)
the above parameter th is a vector consisting of the low and high threshold. Matlab always uses low_threshold = 0.4* high_threshold

Related

How to compute distance and estimate quality of heterogeneous grids in Matlab?

I want to evaluate the grid quality where all coordinates differ in the real case.
Signal is of a ECG signal where average life-time is 75 years.
My task is to evaluate its age at the moment of measurement, which is an inverse problem.
I think 2D approximation of the 3D case is hard (done here by Abo-Zahhad) with with 3-leads (2 on chest and one at left leg - MIT-BIT arrhythmia database):
where f is a piecewise continuous function in R^2, \epsilon is the error matrix and A is a 2D matrix.
Now, I evaluate the average grid distance in x-axis (time) and average grid distance in y-axis (energy).
I think this can be done by Matlab's Image Analysis toolbox.
However, I am not sure how complete the toolbox's approaches are.
I think a transform approach must be used in the setting of uneven and noncontinuous grids. One approach is exact linear time euclidean distance transforms of grid line sampled shapes by Joakim Lindblad et all.
The method presents a distance transform (DT) which assigns to each image point its smallest distance to a selected subset of image points.
This kind of approach is often a basis of algorithms for many methods in image analysis.
I tested unsuccessfully the case with bwdist (Distance transform of binary image) with chessboard (returns empty square matrix), cityblock, euclidean and quasi-euclidean where the last three options return full matrix.
Another pseudocode
% https://stackoverflow.com/a/29956008/54964
%// retrieve picture
imgRGB = imread('dummy.png');
%// detect lines
imgHSV = rgb2hsv(imgRGB);
BW = (imgHSV(:,:,3) < 1);
BW = imclose(imclose(BW, strel('line',40,0)), strel('line',10,90));
%// clear those masked pixels by setting them to background white color
imgRGB2 = imgRGB;
imgRGB2(repmat(BW,[1 1 3])) = 255;
%// show extracted signal
imshow(imgRGB2)
where I think the approach will not work here because the grids are not necessarily continuous and not necessary ideal.
pdist based on the Lumbreras' answer
In the real examples, all coordinates differ such that pdist hamming and jaccard are always 1 with real data.
The options euclidean, cytoblock, minkowski, chebychev, mahalanobis, cosine, correlation, and spearman offer some descriptions of the data.
However, these options make me now little sense in such full matrices.
I want to estimate how long the signal can live.
Sources
J. Müller, and S. Siltanen. Linear and nonlinear inverse problems with practical applications.
EIT with the D-bar method: discontinuous heart-and-lungs phantom. http://wiki.helsinki.fi/display/mathstatHenkilokunta/EIT+with+the+D-bar+method%3A+discontinuous+heart-and-lungs+phantom Visited 29-Feb 2016.
There is a function in Matlab defined as pdist which computes the pairwisedistance between all row elements in a matrix and enables you to choose the type of distance you want to use (Euclidean, cityblock, correlation). Are you after something like this? Not sure I understood your question!
cheers!
Simply, do not do it in the post-processing. Those artifacts of the body can be about about raster images, about the viewer and/or ... Do quality assurance in the signal generation/processing step.
It is much easier to evaluate the original signal than its views.

how to find standard deviation of noise in an image using matlab?

As a part of my project, initially find low resolution of the input image. Then as a second step i need find the noise in the low-resolution image. How to find noise in an image and its standard deviation using matlab?
You can use the std matlab function which returns the standard deviation of a matrix.
std_deviation = std(image);
This will give you the standard deviation of the whole image. However you cannot calculate the noise std since you don't have the original filtered image.
Possible solution: (Not accurate) : This suppose thaht your noise is gaussian
Well, you can render several Noise matrices and test them:
(choose your mean_vector and std_vector)
for i = 1 : length(mean_vector) % or length(std_vector)
Noise(:,:,i) = mean_vector(i) + std_vector(i).*randn(size(your_image))
% extracting the possibly filtered image
filtered_img(:,:,i) = your_image - Noise(:,:,i);
end
Then display every filtered_img and choose the one that looks the less noisy.
You can denoise the image, compute the difference between the raw image and the denoised version, and then compute the standard deviation of the difference.
For instance:
a=imread('input');
a=double(a);
b=imsharpen(a); %you may need to tune the parameters
diff=b-a;
noise=std2(diff);
You can find the variance of noise in the image assuming that you know the distribution. You will know this if you have read some of the great works in image denoising field by Donoho & John.
To find the noise std. dev. of noise in an image with Gaussian contamination (additive), you can use the Median Absolute Deviation (MAD) estimator on the derivative of the image using the following kernel:
I am writing the python code for this, you can easily write it in Matlab:
def find_stddevs(img):
k = np.asmatrix([[-1.0/9, -1.0/9 ,-1.0/9],
[-1.0/9, 8.0/9 , -1.0/9],
[-1.0/9, -1.0/9 ,-1.0/9]])
filtered = convolve(img,k,mode='reflect')
median_a = np.median(filtered)
stddev = np.median(np.absolute(filtered - median_a))/0.67449 #Gaussian noise assumption
return stddev
You can see the derivation and the logic Here on Wikipedia.
Again, most people think it can be done. This is true iff you do not have any prior about the type of contamination in the image.

Detect steps in a Piecewise constant signal

I have a piecewise constant signal shown below. I want to detect the location of step transition (Marked in red).
My current approach:
Smooth signal using moving average filter (http://www.mathworks.com/help/signal/examples/signal-smoothing.html)
Perform Discrete Wavelet transform to get discontinuities
Locate the discontinuities to get the location of step transition
I am currently implementing the last step of detecting the discontinuities. However, I cannot get the precise location and end with many false detection.
My question:
Is this the correct approach?
If yes, can someone shed some info/ algorithm to use for the last step?
Please suggest an alternate/ better approach.
Thanks
Convolve your signal with a 1st derivative of a Gaussian to find the step positions, similar to a Canny edge detection in 1-D. You can do that in a multi-scale approach, starting from a "large" sigma (say ~10 pixels) detect local maxima, then to a smaller sigma (~2 pixels) to converge on the right pixels where the steps are.
You can see an implementation of this approach here.
If your function is really piecewise constant, why not use just abs of diff compared to a threshold?
th = 0.1;
x_steps = x(abs(diff(y)) > th)
where x a vector with your x-axis values, y is your y-axis data, and th is a threshold.
Example:
>> x = [2 3 4 5 6 7 8 9];
>> y = [1 1 1 2 2 2 3 3];
>> th = 0.1;
>> x_steps = x(abs(diff(y)) > th)
x_steps =
4 7
Regarding your point 3: (Please suggest an alternate/ better approach)
I suggest to use a Potts "filter". This is a variational approach to get an accurate estimation of your piecewise constant signal (similar to the total variation minimization). It can be interpreted as adaptive median filtering. Given the Potts estimate u, the jump points are the points of non-zero gradient of u, that is, diff(u) ~= 0. (There are free Matlab implementations of the Potts filters on the web)
See also http://en.wikipedia.org/wiki/Step_detection
Total Variation Denoising can produce a piecewise constant signal. Then, as pointed out above, "abs of diff compared to a threshold" returns the position of the transitions.
There exist very efficient algorithms for TVDN that process millions of data points within milliseconds:
http://www.gipsa-lab.grenoble-inp.fr/~laurent.condat/download/condat_fast_tv.c
Here's an implementation of a variational approach with python and matlab interface that also uses TVDN:
https://github.com/qubit-ulm/ebs
I think, smoothing with a sharper lowpass filter should work better.
Try to use medfilt1() (a median filter) instead, since you have very concrete levels. If you know how long your plateau is, you can take half/quarter of the plateau length for example. Then you would get very sharp edges. The sharp edges should be detectable using a Haar wavelet or even just using simple differentiation.

Define the width of peak in Matlab

I'm trying to find some peaks in Matlab, but the function findpeaks.m doesn't have the width option. The peaks I want to be detected are in the balls. All the detected are in the red squares. As you can see they have a low width. Any help?
here's the code I use:
[pk,lo] = findpeaks(ecg);
lo2 = zeros(size(lo));
for m = 1:length(lo) - 1
if (ecg(m) - ecg(m+1)) > 0.025
lo2(m) = lo(m);
end
end
p = find(lo2 == 0);
lo2(p) = [];
figure, plot(ecg);
hold on
plot(lo, ecg(lo), 'rs');
By the looks of it you want to characterise each peak in terms of amplitude and width, so that you can apply thresholds (or simmilar) to these values to select only those meeting your criteria (tall and thin).
One way you could do this is to fit a normal distribution to each peak, pegging the mean and amplitude to the value you have found already, and using an optimisation function to find the standard deviation (width of normal distribution).
So, you would need a function which calculates a representation of your data based on the sum of all the gaussian distributions you have, and an error function (mean squared error perhaps) then you just need to throw this into one of matlabs inbuilt optimisation/minimisation functions.
The optimal set of standard deviation parameters would give you the widths of each peak, or at least a good approximation.
Another method, based on Adiel's comment and which is perhaps more appropriate since it looks like you are working on ecg data, would be to also find the local minima (troughs) as well as the peaks. From this you could construct an approximate measure of 'thinness' by taking the x-axis distance between the troughs on either side of a given peak.
You need to define a peak width first, determine how narrow you want your peaks to be and then select them accordingly.
For instance, you can define the width of a peak as the difference between the x-coordinates at which the y-coordinates equal to half of the peak's value (see here). Another approach, (which seems more appropriate here) is to measure the gradient at fixed distances from the peak itself, and selecting the peaks accordingly. In MATLAB, you'll probably use a gradient filter for that :
g = conv(ecg, [-1 0 1], 'same'); %// Gradient filter
idx = g(lo) > thr); %// Indices of narrow peaks
lo = lo(idx);
where thr is the threshold value that you need to determine for yourself. Lower threshold values mean more tolerance for wider peaks.
You need to define what it means to be a peak of interest, and what you mean by the width of that peak. Once you do those things, you are a step ahead.
Perhaps you might locate each peak using find peaks. Then locate the troughs, one of which should lie between each pair of peaks. A trough is simply a peak of -y. Make sure you worry about the first and last peaks/troughs.
Next, define the half height points as the location midway in height between each peak and trough. This can be done using a reverse linear interpolation on the curve.
Finally, the width at half height might be simply the distance (on the x axis) between those two half height points.
Thinking pragmatically, I suppose you could use something along the lines of this simple brute-force approach:
[peaks , peakLocations] = findpeaks(+X);
[troughs, troughLocations] = findpeaks(-X);
width = zeros(size(peaks));
for ii = 1:numel(peaks)
trough_before = troughLocations( ...
find(troughLocations < peakLocations(ii), 1,'last') );
trough_after = troughLocations( ...
find(troughLocations > peakLocations(ii), 1,'first') );
width(ii) = trough_after - trough_before;
end
This will find the distance between the two troughs surrounding a peak of interest.
Use the 'MinPeakHeight' option in findpeaks() to pre-prune your data. By the looks of it, there is no automatic way to extract the peaks you want (unless you somehow have explicit indices to them). Meaning, you'll have to select them manually.
Now of course, there will be many more details that will have to be dealt with, but given the shape of your data set, I think the underlying idea here can nicely solve your problem.

Smoothing of histogram with a low-pass filter in MATLAB

I have an image and my aim is to binarize the image. I have filtered the image with a low pass Gaussian filter and have computed the intensity histogram of the image.
I now want to perform smoothing of the histogram so that I can obtain the threshold for binarization. I used a low pass filter but it did not work. This is the filter I used.
h = fspecial('gaussian', [8 8],2);
Can anyone help me with this? What is the process with respect to smoothing of a histogram?
imhist(Ig);
Thanks a lot for all your help.
I've been working on a very similar problem recently, trying to compute a threshold in order to exclude noisy background pixels from MRI data prior to performing other computations on the images. What I did was fit a spline to the histogram to smooth it while maintaining an accurate fit of the shape. I used the splinefit package from the file exchange to perform the fitting. I computed a histogram for a stack of images treated together, but it should work similarly for an individual image. I also happened to use a logarithmic transformation of my histogram data, but that may or may not be a useful step for your application.
[my_histogram, xvals] = hist(reshape(image_volume), 1, []), number_of_bins);
my_log_hist = log(my_histogram);
my_log_hist(~isfinite(my_log_hist)) = 0; % Get rid of NaN values that arise from empty bins (log of zero = NaN)
figure(1), plot(xvals, my_log_hist, 'b');
hold on
breaks = linspace(0, max_pixel_intensity, numberofbreaks);
xx = linspace(0, max_pixel_intensity, max_pixel_intensity+1);
pp = splinefit(xvals, my_log_hist, breaks, 'r');
plot(xx, ppval(pp, xx), 'r');
Note that the spline is differentiable and you can use ppdiff to get the derivative, which is useful for finding maxima and minima to help pick an appropriate threshold. The numberofbreaks is set to a relatively low number so that the spline will smooth the histogram. I used linspace in the example to pick the breaks, but if you know that some portion of the histogram exhibits much greater curvature than elsewhere, you'd want to have more breaks in that region and less elsewhere in order to accurately capture the shape of the histogram.
To smooth the histogram you need to use a 1-D filter. This is easily done using the filter function. Here is an example:
I = imread('pout.tif');
h = imhist(I);
smooth_h = filter(normpdf(-4:4, 0,1),1,h);
Of course you can use any smoothing function you choose. The mean would simply be ones(1,8).
Since your goal here is just to find the threshold to binarize an image you could just use the graythresh function which uses Otsu's method.