How to make "well" a ridge-shape from a given 2d line? (gaussian, matlab) - 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:

Related

Surface from level curves of unkown levels in MATLAB

I want to obtain a n*m matrix with a approximated "height" at each discrete point. The input is a picture (see link below) of the contours from a map, each contourline represents an 5m increase or decrease of the height.
My thoughts:
I imported the picture as a logical png to a matrix called A which means that every contourline in the matrix is a connected strip of '1's and everything else is just 0.
My initial thought was to just start in the upper left corner of the matrix, set that height to zero, declare a new matrix 'height' and start with figuring out height(:,1) by adding 5 meters each time we meet a '1' in the A matrix. Knowing the whole first colonn I now for each row start from the left and add 5 m each time we meet a '1'.
I quickly realized however that this wouldn't work since there is no way for the algorithm to understand whether it should add or subtract height, i.e if we are running uphill or downhill.
If I somehow could approximate the gradient from the intensity of contourlines that would be great even though it would always be possible for a uphill to be a downhill and vice versa but then I could manually decide which is true of these two cases.
Picture:
WORK IN PROGRESS
%% Read and binarize the image
I=imread('https://i.stack.imgur.com/pRkiY.jpg');
I=rgb2gray(I);
I=I>graythresh(I)*255;
%% Get skeleton, i.e. the lines!
sk=bwmorph(~I,'skel',Inf);
%% lines are too thin, dilate them
dilated=~imdilate(sk, strel('disk', 2, 4));
%% label the image!
test=bwlabel(dilated,8);
imshow(test,[]); colormap(plasma); % use colormap parula if you prefer.
Missing: label each adjacent area with a number +1 (or -1) its neighbours (No idea how to do this)
Missing: Interpolate flat areas. This should be doable once the altitudes are known. One can set the pixels in the skeleton image to the altitudes and interpolate the rest using griddata, which will be slow, but still doable.
Disclaimer: not full answer yet, feel free to edit or reuse the code in this answer to further it!

Binary image labeling in MATLAB with Hough transform

I have this binary image:
I'd like to label pixels lying on the same line
like this:
I tried Hough transform. However, as you see it failed finding all lines.
does anyone have any idea?
Hough should be just fine for this application. As Falimond and gfkri suggested, a few parameter tweaks to the standard Hough technique will help.
A few ideas:
Instead of counting Hough votes, determine line density. Once you've found a number of candidate lines, determine the maximum number of pixels each line would occupy in the image. (Lines through the center are longer than lines through points near the corners.) Divide the bin count [votes] by the maximum possible line length to yield line density. Then filter by line density. When you follow this technique, you'll often need to set thresholds for both density and minimum length--you don't want to find 8-pixel segments that happen to be straight but slightly angled from the line to which they belong.
Dilate the image first; from the look of it, you could dilate quite a bit. When the white pixel clumps are dilated, there will be a higher density of points along a Hough fit line. After dilation, you can also downsample the image (e.g. from 640x480 to 160x120) to reduce the number of points you have to process. [You could also apply Gaussian blur to generate a grayscale image. This maintains the original edge point(s) as the strongest peaks. You would then use weighted intensity values in your Hough accumulator.]
As an alternative to (2) dilation, for each (x,y) white pixel in the image add votes to your accumulator bins for all its neighbor pixels, too: (x-1,y-1), (x, y-1), . . . (x+1,y+1). This might be a quicker change to your code.
Reduce your angular resolution. That is, if your Hough accumulator using angular increments of 1 degree, try an angular increment of 2 degrees.
Take your (R,theta) accumulator plot--which you can consider as a sort of 2D intensity image--and smooth it out before identifying peaks. You might only need smoothing if you tend to get two nearly overlapping lines because the points along what you think should be one line are scattered in bimodal clumps.
Long story short: make the task simpler by reducing effective resolution, as gfkri suggested. Think of fitting an N-pixel thick Hough line segment rather than a 1-pixel Hough line.

Variance and Mean of Image

I am calculating mean and variance of my original and stego image to compare them
I am using grayscale BMP image for comaprison
image=imread("image name")
M = mean(image(:))
V = var((image(:)))
Is this is correct way fo calculating mean/var in MATLAB? My Variance is getting more than mean..
Any help appreciated..
These are indeed the correct way to calculate the mean and variance over all the pixels of your image.
It is not impossible that your variance is larger than the mean as both are defined in the following way:
mean = sum(x)/length(x)
variance = sum((x - mean(x)).^2)/(length(x) - 1);
For example, if you generate noise from a standard normal distribution with randn(N,1), you will get N samples, and if you calculate the mean and variance, you will get approximately 0 and 1. So there as well, your variance may well be larger than the mean.
Both have a totally different meaning: the mean gives you an idea where your pixels are (i.e. are they white, black, 50% gray, ...). The mean will give you an idea of what pixel color to choose to summarize the color of the complete image. The variance gives you an idea how the pixel values are spread: e.g. if your mean pixel value is 50% gray, are most of the other pixels also 50% gray (small variance) or do you have 50 black pixels and 50 white pixels (large variance)? So you could also view it as a way to get an idea how well the mean summarizes the image (i.e. with zero variance, most of the information is captured by the mean).
edit: For the RMS value (Root Mean Square) of a signal, just do what the definition says. In most cases you want to remove the DC component (i.e. the mean) before calculating the RMS value.
edit 2: What I forgot to mention was: it also makes little sense to compare the numerical value of the variance with the mean from a physical point of view. The mean has the same dimension as your data (in case of pixels, think of intensity), while the variance has the dimension of your data squared (so intensity^2). The standard deviation (std in MATLAB), which is the square root of the variance on the other hand has the same dimension as the data, so there you could make some comparisons (it is another question whether you should do such comparison).
If you are workign with RGB image (H x W x 3), you have to calculate mean and variance separately for each channel. In this case the mean pixel will also be 3-values vector.
for ch = 1:3
M(ch) = mean(reshape(img(:,:,ch),[],1));
V(ch) = var(reshape(img(:,:,ch),[],1));
end
MATLAB has function image. Avoid using it as a variable.

