Reputation: 218
I'm working on a function that selects all points within a bounding box around a line from a large coordinate set (to speed up a RANSAC fit). I ran the script containing the function last night, and of the total 55k seconds 30k (in 55M calls) were spent in this line (X
are 3×N cartesian coordinates, xmin
, xmax
etc. are vectors of same length as X
and corresponding boundaries):
inlierIdx = ( X(1,:) >= xmin & X(1,:) <= xmax & X(2,:) >= ymin & X(2,:) <= ymax );
Could you help me on how to speed this up? Short-circuiting, which would be a good start to improve performance, doesn't seem to work with indexing.
Or if there would be a completely different better approach, here's the rest of the function's code (p1
, p2
are the points defining the line, a is the offset of the bounding box; the additional steps took another 12k secs for 55M calls):
function [inlierX, xmin, xmax, ymin, ymax] = selectbox(X, L, a) % xmin ... ymax only for plotting purposes
% X: 3xN coords to check; L: 3x2 vector defining line; a: offset of bounding box
p1 = L(:,1);
p2 = L(:,2);
constfactor = (X(3,:)-p1(3))/(p2(3)-p1(3)); % precompute for following steps
xmin = p1(1) + (p2(1)-p1(1))*constfactor - a; % line parallel to x-component of defined line, with offset
xmax = xmin + 2*a;
ymin = p1(2) + (p2(2)-p1(2))*constfactor - a;
ymax = ymin + 2*a;
inlierIdx = ( X(1,:) >= xmin & X(1,:) <= xmax & X(2,:) >= ymin & X(2,:) <= ymax );
if p1(3) == p2(3) % singularity if line is horizontal, discard then
inlierIdx = [];
end
inlierX = X(:,inlierIdx); % save & return inlier coordinates
It just occurred me that I should move that singularity if-clause so the computation is skipped if true, but that's a minor performance hit.
The function is called for each RANSAC sample, to select only points in a reasonable distance from the sampled line to compute their distance for thresholding.
MATLAB version is R2016a.
Parallel Computing Toolbox is available, I tried moving the steps into arrayfuns and calling the whole function with gpuArrays, but it was way slower.
The whole context is as follows:
prepare coordinates
while trialcount < expectedNumTrialsNeeded
draw random sample (generated array of randomly sampled coordinates and index into it)
check if sample is not degenerate and has allowed angle
compute number of inliers:
this calls my selectbox function, to select a smaller set of points, which are near enough to check - takes 30k sec of 55k
take those points and compute their distance to the line, all points within threshold are inliers - takes 12k secs of 55k
if number of inliers > best number of inliers yet, this is new best set
update expected number of trials needed to find sample of only inliers
increment trialcount
end
return best set
I work with arrays of 10k-100k points, and for each line I want to fit I run 1E+5...1E+6 trials to find the best set. I have around 50 lines to fit, I work through them by deleting inliers of found lines and doing the algorithm on the remaining.
Upvotes: 3
Views: 534
Reputation: 724
What about to use columnwise indexing in your code? I mean to use X as Nx3 cartesian coordinates not as 3xN. As far, as I know columnwise indexing can be faster due to MATLAB arrays are stored in a FORTRAN manner.
Upvotes: 1
Reputation: 38042
I doubt that that line is the real problem. You say you do 55 million calls to this function -- may we see the code that does that? Because I strongly suspect that this code is where the true problem lies.
I suspect that you call this function in a loop, which would make it hard/impossible for MATLAB to accelerate the call effectively with its JIT compiler. If that is the case, then indeed, the heaviest burden shown will be this one line, but it's only so slow because your code structure forces MATLAB to keep calling the interpreter, rather than execute machine code. If this is indeed the case, then copy-pasting this small function in-place in the loop body will tremendously speed everything up (that is, if the remainder of the loop body can also be accelerated...)
Well anyway, this is what I could make of it. It reduces the number of comparisons at the cost of additional indexing; I also doubt that this is faster...oh well, just do 1000 calls and compare.
function [inlierX, xmin, xmax, ymin, ymax] = selectbox(X, L, a)
% X: 3xN coords to check; L: 3x2 vector defining line; a: offset of bounding box
p1 = L(:,1);
p2 = L(:,2);
constfactor = (X(3,:)-p1(3)) / (p2(3)-p1(3));
% line parallel to x-component of defined line, with offset
xmin = p1(1) + (p2(1)-p1(1))*constfactor - a;
xmax = xmin + 2*a;
ymin = p1(2) + (p2(2)-p1(2))*constfactor - a;
ymax = ymin + 2*a;
% singularity if line is horizontal, discard then
if p1(3) ~= p2(3)
inlierIdx = X(1,:) >= xmin;
inlierIdx(inlierIdx) = X(1,inlierIdx) <= xmax(inlierIdx);
inlierIdx(inlierIdx) = X(2,inlierIdx) >= ymin(inlierIdx);
inlierIdx(inlierIdx) = X(2,inlierIdx) <= ymax(inlierIdx);
else
inlierIdx = [];
end
inlierX = X(:,inlierIdx); % save & return inlier coordinates
end
Upvotes: 1