The Vision Frontier
Computer vision enables automated extraction of information from video that previously required manual observation. Pose estimation tracks player body positions. Object detection identifies the ball. Motion analysis characterizes movement patterns. These capabilities open new analytical frontiers.
Automated Play Classification
Computer vision can classify plays from video: pick-and-rolls, isolations, cuts, screens. This automation enables systematic analysis of tactical patterns at scale that manual coding could never achieve. Teams can analyze every possession across seasons rather than sampling.
Defensive Coverage Analysis
Video analysis reveals defensive schemes and assignments better than tracking data alone. Computer vision can identify who's guarding whom, detect switches and rotations, and characterize coverage tendencies. This information helps prepare for specific opponents.
Historical Video Analysis
Computer vision enables analysis of historical games from before tracking data existed. By applying modern techniques to old broadcast video, we can generate tracking-style data for historical comparison. This bridges the data gap between eras.
Technical Challenges
Video analysis faces challenges: camera angles are limited, players occlude each other, and broadcast video doesn't show the full court. Current technology handles these constraints imperfectly. Accuracy remains below tracking systems installed in arenas.
Implementation in R
# Passing network analysis
library(tidyverse)
library(igraph)
build_passing_network <- function(pass_data) {
# Create edge list
edges <- pass_data %>%
group_by(passer_name, receiver_name) %>%
summarise(
passes = n(),
assists = sum(assist),
.groups = "drop"
) %>%
filter(passes >= 10)
# Build network
g <- graph_from_data_frame(edges, directed = TRUE)
# Calculate centrality
centrality <- tibble(
player = V(g)$name,
degree = degree(g, mode = "all"),
in_degree = degree(g, mode = "in"),
out_degree = degree(g, mode = "out"),
betweenness = betweenness(g),
pagerank = page_rank(g)$vector
)
list(graph = g, centrality = centrality)
}
passes <- read_csv("team_passes.csv")
network <- build_passing_network(passes)
print(network$centrality)
Implementation in Python
# Passing network analysis
import pandas as pd
import networkx as nx
def build_passing_network(pass_data):
# Aggregate passes
edges = pass_data.groupby(["passer_name", "receiver_name"]).agg({
"pass_id": "count",
"assist": "sum"
}).rename(columns={"pass_id": "passes"}).reset_index()
edges = edges[edges["passes"] >= 10]
# Build network
G = nx.from_pandas_edgelist(
edges,
source="passer_name",
target="receiver_name",
edge_attr=["passes", "assist"],
create_using=nx.DiGraph()
)
# Calculate centrality
centrality = pd.DataFrame({
"player": list(G.nodes()),
"degree": dict(G.degree()).values(),
"betweenness": nx.betweenness_centrality(G).values(),
"pagerank": nx.pagerank(G).values()
})
return G, centrality
passes = pd.read_csv("team_passes.csv")
graph, centrality = build_passing_network(passes)
print(centrality.sort_values("pagerank", ascending=False))
Implementation in R
# Passing network analysis
library(tidyverse)
library(igraph)
build_passing_network <- function(pass_data) {
# Create edge list
edges <- pass_data %>%
group_by(passer_name, receiver_name) %>%
summarise(
passes = n(),
assists = sum(assist),
.groups = "drop"
) %>%
filter(passes >= 10)
# Build network
g <- graph_from_data_frame(edges, directed = TRUE)
# Calculate centrality
centrality <- tibble(
player = V(g)$name,
degree = degree(g, mode = "all"),
in_degree = degree(g, mode = "in"),
out_degree = degree(g, mode = "out"),
betweenness = betweenness(g),
pagerank = page_rank(g)$vector
)
list(graph = g, centrality = centrality)
}
passes <- read_csv("team_passes.csv")
network <- build_passing_network(passes)
print(network$centrality)
Implementation in Python
# Passing network analysis
import pandas as pd
import networkx as nx
def build_passing_network(pass_data):
# Aggregate passes
edges = pass_data.groupby(["passer_name", "receiver_name"]).agg({
"pass_id": "count",
"assist": "sum"
}).rename(columns={"pass_id": "passes"}).reset_index()
edges = edges[edges["passes"] >= 10]
# Build network
G = nx.from_pandas_edgelist(
edges,
source="passer_name",
target="receiver_name",
edge_attr=["passes", "assist"],
create_using=nx.DiGraph()
)
# Calculate centrality
centrality = pd.DataFrame({
"player": list(G.nodes()),
"degree": dict(G.degree()).values(),
"betweenness": nx.betweenness_centrality(G).values(),
"pagerank": nx.pagerank(G).values()
})
return G, centrality
passes = pd.read_csv("team_passes.csv")
graph, centrality = build_passing_network(passes)
print(centrality.sort_values("pagerank", ascending=False))