You should use MORPH_ELLIPSE, with the same size for both axis. Check the OpenCV tutorials.

This will produce a ball / circle shaped element with a diameter of 5:

cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))
Answer from Miki on Stack Overflow
🌐
OpenCV-Python Tutorials
opencv24-python-tutorials.readthedocs.io › en › latest › py_tutorials › py_imgproc › py_morphological_ops › py_morphological_ops.html
Morphological Transformations — OpenCV-Python Tutorials beta documentation
# Rectangular Kernel >>> cv2.getStructuringElement(cv2.MORPH_RECT,(5,5)) array([[1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]], dtype=uint8) # Elliptical Kernel >>> cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5)) array([[0, 0, 1, 0, 0], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [0, 0, 1, 0, 0]], dtype=uint8) # Cross-shaped Kernel >>> cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5)) array([[0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [1, 1, 1, 1, 1], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0]], dtype=uint8)
🌐
Medium
mamuncseru.medium.com › a-brief-discussion-on-morphological-operators-using-opencv-ccf6be076896
A brief discussion on Morphological operators using OpenCV | by Md. Abdullah Al Mamun | Medium
January 20, 2023 - morph_rect = cv2.getStructuringElement(shape=cv2.MORPH_RECT, ksize=(5, 5)) morph_ellipse = cv2.getStructuringElement(shape=cv2.MORPH_ELLIPSE, ksize=(5, 5)) morph_cross = cv2.getStructuringElement(shape=cv2.MORPH_CROSS, ksize=(5, 5)) Figure: Morph Rectangle, Morph Ellipse, Morph Cross ... Dilation I used the built-in OpenCV function to dilate my favorite inverse binary image on three various structuring elements.
🌐
ProgramCreek
programcreek.com › python › example › 89381 › cv2.getStructuringElement
Python Examples of cv2.getStructuringElement
def __init__(self, min_accuracy, min_blend_area, kernel_fill=20, dist_threshold=15000, history=400): self.min_accuracy = max (min_accuracy, 0.7) self.min_blend_area = min_blend_area self.kernel_clean = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(4,4)) self.kernel_fill = np.ones((kernel_fill,kernel_fill),np.uint8) self.dist_threshold = dist_threshold self.history = history # read https://docs.opencv.org/3.3.0/d2/d55/group__bgsegm.html#gae561c9701970d0e6b35ec12bae149814 try: self.fgbg = cv2.bgsegm.createBackgroundSubtractorMOG(history=self.history, nmixtures=5, backgroundRatio=0.7, noiseSigma=0) except AttributeError as error: print ('It looks like your OpenCV version does not include bgsegm.
🌐
coseries
coseries.com › morphological-transformations-in-python-using-opencv
Morphological Transformations in Python using OpenCV | coseries
November 22, 2020 - OpenCV provides the cv2.getStructuringElement() function to obtain the kernel. It takes the desired shape and the size of the kernel.
🌐
Python Geeks
pythongeeks.org › python geeks › learn opencv › morphological operations in opencv
Morphological Operations in OpenCV - Python Geeks
February 14, 2023 - # Importing OpenCV import cv2 # Importing numpy import numpy as np # Reading the image img = cv2.imread(r"C:\Users\tushi\Downloads\PythonGeeks\butterfly with gaps.jpg", 0) # Displaying the original image cv2.imshow('Original',img) cv2.waitKey(0) cv2.destroyAllWindows() # Defining the kernel kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5)) # Printing the kernel kernel # Output array([[1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]], dtype=uint8) # Applying the structuring element with the rectangle shape on the image img_rect = cv2.morphologyEx(img, cv2.MORPH_RECT, kernel) # Displaying the image cv2.imshow('Rectangle',img_rect) cv2.waitKey(0) cv2.destroyAllWindows()
🌐
OpenCV
docs.opencv.org › 4.13.0 › d9 › d61 › tutorial_py_morphological_ops.html
OpenCV: Morphological Transformations
We manually created a structuring elements in the previous examples with help of Numpy. It is rectangular shape. But in some cases, you may need elliptical/circular shaped kernels. So for this purpose, OpenCV has a function, cv.getStructuringElement().
🌐
OpenCV
docs.opencv.org › 3.4.20 › d3 › dbe › tutorial_opening_closing_hats.html
OpenCV: More Morphology Transformations
element = cv.getStructuringElement(morph_elem, (2*morph_size + 1, 2*morph_size+1), (morph_size, morph_size))
🌐
OpenCV
docs.opencv.org › 4.x › dd › dd7 › tutorial_morph_lines_detection.html
OpenCV: Extract horizontal and vertical lines by using morphological operations
Mat verticalStructure = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size( 1,vertical_size)); // Apply morphology operations · Imgproc.erode(vertical, vertical, verticalStructure); Imgproc.dilate(vertical, vertical, verticalStructure); // Show extracted vertical lines · showWaitDestroy("vertical", vertical); Python ·
Find elsewhere
🌐
PyImageSearch
pyimagesearch.com › home › blog › opencv morphological operations
OpenCV Morphological Operations - PyImageSearch
May 9, 2021 - In OpenCV, we can either use the cv2.getStructuringElement function or NumPy itself to define our structuring element.
🌐
Adonline
code.adonline.id.au › structuring-elements-in-python
Structuring elements in Python | Adam Dimech's Coding Blog
When performing image analysis steps such as erosion, dilation, opening or closing steps, I require the use of a structuring element (sometimes called a kernel) to perform the task. There are three methods for generating these in Python: ... array([[1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]], dtype=uint8) OpenCV has a getStructuringElement() function that has three shapes:
🌐
O'Reilly
oreilly.com › library › view › mastering-opencv-4 › 9781789344912 › 01422dee-f186-4817-a960-87f293732bc2.xhtml
Structuring element - Mastering OpenCV 4 with Python [Book]
March 29, 2019 - Structuring element In connection with the structuring element, OpenCV provides the cv2.getStructuringElement() function. This function outputs the desired kernel (a NumPy array of... - Selection from Mastering OpenCV 4 with Python [Book]
Author   Alberto Fernández Villán
Published   2019
Pages   532
🌐
Regenerativetoday
regenerativetoday.com › morphological-operations-for-image-preprocessing-in-opencv-in-detail
Morphological Operations for Image Preprocessing in OpenCV, in Detail – Regenerative
image = cv2.imread('car.jpg') gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (23, 5)) kernel1 = cv2.getStructuringElement(cv2.MORPH_RECT, (35, 8))
🌐
Shimat
shimat.github.io › opencvsharp_docs › html › a83a9f62-e3dc-c3e9-36bd-1758462d2198.htm
Cv2.GetStructuringElement Method (MorphShapes, Size)
Returns a structuring element of the specified size and shape for morphological operations. The function constructs and returns the structuring element that can be further passed to erode, dilate or morphologyEx.
🌐
Readthedocs
opencv-python.readthedocs.io › en › latest › doc › 12.imageMorphological › imageMorphological.html
Morphological Transformations — gramman 0.1 documentation
#-*- coding:utf-8 -*- import cv2 import numpy as np from matplotlib import pyplot as plt dotImage = cv2.imread('images/dot_image.png') holeImage = cv2.imread('images/hole_image.png') orig = cv2.imread('images/morph_origin.png') kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5)) # kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5)) # kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5)) erosion = cv2.erode(dotImage,kernel,iterations = 1) dilation = cv2.dilate(holeImage,kernel,iterations = 1) opening = cv2.morphologyEx(dotImage, cv2.MORPH_OPEN, kernel) closing = cv2.morphologyE
Top answer
1 of 2
1

