When inserting text into MATLAB figures programmatically using text(x,y,'label'), I often find that the text blocks overlap, making them unreadable. I was wondering if there was any automated way to offset the text blocks so they wouldn't overlap. For instance, if I added 3 labels with top-left alignment at the points (0,0), (0.01,0), and (0.02,0), I'd want them to reposition themselves like:
. . .
label1
label2
label3
whereas currently they look like:
. . .
la~~~~~~l3
where the squiggles are unreadable due to the overlap.
If there's not already a way to do this, I could roll my own algorithm/heuristic for the task, but is there a way to query a figure (or the gcf handle) for the bounding boxes of all existing text boxes on it? So then I can call this every time I want to place a label?
Thanks!
Here's a solution I came up with... seems to work OK.
function h = textfit(x,y,txt,varargin)
% textfit(x,y,txt,varargin)
%
% Mike Lawrence 2011
ythresh = 0.4; % maximal allowable overlap (includes cell padding accounted for in "extent" property)
xthresh = 0.1;
n = length(x);
if ~iscell(txt), txt={txt}; end
if length(y)~=n || length(txt)~=n, error('length mismatch between x,y,txt'); end
h = text(x,y,txt,varargin{:});
yl=ylim; ytot=diff(yl);
xl=xlim; xtot=diff(xl);
maxtries = 100;
for t=1:maxtries
ext = nan(n,4);
for i=1:n, ext(i,:) = get(h(i),'extent'); end
xstart=ext(:,1); xsz=ext(:,3); xend=xstart+xsz;
ystart=ext(:,2); ysz=ext(:,4); yend=ystart+ysz;
overlapx = zeros(n,n);
overlapy = zeros(n,n);
for i1=1:n-1, for i2=i1+1:n
if xstart(i1)<=xend(i2)&xstart(i2)<=xend(i1)
overlapx(i1,i2)=(min(xend(i2)-xstart(i1),xend(i1)-xstart(i2)))/(min(xsz(i1),xsz(i2)));
end
if ystart(i1)<=yend(i2)&ystart(i2)<=yend(i1)
overlapy(i1,i2)=(min(yend(i2)-ystart(i1),yend(i1)-ystart(i2)))/(min(ysz(i1),ysz(i2)));
end
end,end
overlapmax = max(overlapx,overlapy);
ov = (overlapx>xthresh & overlapy>ythresh);
[o1 o2] = find(ov);
if isempty(o1), break; end
[tmp ord] = sort(overlapmax(find(ov)));
o1=o1(ord); o2=o2(ord);
moved = false(n,1);
for i=1:length(o1), i1=o1(i); i2=o2(i);
if moved(i1) || moved(i2), continue; end
pos1 = get(h(i1),'position');
pos2 = get(h(i2),'position');
oy = overlapy(i1,i2)*min(ysz(i1),ysz(i2));
ox = overlapx(i1,i2)*min(xsz(i1),xsz(i2));
if oy/ytot < ox/xtot % overlapy is easier to fix
shift = 0.5*(1-ythresh)*oy;
if ystart(i1)<ystart(i2) % i1 above i2
pos1(2)=pos1(2)-shift; pos2(2)=pos2(2)+shift;
else % i1 below i2
pos1(2)=pos1(2)+shift; pos2(2)=pos2(2)-shift;
end
else % overlapx is easier to fix
shift = 0.5*(1-xthresh)*ox;
if xstart(i1)<xstart(i2) % i1 left of i2
pos1(1)=pos1(1)-shift; pos2(1)=pos2(1)+shift;
else % i1 right of i2
pos1(1)=pos1(1)+shift; pos2(1)=pos2(1)-shift;
end
end
set(h(i1),'position',pos1);
set(h(i2),'position',pos2);
moved([i1 i2]) = true;
end
end
if nargout==0, clear h, end
Related
I = imread('Sub1.png');
figure, imshow(I);
I = imcomplement(I);
I = double(I)/255;
I = adapthisteq(I,'clipLimit',0.0003,'Distribution','exponential');
k = 12;
beta = 2;
maxIter = 100;
for i=1:length(beta)
[seg,prob,mu,sigma,it(i)] = ICM(I, k, beta(i), maxIter,5);
pr(i) = prob(end);
hold on;
end
figure, imshow(seg,[]);
and ICM function is defined as
function [segmented_image,prob,mu,sigma,iter] = ICM(image, k, beta, max_iterations, neigh)
[width, height, bands] = size(image);
image = imstack2vectors(image);
segmented_image = init(image,k,1);
clear c;
iter = 0;
seg_old = segmented_image;
while(iter < max_iterations)
[mu, sigma] = stats(image, segmented_image, k);
E1 = energy1(image,mu,sigma,k);
E2 = energy2(segmented_image, beta, width, height, k);
E = E1 + E2;
[p2,~] = min(E2,[],2);
[p1,~] = min(E1,[],2);
[p,segmented_image] = min(E,[],2);
prob(iter+1) = sum(p);
%find mismatch with previous step
[c,~] = find(seg_old~=segmented_image);
mismatch = (numel(c)/numel(segmented_image))*100;
if mismatch<0.1
iter
break;
end
iter = iter + 1;
seg_old = segmented_image;
end
segmented_image = reshape(segmented_image,[width height]);
end
Output of my algorithm is a logical matrix (seg) of size 305-by-305. When I use
imshow(seg,[]);
I am able to display the image. It shows different component with varying gray value. But bwlabel returns 1. I want to display the connected components. I think bwlabel thresholds the image to 1. unique(seg) returns values 1 to 10 since number of classes used in k-means is 10. I used
[label n] = bwlabel(seg);
RGB = label2rgb(label);
figure, imshow(RGB);
I need all the ellipse-like structures which are in between the two squares close to the middle of the image. I don't know the number of classes present in it.
Input image:
Ground truth:
My output:
If you want to explode the label image to different connected components you need to use a loop to extract labels for each class and sum label images to get the out label image.
u = unique(seg(:));
out = zeros(size(seg));
num_objs = 0;
for k = 1: numel(u)
mask = seg==u(k);
[L,N] = bwlabel(mask);
L(mask) = L(mask) + num_objs;
out = out + L;
num_objs = num_objs + N ;
end
mp = jet(num_objs);
figure,imshow(out,mp)
Something like this is produced:
I have tried to do everything out of scratch. I wish it is of some help.
I have a treatment chain that get at first contours with parameters tuned on a trial-and-error basis, I confess. The last "image" is given at the bottom ; with it, you can easily select the connected components and do for example a reconstruction by markers using "imreconstruct" operator.
clear all;close all;
I = imread('C:\Users\jean-marie.becker\Desktop\imagesJPG10\spinalchord.jpg');
figure,imshow(I);
J = I(:,:,1);% select the blue channel because jpg image
J=double(J<50);% I haven't inverted the image
figure, imshow(J);
se = strel('disk',5);
J=J-imopen(J,se);
figure, imshow(J);
J=imopen(J,ones(1,15));% privilegizes long horizontal strokes
figure, imshow(J);
K=imdilate(J,ones(20,1),'same');
% connects verticaly not-to-far horizontal "segments"
figure, imshow(K);
I have some Matlab code lines for drawing Double Moon Classification:
function data=dm(r,w,ts,d)
clear all; close all;
if nargin<4, w=6;end
if nargin<3, r=10;end
if nargin<2, d=-4;end
if nargin < 1, ts=1000; end
ts1=10*ts;
done=0; tmp1=[];
while ~done,
tmp=[2*(r+w/2)*(rand(ts1,1)-0.5) (r+w/2)*rand(ts1,1)];
tmp(:,3)=sqrt(tmp(:,1).*tmp(:,1)+tmp(:,2).*tmp(:,2));
idx=find([tmp(:,3)>r-w/2] & [tmp(:,3)<r+w/2]);
tmp1=[tmp1;tmp(idx,1:2)];
if length(idx)>= ts,
done=1;
end
end
data=[tmp1(1:ts,:) zeros(ts,1);
[tmp1(1:ts,1)+r -tmp1(1:ts,2)-d ones(ts,1)]];
plot(data(1:ts,1),data(1:ts,2),'.r',data(ts+1:end,1),data(ts+1:end,2),'.b');
title(['Perceptron with the double-moon set at distance d = ' num2str(d)]),
axis([-r-w/2 2*r+w/2 -r-w/2-d r+w/2])
save dm r w ts d data;
Results:
My question is how to put a line into the Double Moon Classification so that can separate both classification in Matlab code?
I have made a simulation of your problem and wrote a pragmatic solution based on the steps
generates a grid of points fine enough(step size as small as
possible)
find which of these points are in the space between both points
groups
find which points have an equal distance to both groups
plot points
The main part (without my data generation code and the plot I did) of the code is
PBlues = data(1:ts,:);
PReds = data(ts+1:end,:);
Xs = linspace( min(data(:,1)), max(data(:,1)), 100);
Ys = linspace( min(data(:,2)), max(data(:,2)), 100);
[Xs,Ys] = meshgrid( Xs, Ys);
%% compute point relative distance to each point set (ble and red)
bCouldbeInbetween = false(size(Xs));
minDistsb = zeros(size(Xs));
minDistsr = zeros(size(Xs));
for p=1:numel(Xs)
distb = sqrt( (Xs(p)- PBlues(:,1)).^2+(Ys(p)- PBlues(:,2)).^2);
distr = sqrt( (Xs(p)- PReds(:,1)).^2+(Ys(p)- PReds(:,2)).^2);
minDistsb(p) = min(distb);
minDistsr(p) = min(distr);
i = find(distb == minDistsb(p));
j = find(distr == minDistsr(p));
bCouldbeInbetween(p) = (sign(PBlues(i,1)-Xs(p)) ~= sign(PReds(j,1)-Xs(p))) || ...
(sign(PBlues(i,2)-Ys(p)) ~= sign(PReds(j,2)-Ys(p)));
if bCouldbeInbetween(p)
end
end
% point distance difference to each point set
DistDiffs = abs(minDistsb - minDistsr);
% point with equal distance using proportional decision strategy
bCandidates = DistDiffs./ max(minDistsb,minDistsr) < 0.05;
medianLine = [Xs(bCandidates),Ys(bCandidates)];
[~,I] = sort(medianLine(:,1));
medianLine(:,1) = medianLine(I,1);
medianLine(:,2) = medianLine(I,2);
I hope this helps
I have just made a submission which can be downloaded here: https://de.mathworks.com/matlabcentral/fileexchange/66618-linebetweentwopointsgroups-exchange--
I want to set the Location of my legend to 'Best' (like legend('y1','y2','Location','Best')) so the legend doesn't collide with my lines, but at the same time, I would prefer to have it in a corner if that's possible with no data collision. Is there a way of implementing this?
In case anyone's interested in this, I wrote a function based on #S.. answer that does, what I wanted to achieve. Here's the code:
function setPositionCornerBest( figureHandle )
%Sets the Location of the legend of the figure that is referenced by figureHandle to one of the Corners if there is no data in the Corners. Otherwise it sets it to 'Best'
h = figureHandle;
figObjects = get(h,'Children');
legHandle = findobj(figObjects,'Tag','legend');
axHandle = findobj(figObjects,'Type','axes','-and','Tag','');
lineHandle = findobj(figObjects,'Type','line','-and','Parent',axHandle);
axPos = get(axHandle,'Position');
LimX = get(axHandle,'XLim');
LimY = get(axHandle,'YLim');
xScaling = (LimX(2)-LimX(1))/axPos(3);
yScaling = (LimY(2)-LimY(1))/axPos(4);
locCell = {'NorthWest','NorthEast','SouthEast','SouthWest'};
ii = 1;
interSecFlag = true;
while (interSecFlag) && (ii<=4)
set(legHandle,'Location',locCell{ii});
legPos = get(legHandle,'Position');
x(1) = LimX(1)+(legPos(1)-axPos(1))*xScaling;
x(2) = x(1);
x(3) = LimX(1)+(legPos(1)+legPos(3)-axPos(1))*xScaling;
x(4) = x(3);
x(5) = x(1);
y(1) = LimY(1)+(legPos(2)-axPos(2))*yScaling;
y(2) = LimY(1)+(legPos(2)+legPos(4)-axPos(2))*yScaling;
y(3) = y(2);
y(4) = y(1);
y(5) = y(1);
for jj = 1:numel(lineHandle)
xline = get(lineHandle(jj),'XData');
yline = get(lineHandle(jj),'YData');
[xInter ~] = intersections(x,y,xline,yline);
if numel(xInter) == 0
xInterFlag(jj) = 0;
else
xInterFlag(jj) = 1;
end
end
if all(xInterFlag==0)
interSecFlag = false;
end
ii = ii + 1;
end
if interSecFlag
set(legHandle,'Location','Best');
end
end
I don't have a complete answer, only a sketch. However, you could try to first set the legend in a corner
a=legend('y1', 'y2', 'Location', 'NorthEast')
and then obtain its position
get(a,'Position')
You can convert this position to coordinates and simply test whether your lines cross any border of the legend using
http://www.mathworks.com/matlabcentral/fileexchange/11837-fast-and-robust-curve-intersections
.If this is the case, try another corner, until there is no corner left. In that case, use 'Best'.
I am doing a real-time people detection using HOG-LBP descriptor and using a sliding window approach for the detector also LibSVM for the classifier. However, after classifier I never get multiple detected people, sometimes is only 1 or might be none. I guess I have a problem on my classification step. Here is my code on classification:
label = ones(length(featureVector),1);
P = cell2mat(featureVector);
% each row of P' correspond to a window
% classifying each window
[~, predictions] = svmclassify(P', label,model);
% set the threshold for getting multiple detection
% the threshold value is 0.7
get_detect = predictions.*[predictions>0.6];
% the the value after sorted
[r,c,v]= find(get_detect);
%% Creating the bounding box for detection
for ix=1:length(r)
rects{ix}= boxPoint{r(ix)};
end
if (isempty(rects))
rects2=[];
else
rects2 = cv.groupRectangles(rects,3,'EPS',0.35);
end
for i = 1:numel(rects2)
rectangle('Position',[rects2{i}(1),rects2{i}(2),64,128], 'LineWidth',2,'EdgeColor','y');
end
For the whole my code, I have posted here : [HOG with SVM] (sliding window technique for multiple people detection)
I really need a help for it. Thx.
If you have problems wiith the sliding window, you can use this code:
topLeftRow = 1;
topLeftCol = 1;
[bottomRightCol bottomRightRow d] = size(im);
fcount = 1;
% this for loop scan the entire image and extract features for each sliding window
for y = topLeftCol:bottomRightCol-wSize(2)
for x = topLeftRow:bottomRightRow-wSize(1)
p1 = [x,y];
p2 = [x+(wSize(1)-1), y+(wSize(2)-1)];
po = [p1; p2];
img = imcut(po,im);
featureVector{fcount} = HOG(double(img));
boxPoint{fcount} = [x,y];
fcount = fcount+1;
x = x+1;
end
end
lebel = ones(length(featureVector),1);
P = cell2mat(featureVector);
% each row of P' correspond to a window
[~, predictions] = svmclassify(P',lebel,model); % classifying each window
[a, indx]= max(predictions);
Follow-up question from Matlab: Get coordinates of clicks in figure BUT keep button-callbacks.
Great code.
The function provided only outputs the 'button' of the last click. Is there a way to modify the code to output the specific mouse button clicked for every point selected?
Also, is there a way to have, say, a third input that can be used to specify that there are an unknown number of points to be chosen, and continue to select points until something like 'c' or 'return' is entered with the keyboard?
Regarding your second requirement, when a user presses a key, the control moves from the figure window to the command window, unless you are using waitforbuttonpress , which would unnecessarily complicate things. So an alternative solution could be suggested to use double clicks to signal the end of inputting coordinates. This is done in the modified version of the linked stackoverflow code shown next.
Code
function varargout = ginput_ax_mod2(ha,n)
if nargin<2
n=1;
end
k = 0;
button = 0;
%%// Tolerance so that in the inifnity case, this could act as
%%// the thresholding distance below which the
%%// input extracting operation must be terminated
TOL = 0.01;
%%// Placeholders for X-Y and button type could be stored
button1 = [];
xy = [];
hf = get(ha,'parent');
figure(hf);
set(hf,'WindowButtonMotionFcn',#changepointer)
set(ha,'ButtonDownFcn',#getpoints)
hp = get(ha,'children');
ht = get(hp,'hittest');
set(hp,'hittest','off')
axlim = get(ha,'Position');
fglim = get(hf,'Position');
x1 = axlim(1)*fglim(3) + fglim(1);
x2 = (axlim(1)+axlim(3))*fglim(3) + fglim(1);
y1 = axlim(2)*fglim(4) + fglim(2);
y2 = (axlim(2)+axlim(4))*fglim(4) + fglim(2);
waitfor(hf,'WindowButtonMotionFcn',[])
if iscell(ht)
for jj=1:length(ht)
set(hp(jj),'hittest',ht{jj})
end
else
set(hp,'hittest',ht)
end
selType = get(hf,'SelectionType');
% Mouse-Button recognition...
if(strcmp(button, 'normal'))
button = 1; % left
elseif(strcmp(button, 'extend'))
button = 2; % right
elseif(strcmp(button, 'alt'))
button = 3; % middle
else
button = 4; % double click any mousebutton
end
if nargout==3
varargout{1} = xy(:,1);
varargout{2} = xy(:,2);
varargout{3} = button1(:,1);
elseif nargout==2
varargout{1} = xy(:,1);
varargout{2} = xy(:,2);
else
varargout{1} = xy;
end
function changepointer(~,~)
pntr = get(0,'PointerLocation');
if pntr(1)>x1 && pntr(1)<x2 && pntr(2)>y1 && pntr(2)<y2
set(hf,'Pointer','crosshair')
else
set(hf,'Pointer','arrow')
end
end
function getpoints(src,evnt)
cp = get(src,'CurrentPoint');
button = get(hf, 'SelectionType');
k = k+1;
if k==1
xy = [xy ;cp(1,1:2)];
button1 = [button1; {button}];
end
if k>=2
if pdist2(cp(1,1:2),xy(k-1,:))<TOL && isinf(n)
k = n;
else
xy = [xy ;cp(1,1:2)];
button1 = [button1; {button}];
end
end
if k==n
set(hf,'Pointer','arrow')
set(hf,'WindowButtonMotionFcn',[])
set(ha,'ButtonDownFcn',[])
return;
end
end
end
Sample runs
Run 1: Infinite points case
[x_coords,y_coords,button_types] = ginput_ax_mod2(gca, Inf)
Run 2: 10 points case
[x_coords,y_coords,button_types] = ginput_ax_mod2(gca, 10)
Sample output with infinite case but double clicks after third click -
x_coords =
2.0472
4.3076
5.9873
y_coords =
24.4152
25.2924
26.7544
button_types =
'normal'
'alt'
'normal'