copyobj copies entire image instead of axes only - image

What I have is a plot showing the area of connected components. What I want to do is to further work on the plot figure such as clean it up a bit or imcomplement it etc. and then be able to apply the axes from the original plot to this image and be able to extract the ylabel.
Let me explain the above issue with my code and some examples.
This is the plot I have, the y-axis represents the object areas. This is the important axis that I want to transfer to the new image.
Since I am interested in the axes only I copy that using
h = findobj(gcf,'type','axes');
So that I can work with the figure without the axes and borders interfering I save it without these attributes
set(gca, 'visible', 'off'); % Hide the axis and borders
hgexport(gcf, 'plot1.jpg', hgexport('factorystyle'), 'Format', 'jpeg');
This is what I get:
So far so good.
Now comes the processing or in other words changing the plot to my needs.
plot_img = rgb2gray(imread('plot1.jpg'));
img_bw_plot = im2bw(plot_img, graythresh(plot_img));
[rows cols] = size(plot_img);
new = zeros(size(plot_img));
for i = 1: rows
for j = 1: cols
if (img_bw_plot(i,j) == 0)
new(i, 1:10) = 255;
end
end
end
f = figure;
imshow(new);
copyobj(h,f)
This produces a weird overlapped image where instead of copying only the axes, the entire image is copied on top of the new. The datacursormode also fails to work beyond the over lapping image.

First of all I'm a little bit confused that if you have the figure in the first place why aren't you extracting your data from it using something like:
lines=findobj(gca,'type','line');
y=zeros(1,length(lines));
for i=1:length(lines)
y(i)=get(lines(i),'ydata');
end
and there you'll have all the data.
But let's say the original figure isn't like a figure figure where you'd have access to the children of the axes object (though all of them being copied together kind of suggests that this is not the case). What you need to realize is that an "axes" object in MATLAB isn't just the axes of the graph, but the whole graph. For example when you have 5 subplots, each of those smaller plots is an axes object and the graph itself is one of its children which is a "line" object (refer to my example above).
So after this long lecture :), one solution is that you could manually create those axes around your newly drawn image instead of copying the axes object as such:
set(gca,'visible','on');
s=size(new);
set(gca,'ytick',linspace(1,s(1),7),'yticklabel',linspace(6000,0,7));
This should do the trick of placing 7 ticks on the y-axis in the same manner as you have on your original figure. The same method would apply to manually creating the labels for the x-axis.
(I tried putting the resulting image here but I don't have the enough reputations to do so. That's on stackoverflow bro!)
Mind you, though, that this creates the labels on the graph giving you the illusion of the same axis while the actual coordinates of the points are actually determined by the size of the image you're saving. So if you want to make sure the image is the same size, you need to work on resizing your original figure to end up being the same size, which given then 0-6000, would be a really big image.

Related

Draw a curve in the segmented image using matlab