This answer explains how to use MORPH_CLOSE around the edge of an image by adding a buffer to the image.

You can add a buffer by creating an image of zeros using numpy:

# Import packages
import cv2
import numpy as np
import matplotlib.pyplot as plt
# Read in the image
img = cv2.imread('/home/stephen/Desktop/OKoUh.png', 0)
# Create a black bufffer around the image
h,w = img.shape
buffer = max(h,w)
bg = np.zeros((h+buffer*2, w+buffer*2), np.uint8)
bg[buffer:buffer+h, buffer:buffer+w] = img

Then you can iterate and check how it looks at different kernel sizes:

for close_size in range(1,11):
    temp  = bg.copy()
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (close_size, close_size))
    result = cv2.morphologyEx(temp, cv2.MORPH_CLOSE, kernel)
    results = result[buffer:buffer+h, buffer:buffer+w]
    cv2.imshow('img', result)
    cv2.waitKey()

My results:

2 of 2
1

Based on Stephen's answer, here is the snippet I ended up implementing :

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (close_size, close_size))
# Padding
im=cv2.copyMakeBorder(im,close_size,close_size,close_size,close_size,
                      borderType=cv2.BORDER_CONSTANT, value = 0)
# Closing
im = cv2.morphologyEx(im, cv2.MORPH_CLOSE, kernel)
# Unpadding
im = im[close_size:-close_size,close_size:-close_size]

As I mentionned in a comment, this can lead to a much longer computation time for large kernel sizes. It's possible that padding with lower values, eg close_size/2 would be enough to prevent border issues (didn't test it).

🌐
GitHub
github.com › opencv › opencv-python › issues › 337
Issue using cv2.getStructuringElement · Issue #337 · opencv/opencv-python
June 3, 2020 - Expected behaviour Return array of given dimensions Actual behaviour TypeError: an integer is required (got type _NoValueType) Steps to reproduce cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) W...
Author   opencv
🌐
Blogger
opencvexamples.blogspot.com › 2013 › 10 › create-structuring-element-for.html
Learn OpenCV by Examples: Create structuring element for morphological operations
Learn OpenCV with basic implementation of different algorithms. Please visit LearnOpenCV.com for newer articles. Home · For Beginners · Table of Contents · Keywords · Resources · Mat · getStructuringElement(int shape, Size ksize, Point anchor=Point(-1,-1)) Parameters: shape – Element shape that could be one of the following: MORPH_RECT (0) - a rectangular structuring element: MORPH_ELLIPSE (2) - an elliptic structuring element, that is, a filled ellipse inscribed into the rectangle Rect(0, 0, esize.width, esize.height) MORPH_CROSS (1)- a cross-shaped structuring element: CV_SHAPE_CUSTOM (100) - custom structuring element (OpenCV 1.x API) ksize – Size of the structuring element.
Top answer
1 of 1
3

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')

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()

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