Calculate peak with of peaks on top of a plateau in Python - scipy

I am using scipy's find_peaks and peak_widths to get the peaks and their widths.
peak_ndxs, _ = find_peaks(x, prominence=prominence)
widths, heights, left_ips, right_ips = peak_widths(x, peak_ndxs, rel_height=0.9)
It works great for peaks that are not on top of a plateau. However, when the peak is on top of a plateau which is above the 0.9 rel_height level, the peak width is exaggerated.
What I am actually interested in is the width of the peak at the base justa above the plateau.
Is there a better suited function that I should be using in `scipy'?

Related

Setting physical (screen) size of an axis in MATLAB

I thought this problem would be trivial but I haven't been able to figure it out. I have two 3D figures, representing two data sets that are of different physical size (ie. one might represent something 10cm wide, one 20cm wide). I non-dimensionalize everything (so width goes from 0 to 1) and then I use daspect to change the aspect ratio of the plot so that the width appears to change between the plots, even though the axis limits do not (I set these to be constant).
When I plot them, in different figures, however, MATLAB automatically scales the axis to fit in the figure. This means that my objects change size slightly, whilst I want them to keep the x and z scales exactly the same.
Is there some way I can manually set how many pixels one unit of an axis takes up? You can see in the images that the scales on the z axis of the two figures are different, ie. the same length in zaxis units takes up different lengths in pixels.
The code I used for the axes, view, and aspect ratio is:
xlim([-0.3 .6])
ylim([-.8 .8])
zlim([-0.2 1.3])
view([60 15])
daspect([1/268.3 1/(99.9730*width) 1/74])
James

How to make "well" a ridge-shape from a given 2d line? (gaussian, matlab)

My goal is to make a ridge(mountain)-like shape from the given line. For that purpose, I applied the gaussian filter to the given line. In this example below, one line is vertical and one has some slope. (here, background values are 0, line pixel values are 1.)
Given line:
Ridge shape:
When I applied gaussian filter, the peak heights are different. I guess this results from the rasterization problem. The image matrix itself is discrete integer space. The gaussian filter is actually not exactly circular (s by s matrix). Two lines also suffer from rasterization.
How can I get two same-peak-height nice-looking ridges(mountains)?
Is there more appropriate way to apply the filter?
Should I make a larger canvas(image matrix) and then reduce the canvas by interpolation? Is it a good way?
Moreover, I appreciate if you can suggest a way to make ridges with a certain peak height. When using gaussian filter, what we can do is deciding the size and sigma of the filter. Based on those parameters, the peak height varies.
For information, image matrix size is 250x250 here.
You can give a try to distance transform. Your image is a binary image (having only two type of values, 0 and 1). Therefore, you can generate similar effects with distance transform.
%Create an image similar to yours
img=false(250,250);
img(sub2ind(size(img),180:220,linspace(20,100,41)))=1;
img(1:200,150)=1;
%Distance transform
distImg=bwdist(img);
distImg(distImg>5)=0; %5 is set manually to achieve similar results to yours
distImg=5-distImg; %Get high values for the pixels inside the tube as shown
%in your figure
distImg(distImg==5)=0; %Making background pixels zero
%Plotting
surf(1:size(img,2),1:size(img,1),double(distImg));
To get images with certain peak height, you can change the threshold of 5 to a different value. If you set it to 10, you can get peaks with height equal to the next largest value present in the distance transform matrix. In case of 5 and 10, I found it to be around 3.5 and 8.
Again, if you want to be exact 5 and 10, then you may multiply the distance transform matrix with the normalization factor as follows.
normalizationFactor=(newValue-minValue)/(maxValue-minValue) %self-explanatory
Only disadvantage I see is, I don't get a smooth graph as you have. I tried with Gaussian filter too, but did not get a smooth graph.
My result:

Decomposing an image into two frequency components using DCT?

I am a beginner in digital image processing field, recently I am working on a project where I have to decompose an image into two frequency components namely (low and high) using DCT. I searched a lot on web and I found that MATLAB has a built-in function for Discrete Cosine Transform which is used like this:
dct_img = dct2(img);
where img is input image and dct_img is resultant DCT of img.
Question
My question is, "How can I decompose the dct_img into two frequency components namely low and high frequency components".
As you've mentioned, dct2 and idct2 will do most of the job for you. The question that remains is then: What is high frequency and what is low frequency content? The coefficients after the 2 dimensional transform will actually represent two frequencies each (one in x- and one in y-direction). The following figure shows the bases for each coefficient in an 8x8 discrete cosine transform:
Therefore, that question of low vs. high can be answered in different ways. A common way, which is also used in the JPEG encoding, proceeds diagonally from zero-frequency downto the max as shown above. As we can see in the following example that is mostly motivated because natural images are largely located in the "top left" corner of "low" frequencies. It is certainly worth looking at the result of dct2 and play around with the actual choice of your regions for high and low.
In the following I'm dividing the spectrum diagonally and also plotting the DCT coefficients - in logarithmic scale because otherwise we would just see one big peak around (1,1). In the example I'm cutting far above half of the coefficients (adjustable with cutoff) we can see that the high-frequency part ("HF") still contains some relevant image information. If you set cutoff to 0 or below only noise of small amplitude will be left.
%// Load an image
Orig = double(imread('rice.png'));
%// Transform
Orig_T = dct2(Orig);
%// Split between high- and low-frequency in the spectrum (*)
cutoff = round(0.5 * 256);
High_T = fliplr(tril(fliplr(Orig_T), cutoff));
Low_T = Orig_T - High_T;
%// Transform back
High = idct2(High_T);
Low = idct2(Low_T);
%// Plot results
figure, colormap gray
subplot(3,2,1), imagesc(Orig), title('Original'), axis square, colorbar
subplot(3,2,2), imagesc(log(abs(Orig_T))), title('log(DCT(Original))'), axis square, colorbar
subplot(3,2,3), imagesc(log(abs(Low_T))), title('log(DCT(LF))'), axis square, colorbar
subplot(3,2,4), imagesc(log(abs(High_T))), title('log(DCT(HF))'), axis square, colorbar
subplot(3,2,5), imagesc(Low), title('LF'), axis square, colorbar
subplot(3,2,6), imagesc(High), title('HF'), axis square, colorbar
(*) Note on tril: The lower triangle-function operates with respect to the mathematical diagonal from top-left to bottom-right, since I want the other diagonal I'm flipping left-right before and afterwards.
Also note that this kind of operations are not usually applied to entire images, but rather to blocks of e.g. 8x8. Have a look at blockproc and this article.
An easy example:
I2 = dct_img;
I2(8:end,8:end) = 0;
I3 = idct2(I2);
imagesc(I3)
I3 can be seen as the image after low pass filter (the low frequency components), then idct2(dct_img - I2) can be viewed as high frequency.

Using bwdist (Distance transform) with a specific window size

I am using bwdist in MATLAB, but I want to use a specific window size. I think MATLAB uses its default window size. Is there any other function which let me to use my own window size?
Euclidean Distance transform measures the exact distance between pixels. Since exact distance involves calculation of sqrt() it becomes computational heavy.
In order to solve that issue, a Chamfer mask of 3x3 can be used to approximate the distance up to error of 8% with only liner calculations (without sqrt()).
Mask of size 5x5 approximated up to ~3% error.
If you want a distance transform with non-quclidian distance (for example Manhattan distance) than Chamfer is completely irrelevant.
In conclusion: there is no window size in Matlab. It uses exact calculation when you ask for Euclidian distance transform. In order to approximate it one can uses quasi-euclidian distance. For other distances (chessboard/Manhattan) the calculation is precise and fast.
You cannot simulate Chamfer masks with distance transform.
In openCV - distance transform uses Chamfer mask and you can set the size of the mask. Typically 5x5 mask is used because bigger masks will yiled almost the same results (unnoticeable difference for the human eye)

What exactly is edge density?

I'm kinda confuse of the meaning of edge density. From the equation,
edge density = sum(x=1,w) sum(y=1,h) e(x,y)/N
where e is the edge map image (magnitude of vertical edge at (x,y)), there are two version of N.
1st version - N = w x h (width x height)
2nd version - N = number of non zero vertical edge pixel
What I don't understand is how can I calculate the edge density? Is it just summation of the white edges pixels?
Edited
Hi all, from what I understand from reading the paper given by #Gilgamesh, the N is the area of the region, width times its height but from the answer given it seems there is a conflict whereby N refers to number of non white pixels(black pixel). So, which is the correct one? Here is another reference on the N value calculating edge density.
basically the edge density is really just a (local) average density, which you can either calculate over binarized images or, more common, over grey scale images.
And yes, it is basically just summing up over both x and y coordinates in a subimage in most cases, see equation (1) here
http://ro.uow.edu.au/cgi/viewcontent.cgi?article=1517&context=infopapers
and averaging afterwards.
Regards,
G.
From what I understand about edge density, it is defined as where are the white pixels and is the total number of pixels i.e. .
cannot be the number of black pixels because the number could be arbitrary, ranging from zero to all pixels and the range of edge density will then be .
When is the area, the range will be which portrays just what we want, the places where edges are dense (or sparse depending on your requirement).
The edge map is the map of gradient magnitude (i.e. the length of the gradient vector). So the edge density is the average of the gradient magnitude over a neighborhood.
If you have a binary edge map where 0 means no-edge, 1 means edge (this can be obtained by thresholding the gradient magnitude), then the edge density is just the ratio of edge/non-edge pixels.