pythonopencvimage-processingimage-morphology

Choosing right structuring element


I am creating an program to automatically separate the solar cells of from a pv module for which i first thresholded the image using adaptive threshold to obtain the following image

Thresholded image

After which i intend to remove the the black pixels within the cell boundries by using dilation for which i have used a elliptical structuring element of size (10,10) and have obtained the following image

Thresholded image after dilation (structuring element size (10,10)

As you can see there are still some black pixels left, now if i increase the size of the structuring element i lose the cell boundaries

Thresholded image after dilation (structuring element size (15,15)

I have tried the other structuring elements available such as cross and rectangular without any success hence i would need to define a custom kernel and don't have an idea about how do i go about defining one.

import numpy as np
import cv2
img=cv2.imread('result2.jpg',0)
th1 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,\
cv2.THRESH_BINARY,25,-2)
kernel=cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(15,15))
closing = cv2.morphologyEx(th1, cv2.MORPH_CLOSE, kernel)
cv2.imwrite('closing.jpg',closing)
cv2.imwrite('threshold.jpg',th1)
cv2.waitKey(0)
cv2.destroyAllWindows()

Original image before thresholding


Solution

  • My first piece of advice is to not threshold right away. Thresholding is something you want to do way at the end. Thresholding throws away valuable information. Morphological operations work on grey-value images too!

    Choosing the right morphological operators to keep only the shapes you are interested in is actually quite intuitive. In this case, you want to keep both horizontal and vertical lines. Let's use line structuring elements. The lines are dark, so we use a closing to remove things that do not look like our lines.

    A closing with vertical lines will remove all horizontal lines, and a closing with horizontal lines will remove all vertical lines. So how to combine these two? It turns out that the infimum (pixel-wise minimum) of two closings is a closing also. So the infimum of a closing with vertical lines and one with horizontal lines is a closing with the two lines at the same time, you'll preserve shapes where either of those two lines fit.

    Here is an example. I'm using PyDIP (I don't have OpenCV).

    import diplib as dip
    img = dip.ImageRead('/Users/cris/Downloads/ZrF7k.tif')
    img = img.TensorElement(1) # keep only green channel
    img = img[0:-2,1:-1]       # let's remove the artifacts at the right and top edges
    f1 = dip.Closing(img, dip.SE([50,1],'rectangular'))
    f2 = dip.Closing(img, dip.SE([1,50],'rectangular'))
    out = dip.Infimum(f1, f2)
    out.Show('lin')
    

    enter image description here

    You can try to tweak that a bit, and add some additional processing, and add your adaptive thresholding at the end to get the edges of the PV cells. But there is actually a much better way of finding these.

    I'm taking advantage here of the fact that the panel is so very straight w.r.t. the image, and that it covers the whole image. We can simply take a mean projection along rows and along columns:

    x = dip.Mean(out, process=[1, 0]).Squeeze()
    y = dip.Mean(out, process=[0, 1]).Squeeze()
    import matplotlib.pyplot as pp
    pp.subplot(2,1,1)
    pp.plot(x)
    pp.subplot(2,1,2)
    pp.plot(y)
    pp.show()
    

    enter image description here

    It should be fairly straight-forward to detect the edges of the cells from these projections.