10
$\begingroup$

For a curve taken from a picture, is there any method to fit it with an equation if it appears to be some standard curve?

For example, in the following picture, the curve looks like an ellipse or a circle or something else of a conic section. How can I fit the shape with a proper equation using Mathematica? Furthermore, is it possible to assess two different fittings with a criterion (e.g. error)? Thank you in advance.

enter image description here

$\endgroup$
9
$\begingroup$

A simple approach is as follows. Define the input curve

Input curve

Extract the (x,y) points

xy = SortBy[
   Reverse /@ 
    First /@ Drop[ArrayRules[1 - ImageData[Binarize[curve]]], -1], 
   First];

Find a formula - you will probably want to do a little better than this - FindFormula offers many options

fitxy = FindFormula[xy];

and compare the results

Show[{ListLinePlot[xy], 
  Plot[fitxy[x], {x, Min[First /@ xy], Max[First /@ xy]}, 
   PlotStyle -> Red]}]

fitted curve

$\endgroup$
5
  • 2
    $\begingroup$ xy might be extracted more easily with xy = PixelValuePositions[curve, 0 ] $\endgroup$ – Ulrich Neumann Mar 13 at 15:04
  • $\begingroup$ @UlrichNeumann that doesn't work for me - but I may have a different definition of curve. I need to apply Binarize to curve. And perhaps fiddle some more. $\endgroup$ – mikado Mar 13 at 15:08
  • 1
    $\begingroup$ I just copied the image into the Mathematica code curve= "copy of image" . No need to binarize because black&white image. $\endgroup$ – Ulrich Neumann Mar 13 at 15:21
  • $\begingroup$ @UlrichNeumann I guess we copied the image in different ways. $\endgroup$ – mikado Mar 13 at 15:29
  • $\begingroup$ my way: right click -> copy picure ->paste in Mathematica $\endgroup$ – Ulrich Neumann Mar 13 at 16:00
6
$\begingroup$

Get (x, y) points from the image pixel positions

curve = Import["https://i.stack.imgur.com/WG8W9.png"];

Get points from the image. For RGB images, use PixelValuePositions and Black to locate pixel positions, or use 0 with single-channel images.

(*for RGB images*)    
xy = DeleteDuplicatesBy[
  NumericalSort[PixelValuePositions[curve, Black, .5]], First];

(*for single-channel images*)
curve2 = ColorSeparate[curve, "I"];(*make a 1-channel image*)
xy = DeleteDuplicatesBy[
  NumericalSort[PixelValuePositions[curve2, 0, .5]], First];

Demonstrate various methods to fit a model to the (x, y) data

1. Use FindFormula to make a piece-wise fit

fit = FindFormula[xy, x, TargetFunctions->{Times, Plus, Power}];
Show[{ListLinePlot[xy],
  Plot[fit, {x, Min[First/@xy], Max[First/@xy]},
   PlotStyle->Red]}]

piece-wise plot

2. Assuming a circle fits the (x, y) data, use three points to find the center and radius

pts = xy[[{1, Round@Length[xy]/2, -1}]]
(*{{34, 78}, {156, 175}, {279, 78}}*)
circle = CircleThrough[pts];
center = RegionCentroid[circle];
radius = Last@circle;
Graphics[{circle,
  Point[xy],
  PointSize[Scaled[.02]], Blue, Point[center], Red, Point[pts]}]

circle plot

3. Use Bspline and Bezier modeling

Find a function definition with BSplineFunction:

f = BSplineFunction[xy];
Show[{Graphics[{Point[xy], PointSize[Scaled[.02]]}],
  ParametricPlot[f[t], {t, 0, 1}, PlotStyle->Red]}]

BSline plot

Or use BezierCurve to plot a spline curve:

Graphics[{Point[xy], Red, BezierCurve[xy]}]

BezierCurve plot

4. Fitting using Fit, FindFit, etc.

Demonstrate Fit using quadratic and quartic models

quad = Fit[xy, {1, x, x^2}, x]
(*25.3532 + 1.72048x - 0.00950413 x^2*)
quart = Fit[xy, {1, x, x^2, x^3, x^4}, x]
(*-17.13974517615123 + 4.458526401451207 x - 0.0657189150882205 x^2 + 0.00045398967260661826 x^3 - 1.2540520088334156*^-6 x^4*)
{x1, xn} = First/@xy[[{1, -1}]];
Show[ListPlot[xy, PlotStyle->Black],
  Plot[{quad, quart}, {x, x1, xn}, PlotStyle->{{Thick, Red}, {Thick, Blue}}]]

Fit plot

5. LinearModelFit provides information that compares the fitted models

lm1 = LinearModelFit[xy, {1, x, x^2}, x];
lm1[{"BestFit", "RSquared", "ANOVATable"}] // Column

quadratic model fit

lm2 = LinearModelFit[xy, {1, x, x^2, x^3, x^4}, x];
lm2[{"BestFit", "RSquared", "ANOVATable"}] // Column

quartic model fit

{x1, xn} = First/@xy[[{1, -1}]];
Show[ListPlot[xy, PlotStyle->Black],
Plot[{lm1[x], lm2[x]}, {x, x1, xn}, PlotStyle->{{Thick, Red}, {Thick, Blue}}]]

LinearModelFit plot

$\endgroup$
1
  • 2
    $\begingroup$ Given such a comprehensive answer, it seems pedantic to note that least squares fitting considers only the error in y, which is not quite the right thing to do (with some assumptions, anyway) $\endgroup$ – mikado Mar 15 at 16:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.