0

I'm trying to find the 4 steepest local maxima (corners of a piece) in a polar coordinate plot generated from a Jigsaw Piece, example plot:

Example Polar Coordinate Plot

My current implementation, which uses the same idea as this answer and works perfectly, but when combined with a "steepest peaks" function, does not work well, the issue most certainly lies in how a peak is measured for its "steepness".

Here is the "steepest peak" determining function, it returns a single number, smaller is better.

    public double GetMagnitude(List<Point> pointList, int centerIndex)
    {
        Point crrt = pointList[centerIndex];
        Point prev = pointList[centerIndex - 1];
        Point next = pointList[centerIndex + 1];

        Point rhs = new Point((next - crrt).x, (next - crrt).y);
        Point lhs = new Point((prev - crrt).x, (prev - crrt).y);

        // Calculate the gradient of the lhs against the rhs
        return Math.Abs((lhs.y - rhs.y) / (lhs.x - rhs.x));
    }

It's called like so (where averageRange is 20, and the number of sample points to take across the curve from the centerIndex)

    public double GetAverageMagnitude(List<Point> pointList, int centerIndex)
    {
        double mag = 0;
        int halfAverageRange = averageRange / 2;
        for (int j = -halfAverageRange; j < halfAverageRange; j++)
        {
            mag += GetMagnitude(pointList, centerIndex + j);
        }

        return mag / averageRange;
    }

The math at the end of the GetMagnitude() function could be entirely wrong, I have also tried:

  • Cross product of lhs against rhs
  • Separately calculating gradient of lhs and rhs and averaging them (absolute) No luck on any of those methods, the current one is the best so far, yet it's not good enough.

See example below for incorrect vs correct identification: Incorrect Identification Correct Identification

Note: Each consecutive points X value follows X=i, as in, X0 = 0, X1 = 1

How can I improve these results?

Fildor
  • 14,510
  • 4
  • 35
  • 67
Jordan
  • 11
  • 4
  • Seems like you are struggling with the math rather than the code? – Fildor Mar 03 '23 at 15:29
  • @Fildor that's probably the case. The reason I've posted here is to get help with producing a programmatic solution for the math that should be involved. – Jordan Mar 03 '23 at 15:40
  • 1
    It looks like you want "sharp" rather than steep. Run the data through a second derivative -- I have always gotten good results from an FIR filter with |H(f)| proportional to pow(f,2) in some passband -- and sort your local maxima by the most negative second derivative. BTW a function of polar angle should be periodic, so account for that in your search. And it means you can safely use FFT for filtering operations. – Ben Voigt Mar 03 '23 at 16:25
  • (FFT = Fast Fourier Transform ;) ) – Fildor Mar 03 '23 at 16:29
  • @BenVoigt that sounds promising, though I do have some questions, the second derivative of what? I don't have an equation for the line in this instance, just a set of points. Could you also clarify what FIR is and further explain its purpose in the implementation? I have a fairly small knowledge of maths beyond derivatives so I don't quite follow. – Jordan Mar 03 '23 at 16:58
  • @Jordan: Ahh, let me see if I can find you some tutorial on numeric evaluation of derivatives. Having samples of a curve rather than an algebraic equation is very common -- for processing sampled waveforms we have the field of "Digital Signal Processing", while derivatives on algebraic equations would fall into the field of "Computer Algebra Systems". – Ben Voigt Mar 03 '23 at 17:01
  • https://terpconnect.umd.edu/~toh/spectrum/Differentiation.html And in my earlier comment, "FIR" meant "Finite Impulse Response (digital filter)" – Ben Voigt Mar 03 '23 at 17:05
  • @BenVoigt I've got something working for this, at least the second derivative part. I don't see how to incorporate what you initially said though, about the FIR filter, or even the FFT, do these come before taking the second derivative as a method of smoothing the data? – Jordan Mar 03 '23 at 17:29
  • @Jordan: I was just suggesting a way to efficiently perform the smoothing and second derivative in a single step. If you have a working second derivative and it is fast enough and smooth enough for you, keep using it. – Ben Voigt Mar 03 '23 at 18:54
  • @BenVoigt sorry I should have clarified, I have something working with regard to the second derivative detecting the sharp points but the accuracy is around about the same. I do think that smoothing will significantly help here, though I've just tried to implement the FIR filter and the results are less than ideal, and looking at what an FIR filter is supposed to do (not what I have), I don't see how it could be used to find point at the top of a peak, since it shifts the Y values along the X axis, no? Perhaps I misunderstand. Are the inputs supposed to be the X and Y values? – Jordan Mar 04 '23 at 11:24

2 Answers2

0

I don't know if it will be useful, but this is a pretty straight-forward algorithm I've just coded. It is not math-based, you will definitely find counter-examples that won't work I'm afraid.

private static void GetPeeks(List<int> points)
{
    var acc = ForwardIncrease(points);
    var dec = BackwardIncrease(points);

    var mul = new List<int>();

    for (int i = 0; i < acc.Count; i++)
    {
        mul.Add(acc[i] * dec[i]);
    }

    //in mul, greatest values are the peaks (minima and maxima)
}

private static List<int> ForwardIncrease(List<int> points)
{
    var result = new List<int>();
    var previous = points[0];
    var cumul = 0;

    foreach (var point in points)
    {
        var diff = point - previous;

        if (diff >= 0 && cumul >= 0)
        {
            cumul += diff;
        }
        else if (diff <= 0 && cumul <= 0)
        {
            cumul += diff;
        }
        else
        {
            cumul = diff;
        }

        result.Add(cumul);
        previous = point;
    }

    return result;
}

private static List<int> BackwardIncrease(List<int> points)
{
    var result = new List<int>();
    var previous = points[points.Count - 1];
    var cumul = 0;

    for (int i = points.Count - 1; i >= 0; i--)
    {
        var point = points[i];
        var diff = point - previous;

        if (diff >= 0 && cumul >= 0)
        {
            cumul += diff;
        }
        else if (diff <= 0 && cumul <= 0)
        {
            cumul += diff;
        }
        else
        {
            cumul = diff;
        }

        result.Insert(0, cumul);
        previous = point;
    }

    return result;
}
Mad hatter
  • 569
  • 2
  • 11
0

I managed to fix this relatively easy after lots of experimentation.

Firstly, I made sure to reduce any noise in the data where possible, giving cleaner looking curves without sacrificing (too much) the sharpness of the peaks of interest. In my case, I increased the blur on the original image that the polar plot was made from.

Secondly, I took @BenVoigt's advice and switched to using a second derivative equation for calculating the "sharpness" of points along the curve. This gave more accurate readings across all points on the graph.

Then, I switched from using a mean average to a median average, this left the steeper, more curved parts of the graph with a distinctly greater median making the selection process much more accurate.

To help with testing, I also decreased the scale of the Y axis to make it easier to see what was happening, example below: New Polar Plot

Jordan
  • 11
  • 4