Image downsampling and upsampling using bilinear interpolation

I am trying to understand how exactly the upsampling and downsampling of a 2D image I have, would happen using Bilinear interpolation. Now I am aware of how bilinear interpolation works using a 2x2 neighbourhood values to interpolate the data point inside this 2x2 area using weights. But what I am not aware of, is asked below. My objectives and specific queries are -
1.To start with I have a 2D image of values(size MxN). The width(M) and height(N) of this image is not fixed, but will change from case to case. This 2D image needs to be down-sampled using bilinear interpolation to a grid of size PxQ (P and Q are to be configured as input parameters) e.g. lets take PxQ is 8x8. And assume input 2D array image is of size 200x100. i.e 200 columns, 100 rows.
Now how while performing downsampling using bilinear interpolation of this 200x100 image, should I first obtain a downsampled image of size 100x50 (downsampling by 2 in both dimensions using bilinear interpolation); then a 50x25 image(again by doing downsampling by 2 in both dimensions), then a 25x12 image, then a 12x12(this time doing downsampling by linear(not bilinear!) interpolation only along the rows, and finally drop some pixels to get 8x8.
Any pointers to exact algorithm or different ways to achieve this, are appreciated.
2.Above question raises another one - how to downsample using bilinear interpolation by a non-integer scale factor, e.g. how to go from a say 8x8 image array to a 6x2 image wherein resampling/scaling factors in both dimensions are not integers.
3.Then when I get a 8x8 sized image I need to upsample it by bilinear interpolation to the same original size I started with- MxN. If I need to go from 8x8 to say 20x20. How would it interpolate in between points in a row and would it interpolate a full row by some means. Again in case of non-integer scale factors how would bilinear interpolation for upsampling happen. Exact steps.
And finally I need to implement this in C.
I tried visualizing these particular questions by taking different examples, but not got a clear picture of how this bilinear interpolation would happen while downsampling and upsampling. All I have is plenty of paper sheets having'dots and crossed' pictures on my desk, but still no clear solution!
Any detailed reading material, books appreciated.

MATLAB: Return array of values between two co-ordinates in a large matrix (diagonally)

If I explain why, this might make more sense
I have a logical matrix (103x3488) output of a photo of a measuring staff having been run through edge detect (1=edge, 0=noedge). Aim- to calculate the distance in pixels between the graduations on the staff. Problem, staff sags in the middle.
Idea: User inputs co-ordinates (using ginput or something) of each end of staff and the midpoint of the sag, then if the edges between these points can be extracted into arrays I can easily find the locations of the edges.
Any way of extracting an array from a matrix in this manner?
Also open to other ideas, only been using matlab for a month, so most functions are unknown to me.
edit:
Link to image
It shows a small area of the matrix, so in this example 1 and 2 are the points I want to sample between, and I'd want to return the points that occur along the red line.
Cheers
Try this
dat=imread('83zlP.png');
figure(1)
pcolor(double(dat))
shading flat
axis equal
% get the line ends
gi=floor(ginput(2))
x=gi(:,1);
y=gi(:,2);
xl=min(x):max(x); % line pixel x coords
yl=floor(interp1(x,y,xl)); % line pixel y coords
pdat=nan(length(xl),1);
for i=1:length(xl)
pdat(i)=dat(yl(i),xl(i));
end
figure(2)
plot(1:length(xl),pdat)
peaks=find(pdat>40); % threshhold for peak detection
bigpeak=peaks(diff(peaks)>10); % threshold for selecting only edge of peak
hold all
plot(xl(bigpeak),pdat(bigpeak),'x')
meanspacex=mean(diff(xl(bigpeak)));
meanspacey=mean(diff(yl(bigpeak)));
meanspace=sqrt(meanspacex^2+meanspacey^2);
The matrix pdat gives the pixels along the line you have selected. The meanspace is edge spacing in pixel units. The thresholds might need fiddling with, depending on the image.
After seeing the image, I'm not sure where the "sagging" you're referring to is taking place. The image is rotated, but you can fix that using imrotate. The degree to which it needs to be rotated should be easy enough; just input the coordinates A and B and use the inverse tangent to find the angle offset from 0 degrees.
Regarding the points, once it's aligned straight, all you need to do is specify a row in the image matrix (it would be a 1 x 3448 vector) and use find to get non-zero vector indexes. As the rotate function may have interpolated the pixels somewhat, you may get more than one index per "line", but they'll be identifiable as being consecutive numbers, and you can just average them to get an approximate value.