I have a dataset with points (X,Y coordinates) that represent the shape of a glacier. However, when I plot them with
% Import glacier shape
Glaciershape = readtable('dem_glacierlocation.txt');
figure(1);
S = Glaciershape(:,1);
T = Glaciershape(:,2);
plot(S,T,'-')
It seems that an points connect when they don't need to (see attachment, at the left upper corner of the shape). Is there a way to fix this? You can download the dataset in the link below
Thanks!
Download text file glacier
If you are looking for the shortest path connecting all dots in a loop, this is known as the traveling salesman problem, which is very difficult to solve.
If you are just looking for an easy way to visualize try:
plot(S,T,'.')
where you replace the '-' you had above with '.'. This will plot the x,y coordinates unconnected and you can let the human brain do the connections, which it is good at.
Here is the image without connections. It looks like at the top there are two areas which are holes, which is why connecting all the points might be problematic.
plot will assume every set of x,y coords is sequential in a series and plot them all without a care as to content.
If you could make some assumption about the distribution of points then you could use that to break on those that fail. For example, a point in the series is always less than 100 units from the previous point. Otherwise, this begins a new series list. Using that you could do a check on distance between subsequent points using diff as in:
% assume data stored in xcoord,ycoord
% check for distance greater than 100
idx = find(sqrt(diff(xcoords).^2 + diff(ycoords).^2) > 100 );
% in this particular data set there are 3 disjoint sections
% plot out each section - here each done explicitly for illustration
plot(xcoords(1:idx(1)),ycoords(1:idx(1)));
hold on;
plot(xcoords(idx(1)+1:idx(2)),ycoords(idx(1)+1):idx(2));
plot(xcoords(idx(2)+1:idx(3)),ycoords(idx(2)+1):idx(3));
plot(xcoords(idx(3)+1:end),ycoords(idx(2)+1):end);
edit: added other plot's after looking at data file provided
hope that helps...
Related
Is it possible to make multiple vertical histograms plot in Matlab into one? Much like the excel sheet enclosed ( https://drive.google.com/file/d/1H_mbyrIoln3XrnK1hLajnVNBKn13y_np/view?usp=sharing )
I want to make a plot of many vertical histogram plots into one figure, by importing excel-files, where on the y axis it has the elevation, the x axis the distance between the histogram vertical lines and the length of the histogram bars is the values in the excel sheet. The vertical height of each bar is 5.
Is this even possible? I have to put in a number of conditions for Matlab to know where to plot, but could some one show me the basic methodology?
Help would be very much appreciated!
The problem is that the parent of a Baseline object is the Axis, which prevents us from doing something like
barh(bins1,counts1,'Basevalue',baseline1); hold on;
barh(bins2,counts2,'Basevalue',baseline2); hold off;
because the plots will automatically share the second baseline value set. There might be a workaround for this that I do not know of, so I invite anybody who knows it to show me how its done.
For now, I was able to sort-of replicate the plot you posted a picture of in a much less elegant way. I will post code below, but before I do, I would like to argue against the use of a plot like this. Why? Because I think it is confusing, as the x-axis both relates to the plot number as well as the bin count numbers. You are in fact trying to display a 3-D data set, the three dimensions being bins, bin counts, and 'histogram number'. A plethora of methods exist for displaying 3-D data, and a series of 2-D histograms may not be the best way to go.
That being said, here is a code that more-or-less creates the picture above, as promised. Any changes you may want to make will be more cumbersome than usual :-)
testData = randn(10000,1); % Generate some data
[counts,bins] = hist(testData); % Bin the data
% First histogram
baseline1 = 0;
p1=subplot(1,3,1); barh(bins,counts,'BaseValue',baseline1);
xticks(baseline1); xticklabels({0}); % Graph number on x axis at baseline (0)
box off; % Remove box on right side of plot
ylabel('Property');
% Second histogram
baseline2 = max(counts)*1.2;
sepdist = baseline2-baseline1; % Distance that separates two baselines
counts2 = baseline2 + counts;
p2=subplot(1,3,2); barh(bins,counts2,'BaseValue',baseline2)
xticks(baseline2); xticklabels({1}); % Graph number on x axis at baseline
box off;
Y=gca; Y.YAxis.Visible='off';
p1p=p1.Position; p2p=p2.Position;
p2p(1)=p1p(1)+p1p(3); p2.Position=p2p; % Move subplot so they touch
% Third histogram
baseline3 = baseline2 + sepdist;
counts3 = baseline3+counts;
p3=subplot(1,3,3); barh(bins,counts3,'BaseValue',baseline3)
xticks(baseline3); xticklabels({2});
Y=gca; Y.YAxis.Visible='off';
box off
p3p=p3.Position;
p3p(1)=p2p(1)+p2p(3); p3.Position=p3p;
% Add x-label when you are done:
xl=xlabel('Test xlabel'); xl.Units='normalized';
% Fiddle around with xl.Position(1) until you find a good centering:
xl.Position(1) = -0.49;
Result:
I am trying to write a MATLAB script to give me a contour map. The contour map must be created from inputs that I generated from 100 images.
The story is like this:
I have 100 images on which I ran an image processing algorithm for optimization. Now, I got their energy curves. So, I have 100 energy curves. I want to create a contour map that will show me where the points are denser on the plot. (the energy curves are plotted as energy vs. iteration with fixed number of iterations)
The following is my variable:
energy(iteration,numImages)
Hope I explained it well.
Thanks in advance.
I interpret your question to boil down to how can I create a surface plot with colors according to the energy found in energy. I would solve this by using the contour function with a grid generated using meshgrid. If each image is described in 1000 data points with 100 files the plot can be generated as follows:
% using stuff as random junk instead of energy
numPoints = 1000;
numFiles = 100;
stuff = rand(1000,100); % replace with actual information
[X, Y] = meshgrid(1:numFiles, 1:numPoints);
contour(X,Y,stuff);
You can also create a 3D surface plot using surf and the same logic.
From what i see of you graph (and using the comments also), one possible way is to use plot3 to plot a line in 3D for every plot.
For doing so, you can use something like this code:
x=(0:0.01:1)';
aexp=zeros(100,numel(x));
hold on
for ii=1:100;
% aexp(ii,:)=exp((-x+ii/10)); %exponential
aexp(ii,:)=exp(-(x-ii/100).^2); %~gaussian
% aexp(ii,:)= x*ii; %linear increase
plot3(x,aexp(ii,:),ii*ones(1,numel(x)));
end
% set(gca,'yscale','log'); % uncomment if you need logscale.
giving
I have a few options of plot. It always plot from the XY view. I changed by hand, but you can use the view command. Notice that i used a simple counter to make the spacing in the z direction.
In a similar manner, you can plot using the contour. For my code, after the data have been generated in the for loop, remove/comment the plot3 and add:
contour(aexp) %outside the for loop,
giving
Notice that i have not really take care what i'm plotting. You can find more info on contour in the Matlab page .
You commented that the x-axis should be number of iterations, y-axis should be energy and z-axis should be the information containing how many lines are passing through from some areas. For this, make a qq variable, being it qq=number_of_lines(number of iterations,energy) . Make a discrete grid for the energy if you don't have one. Number of iterations is probably discrete anyway. The function is you who need to devise, but i would go for something which checks the number of lines for every energy and every iteration. In this case you will have the z-function that depends on y and x, that is the case to use contour or surface.
My function above make a line for every ii point, to have a 3d function. An edition for another extra loop is not hard. Just remember to have the same regular grid for every point, otherwise you will have trouble.
I'm trying to find a starting point, but I can't seem to find the right answer. I'd be very grateful for some guidance. I also don't know the proper terminology, hence the title.
I took an image of a bag with a black background behind it.
And I want to extract the bag, similar to this.
And if possible, find the center, like this.
Essentially, I want to be able to extract the blob of pixels and then find the center point.
I know these are two separate questions, but I figured if someone can do the latter, then they can do the first. I am using MATLAB, but would like to write my own code and not use their image processing functions, like edge(). What methods/algorithms can I use? Any papers/links would be nice (:
Well, assuming that your image only consists of a black background and a bag inside it, a very common way to perform what you're asking is to threshold the image, then find the centroid of all of the white pixels.
I did a Google search and the closest thing that I can think of that matches what you want looks like this:
http://ak.picdn.net/shutterstock/videos/3455555/preview/stock-footage-single-blank-gray-shopping-bag-loop-rotate-on-black-background.jpg
This image is RGB for some reason, even though it's grayscale so we're going to convert this to grayscale. I'm assuming you can't use any built-in MATLAB functions and so rgb2gray is out. You can still implement it yourself though as rgb2gray implements the SMPTE Rec. 709 standard.
Once we read in the image, you can threshold the image and then find the centroid of all of the white pixels. That can be done using find to determine the non-zero row and column locations and then you'd just find the mean of both of them separately. Once we do that, we can show the image and plot a red circle where the centroid is located. As such:
im = imread('http://ak.picdn.net/shutterstock/videos/3455555/preview/stock-footage-single-blank-gray-shopping-bag-loop-rotate-on-black-background.jpg');
%// Convert colour image to grayscale
im = double(im);
im = 0.299*im(:,:,1) + 0.587*im(:,:,2) + 0.114*im(:,:,3);
im = uint8(im);
thresh = 30; %// Choose threshold here
%// Threshold image
im_thresh = im > thresh;
%// Find non-zero locations
[rows,cols] = find(im_thresh);
%// Find the centroid
mean_row = mean(rows);
mean_col = mean(cols);
%// Show the image and the centroid
imshow(im); hold on;
plot(mean_col, mean_row, 'r.', 'MarkerSize', 18);
When I run the above code, this is what we get:
Not bad! Now your next concern is the case of handling multiple objects. As you have intelligently determined, this code only detects one object. For the case of multiple objects, we're going to have to do something different. What you need to do is identify all of the objects in the image by an ID. This means that we need to create a matrix of IDs where each pixel in this matrix denotes which object the object belongs to. After, we iterate through each object ID and find each centroid. This is performed by creating a mask for each ID, finding the centroid of that mask and saving this result. This is what is known as finding the connected components.
regionprops is the most common way to do this in MATLAB, but as you want to implement this yourself, I will defer you to my post I wrote a while ago on how to find the connected components of a binary image:
How to find all connected components in a binary image in Matlab?
Mind you, that algorithm is not the most efficient one so it may take a few seconds, but I'm sure you don't mind the wait :) So let's deal with the case of multiple objects now. I also found this image on Google:
We'd threshold the image as normal, then what's going to be different is performing a connected components analysis, then we iterate through each label and find the centroid. However, an additional constraint that I'm going to enforce is that we're going to check the area of each object found in the connected components result. If it's less than some number, this means that the object is probably attributed to quantization noise and we should skip this result.
Therefore, assuming that you took the code in the above linked post and placed it into a function called conncomptest which has the following prototype:
B = conncomptest(A);
So, take the code in the referenced post, and place it into a function called conncomptest.m with a function header such that:
function B = conncomptest(A)
where A is the input binary image and B is the matrix of IDs, you would do something like this:
im = imread('http://cdn.c.photoshelter.com/img-get2/I0000dqEHPhmGs.w/fit=1000x750/84483552.jpg');
im = double(im);
im = 0.299*im(:,:,1) + 0.587*im(:,:,2) + 0.114*im(:,:,3);
im = uint8(im);
thresh = 30; %// Choose threshold here
%// Threshold image
im_thresh = im > thresh;
%// Perform connected components analysis
labels = conncomptest(im_thresh);
%// Find the total number of objects in the image
num_labels = max(labels(:));
%// Find centroids of each object and show the image
figure;
imshow(im);
hold on;
for idx = 1 : num_labels
%// Find the ith object mask
mask = labels == idx;
%// Find the area
arr = sum(mask(:));
%// If area is less than a threshold
%// don't process this object
if arr < 50
continue;
end
%// Else, find the centroid normally
%// Find non-zero locations
[rows,cols] = find(mask);
%// Find the centroid
mean_row = mean(rows);
mean_col = mean(cols);
%// Show the image and the centroid
plot(mean_col, mean_row, 'r.', 'MarkerSize', 18);
end
We get:
I have no intention of detracting from Ray's (#rayryeng) excellent advice and, as usual, beautifully crafted, reasoned and illustrated answer, however I note that you are interested in solutions other than Matlab and actually want to develop your own code, so I though I would provide some additional options for you.
You could look to the excellent ImageMagick, which is installed in most Linux distros and available for OS X, other good operating systems and Windows. It includes a "Connected Components" method and if you apply it to this image:
like this at the command-line:
convert bags.png -threshold 20% \
-define connected-components:verbose=true \
-define connected-components:area-threshold=600 \
-connected-components 8 -auto-level output.png
The output will be:
Objects (id: bounding-box centroid area mean-color):
2: 630x473+0+0 309.0,252.9 195140 srgb(0,0,0)
1: 248x220+0+0 131.8,105.5 40559 srgb(249,249,249)
7: 299x231+328+186 507.5,304.8 36620 srgb(254,254,254)
3: 140x171+403+0 458.0,80.2 13671 srgb(253,253,253)
12: 125x150+206+323 259.8,382.4 10940 srgb(253,253,253)
8: 40x50+339+221 357.0,248.0 1060 srgb(0,0,0)
which shows 6 objects, one per line, and gives the bounding boxes, centroids and mean-colour of each. So, the 3rd line means a box 299 pixels wide by 231 pixels tall, with its top-left corner at 328 across from the top-left of the image and 186 pixels down from the top-left corner.
If I draw in the bounding boxes, you can see them here:
The centroids are also listed for you.
The outout image from the command above is like this, showing each shape shaded in a different shade of grey. Note that the darkest one has come up black so is VERY hard to see - nearly impossible :-)
If, as you say, you wish to look at writing your own connected component code, you could look at my code in another answer of mine... here
Anyway, I hope this helps and you see it just as an addition to the excellent answer Ray has already provided.
I want to assign vector to a contourf graph, in order to show the direction and magnitude of wind.
For this I am using contourf(A) and quiver(x,y), where as A is a matrix 151x401 and x,y are matrices with the same sizes (151x401) with magnitude and direction respectively.
When I am using large maps i get the position of the arrows but they are to densily placed and that makes the graph look bad.
The final graph has the arrows as desired, but they are to many of them and too close, I would like them to be more scarce and distributed with more gap between them, so as to be able to increase their length and at the same time have the components of the contour map visible.
Can anyone help , any pointers would be helpful
i know its been a long time since the question was asked, but i think i found a way to make it work.
I attach the code in case someone encounters the same issues
[nx,ny]= size(A) % A is the matrix used as base
xx=1:1:ny; % set the x-axis to be equal to the y
yy=1:1:nx; % set the y-axis to be equal to the x
contourf(xx,yy,A)
hold on, delta = 8; %delta is the distance between arrows)
quiver(xx(1:delta:end),yy(1:delta:end),B(1:delta:end,1:delta:end),C(1:delta:end,1:delta:end),1) % the 1 at the end is the size of the arrows
set(gca,'fontsize',12);, hold off
A,B,C are the corresponding matrices ones want to use
I want an approach and method to separate the connected lines. Here is my image
and here is the result I would like
How do I solve that problem? Thank you in advance!
Sincerely
The watershed would be a problem as you have shown it produces multiple segmentations of the original line. Originally the watershed works for grains due to their convex shapes, while here in the case of lines there is no global convex shape to cause a good fragmentation, it would be good to use the watershed with some constraints.
It would be good to try solving a simpler version of the problem. Imagine that there are only horizontal and vertical lines possible. So in this case it would mean separating the horizontal long lines by cutting the short vertical lines (length measured by projecting on the x-y gradient). The basic hint is to use the gradient/slope of these lines to help decide where to cut - orthogonal line. In the more general case the problem requires a measure of local curvature or geodesic distance.
A simpler solution(in edit) is just removing the junction points in the skeleton you have.
This would cause some of your lines which are connected horizontally to be segmented but i guess this can be fixed with some end point filtering. A simple try here:
J = imread('input.png');
B = bwmorph(J,'branchpoints');
L = bwlabel((J>0).*(~B),8); %removing the branch points from the skeleton
Label = label2rgb(bwlabel((J>0).*(~B),8),'jet',[0 0 0]);
Final labeled line components. This requires further end point prefiltering, direction based filtering.
The parts of the contour that should be separated are basically the sections that are not in the same direction as most of the rest of the contour.
I can only give you a basic way to do this without specific code or functions and I doubt it is the most efficient, but since there are not too many answers here...also this is using the knowledge of the problem and the solution...
Find the connected contour with all its branches as a set of pixel coordinates (which represent the line as a single pixel wide contour)
Convert the contour list to a set of angles between each adjacent pixel coordinate
Optional: Filter out the high frequency components with an averaging filter
Histogram the angles to find the angle most of the contour lines lay on (call it the common angle)
Search the contour looking for sections that go from +/-common angle (tolerance of +/-30 degrees) to the negative of that (-/+ common angle with similar tolerance).
For each section delete the pixels associated with angles between the two thresholds above (i.e. common angle + 30 deg to -common angle - 30 degrees.
Repeat for each connected contour
Hope this helps some