Transform Point Cloud Using Lidar Viewer
This example shows how to create a custom algorithm to transform a point cloud by using the Lidar Viewer app.
Read Point Cloud
Read point cloud data into the workspace by using the pcread
function.
fileName = "highwayScene.pcd";
ptCloud = pcread(fileName);
Import Point Cloud into Lidar Viewer
To open the Lidar Viewer app, enter this command in the MATLAB® command window.
lidarViewer
Alternatively, you can select the app from the Image Processing and Computer Vision section of the Apps tab.
On the app toolstrip, select Import > From Workspace. In the Import from Workspace dialog box, select ptCloud
and click OK.
The app loads the point cloud data and displays it in the Point Cloud Display pane.
Create Custom Preprocessing Algorithm
Create Custom Algorithm
On the app toolstrip, select Edit Point Cloud to open the Edit Point Cloud tab. To create a class-based preprocessing algorithm, select Create Algorithm > Class Template.
MATLAB opens a new script containing the code template and the instructions to
create a class-based definition for the algorithm. You can also define the algorithm parameters as user interface (UI) elements using this code template.
This example creates an EditTransform
class based on the class template. You can use this algorithm class to transform point clouds.
Write Algorithm Class Definition
Write the class definition for a custom algorithm inherited from the lidar.internal.lidarViewer.edits.EditAlgorithm
class.
First, define the name, icon, and description for the algorithm.
properties (Constant) EditName = "Rigid transform"; Icon = fullfile(matlabroot, "toolbox", "lidar", "lidar", "+lidar",... "+internal", "+lidarViewer", "+view", "+icons", "classTemplate_24.png"); Description = "Transform point cloud" end
Next, specify the custom properties for the algorithm.
properties (Access = private) % Properties related to rotation around x-axis RotationAroundX = 0 RotationAroundXMinValue = -360 RotationAroundXMaxValue = 360 RotationAroundXLabel RotationAroundXSlider RotationAroundXEB % Properties related to rotation around y-axis RotationAroundY = 0 RotationAroundYMinValue = -360 RotationAroundYMaxValue = 360 RotationAroundYLabel RotationAroundYSlider RotationAroundYEB % Properties related to rotation around z-axis RotationAroundZ = 0 RotationAroundZMinValue = -360 RotationAroundZMaxValue = 360 RotationAroundZLabel RotationAroundZSlider RotationAroundZEB % Properties related to translation around x-axis TransAcrossX = 0 TransAcrossXMinValue = -100 TransAcrossXMaxValue = 100 TransAcrossXLabel TransAcrossXSlider TransAcrossXEB % Properties related to translation around y-axis TransAcrossY = 0 TransAcrossYMinValue = -100 TransAcrossYMaxValue = 100 TransAcrossYLabel TransAcrossYSlider TransAcrossYEB % Properties related to translation around z-axis TransAcrossZ = 0 TransAcrossZMinValue = -100 TransAcrossZMaxValue = 100 TransAcrossZLabel TransAcrossZSlider TransAcrossZEB end
Third, specify the function definitions.
The setUpEditOperation
function initializes variables of the edit algorithm.
function setUpEditOperation(this, varargin) % Overwrite base class method to set default values for % parameters. this.PointCloud = varargin{1}; if nargin > 2 % If you edit the applied algorithm from the History pane, % retain the previous parameter values. % Otherwise, set default values for the parameters. this.RotationAroundX = varargin{2}.RotationAroundX; this.RotationAroundY = varargin{2}.RotationAroundY; this.RotationAroundZ = varargin{2}.RotationAroundZ; this.TransAcrossX = varargin{2}.TransAcrossX; this.TransAcrossY = varargin{2}.TransAcrossY; this.TransAcrossZ = varargin{2}.TransAcrossZ; else this.RotationAroundZ = 0; this.RotationAroundY = 0; this.RotationAroundX = 0; this.TransAcrossX = 0; this.TransAcrossY = 0; this.TransAcrossZ = 0; end end
The setUpAlgorithmConfigurePanel
function adds all the UI elements to the Algorithm Parameters pane in the Lidar Viewer app.
function setUpAlgorithmConfigurePanel(this,grid) % Function to configure the Algorithm Parameters pane to % tune the algorithm parameters. % Assign width and height of the grid in the Algorithm % Parameters pane. For this algorithm, 45 pixels are used for % default buttons at the bottom of the panel, 40 pixels for % sliders, and 25 pixels for UI text and edit boxes. grid.ColumnWidth = {'1x', 45}; grid.RowHeight = {25, 40, 25, 40, 25, 40, 25, 40, 25, 40, 25, 40}; % Create 'Rotation around x-axis' UI text. this.RotationAroundXLabel = uilabel('Parent', grid, 'Text', ... 'Rotation around x-axis', ... 'Tooltip','Amount of rotation around x-axis(in degrees)'); this.RotationAroundXLabel.Layout.Row = 1; this.RotationAroundXLabel.Layout.Column = 1; % Create edit box for 'Rotation around x-axis'. this.RotationAroundXEB = uieditfield('parent',grid, ... 'ValueChangedFcn',@(~,evt)editBoxValueChanged(this, evt,'RotationAroundX'),... 'Value',num2str(this.RotationAroundX), ... 'Tooltip','Amount of rotation around x-axis(in degrees)', ... 'Tag','RotationAroundXEB'); this.RotationAroundXEB.Layout.Row = 1; this.RotationAroundXEB.Layout.Column = 2; % Create a slider for 'Rotation around x-axis'. this.RotationAroundXSlider = uislider('Parent', grid,... 'Limits', [this.RotationAroundXMinValue, this.RotationAroundXMaxValue], ... 'MajorTicks', [this.RotationAroundXMinValue, this.RotationAroundXMaxValue], ... 'Value', this.RotationAroundX,... 'ValueChangingFcn',@(~,evt)sliderChanging(this, evt,'RotationAroundX'), ... 'Tag','RotationAroundXSlider'); this.RotationAroundXSlider.Layout.Row = 2; this.RotationAroundXSlider.Layout.Column = [1 2]; % Create 'Rotation around y-axis' UI text. this.RotationAroundYLabel = uilabel('Parent', grid, 'Text', ... 'Rotation around y-axis', ... 'Tooltip','Amount of rotation around y-axis(in degrees)'); this.RotationAroundYLabel.Layout.Row = 3; this.RotationAroundYLabel.Layout.Column = 1; % Create an edit box for 'Rotation around y-axis'. this.RotationAroundYEB = uieditfield('parent',grid, ... 'ValueChangedFcn',@(~,evt)editBoxValueChanged(this, evt,'RotationAroundY'),... 'Value',num2str(this.RotationAroundY), ... 'Tooltip','Amount of rotation around y-axis(in degrees)', ... 'Tag','RotationAroundYEB'); this.RotationAroundYEB.Layout.Row = 3; this.RotationAroundYEB.Layout.Column = 2; % Create a slider box for 'Rotation around y-axis'. this.RotationAroundYSlider = uislider('Parent', grid,... 'Limits', [this.RotationAroundYMinValue, this.RotationAroundYMaxValue], ... 'MajorTicks', [this.RotationAroundYMinValue, this.RotationAroundYMaxValue], ... 'Value', this.RotationAroundY,... 'ValueChangingFcn',@(~,evt)sliderChanging(this, evt,'RotationAroundY'), ... 'Tag','RotationAroundYSlider'); this.RotationAroundYSlider.Layout.Row = 4; this.RotationAroundYSlider.Layout.Column = [1 2]; % Create 'Rotation around z-axis' UI text. this.RotationAroundZLabel = uilabel('Parent', grid, 'Text', ... 'Rotation around z-axis', ... 'Tooltip','Amount of rotation around z-axis(in degrees)'); this.RotationAroundZLabel.Layout.Row = 5; this.RotationAroundZLabel.Layout.Column = 1; % Create an edit box for 'Rotation around z-axis'. this.RotationAroundZEB = uieditfield('parent',grid, ... 'ValueChangedFcn',@(~,evt)editBoxValueChanged(this, evt,'RotationAroundZ'),... 'Value',num2str(this.RotationAroundZ), ... 'Tooltip','Amount of rotation around z-axis(in degrees)', ... 'Tag','RotationAroundZEB'); this.RotationAroundZEB.Layout.Row = 5; this.RotationAroundZEB.Layout.Column = 2; % Create a slider box for 'Rotation around z-axis'. this.RotationAroundZSlider = uislider('Parent', grid,... 'Limits', [this.RotationAroundZMinValue, this.RotationAroundZMaxValue], ... 'MajorTicks', [this.RotationAroundZMinValue, this.RotationAroundZMaxValue], ... 'Value', this.RotationAroundZ,... 'ValueChangingFcn',@(~,evt)sliderChanging(this, evt,'RotationAroundZ'), ... 'Tag','RotationAroundZSlider'); this.RotationAroundZSlider.Layout.Row = 6; this.RotationAroundZSlider.Layout.Column = [1 2]; % Create 'Translation about x-axis' UI text. this.TransAcrossXLabel = uilabel('Parent', grid, 'Text', ... 'Translation about x-axis', ... 'Tooltip','Amount of translation across x-axis'); this.TransAcrossXLabel.Layout.Row = 7; this.TransAcrossXLabel.Layout.Column = 1; % Create an edit box for 'Translation about x-axis'. this.TransAcrossXEB = uieditfield('parent',grid, ... 'ValueChangedFcn',@(~,evt)editBoxValueChanged(this, evt,'TransAcrossX'),... 'Value',num2str(this.TransAcrossX), ... 'Tooltip','Amount of translation across x-axis', ... 'Tag','TransAcrossXEB'); this.TransAcrossXEB.Layout.Row = 7; this.TransAcrossXEB.Layout.Column = 2; % Create a slider box for 'Translation about x-axis'. this.TransAcrossXSlider = uislider('Parent', grid,... 'Limits', [this.TransAcrossXMinValue, this.TransAcrossXMaxValue], ... 'MajorTicks', [this.TransAcrossXMinValue, this.TransAcrossXMaxValue], ... 'Value', this.TransAcrossX,... 'ValueChangingFcn',@(~,evt)sliderChanging(this, evt,'TransAcrossX'), ... 'Tag','TransAcrossXSlider'); this.TransAcrossXSlider.Layout.Row = 8; this.TransAcrossXSlider.Layout.Column = [1 2]; % Create 'Translation about y-axis' UI text. this.TransAcrossYLabel = uilabel('Parent', grid, 'Text', ... 'Translation about y-axis', ... 'Tooltip','Amount of translation across y-axis'); this.TransAcrossYLabel.Layout.Row = 9; this.TransAcrossYLabel.Layout.Column = 1; % Create an edit box for 'Translation about y-axis'. this.TransAcrossYEB = uieditfield('parent',grid, ... 'ValueChangedFcn',@(~,evt)editBoxValueChanged(this, evt,'TransAcrossY'),... 'Value',num2str(this.TransAcrossY), ... 'Tooltip','Amount of translation across y-axis', ... 'Tag','TransAcrossYEB'); this.TransAcrossYEB.Layout.Row = 9; this.TransAcrossYEB.Layout.Column = 2; % Create a slider box for 'Translation about y-axis'. this.TransAcrossYSlider = uislider('Parent', grid,... 'Limits', [this.TransAcrossYMinValue, this.TransAcrossYMaxValue], ... 'MajorTicks', [this.TransAcrossYMinValue, this.TransAcrossYMaxValue], ... 'Value', this.TransAcrossY,... 'ValueChangingFcn',@(~,evt)sliderChanging(this, evt,'TransAcrossY'), ... 'Tag','TransAcrossYSlider'); this.TransAcrossYSlider.Layout.Row = 10; this.TransAcrossYSlider.Layout.Column = [1 2]; % Create 'Translation about z-axis' UI text. this.TransAcrossZLabel = uilabel('Parent', grid, 'Text', ... 'Translation about z-axis', ... 'Tooltip','Amount of translation across z-axis'); this.TransAcrossZLabel.Layout.Row = 11; this.TransAcrossZLabel.Layout.Column = 1; % Create an edit box for 'Translation about z-axis'. this.TransAcrossZEB = uieditfield('parent',grid, ... 'ValueChangedFcn',@(~,evt)editBoxValueChanged(this, evt,'TransAcrossZ'),... 'Value',num2str(this.TransAcrossZ), ... 'Tooltip','Amount of translation across z-axis', ... 'Tag','TransAcrossZEB'); this.TransAcrossZEB.Layout.Row = 11; this.TransAcrossZEB.Layout.Column = 2; % Create a slider box for 'Translation about z-axis'. this.TransAcrossZSlider = uislider('Parent', grid,... 'Limits', [this.TransAcrossZMinValue, this.TransAcrossZMaxValue], ... 'MajorTicks', [this.TransAcrossZMinValue, this.TransAcrossZMaxValue], ... 'Value', this.TransAcrossZ,... 'ValueChangingFcn',@(~,evt)sliderChanging(this, evt,'TransAcrossZ'), ... 'Tag','TransAcrossZSlider'); this.TransAcrossZSlider.Layout.Row = 12; this.TransAcrossZSlider.Layout.Column = [1 2]; end
The getCurrentParams
function creates a structure for all the required algorithm parameters.
function params = getCurrentParams(this) % Get method to get the current algorithm parameter values % encapsulated in a structure params = struct(); params.RotationAroundZ = this.RotationAroundZ; params.RotationAroundY = this.RotationAroundY; params.RotationAroundX = this.RotationAroundX; params.TransAcrossX = this.TransAcrossX; params.TransAcrossY = this.TransAcrossY; params.TransAcrossZ = this.TransAcrossZ; end
The applyEdits
function defines the rigid transformation algorithm of this class. This function contains the code to transform point clouds.
function pointCloudOut = applyEdits(pointCloudIn, parameters) % Function to apply the rigid transform function to the input % point cloud object based on the parameters. % Rotation around Z-axis angle = parameters.RotationAroundZ *(pi/180); xTrans = parameters.TransAcrossX; yTrans = parameters.TransAcrossY; zTrans = parameters.TransAcrossZ; A = [cos(angle) sin(angle) 0 0; ... -sin(angle) cos(angle) 0 0; ... 0 0 1 0; ... 0 0 0 1]; tform = rigidtform3d(A); pointCloudOut = pctransform(pointCloudIn, tform); % Rotation around X-axis angle = parameters.RotationAroundX *(pi/180); A = [1 0 0 0; ... 0 cos(angle) sin(angle) 0; ... 0 -sin(angle) cos(angle) 0; ... 0 0 0 1]; tform = rigidtform3d(A); pointCloudOut = pctransform(pointCloudOut, tform); % Rotation around Y-axis angle = parameters.RotationAroundY *(pi/180); A = [cos(angle) 0 -sin(angle) 0; ... 0 1 0 0; ... sin(angle) 0 cos(angle) 0; ... 0 0 0 1]; tform = rigidtform3d(A); pointCloudOut = pctransform(pointCloudOut, tform); % Translation about X-, Y-, Z-axis A = [1 0 0 xTrans; ... 0 1 0 yTrans; ... 0 0 1 zTrans; ... 0 0 0 1]; tform = rigidtform3d(A); pointCloudOut = pctransform(pointCloudOut, tform); end
To dynamically update the point cloud with the latest algorithm parameters, add callback functions for each UI component.
Create a +lidar/+lidarViewer
package directory within a folder that is already on the MATLAB path and save EditTransform.m
file under that directory.
Import Custom Algorithm into Lidar Viewer
To import the algorithm into Lidar Viewer, on the app toolstrip, select Import Algorithm > Import Class. Then, in the dialog box, select the algorithm class file EditTransform.m
and click Open. The app adds this algorithm to the algorithms list on the Edit Point Cloud toolstrip. If you do not see the algorithm in the list, click Refresh List.
Select the Rigid Transform algorithm. You can tune the algorithm parameters in the Algorithm Parameters pane. The Lidar Viewer app dynamically updates the point cloud as you tune the parameters.
After you tune the parameters, click OK to apply the edit algorithm to the point cloud.