function [bboxes,scores,labels] = helpergeneratePointPillarDetections(YPredictions,gridParams,pillarIndices,...
                                       anchorBoxes,confidenceThreshold,overlapThreshold,classNames)
    
    pillarIndices = extractdata(pillarIndices);
                                   
    % Generate the tiled anchors from grid parameters and anchor boxes.
    [~, anchorsBEV] = createAnchors(gridParams, anchorBoxes);
    
    % Calculate the anchor mask from the pillar indices.
    anchorMask = createAnchorMask(pillarIndices, gridParams, anchorsBEV);
    
    % Find the anchors that has points inside them.
    anchorMask = anchorMask > 1;
    gridX = gridParams{1,4}{1}/gridParams{1,3}{3};
    gridY = gridParams{1,4}{2}/gridParams{1,3}{3};
    numAnchors = size(anchorBoxes,2);
    anchorMask = reshape(anchorMask,[numAnchors,gridX,gridY]);
    anchorMask = permute(anchorMask,[2,3,1]);
    
    YPredictionsMod = cell(size(YPredictions));
    for i = 1:size(YPredictions,2)
        YPredictionsMod{1,i} = squeeze(extractdata(YPredictions{1,i}));
    end

    % Extract the data from the dlarray.
    predAngle = YPredictionsMod{1,6};
    predOcc = YPredictionsMod{1,3};
    predLoc = reshape(YPredictionsMod{1,2},[gridX,gridY,numAnchors,3]);
    predSz = reshape(YPredictionsMod{1,1},[gridX,gridY,numAnchors,3]);
    predHeading = YPredictionsMod{1,5};
    predClassification = YPredictionsMod{1,4};
    
    posIndices = find((predOcc > confidenceThreshold) & (anchorMask));
    [row,col,anchorNum] = ind2sub(size(predOcc),posIndices);
    
    % Calculate the confidence score of the boxes
    confScore = arrayfun(@(x,y,z) predOcc(x,y,z),row,col,anchorNum);
    
     % Calculate the box centre.
    xCen = (gridParams{1,3}{1})*(gridParams{1,3}{3})*(row-1) + gridParams{1,1}{1} + gridParams{1,3}{1};
    yCen = (gridParams{1,3}{2})*(gridParams{1,3}{3})*(col-1) + gridParams{1,1}{2} + gridParams{1,3}{2};
    
    % Decode the box locations.
    xGt = arrayfun(@(x,y,a,c) (predLoc(x,y,a,1)*(sqrt(anchorBoxes{1,a}{1}^2 + anchorBoxes{1,a}{2}^2))+c),row,col,anchorNum,xCen);
    yGt = arrayfun(@(x,y,a,c) (predLoc(x,y,a,2)*(sqrt(anchorBoxes{1,a}{1}^2 + anchorBoxes{1,a}{2}^2))+c),row,col,anchorNum,yCen);
    zGt = arrayfun(@(x,y,a,c) (predLoc(x,y,a,3)*anchorBoxes{1,a}{3} + anchorBoxes{1,a}{4}), row,col,anchorNum);
    zGt = 0.5*zGt;
    
    % Decode the box dimensions.
    lGt = arrayfun(@(x,y,a) (exp(predSz(x,y,a,1))*anchorBoxes{1,a}{1}),row,col,anchorNum);
    wGt = arrayfun(@(x,y,a) (exp(predSz(x,y,a,2))*anchorBoxes{1,a}{2}),row,col,anchorNum);
    hGt = arrayfun(@(x,y,a) (exp(predSz(x,y,a,3))*anchorBoxes{1,a}{3}),row,col,anchorNum);
    
    hdGt = arrayfun(@(x,y,a) predHeading(x,y,a),row,col,anchorNum);
    hdGt(hdGt >= 0.5) = 1;
    hdGt(hdGt < 0.5) = -1;
    
    % Decode the box yaw angle.
    predAngle((predAngle > 1) | (predAngle < -1)) = 0;
    angGt = arrayfun(@(x,y,a,h) (h*asin(predAngle(x,y,a))+anchorBoxes{1,a}{5}),row,col,anchorNum,hdGt);
    angGt = iWrapToPi(angGt);
    
    % Find the class of the object.
    predClassification = reshape(predClassification, gridX, gridY, numAnchors, []); 
    numClasses = size(predClassification, 4);
    
    % Find the class of the object.
    if(numClasses > 1) 
        cls = arrayfun(@(x,y,a) squeeze(predClassification(x,y,a,:))',row,col,anchorNum, 'UniformOutput', false);
        clsMat = cell2mat(cls);
        [~, cls] = max(clsMat,[],2);        
    else
        cls = arrayfun(@(x,y,a) predClassification(x,y,a),row,col,anchorNum);
        cls = 1./(1.+exp(-cls));
        cls(cls >= confidenceThreshold) = 1;
        cls(cls < confidenceThreshold) = 0;
    end
      
    boxPreds = [xGt,yGt,zGt,lGt,wGt,hGt,angGt,confScore,cls];
     
    % Convert the yaw angle from radians to degrees.
    boxPreds(:,7) = rad2deg(boxPreds(:,7));

    if ~isempty(posIndices)  
        bboxRotRect = boxPreds(:,[1,2,4,5,7]);
        scores = boxPreds(:,8);
        labels = boxPreds(:,9);
            
        [~,~,labels,idx] = selectStrongestBboxMulticlass(bboxRotRect,scores,labels,...
                                     'RatioType','Min','OverlapThreshold',overlapThreshold);
        labels = categorical(classNames(labels));
        box3D = boxPreds(idx,:);

        % Convert the boxes to cuboid format.
        bboxes = zeros(size(box3D,1),9);
        bboxes(:,[1,2,3,4,5,6,9]) = box3D(:,1:7);

    else
        bboxes = [];
        scores = [];
        labels = [];
    end
   
end
%% Create Anchors
function [anchors3D,anchorsBEV] = createAnchors(gridParams, anchorBoxes)
% createAnchors creates set of anchors for the entire point cloud given the point cloud
% range and anchor dimensions for the objects to be detected.
    voxelSize = [gridParams{1,3}{1}, gridParams{1,3}{2}];
    coorsRange = [gridParams{1,1}{1}, gridParams{1,1}{2}, gridParams{1,1}{3}..., 
                  gridParams{1,2}{1}, gridParams{1,2}{2}, gridParams{1,2}{3}];
    downsamplingFactor = gridParams{1,3}{3};
    anchors = [];
    for i = 1:numel(anchorBoxes)
        tmp = [anchorBoxes{1,i}{1}, anchorBoxes{1,i}{2}, anchorBoxes{1,i}{3}, anchorBoxes{1,i}{4}, anchorBoxes{1,i}{5}];
        anchors = [anchors;tmp];
    end
    numAnchors = size(anchors,1);

    xStep = voxelSize(1,1) * downsamplingFactor;
    yStep = voxelSize(1,2) * downsamplingFactor;
    gridX = (coorsRange(1,4) - coorsRange(1,1))/xStep;
    gridY = (coorsRange(1,5) - coorsRange(1,2))/yStep;

    x = linspace(coorsRange(1,1)+xStep*0.5 , coorsRange(1,4)-xStep*0.5, gridX);
    y = linspace(coorsRange(1,2)+yStep*0.5 , coorsRange(1,5)-yStep*0.5, gridY);
    [yy,xx] = meshgrid(y,x);
    xxT = reshape(xx,[numel(xx),1]);
    yyT = reshape(yy,[numel(yy),1]);
    tmp = cat(2,xxT,yyT);
    tmp = repelem(tmp,numAnchors,1);

    anchors3D = zeros([gridX*gridY*numAnchors,7]);
    anchors3D(:,1:2) = tmp;
    anchorsDim = repmat(anchors,[gridX*gridY,1]);
    anchors3D(:,3) = anchorsDim(:,4);
    anchors3D(:,4:6) = anchorsDim(:,1:3);
    anchors3D(:,7) = anchorsDim(:,5);

    anchors3dMod = anchors3D;
    anchors3dMod(2:2:size(anchors3D,1),[4,5]) = anchors3dMod(2:2:size(anchors3D,1),[5,4]);
    tmp1 = anchors3dMod(:,[1,2]) - anchors3dMod(:,[4,5])/2.0;
    tmp2 = anchors3dMod(:,[1,2]) + anchors3dMod(:,[4,5])/2.0;
    anchorsBEV = cat(2,tmp1,tmp2);
    
end
%% Create AnchorMask
function anchorMask = createAnchorMask(pillarIndices, gridParams, anchorsBEV)
    % createAnchorMask creates anchor mask for the anchors given the pillar Indices, point
    % cloud range and the bird's eye view dimensions of the anchors.
    sparseVoxelMap = zeros(gridParams{1,4}{1},gridParams{1,4}{2});
    numPillars = nnz(pillarIndices)/2;
    pillarIndices = pillarIndices(1:numPillars,:);
    sparseVoxelMap(sub2ind([gridParams{1,4}{1},gridParams{1,4}{2}],pillarIndices(:,1),pillarIndices(:,2))) = 1;

    sparseVoxelMap = cumsum(sparseVoxelMap, 1);
    sparseVoxelMap = cumsum(sparseVoxelMap, 2);
    anchorMask = zeros(size(anchorsBEV,1),1);
    
    for k = 1:size(anchorsBEV,1)

        % Calculating Bird's eye view dimensions of anchor
        bevXmin = anchorsBEV(k,1);
        bevYmin = anchorsBEV(k,2);
        bevXmax = anchorsBEV(k,3);
        bevYmax = anchorsBEV(k,4);

        xminGrid = floor((bevXmin - gridParams{1,1}{1})/gridParams{1,3}{1});
        yminGrid = floor((bevYmin - gridParams{1,1}{2})/gridParams{1,3}{2});
        xmaxGrid = floor((bevXmax - gridParams{1,1}{1})/gridParams{1,3}{1});
        ymaxGrid = floor((bevYmax - gridParams{1,1}{2})/gridParams{1,3}{2});

        xminGrid = max(xminGrid, 1);
        yminGrid = max(yminGrid, 1);
        xmaxGrid = min(xmaxGrid, gridParams{1,4}{1});
        ymaxGrid = min(ymaxGrid, gridParams{1,4}{2});

        ID = sparseVoxelMap(xmaxGrid, ymaxGrid);
        IA = sparseVoxelMap(xminGrid, yminGrid);
        IB = sparseVoxelMap(xminGrid, ymaxGrid);
        IC = sparseVoxelMap(xmaxGrid, yminGrid);

        anchorMask(k,1) = ID - IC - IB + IA;
    end
end
%% * Wrap from [-pi,pi] *
function alpha =iWrapToPi(alpha)
    idx = alpha>pi;
    alpha(idx) = alpha(idx)-2*pi;

    idx = alpha < -pi;
    alpha(idx) = alpha(idx)+2*pi;
end