This answer was inspired by this excellent post.

import numpy as np
import cv2

if __name__ == '__main__':

    image = cv2.imread('image.png',cv2.IMREAD_UNCHANGED)

    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    ret,binary = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU | cv2.THRESH_BINARY)

    binary = cv2.bitwise_not(binary)

    H = cv2.Sobel(binary, cv2.CV_8U, 0, 2)
    V = cv2.Sobel(binary, cv2.CV_8U, 2, 0)

    rows,cols = image.shape[:2]

    _,contours,_ = cv2.findContours(V, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

    for cnt in contours:
        (x,y,w,h) = cv2.boundingRect(cnt)
        # rows/3 is the threshold for length of line
        if h > rows/3:
            cv2.drawContours(V, [cnt], -1, 255, -1)
            cv2.drawContours(binary, [cnt], -1, 255, -1)
        else:
            cv2.drawContours(V, [cnt], -1, 0, -1)

    _,contours,_ = cv2.findContours(H, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

    for cnt in contours:
        (x,y,w,h) = cv2.boundingRect(cnt)
        # cols/3 is the threshold for length of line
        if w > cols/3:
            cv2.drawContours(H, [cnt], -1, 255, -1)
            cv2.drawContours(binary, [cnt], -1, 255, -1)
        else:
            cv2.drawContours(H, [cnt], -1, 0, -1)

    kernel = cv2.getStructuringElement(shape=cv2.MORPH_RECT, ksize=(3,3))
    H = cv2.morphologyEx(H, cv2.MORPH_DILATE, kernel,iterations = 3)
    V = cv2.morphologyEx(V, cv2.MORPH_DILATE, kernel, iterations = 3)

    cross = cv2.bitwise_and(H, V)

    _,contours,_ = cv2.findContours(cross,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
    centroids = []
    for cnt in contours:
        mom = cv2.moments(cnt)
        (x,y) = mom['m10']/mom['m00'], mom['m01']/mom['m00']
        cv2.circle(image,(int(x),int(y)),4,(0,255,0),-1)
        centroids.append((x,y))

    centroids.sort(key = lambda x: x[0], reverse = False)
    centroids.sort(key = lambda x: x[1], reverse = False)

    dx = int(centroids[1][0] - centroids[0][0])
    centroids = np.array(centroids, dtype = np.float32)
    (x,y,w,h) = cv2.boundingRect(centroids)

    if x-dx > -5: x = max(x-dx,0)
    if h+dx <= rows+5: h = min(h+dx,rows)
    if w+dx <= cols+5: w = min(w+dx,cols)
    cv2.rectangle(image, (x,y), (x+w,y+h), (0,255,0))

    roi = binary[y:y+h,x:x+w]

    roi = cv2.morphologyEx(roi, cv2.MORPH_OPEN, kernel,iterations = 1)

    cv2.imshow('image', image)
    cv2.imshow('roi', roi)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

Answer from zindarod on Stack Overflow
🌐
OpenCV
docs.opencv.org › 4.13.0 › d9 › d61 › tutorial_py_morphological_ops.html
OpenCV: Morphological Transformations
void morphologyEx(InputArray src, OutputArray dst, int op, InputArray kernel, Point anchor=Point(-1,-1), int iterations=1, int borderType=BORDER_CONSTANT, const Scalar &borderValue=morphologyDefaultBorderValue())
Top answer
1 of 1
8

This answer was inspired by this excellent post.

import numpy as np
import cv2

if __name__ == '__main__':

    image = cv2.imread('image.png',cv2.IMREAD_UNCHANGED)

    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    ret,binary = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU | cv2.THRESH_BINARY)

    binary = cv2.bitwise_not(binary)

    H = cv2.Sobel(binary, cv2.CV_8U, 0, 2)
    V = cv2.Sobel(binary, cv2.CV_8U, 2, 0)

    rows,cols = image.shape[:2]

    _,contours,_ = cv2.findContours(V, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

    for cnt in contours:
        (x,y,w,h) = cv2.boundingRect(cnt)
        # rows/3 is the threshold for length of line
        if h > rows/3:
            cv2.drawContours(V, [cnt], -1, 255, -1)
            cv2.drawContours(binary, [cnt], -1, 255, -1)
        else:
            cv2.drawContours(V, [cnt], -1, 0, -1)

    _,contours,_ = cv2.findContours(H, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

    for cnt in contours:
        (x,y,w,h) = cv2.boundingRect(cnt)
        # cols/3 is the threshold for length of line
        if w > cols/3:
            cv2.drawContours(H, [cnt], -1, 255, -1)
            cv2.drawContours(binary, [cnt], -1, 255, -1)
        else:
            cv2.drawContours(H, [cnt], -1, 0, -1)

    kernel = cv2.getStructuringElement(shape=cv2.MORPH_RECT, ksize=(3,3))
    H = cv2.morphologyEx(H, cv2.MORPH_DILATE, kernel,iterations = 3)
    V = cv2.morphologyEx(V, cv2.MORPH_DILATE, kernel, iterations = 3)

    cross = cv2.bitwise_and(H, V)

    _,contours,_ = cv2.findContours(cross,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
    centroids = []
    for cnt in contours:
        mom = cv2.moments(cnt)
        (x,y) = mom['m10']/mom['m00'], mom['m01']/mom['m00']
        cv2.circle(image,(int(x),int(y)),4,(0,255,0),-1)
        centroids.append((x,y))

    centroids.sort(key = lambda x: x[0], reverse = False)
    centroids.sort(key = lambda x: x[1], reverse = False)

    dx = int(centroids[1][0] - centroids[0][0])
    centroids = np.array(centroids, dtype = np.float32)
    (x,y,w,h) = cv2.boundingRect(centroids)

    if x-dx > -5: x = max(x-dx,0)
    if h+dx <= rows+5: h = min(h+dx,rows)
    if w+dx <= cols+5: w = min(w+dx,cols)
    cv2.rectangle(image, (x,y), (x+w,y+h), (0,255,0))

    roi = binary[y:y+h,x:x+w]

    roi = cv2.morphologyEx(roi, cv2.MORPH_OPEN, kernel,iterations = 1)

    cv2.imshow('image', image)
    cv2.imshow('roi', roi)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

🌐
PyImageSearch
pyimagesearch.com › home › blog › opencv morphological operations
OpenCV Morphological Operations - PyImageSearch
May 9, 2021 - The first required argument of cv2.morphologyEx is the image we want to apply the morphological operation to. The second argument is the actual type of morphological operation — in this case, it’s an opening operation.
🌐
GeeksforGeeks
geeksforgeeks.org › python-opencv-morphological-operations
Python OpenCV - Morphological Operations - GeeksforGeeks
January 3, 2023 - Then we can make use of the Opencv cv.morphologyEx() function to perform an Opening operation on the image. Python3 ·
🌐
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\dots.jpg", 0) # Displaying the image cv2.imshow('Original',img) cv2.waitKey(0) cv2.destroyAllWindows() # Defining the kernel kernel = np.ones((5,5),np.uint8) # Apply the opening morphological operation using cv2.morphologyEx() function img_opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel) # Displaying the image after the opening morphological operation cv2.imshow('Opening',img_opening) cv2.waitKey(0) cv2.destroyAllWindows()
🌐
PyTutorial
pytutorial.com › python-opencv-cv2morphologyex-guide
PyTutorial | Python OpenCV cv2.morphologyEx() Guide
January 16, 2025 - The cv2.morphologyEx() function is used to perform advanced morphological operations on binary or grayscale images.
Top answer
1 of 1
3

The main issue is the (3,11) argument passed to cv2.morphologyEx.

According to the documentation of morphologyEx, kernel is a Structuring element, and not the size of the kernel.

Passing (3,11) is probably like passing np.array([1, 1]) (or just undefined behavior).

The correct syntax is passing 3x11 NumPy a array of ones (and uint8 type):

img_adj = cv2.morphologyEx(blurred, cv2.MORPH_OPEN, np.ones((3, 11), np.uint8), iterations=25)

Using large kernel with 25 iterations is too much, so I reduced it to 3x5 and 5 iterations.

The following code sample shows that the image is not shifted:

import cv2
import numpy as np

test_image = "test.bmp"
#image = cv2.imread(test_image, cv2.COLOR_BAYER_BG2RGB) # cv2.COLOR_BAYER_BG2RGB is not in place
image = cv2.imread(test_image, cv2.IMREAD_GRAYSCALE)  # Read image as grayscale
blurred = cv2.medianBlur(image, 3) 
ret, binary = cv2.threshold(blurred, 0, 255, cv2.THRESH_OTSU | cv2.THRESH_BINARY)
#img_adj = cv2.morphologyEx(blurred, cv2.MORPH_OPEN, (3, 11), iterations=25)
img_adj = cv2.morphologyEx(blurred, cv2.MORPH_OPEN, np.ones((3, 5), np.uint8), iterations=5)

montage_img = np.dstack((255-image, 0*image, 255-img_adj)) # Place image in the blue channel and img_adj in the red channel

# Show original and output images using OpenCV imshow method (instead of using matplotlib)
cv2.imshow('image', image)
cv2.imshow('img_adj', img_adj)
cv2.imshow('montage_img', montage_img)
cv2.waitKey()
cv2.destroyAllWindows()

image:

img_adj:

montage_img:


A better solution would be finding the largest connected component (that is not the background):

import cv2
import numpy as np

test_image = "test.bmp"
image = cv2.imread(test_image, cv2.IMREAD_GRAYSCALE)  # Read image as grayscale
ret, binary = cv2.threshold(image, 0, 255, cv2.THRESH_OTSU | cv2.THRESH_BINARY_INV)

nb_components, output, stats, centroids = cv2.connectedComponentsWithStats(binary, 8)  # Finding connected components

# Find the largest non background component.
# Note: range() starts from 1 since 0 is the background label.
max_label, max_size = max([(i, stats[i, cv2.CC_STAT_AREA]) for i in range(1, nb_components)], key=lambda x: x[1])

res = np.zeros_like(binary) + 255
res[output == max_label] = 0

cv2.imshow('res', res)
cv2.waitKey()
cv2.destroyAllWindows()

Result:

🌐
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 - white_noise = (np.random.randint(low=0, high=2, size=binary_img.shape) * 255)\ .astype('uint8') binary_with_noise_img = binary_img + white_noise opening_img = cv2.morphologyEx(binary_with_noise_img, cv2.MORPH_OPEN,\ morph_ellipse) plt.imshow(opening_img)
Find elsewhere
🌐
TheAILearner
theailearner.com › tag › cv2-morphologyex
cv2.morphologyEx – TheAILearner
July 31, 2019 - Below is the code for this. The hit-or-miss can be implemented using the OpenCV cv2.morphologyEx() function by passing the flag cv2.MORPH_HITMISS as shown below.
🌐
TutorialsPoint
tutorialspoint.com › opencv › opencv_morphological_operations.htm
OpenCV - Morphological Operations
In addition to these two, OpenCV has more morphological transformations. The morphologyEx() of the method of the class Imgproc is used to perform these operations on a given image.
🌐
Python Programming
pythonprogramming.net › morphological-transformation-python-opencv-tutorial
Morphological Transformations OpenCV Python Tutorial
cap = cv2.VideoCapture(1) while(1): _, frame = cap.read() hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) lower_red = np.array([30,150,50]) upper_red = np.array([255,255,180]) mask = cv2.inRange(hsv, lower_red, upper_red) res = cv2.bitwise_and(frame,frame, mask= mask) kernel = np.ones((5,5),np.uint8) opening = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) closing = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) cv2.imshow('Original',frame) cv2.imshow('Mask',mask) cv2.imshow('Opening',opening) cv2.imshow('Closing',closing) k = cv2.waitKey(5) & 0xFF if k == 27: break cv2.destroyAllWindows() cap.release()
🌐
EDUCBA
educba.com › home › software development › software development tutorials › programming languages tutorial › opencv morphology
OpenCV Morphology | How to work Morphology function in OpenCV?
April 18, 2023 - We make use of the operation MORPH_OPEN in morphologyEx() function to perform opening morphological operations on a given image.
Address   Unit no. 202, Jay Antariksh Bldg, Makwana Road, Marol, Andheri (East),, 400059, Mumbai
🌐
Master Data Science
datahacker.rs › 006-morphological-transformations-with-opencv-in-python
#006 Morphological transformations with OpenCV in Python
August 15, 2020 - # Performing opening and closing opening_1 = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel_1) opening_2 = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel_2) opening_3 = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel_3) closing_1 = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel_1) closing_2 = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel_2) closing_3 = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel_3)
🌐
Regenerativetoday
regenerativetoday.com › morphological-operations-for-image-preprocessing-in-opencv-in-detail
Morphological Operations for Image Preprocessing in OpenCV, in Detail – Regenerative
opening1 = cv2.morphologyEx(gray, cv2.MORPH_OPEN, kernel) cv2.imwrite('open1.jpg', opening1) opening2 = cv2.morphologyEx(gray, cv2.MORPH_OPEN, kernel1) ) cv2.imwrite(‘open2.jpg’, opening2)
🌐
GeeksforGeeks
geeksforgeeks.org › python › python-morphological-operations-in-image-processing-opening-set-1
Python | Morphological Operations in Image Processing (Opening) | Set-1 - GeeksforGeeks
August 11, 2025 - cv2.morphologyEx(image, cv2.MORPH_OPEN, ... program captures live video from webcam, detects blue-colored regions using HSV color filtering and then cleans small white noise from the mask using Opening morphological operation...
🌐
coseries
coseries.com › morphological-transformations-in-python-using-opencv
Morphological Transformations in Python using OpenCV | coseries
November 22, 2020 - The cv2.morphologyEx() is used to perform compound morphological operations. It takes an image, type of the operation, kernel, anchor position, iterations, borderType, and borderValue.
🌐
GitHub
github.com › opencv › opencv › issues › 10571
Python: Incorrect result in cv2.morphologyEx() with cv2.MORPH_CLOSE · Issue #10571 · opencv/opencv
January 10, 2018 - res_img = (cv2.morphologyEx(img.copy(), cv2.MORPH_CLOSE, np.ones((4,4), np.uint8))) * filled_img
Author   opencv
🌐
Medium
medium.com › analytics-vidhya › morphological-transformations-of-images-using-opencv-image-processing-part-2-f64b14af2a38
Morphological Transformations of Images using OpenCV | Image Processing Part-2 | by Ravjot Singh | Analytics Vidhya | Medium
November 5, 2020 - Opening is just another name of erosion followed by dilation. It is useful in removing noise, as we explained above. Here we use the function, cv2.morphologyEx().