How to find inflection point in python?

Question:

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()
Asked By: khadoudj

||

Answers:

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

Answered By: norok2

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

Answered By: danquin
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.