You can use MeshFunctions
to do the trick:
g = BSplineFunction[{RandomReal[1, 20], RandomReal[1, 20]}\[Transpose]];
dg = g';
ParametricPlot[
g[t], {t, 0, 1},
MeshFunctions -> Function[{x, y, t}, dg[t].{0, 1}],
Mesh -> {{0}},
MeshStyle -> Directive[AbsolutePointSize[5], Red]
]

Here the MeshFunctions
specifies the value of dg[t].{0, 1}
, i.e. the $y$ component of the tangential vector of $g(t)$ at $t$, is used to generate mesh levels. Then Mesh -> {{0}}
specifies that we only draw meshes where dg[t].{0, 1} == 0
, which is exactly the extrema of $g(t)$.
To tell maxima from minima, use the RegionFunction
:
maximapart = ParametricPlot[
g[t], {t, 0, 1},
PlotStyle -> Lighter[Blue, .6],
RegionFunction -> Function[{x, y, t}, g''[t].{0, 1} < 0],
MeshFunctions -> Function[{x, y, t}, dg[t].{0, 1}],
Mesh -> {{0}},
MeshStyle -> Directive[AbsolutePointSize[5], Red]
]
minimapart = ParametricPlot[
g[t], {t, 0, 1},
PlotStyle -> Lighter[Brown, .6],
RegionFunction -> Function[{x, y, t}, g''[t].{0, 1} > 0],
MeshFunctions -> Function[{x, y, t}, dg[t].{0, 1}],
Mesh -> {{0}},
MeshStyle -> Directive[AbsolutePointSize[5], Blue]
]
Show[{maximapart, minimapart}]

Extracting the points is straightforward:
maximaptSet = Cases[maximapart, GraphicsComplex[pt_, __] :> pt, ∞][[1]];
maximaIdx = Cases[maximapart, Point[pt_] :> pt, ∞][[1]];
maximaptSet[[maximaIdx]]
minimaptSet = Cases[minimapart, GraphicsComplex[pt_, __] :> pt, ∞][[1]];
minimaIdx = Cases[minimapart, Point[pt_] :> pt, ∞][[1]];
minimaptSet[[minimaIdx]]