There's probably many codes around on fileexchange or highly optimized solutions on the web (for texture projection for instance), anyway here is my solution:
I assume pillar data is a matrix of size [tcount x zcount]
where tcount
is the number of scan positions along angular axis and zcount
is the number of scan positions along height axis.
So here is the code:
%
% PURPOSE:
%
% Project cone data to plane data
%
% INPUT:
%
% thetaValues: Angular-scan values (in radians !!)
% zValues: z-scan values (whathever unit)
% coneData: Scanned data of size thetaCount-by-zCount
% zminRadius: Radius at zmin position (same unit as zValues)
% zmaxRadius: Radius at zmax position (same unit as zValues)
% xvalues: Values to have along x-axis (by default biggestRadius * thetaValues centered around theta = 0)
%
% OUTPUT:
%
% xvalues: Positions along x-axis (same unit as zValues)
% zvalues: Positions along z-axis (same values/unit as input)
% planeData: Plane data of size xcount-by-zcount
%
function [xvalues, zValues, planeData] = Cone2Plane(thetaValues, zValues, coneData, zminRadius, zmaxRadius, xvalues)
%[
% Default arguments
if (nargin < 6), xvalues = []; end
% Init
zmin = min(zValues);
zmax = max(zValues);
smallestRadius = min(zminRadius, zmaxRadius);
biggestRadius = max(zminRadius, zmaxRadius);
% Ensure thetaValues range in -pi;+pi;
thetaValues = mod(thetaValues + pi, 2*pi) - pi;
[thetaValues, si] = sort(thetaValues);
coneData = coneData(si, :);
% Intercept theorem (Thales)
%
% A-------+-------\
% /| | \
% / | | \
% D--E-------+----------\
% / | | \
% B----C-------+------------\
BC = biggestRadius - smallestRadius;
AE = zmax - zValues(:);
AC = (zmax - zmin);
DE = (BC * AE) / AC;
radiuses = smallestRadius + DE;
% Projection
if (isempty(xvalues)), xvalues = biggestRadius * thetaValues; end
xcount = length(xvalues);
zcount = length(zValues);
planeData = zeros(xcount, zcount);
for zi = 1:zcount,
localX = radiuses(zi) * thetaValues;
localValues = coneData(:, zi);
planeData(:, zi) = interp1(localX, localValues, xvalues, 'linear', 0);
end
%]
end
I will not enter into details of Thales theorem to compute radiuses
for each z-position.
I reorder angular positions to be in [-180°;+180°] so as to have symmetric x-positions (i.e. xLocal = radiusLocal * angularPos
).
The tricky part is to re-interpolate xLocal
to some xGlobal
positions (Which I assume to be xLocal = biggestRadius * angularPos
, but you can of course provide your own values as an argument to Cone2Plane
routine).
Here some test case:
function [] = TestCone2Plane()
%[
% Scanning info
zminRadius = 1.8506;
zmaxRadius = 1.6849;
zValues = linspace(0.0, 24.6, 100);
thetaValues = linspace(0, 359, 360) * pi / 180;
% Build dummy cone data
tcount = length(thetaValues);
zcount = length(zValues);
coneData = rand(tcount, zcount);
% Convert to plane data
xvalues = []; % Means automatic x-positions
[xvalues, zValues, planeData] = Cone2Plane(thetaValues, zValues, coneData, zminRadius, zmaxRadius, xvalues);
% Display
[X, Z] = ndgrid(xvalues, zValues);
pcolor(X, Z, planeData);
shading flat;
%]
end
Edit
It is unclear what exact input data you have, but if pillar is a set of (x,y,z, intensity) points, you may reduce data set to coneData as follow:
thetas = atan2(y(:), x(:));
thetaValues = unique(thetas); tcount = length(thetaValues);
zValues = unique(z); zcount = length(zValues);
coneData = zeros(tcount, zcount);
for ti = 1:tcount,
logict = (thetas == thetaValues(ti));
for zi = 1:zcount,
logicz = (z == zValues(zi));
coneData(ti, zi) = mean(intensity(logict & logicz));
end
end
NB In practice, you may want to use some unique with tolerance (+ logic = abs(val-pos) < tol
) to further reduce data while accounting for noise in scan positions.