'Why opencv 'cv2.morphologyEx' operations shift images in one direction during iterations?
I am currently using "morphologyEx" operations in OpenCV to eliminate some noise in the image. It's working successfully but for some strange reason, roi keeps moving south during iterations.
The original image is : 
The python script that I am running is
test_image = r"C:/test/test.bmp"
image = cv2.imread(test_image,cv2.COLOR_BAYER_BG2RGB)
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)
#imshow(binary)
imshow(img_adj)
But after iterations it is as follows :

Image roi has shifted south which is proportional to iterations. How can I prevent shifting ?
Solution 1:[1]
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()
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()
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Rotem |





