Search code examples
matlabplotfilterpointsscatter

Merge similar points in point cloud by its mean


I have a set of x,y,z coordinates where for each z value, there are some similar x,y values (not same but similar). That means when I plot the 3D object, some points are repeating in each frame(for each z) and also those points are little bit shifted because the objects may move little bit while obtaining the data.

In each frame the number of repeating points can be different. For the sake of accuracy of the point positions, I can't eliminate those repeating points. I have to merge them to their centre of mass position and construct the 3D object.

A part of my data looks like this: where, z is increasing by the value of 1 in each frame and I get a set of x,y values for that.

211.312500 242.803571 5.000000
213.864407 267.259887 5.000000
211.986111 214.125000 5.000000
224.198864 298.812500 5.000000
233.244444 225.170370 5.000000
238.354067 249.894737 5.000000
240.807692 198.725275 5.000000
247.603922 281.317647 5.000000
251.493671 316.481013 5.000000
260.176796 223.276243 5.000000
269.823529 256.522876 5.000000
276.506073 188.214575 5.000000
276.941176 292.972851 5.000000
286.388060 327.701493 5.000000
291.673684 223.368421 5.000000
302.773913 261.330435 5.000000
309.080189 300.816038 5.000000
315.031746 196.456349 5.000000
331.283784 231.896396 5.000000
338.495327 271.518692 5.000000
345.351064 303.595745 5.000000
362.117647 243.806723 5.000000
185.085714 256.892857 6.000000
193.821918 287.328767 6.000000
192.929293 227.070707 6.000000
210.651163 242.682171 6.000000
211.977654 213.435754 6.000000
213.306122 266.918367 6.000000
214.909091 183.415584 6.000000
224.152941 298.235294 6.000000
223.957746 327.887324 6.000000
233.672131 225.754098 6.000000
240.058091 198.863071 6.000000
238.150943 250.150943 6.000000
244.044586 170.891720 6.000000
246.847561 280.390244 6.000000
250.793651 316.892857 6.000000
263.232143 354.392857 6.000000
272.419643 157.517857 6.000000
275.139423 187.812500 6.000000
285.312217 327.692308 6.000000
304.767606 167.133803 6.000000
313.893519 195.916667 6.000000
317.795238 334.623810 6.000000
330.388430 230.388430 6.000000
337.775510 271.877551 6.000000
345.485294 304.955882 6.000000
355.268456 206.140940 6.000000
362.585635 243.928177 6.000000
372.008850 279.336283 6.000000
184.281818 255.945455 7.000000
185.614035 201.964912 7.000000
193.048673 286.907080 7.000000
192.668790 227.694268 7.000000
202.280000 316.160000 7.000000
214.126829 184.546341 7.000000
212.371795 212.762821 7.000000
212.763636 266.836364 7.000000
224.266332 298.502513 7.000000
224.325758 327.977273 7.000000
233.230088 349.371681 7.000000
239.947977 199.000000 7.000000
243.050251 170.839196 7.000000
250.298246 317.192982 7.000000
262.468900 354.789474 7.000000
271.629213 156.535581 7.000000
285.615385 328.750000 7.000000
296.467949 358.865385 7.000000
304.243697 165.693277 7.000000
312.126761 196.197183 7.000000
317.313725 334.406863 7.000000
337.482759 177.057471 7.000000
344.804598 305.028736 7.000000
354.792929 205.030303 7.000000
362.387500 243.662500 7.000000
371.720000 279.490000 7.000000

Solution

  • Alright, I'm guessing a lot, but we will see.

    First for the sake of completeness, your data (shortened):

    data = [...
    211.312500 242.803571 5.000000
    ...
    362.387500 243.662500 7.000000
    371.720000 279.490000 7.000000]
    

    Then you need to set a threshold, you suggested 1.5 I'd rather go for at least 3.

    thresh = 3;
    

    Then the algorithm:

    %// sort rows, first regarding column 1, then 2
    s = sortrows(data,[1 2]);
    
    %// get differences
    sdiff = diff([0 0 0; s],1,1);
    
    %// creat mask with threshold
    mask = (abs(sdiff(:,1)) < thresh) & (abs(sdiff(:,2)) < thresh);
    
    %// assign indices to "similar" values
    subs = cumsum(~mask);
    
    %// calculate the means of similar values
    xmean = accumarray(subs,s(:,1),[],@mean)
    ymean = accumarray(subs,s(:,2),[],@mean)
    
    %// create new filtered dataset
    fdata = [xmean(subs) ymean(subs) s(:,3)]
    
    %// optional: resort data
    fdata = sortrows(fdata,3);
    

    And the results, with in each case the point with the biggest z on top:

    figure(1)
    subplot(121)
    scatter(data(:,1),data(:,2),[],data(:,3))
    title('unfiltered')
    xlim([150,400])
    ylim([150,400])
    colormap(jet)
    subplot(122)
    scatter(fdata(:,1),fdata(:,2),[],data(:,3))
    title('filtered')
    xlim([150,400])
    ylim([150,400])
    

    enter image description here

    Is that what you want? If not, you need to be a very much clearer and post an example.

    As you may see, it's not perfectly filtered for one single point, you can fix it be re-running the algorithm with different sorting:

    s = sortrows(data,[2 1]);
    

    Edit: as you said, you need 5-6 runs, you can use a loop like this:

    fdata = data;
    for i = 1:5
        shiftvec = circshift([1 2],i,2);
        s = sortrows(fdata,shiftvec);
        sdiff = diff([0 0 0; s],1,1);
        mask = (abs(sdiff(:,1)) < thresh) & (abs(sdiff(:,2)) < thresh);
        subs = cumsum(~mask);
        xmean = accumarray(subs,s(:,1),[],@mean);
        ymean = accumarray(subs,s(:,2),[],@mean);
        fdata = [xmean(subs) ymean(subs) s(:,3)]; 
        fdata = sortrows(fdata,3); 
    end
    

    Final results:

    enter image description here