pythonimage-processingopencvgeometric-mean

Geometric mean filter with opencv


I want to apply a geometric mean filter on an image in opencv (python). Is there a builtin function or should I implement the filter myself? What is the most efficient way to implement a nonlinear filter in opencv?


Solution

  • Recall from logarithmic identities that

    log((x1 * x2 * ... * xn)^(1/n)) = (1/n) * (log(x1) + log(x2) + ... + log(xn))
    

    From Wikipedia:

    The geometric mean can also be expressed as the exponential of the arithmetic mean of logarithms. By using logarithmic identities to transform the formula, the multiplications can be expressed as a sum and the power as a multiplication.

    This means that a geometric mean can be simply calculated as an arithmetic mean, i.e. a cv2.boxFilter() of the logarithm of the image values. Then you just exponentiate the result and you're done!

    For e.g., let's test the manual method and this method and check the results. First load the image and define the kernel size:

    import cv2
    import numpy as np
    
    img = cv2.imread('cameraman.png', cv2.IMREAD_GRAYSCALE).astype(float)
    rows, cols = img.shape[:2]
    ksize = 5
    

    Cameraman

    Next let's pad the image and calculate the geometric mean manually:

    padsize = int((ksize-1)/2)
    pad_img = cv2.copyMakeBorder(img, *[padsize]*4, cv2.BORDER_DEFAULT)
    geomean1 = np.zeros_like(img)
    for r in range(rows):
        for c in range(cols):
            geomean1[r, c] = np.prod(pad_img[r:r+ksize, c:c+ksize])**(1/(ksize**2))
    geomean1 = np.uint8(geomean1)
    cv2.imshow('1', geomean1)
    cv2.waitKey()
    

    Geometric mean 1

    Looks like what we'd expect. Now instead of this, if we use the use the logarithmic version, all we need to do is take the exponential of the box filter running on the log of the image:

    geomean2 = np.uint8(np.exp(cv2.boxFilter(np.log(img), -1, (ksize, ksize))))
    cv2.imshow('2', geomean2)
    cv2.waitKey()
    

    Geometric mean 2

    Well, they certainly look the same. Actually I cheated, this is the same uploaded image as above. But that's okay because:

    print(np.array_equal(geomean1, geomean2))
    

    True