I have a segmented image as shown here
i want to fit a curve along the top pixels of the segmented image(show as red curve) and i want to find the top point along the curve show in blue. I have already worked on basic idea like traversing through the top to bottom and collecting the top point along each column. i want to know is there any easy solution for this problem like directly taking out the boundary pixels and find the top point.I am using MATLAB for this problem
%download the image
img = logical(imread('http://i.stack.imgur.com/or2iX.png'));
%for some reason it appeared RGB with big solid borders.
%to monochrome
img = img(:,:,1);
%remove borders
img = img(~all(img,2), ~all(img,1));
%split into columns
cimg = num2cell(img,1);
%find first nonzero element per column
ridx = cellfun(#(x) find(x,1,'first'), cimg);
figure, imshow(img)
hold on
%image dim1 is Y, dim2 is X
plot(1:size(img,2),ridx-1,'r','linewidth',2)
%find top point
[yval, xval] = min(ridx);
If you want a smoother curve, try polyfit/polyval
#EDIT
If we want the line to have break at gaps between connected components, we should change the code to something like
bord_idx = sub2ind(size(img), ridx, 1:size(img,2));
regs=regionprops(bwlabel(img),'pixelidxlist');
regs_idx = struct2cell(regs);
split_step = cellfun(#(x) sum(ismember(bord_idx,x)), regs_idx);
split_step = split_step(split_step>0);
split_yvals = mat2cell(ridx',split_val);
split_xvals = mat2cell([1:size(img,2)]',split_val);
figure, imshow(img)
hold on
for k = 1:length(split_step),
plot(split_xvals{k}, split_yvals{k}, 'r', 'linewidth', 2),
end
However, the result is not ideal if one region is positioned over the other. If the "shadowed" points are needed, you should try bwtraceboundary or convexhull and find where the border turns down
As far as "simplest matlab solution" by which I think you mean built in matlab functions: imclose()->edge()->bwboundaries()->findpeaks()'on each boundary'->'filter results based on width and magnitude of peaks'. *you will need to tune all the parameters in these functions, I am just listing what would get you there if appropriately applied.
As far as processing speed is concerned, I think I would have done exactly what you did, basically collecting the top edge from a top down column search and then looking for the point of highest inflection. As soon as you start doing processing of any type, you start doing several operations per pixel which will quickly become more expensive than your initial search (just requires that your image and target are simple enough)
That being said, here are some ideas that may help:
1:If you run a sufficiently heavy closing (dilate->erode), that should fill in all that garbage at the bottom.
2: If you know that your point of interest is not at left or right of picture (boundaries), you could take the right and left edge points and calculate a slope to be applied as an offset to flatten the whole image.
3: If your image always has the large dark linear region below the peak as seen here, you could locate those edges with houghlines looking for verticals and then search only the columns between them.
4: If speed is a concern, you could do a more sophisticated search pattern than left to right, as your peak has a pretty good distribution around it which could help with faster localization of maxima.

How to overly depth data on 2D image?

All of this is done in Matlab. I have a 2D RGB image, with some depth data for key vertexes. That is, I have three vectors (m x 1): X, Y, Z. Together, [X(i), Y(i)] specifies the point in the image with depth Z(i).
The crux of my problem is this:
I would like to plot the image "warped" with the depth information. But, each time I keep calling functions like 'mesh(X,Y,Z,RGBImage)' and 'surf', I get weird errors like "Z need to be matrix not vector". Also, I haven't even been able to implement the 'warp' function, as I am not sure how to translate my data into a usable format.
Any help would be really appreciated.
EDIT: I finally got it to work the way I would like. The only thing that needed to be changed, that was not answered, was to include the line
s = surf(Xmat,Ymat,Zmat, T1, 'edgecolor', 'none', 'FaceColor', ...
'texturemap');
Where
T1 = rgb2gray(OrignialRGBImage);. Much thanks!
I don't have access to MATLAB right now, so I am giving the outline of the solution.
First, create two matrices using meshgrid: [Xmat,Ymat] = meshgrid(1:imgCols,1:imgRows);. Now, you can find every row of [X,Y] at [Xmat(i,j),Ymat(i,j)] for some (i,j). In the next step, create Zmat such that Zmat(i,j) is the correct depth value for [Ymat(i,j),Xmat(i,j)]. You should be able to do this as follows:
Zmat = zeros(imgRows,imgCols);
for i=1:size(X,1)
Zmat(Y(i),X(i))=Z(i);
end
Now you can do surf with Xmat,Ymat,Zmat as follows. Then overlay image as texture.
s = surf(Xmat,Ymat,Zmat);
set(s, 'faceColor', 'texture','edgecolor', 'none','cdata', subimage); %not tested,
%see if it works
Overlaying part taken from here.
If the above part doesn't work, then you can overlay Zmat in the heatmap style, see this question.

Fast redrawing of image in MATLAB using Image.CData

I am want to draw once, and then update (very) often and redraw (less) often an image in MATLAB. My image is a vector which is updated and redrawn. To show this image, I used I = imagesc(reshape(data, nVoxels)) to draw and I.CData(:) = data to update. (Redrawing is taken care of seperately.) This worked fine.
Now, in order to make the correspondence to an x-y-coordinate system (x horizontal, y vertical - very standard), where the first dimension of reshape(data, nVoxels) is x and the second is y, I need to draw like this:
I = imagesc(reshape(data, nVoxels)');
axis('xy');
But how can I make a quick update of the image data now?
So far, I have found I need to do
I.CData = reshape(data, nVoxels)';
but I would prefer to do something like before, updating CData without having to reallocate and without having to transpose the data.
Is that possible? I am specifically interested in updating very often in a loop; redrawing is handled independently using a timer.
The transpose can be avoided by setting x and y limits when you create the image to flip it, and rotating the axes:
I = imagesc([nVoxels(2) 1], [1 nVoxels(1)], reshape(data, nVoxels));
camroll(90);
then using
I.CData(:) = data;
again.
However, the transpose time is probably negligible compared to updating the figure using drawnow().

replacing array values for images-matlab

I have an image with an item in it and I am trying to find its edge.
I have applied an edge detection algorithm and I have successfully found the trace around the image using regionprops. I also have the pixels inside of the traced shape. What I want to do is replace all the other pixels with a black background.
A is an RGB image and Aseg is a segmented image
A1=rgb2gray(A)
pixels=regionprops(logical(Aseg),'PixelList');
I know how to plot the pixels that I need on top of the original image
imshow(A1);hold on
plot(pixels.PixelList(:,1),pixels.PixelList(:,2))
But I want to plot just the pixels i want to keep with their original value and just replace all the others with a black background and I can not find a way to do it.
Anyone has any suggestions?
Thank you.
If all you want is to maintain the pixels of the shape and set everything else to black, simply create a new blank image and copy the locations from the original image over to the new image, and leave everything else as black. This essentially sets all of the other locations to black while leaving the desired region intact.
You can achieve that by sub2ind to generate linear indices to directly access your image and copy those values over into the new image.
As such, try something like this:
Anew = zeros(size(A1), class(A1));
ind = sub2ind(size(A1), pixels.PixelList(:,2), pixels.PixelList(:,1));
Anew(ind) = A1(ind);
Anew contains the new image with the background pixels set to black. PixelList is a structure where the first column are the x or column coordinates and the second column are the y or row coordinates. With sub2ind, you must specify the rows first, then the columns after which is why we index the second column first followed by the first first column after.
Alternatively, you can achieve the same thing using logical indexing by creating a sparse matrix that is logical, then using this matrix to index directly into the image and set the proper locations:
Anew = zeros(size(A1), class(A1));
ind = logical(sparse(pixels.PixelList(:,2), pixels.PixelList(:,1), 1, size(A1,1), size(A1,2)));
Anew(ind) = A1(ind);
Note that the rows and columns ordering is the same as using sub2ind.

Plotting several sequences of frames from tiff files with a chosen size

I am analyzing some experimental data in the form of .tiff multi frames. Withins these tiff files i need to visualize and compare some specific sequences of frames. I want to generate a figure that contains the frames i have chosen from the files i have chosen. The file list and frames indexes list are generated with a user interface, which calls a plot function when parameters are filled.
The problem : What is the best solution so as to plot, with an optimal size BUT keeping square images (like the original), the chosen frames ? Simpler, how to choose the position and size of each frame i plot in the figure ?
I have tried with sub plot : it works but I can't manage to control the images size.
pos=0;
for j = 1:length(file_list)
for i = 1:length(index_list)
pos=pos+1;
subplot(size(file_list,1),length(index_list),pos)
a =imagesc(imread(file_list{j,:},index_list(i)));
I have also tried
for j = 1:length(file_list)
for i = 1:length(index_list)
a =imagesc(imread(file_list{j,:},index_list(i)));
set(gca,'Units','Pixels', 'Position', [10+100*i 10+100*j 100 100]);
But it seems like i can't set this individually without overwriting the last modification.
Finally, i have considered using "montage", but the way I save the images in a list doesn't seem to be ok.
frm_list=zeros(1,length(FL)*length(index_list));
for j = 1:length(FL)
for i = 1:length(index_list)
a =(imread(FL{j,:},index_list(i)));
frm_list=[frm_list a];
end
end
montage(frm_list,'Size', [length(FL) length(index_list)]);
Thanks
JC
You can use axis image to keep the same aspect ratio of the original image.
subplot('Position', [left bottom width height]) allows you to specify the relative position of the image to the figure window.
If you want to use a command other than imagesc, you can scale the data range of the image before drawing , then use colormap to apply false coloring to the image.

Resources