How to find inflection point in python?
Asked Answered
M

2

12

I have a histogram of an image in RGB which represents the three curves of the three components R, G and B. I want to find the inflection points of each curve. I used the second derivative to find them but I can't, the second derivative does not cancel its returns null. So how can I find the inflection point? Is there any other method to find them?

enter image description here

import os, cv2, random
import numpy as np
import matplotlib.pyplot as plt
import math
from sympy import *

image = cv2.imread('C:/Users/Xers/Desktop/img.jpg')

CHANNELS = ['r', 'g', 'b']

for i, channel in enumerate( CHANNELS ):
    

  histogram = cv2.calcHist([image], [i], None, [256], [0,256])

  histogram = cv2.GaussianBlur( histogram, (5,5), 0)

  plt.plot(histogram, color = channel)

     
  x= plt.xlim([0,256])
  y = plt.ylim([0, 24000])


  derivative1= np.diff(histogram, axis=0)
  derivative2= np.diff(derivative1, axis=0)

  inf_point = np.where ( derivative2 == 0)[0]
  print(inf_point)
plt.show()
Mugwump answered 23/6, 2020 at 14:53 Comment(6)
you mean inflection point?Epidemic
Are you asking a maths question? Welcome to SO please take the time to take the tour and readHow to Ask and the other links found on that page.Youngyoungblood
@Youngyoungblood The question does not seem to be about math, but more about how to put that math into code.Addia
@Epidemic yes inflection pointMugwump
derivative2= np.diff(derivative1, axis=0).astype(int)Transducer
@Transducer i get insignificant points for exemple i get 103 and 184 for the red curve and this in not the inflection point.Mugwump
A
28

There are two issues of numerical nature with your code:

  • the data does not seem to be continuous enough to rely on the second derivative computed from two subsequent np.diff() applications
  • even if it were, the chances of it being exactly 0 are very slim

To address the first point, you should smooth your histogram (e.g. using a uniform or Gaussian filter on the histogram itself).

To solve the second point, instead of looking for == 0, look for positive-to-negative (and viceversa) switching point.


To give you some minimal example of a possible approach:

import numpy as np
import matplotlib.pyplot as plt
from scipy.ndimage import gaussian_filter1d


np.random.seed(0)

# generate noisy data
raw = np.cumsum(np.random.normal(5, 100, 1000))
raw /= np.max(raw)

# smooth
smooth = gaussian_filter1d(raw, 100)

# compute second derivative
smooth_d2 = np.gradient(np.gradient(smooth))

# find switching points
infls = np.where(np.diff(np.sign(smooth_d2)))[0]

# plot results
plt.plot(raw, label='Noisy Data')
plt.plot(smooth, label='Smoothed Data')
plt.plot(smooth_d2 / np.max(smooth_d2), label='Second Derivative (scaled)')
for i, infl in enumerate(infls, 1):
    plt.axvline(x=infl, color='k', label=f'Inflection Point {i}')
plt.legend(bbox_to_anchor=(1.55, 1.0))

plot

Addia answered 23/6, 2020 at 15:58 Comment(8)
i used a gaussian filter on the histogram using cv2.GaussianBlur and how i can find this switching point please?Mugwump
@Mugwump Then use a wider sigma, the data is still too noisy. And look for ` >= 0` with non-consecutive indices or something to get the switching point.Addia
i used the max of gaussian filter is (7,7) and it's still too noisyMugwump
@Mugwump I updated the answer with some minimal code to show that the approach I described should work well on sufficiently smoothed data.Addia
thank you so much i have a question why you use 100 in gaussian_filter1d?Mugwump
@Mugwump To have enough smoothing. It should be ~2x the size of the noisy peaks you want to overlookAddia
how i can know the size of the noisy peaks please?Mugwump
@Mugwump I just looked at the plot.Addia
P
1

I used the code provided by nook2, but scaling the second derivate to a different range (also it will work for any input data, you don't have to change it every time)

import numpy as np
import matplotlib.pyplot as plt
from scipy.ndimage import gaussian_filter1d

np.random.seed(0)

# generate noisy data
raw = np.cumsum(np.random.normal(5, 100, 1000))
raw /= np.max(raw)

# smooth
smooth = gaussian_filter1d(raw, 100)

# compute second derivative
smooth_d2 = np.gradient(np.gradient(smooth))

# find switching points
infls = np.where(np.diff(np.sign(smooth_d2)))[0]

# plot results
plt.plot(raw, label='Noisy Data')
plt.plot(smooth, label='Smoothed Data')
plt.plot(np.max(smooth)*(smooth_d2)/(np.max(smooth_d2)-np.min(smooth_d2)) , label='Second Derivative (scaled)')
for i, infl in enumerate(infls, 1):
    plt.axvline(x=infl, color='k', label=f'Inflection Point {i}')
plt.legend(bbox_to_anchor=(1.55, 1.0))

enter image description here

Pycnometer answered 21/8, 2022 at 21:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.