画像の二値化 - トライアングル法

閾値だけが欲しい場合もある。参考はhttps://forum.image.sc/t/understanding-imagej-implementation-of-the-triangle-algorithm-for-threshold/752/10辺り。
多少冗長だが、あとで見返したときのために。

import cv2
import numpy as np


def thresh_triangle(hist):
    start, end = hist.nonzero()[0][[0,-1]]
    peak = hist.argmax()

    if peak - start > end - peak:
        x1, x2 = start, peak
        y1, y2 = 0, hist[x2]
    else:
        x1, x2 = peak, end
        y1, y2 = hist[x1], 0

    # 点(X0, Y0)と直線(X1, Y1) - (X2, Y2)との距離D
    #   A = (Y2 - Y1)*X0 - (X2 - X1)*Y0 + (X2*Y1 - X1*Y2)
    #   B = SQRT((Y2 - Y1)^2 + (X2 - X1)^2)
    #   D = A/B
    d = [(y2-y1)*i - (x2-x1)*hist[i]
        for i in range(x1, x2+1)]

    return np.array(d).argmax() + x1

opencvのもの(THRESH_TRIANGLE)とはちょうど1ずれる。画像はFile:Pavlovsk Railing of bridge Yellow palace Winter.jpg - Wikimedia Commonsから。

img = cv2.imread("800px-Pavlovsk_Railing_of_bridge_Yellow_palace_Winter.jpg", 0)
hist = cv2.calcHist([img], [0], None, [256], [0,256]).ravel()
th1 = thresh_triangle(hist)
th2, _ = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_TRIANGLE)
print(th1, th2)
# => 218 217.0

https://kakasi.skr.jp/images/tresh-triangle.jpg