Remove duplicate locations and replace by a single one

Hi.
I am trying to locate the 6 dots in my frequency domain image (its the FFT of a hexagonal array of spots)
Using a technique called "template matching" via matlabs function c=normxcorr2(template,Raw), I'm able to locate the 6 best matches. However, as below some of the matches occur that are 2 for the same object (just displaced by say 1 pixel in the image)
the location x, y and actual intensity of the 6 matches are:
75.0000 204.0000 0.7882
75.0000 205.0000 0.7882
140.0000 315.0000 0.7607
268.0000 93.0000 0.7607
333.0000 203.0000 0.7555
333.0000 204.0000 0.7555
How can I replace similar locations that are within say 10 pixels in either x or y of each other with the one that has the highest intensity. If the intensities are the same just pick the first one?
Thanks for any help Jason

1 Comment

It would rather depend on the specific case as to what is best to do with cases up to 10 pixels away and with different intensities. In your example it is easy as they are 1 pixel apart and the same intensities.
The surface of the correlations could vary hugely over 10 pixels in both x and y though with massive dips between the two or it could be very smooth in which case averaging the x and y to take a centred location would make sense. Clearly in the first example with a very varied correlation this would not make sense though. But that comes back to the specific interpretation. Maybe if your correlation surface is so varied as to have big dips between two points within 10 pixels of each other then these should be considered as independent of each other anyway. Maybe this is a scenario that can never happen in your case?
In the example you give a simple variation of the unique function where you give a distance tolerance and pick the first or average would work fine.

Sign in to comment.

 Accepted Answer

https://www.mathworks.com/help/matlab/ref/uniquetol.html

17 Comments

Wow, didn't even know that existed. I can't quite get the syntax right though:
x
y
tol = 5; %5 pixels
C1=uniquetol([x;y],tol)
C2=uniquetol([x;y],tol,'ByRows',true)
gives me:
x =
75
75
140
268
333
333
y =
204
205
315
93
203
204
C1 =
75
C2 =
75
OK, I have to sue the DataScale options...
A=[x,y]
tol = 5;
C3=uniquetol(A,tol,'ByRows',true,'DataScale', 1)
gives:
A =
75 204
75 205
140 315
268 93
333 203
333 204
C3 =
75 204
140 315
268 93
333 203
But its automatically just included the 1st ocurrance of any duplicate within the tolerance (5 in my case).
But this isn't what I really want.
I want it to chose the rows which it considers duplicate but based on the highest intensity. i.e. for the considered duplicates,
75.0000 204.0000 0.7882
75.0000 205.0000 0.9883
333.0000 203.0000 0.7555
333.0000 204.0000 0.8556
I would want
75.0000 205.0000 0.9883
333.0000 204.0000 0.8556
uniquetol knows nothing about your intensities, but I guess if you just sort your [x y] data in rows by intensity then the first row it selects of each non-unique pair will always be that of highest intensity.
Oh thats clever! Once i have the list of coordinates to accept,
C3 =
75 204
140 315
268 93
333 203
how do i then match this back up to obtain the intensities associated with that x,y position
Esorted =
75.0000 204.0000 0.7882
75.0000 205.0000 0.7882
140.0000 315.0000 0.7607
268.0000 93.0000 0.7607
333.0000 203.0000 0.7555
333.0000 204.0000 0.7555
Adam, your suggestion I thought was going to be the solution. However, the uniquetol seems to reorder.
this is my x,y,intensity ordered by intensity
Esorted =
268.0000 93.0000 0.7882
140.0000 315.0000 0.7882
333.0000 203.0000 0.7607
75.0000 205.0000 0.7607
75.0000 204.0000 0.7555
333.0000 204.0000 0.7555
75.0000 394.0000 0.6499
333.0000 14.0000 0.6499
333.0000 24.0000 0.6448
75.0000 384.0000 0.6448
and after applying uniquetol by:
A=[Esorted(:,1),Esorted(:,2)]
tol = 5;
C3=uniquetol(A,tol,'ByRows',true,'DataScale', 1);
Its not kept the order and hence not picked the replacement of the x,y duplicates based on the order I set (i.e. on intensity)
C3 =
75 204
75 384
75 394
140 315
268 93
333 14
for x=75, it should have picked y=205 not 204. Seems like its reordering first.
In your situation instead of taking mean() you would use max() on the appropriate part of the array and select that entry to be the one you save.
Give us the larger picture. WHY do you need the coordinates? What are you going to do with them once you have them? Why is the order important? Why do you need to throw some pixels out?
Thanks Walter. I will check it out on Monday.
I.A The locations in frequency space allow me to obtain the pitch in the spatial domain of my hexagonal array.
Have you considered cluster analysis on them, like kmeans or something?
Hi I.A "Have you considered cluster analysis on them, like kmeans or something?"
I wouldn't know where to start. Do you think this approach would work? Thanks
Like this:
[indexes, clusterLocations] = kmeans(x, y, numClusters);
Walter. I took a look and see the way where I can ignore my 3rd column using inf as the tolerance. when I then follow the example and swap from mean to max by:
for k = 1:length(ia)
%aveA(k,:) = mean(A(ia{k},:),1);
aveA(k,:) = max(A(ia{k},:));
end
I get some erroneous values for my x,y coordinates (my image is only 200x200) and I output e.g. the x coordinate following the loop above
aveA=x,y, Intensity
which gives me several x (& y values) greater than 200
195 199 4982
199 157 4318
200 26 3308
200 129 5596
200 17 3968
200 136 5945
200 86 7340
200 98 4260
200 106 1454
200 147 3321
200 174 3145
200 188 2778
200 38 5834
200 45 3824
200 72 1283
200 113 4025
725 725 725
846 846 846
1042 1042 1042
1045 1045 1045
1082 1082 1082
1125 1125 1125
1372 1372 1372
1440 1440 1440
1463 1463 1463
1598 1598 1598
1687 1687 1687
3778 3778 3778
4147 4147 4147
4709 4709 4709
5009 5009 5009
5452 5452 5452
6058 6058 6058
It seems to be perhaps putting the 3rd column (i.e. intensity) for some of the rows
I.A. So do I need to know before hand how many objects...Unfortunately I won't have that.
[indexes, clusterLocations] = kmeans(x, y, numClusters);
There are other methods in the Stats toolbox where you don't need to know how many clusters there are. Use the Classification Learning app on the Apps tab of the tool ribbon if you're interested. I'm not super familiar with them. See http://www.mathworks.com/help/stats/cluster-analysis.html
Thanks I.A. will take a look
aveA(k,:) = max(A(ia{k},:), 1);
In the case where there was only a single row, max by default works along the columns.
But I suspect you want something closer to
[~, maxidx] = max(A(ia{k},3));
aveA(k,:) = A(ik{k(maxidx)},:);
hmmm, I feel Im nearly there but not quite....
[C,ia] = uniquetol(double(A), 3, 'ByRows', true, ...
'OutputAllIndices', true, 'DataScale', DS);
%Average each group of points that are within tolerance
length(ia)
for k = 1:length(ia)
k
[~, maxidx] = max(A(ia{k},3))
aveA(k,:) = A(ia{k(maxidx)},:);
end
gives me
Index exceeds matrix dimensions.
Error in AfunctionATS_QC>pushbutton62_Callback (line 4194)
aveA(k,:) = A(ia{k(maxidx)},:);

