2
$\begingroup$

I am creating parametric equations of objects to find their centroids. With some help from others in a different stack exchange post, I was able to find some code that can accurately parametrize any closed curve. The only problem is that the coordinates of the pixels in the corresponding image is different from the coordinates for the parametric plot. Here is an example.

enter image description here

enter image description here

The original picture is only 320 pixels wide and 37 pixels high but the plot is over 500 units wide and the y coordinates are also off for some reason. Could someone explain why this is happening and possibly propose how to fix this. For context here is the code I have been using.

param[x_, m_, t_] := Module[{f, n = Length[x], nf},
f = Chop[Fourier[x]][[;; Ceiling[Length[x]/2]]];
nf = Length[f];
Total[Rationalize[
 2 Abs[f]/Sqrt[n] Sin[Pi/2 - Arg[f] + 2. Pi Range[0, nf - 1] t], .01][[;; Min[m, nf]]]]]

tocurve[Line[data_], m_, t_] := param[#, m, t] & /@ Transpose[data]
img = Import["Blue Coronal Holes.jpg"]

img = Binarize[img~ColorConvert~"Grayscale"];
lines = Cases[Normal@ListContourPlot[Reverse@ImageData[img], Contours -> {0.5}], _Line, -1];

ParametricPlot[Evaluate[tocurve[#, 500, t] & /@ lines], {t, 0, 1}, Frame -> True, Axes -> False]

Sorry if this is an obvious question. I'm new to Mathematica.

$\endgroup$
  • 1
    $\begingroup$ you resized the input image. Remove ~ImageResize~500 from the line img =Binarize... to keep the original image size. $\endgroup$ – kglr Jul 16 at 1:42
  • $\begingroup$ Oh I'm sorry. I accidentally sent a older version of my code. I removed the ~ImageResize~500 but it is still creating the same plot out of the image I showed above. $\endgroup$ – Jacob Jul 16 at 15:21
  • $\begingroup$ I updated the code above. $\endgroup$ – Jacob Jul 16 at 22:17
1
$\begingroup$

An alternative approach using BSplineFunction:

img = Import["https://i.stack.imgur.com/Mwasr.jpg"]

img2 = Binarize[img~ColorConvert~"Grayscale"];

bsFs = Cases[Normal @ ListContourPlot[Reverse @ ImageData[img2], Contours -> {0.5}],
    Line[x_] :> BSplineFunction[x], All];

ParametricPlot[Evaluate[Through@bsFs@t], {t, 0, 1},
  Frame -> True, Axes -> False, ImageSize -> Large]

enter image description here

$\endgroup$
  • $\begingroup$ Thanks, but I don't know if I'm going to be able to implement this method. To calculate the centroids of images like this I need to calculate the two following line integrals in this link i.stack.imgur.com/KdxTi.png . I tried to get Mathematica to print the b-spline functions but I wasn't able to. After doing some research I found that there is probably no build in way to do it. Also, I don't know if you can even calculate line integrals with b-spline functions. I haven't had any exposure to these functions until now. $\endgroup$ – Jacob Jul 18 at 18:55
0
$\begingroup$

Okay, I found a solution to my problem that allows me to create a parametric plot with same coordinates as the image, like the B spline function that kglr posted, while still being able to retrieve the parametric equations needed to integrate. I'm posting what I did here for future reference or in case someone else finds this useful. Basically since the size or scale of the objects in the image are the same I just created a program that would perform a horizontal shift on the original parametric equations based on information I got from the B-spline function. Although I was unable to retrieve the equations I was able to plug in points and create a table of values for it. From there I found a way to find the minimum values for each object from that table. After that I found the minimum values for the objects in the original parametric plot and then setup some equations to perform the appropriate horizontal shift on the original equations. Here is a break down of the code.

param[x_, m_, t_] := 
 Module[{f, n = Length[x], nf}, 
  f = Chop[Fourier[x]][[;; Ceiling[Length[x]/2]]];
  nf = Length[f];
  Total[Rationalize[
 2 Abs[f]/Sqrt[n] Sin[
   Pi/2 - Arg[f] + 2. Pi Range[0, nf - 1] t], .01][[;; 
 Min[m, nf]]]]]
tocurve[Line[data_], m_, t_] := param[#, m, t] & /@ Transpose[data]
SetDirectory["/home/jacobh/WORKING/Pictures"]
Import["Coronal Holes.jpg"]
img = Binarize[img~ColorConvert~"Grayscale"];
lines = Cases[
Normal@ListContourPlot[Reverse@ImageData[img], 
 Contours -> {0.5}], _Line, -1];

ParametricPlot[
Evaluate[tocurve[#, 100000000, t] & /@ lines], {t, 0, 1}, 
Frame -> True, Axes -> False, ImageSize -> Large]
ParametricEquations = Evaluate[tocurve[#, 100000000, t] & /@ lines]
n = Length[ParametricEquations]
img2 = Binarize[img~ColorConvert~"Grayscale"];

bsFs = Cases[
Normal@ListContourPlot[Reverse@ImageData[img2], Contours -> {0.5}],
Line[x_] :> BSplineFunction[x], All];

ParametricPlot[Evaluate[Through@bsFs@t], {t, 0, 1}, Frame -> True, 
Axes -> False, ImageSize -> Large, Axes -> false, Frame -> True]

This is just code I originally posted above along with kglr's B spline method. Also I included ParametricEquations = Evaluate[tocurve[#, 100000000, t] & /@ lines] and n = Length[ParametricEquations]because I'll be working with multiple images with a different number of objects in the image. This will be needed to make the code more dynamic.

stepsize = 0.001
list = Table[
N[Evaluate[Through@bsFs@t] /. t -> i], {t, 0, 1, stepsize}]
xmatrix = 
Table[Table[list[[i, j, 1]], {i, 1, (1/stepsize) + 1}], {j, 1, n}]
ymatrix = 
Table[Table[list[[i, j, 2]], {i, 1, (1/stepsize) + 1}], {j, 1, n}]

This is where I begin plugging values in the parameter and create a table of values for the spline function. From there I create two tables of tables, one with the x values and one with the y values. I did this so I could access the x and y values for each object individually. I also did this so I could create n number of tables based on the number of objects in the image.

Minx = Table[xmatrix[[i, Ordering[xmatrix[[i]], 1]]], {i, 1, n}]
Miny = Table[ymatrix[[i, Ordering[ymatrix[[i]], 1]]], {i, 1, n}]

This creates a list of minimum x values and y values for each of objects in the B spline plot, which will be accessed individually later.

x = Table[ParametricEquations[[i, 1]], {i, 1, n}]
y = Table[ParametricEquations[[i, 2]], {i, 1, n}]

This creates a list of parametric equations for the x components and y components which can be accessed individually later.

Mx = Table[NMinValue[{x[[i]], 0 <= t <= 1}, t], {i, 1, n}]
My = Table[NMinValue[{y[[i]], 0 <= t <= 1}, t], {i, 1, n}]

This creates two lists of minimum x and y values which will be accessed individually later.

NewParametricEquations = 
Table[{x[[i]] - (Mx[[i]] - Minx[[i]]), 
y[[i]] - (My[[i]] - Miny[[i]])}, {i, 1, n}]
ParametricPlot[NewParametricEquations, {t, 0, 1}, ImageSize -> Large, 
Axes -> False, Frame -> True]

And finally, this calculates the new set of parametric equations that have the correct coordinates and plots them.

There are probably better ways to do this but this was the only thing I could think of that actually works.

$\endgroup$

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.