I'm going to assume we aren't really concerned about how fast this runs, or about using it to detect players in several frames of a video -- just this single still image. In this case, the quickest way I can come up with is to convert the white "blobs" in the image into a graph:
First, make a matrix from your data:
L = {{95.8636, 1120.96}, {1227.08, 1058.48}, {1598.58,
1049.69}, {1501.61, 919.903}, {1481.09, 854.259}, {83.0436,
740.669}, {61.3402, 662.205}, {786.63, 325.184}, {735.35,
250.843}, {790.906, 207.406}};
A[\[Alpha]_] :=
Table[If[Norm[L[[i]] - L[[j]]] < \[Alpha], 1, 0], {i, 1,
Length[L]}, {j, 1, Length[L]}];
Manipulate[MatrixForm[A[\[Alpha]]], {\[Alpha], 0, 500}]
This A[\[Alpha]]
command gives you the adjacency matrix of a graph. Then you should make a corresponding graph:
<< Combinatorica`
G[\[Alpha]_] := FromAdjacencyMatrix[A[\[Alpha]]]
You can get an idea of what the abstract graph looks like by varying \[Alpha]
:
Manipulate[ShowGraph[G[\[Alpha]]], {\[Alpha], 0, 500}]
The parameter alpha is the distance between (centroids of) white blobs that we are willing to allow before we consider them to be pieces of the same person. (Note that this idea, like many others, will not work if two players are standing right next to each other, e.g. to block a free kick.)
So the question is which alpha to pick? Well, first you can overlay with your image and see how it looks:
img = Import["https://i.sstatic.net/K60Nl.gif"];
Manipulate[
Show[
img,
GraphPlot[G[\[Alpha]],
VertexCoordinateRules -> Table[i -> L[[i]], {i, 1, Length[L]}]],
ImageSize -> 300
],
{\[Alpha], 0, 500}]
This uses GraphPlot
instead of GraphShow
since we're now plotting the graph in the x-y plane with the image as background. We use VertexCoordinateRules
because we want the vertices to actually show up where the players arms, legs, torsos, etc are.
You can play with the parameter alpha and when it gets to be about 127, the very last piece of a guy is connected (the guy in the front). Even at alpha=95 that guy is connected, just not every part to every other part.
If you take alpha to be too large, like 160, you start connecting pieces of two different guys (up in the top right).
In order to make this automatic, you can simply run through all the alphas until you get the right number of guys:
\[Epsilon] = 0.1; \[Alpha] = 0; n = 6;
While[Length[ConnectedComponents[G[\[Alpha]]]] > n, \[Alpha] += \[Epsilon]]
Show[img,
GraphPlot[G[\[Alpha]],
VertexCoordinateRules -> Table[i -> L[[i]], {i, 1, Length[L]}]],
ImageSize -> 300
]
Here epsilon is the step-size for alpha (0.1 is very cautious, you could use 1 or even 5) and n is the number of players you expect to show up. Once there are n connected components of the graph of body parts, the thing assumes it is done.
One thing this algorithm could, but does not, accommodate is the slope of the field. Players who are closer to the camera will have body parts that are further apart. I see no need to really hash out those kinds of tweaks in a test-case like this, but for high-scale implementation this might be important too.
Anyway, once you know the correct value of alpha, you can just lump them together according to those connected components:
PLAYERS = ConnectedComponents[G[\[Alpha]]]
Table[
Mean[L[[PLAYERS[[i]]]]],
{i, 1, Length[PLAYERS]}]
Assuming you didn't change the value of alpha since running that loop, there will be six coordinates in the list PLAYERS
representing their coordinates. You could use a different method besides just the Mean[]
here, but I'm doing this the quick and lazy way. And if you want to wrap it up nicely to see how it all goes, maybe this:
TEAM[\[Alpha]_] :=
Show[
img,
GraphPlot[G[\[Alpha]],
VertexCoordinateRules -> Table[i -> L[[i]], {i, 1, Length[L]}]],
ListPlot[Table[Mean[L[[ConnectedComponents[G[\[Alpha]]][[i]]]]],
{i, 1, Length[ConnectedComponents[G[\[Alpha]]]]}],
PlotStyle -> Green],
Graphics[
Text["# Players: " <>
ToString[Length[ConnectedComponents[G[\[Alpha]]]]], {100, 100}]],
ImageSize -> 300
]
Manipulate[TEAM[\[Alpha]], {\[Alpha], 0, 500}]
Notice that when you make alpha too big, the centroids of certain guys start combining at some point between the two (or more) of them.