Sign in to comment.

More Answers (2)

The way I find spikes in spectra that look like that is to first flatten the hump with adapthisteq(). Then maybe some noise reduction if needed. Then imregionalmax() or simple thresholding. Then if there are still small noise specs, call bwareaopen(). At this point you should have fairly large round spots. Finally call regionprops() to get the centroids of those spots.

2 Comments

So my FFT image as displayed (fLog )is via f = fftshift(fft2(IM2)); fabs=abs(f); fLog = log(1 + abs(f));
typical values are:
14.3449 14.0344 13.9478 14.5760 14.7319 14.7613 14.0889 13.8858 14.0256 13.8655 14.3519 14.6125 14.4053 14.5912 13.9406 14.4580 14.6937 14.6081 14.2810
13.6524 13.6782 13.1208 14.0463 12.8391 13.6880 14.5778 14.0849 13.8230 13.0803 13.9648 13.6392 14.9358 13.0224 13.1800 15.0674 14.7089 14.4756 14.4593
13.2441 14.0987 13.8662 14.9372 13.7176 14.4019 14.2667 15.1542 14.4959 14.4358 14.4857 14.3052 14.6665 13.9729 13.2541 14.8806 13.9349 14.7924 14.8968
14.4770 14.1713 14.4296 14.3623 14.3190 14.2337 14.3327 13.6597 14.2302 13.5637 13.5564 13.7410 14.6453 13.8705 13.6382 12.6438 13.7971 14.3293 14.0198
14.1876 14.1564 14.9214 12.9028 14.8500 14.1732 14.1156 14.1639 14.6839 13.0326 14.8514 14.2794 14.0046 14.4100 14.1975 14.2900 14.8544 13.5223 14.6178
12.9660 14.2728 14.1810 12.8452 13.8589 14.9243 14.2886 13.8030 14.5397 14.5398 14.0764 14.7958 12.8295 14.2470 13.4628 14.3374 14.1477 14.7460 13.5973
14.3420 14.3576 14.0068 13.7979 14.8972 14.3773 13.8775 13.4591 13.7093 14.0612 14.7619 13.8294 14.2826 13.4302 14.6212 14.6125 13.7437 14.0563 15.0035
13.4081 12.1394 14.4322 13.9806 11.5343 12.6346 13.5777 13.3499 14.6571 13.3027 14.2555 13.0297 12.6997 12.9625 14.0681 14.6287 14.4310 15.0224 14.7864
When I perform
J = adapthisteq(fLog)
the resultant image is just 1 everywhere.
You must not be doing it correctly. Try varying some of the parameters.
For what it's worth, attached is a demo where I take the log of the spectrum to sort of flatten it, and then threshold and filter.
People could help more if you'd attach your original image.

Sign in to comment.

If you simply want to isolate the points, perhaps my findclump function could be of help:
x = [...
75
75
140
268
333
333];
y = [...
204
205
315
93
203
204];
z = rand(size(x));
[xyunq, ixy] = findclump([x y], 10);
zmax = accumarray(ixy, z, [max(ixy) 1], @max);
[~,row] = ismember([(1:max(ixy))' zmax], [ixy z], 'rows');
Original points:
[x y z]
ans =
75 204 0.25999
75 205 0.78686
140 315 0.51158
268 93 0.56253
333 203 0.68479
333 204 0.092397
Clumped points, keeping one with highest z value:
[x(row) y(row) z(row)]
ans =
75 205 0.78686
140 315 0.51158
268 93 0.56253
333 203 0.68479

Categories

Asked:

on 6 Jan 2017

Commented:

on 11 Jan 2017

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!