OpenCV treats all white pixels as foreground and the black ones as background. The green contours visualize the detected foreground.

If you wish to highlight the black circle, you'd need to invert the image beforehand. Alternatively you can use the threshold type "THRESH_BINARY_INV" (http://docs.opencv.org/modules/imgproc/doc/miscellaneous_transformations.html?highlight=threshold#threshold).

Answer from Falko on Stack Overflow
๐ŸŒ
OpenCV
docs.opencv.org โ€บ 4.13.0 โ€บ d7 โ€บ d4d โ€บ tutorial_py_thresholding.html
OpenCV: Image Thresholding
Here, the matter is straight-forward. For every pixel, the same threshold value is applied. If the pixel value is smaller than or equal to the threshold, it is set to 0, otherwise it is set to a maximum value. The function cv.threshold is used to apply the thresholding.
๐ŸŒ
OpenCV-Python Tutorials
opencv24-python-tutorials.readthedocs.io โ€บ en โ€บ latest โ€บ py_tutorials โ€บ py_imgproc โ€บ py_thresholding โ€บ py_thresholding.html
Image Thresholding โ€” OpenCV-Python Tutorials beta documentation
Here, the matter is straight forward. If pixel value is greater than a threshold value, it is assigned one value (may be white), else it is assigned another value (may be black). The function used is cv2.threshold. First argument is the source image, which should be a grayscale image.
๐ŸŒ
OpenCV
opencv.org โ€บ home โ€บ news โ€บ image thresholding using opencv
Image Thresholding using OpenCV
April 24, 2025 - Sets pixels to the threshold value if they are above it; otherwise, leaves them unchanged. _, thresh_img = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
๐ŸŒ
GeeksforGeeks
geeksforgeeks.org โ€บ python โ€บ simple-thresholdin-using-opencv
Simple Thresholding using OpenCV - GeeksforGeeks
August 18, 2025 - It works by comparing each pixel ... 0 (black) or 255 (white).In OpenCV with Python, the function cv2.threshold is used for thresholding....
๐ŸŒ
PyImageSearch
pyimagesearch.com โ€บ home โ€บ blog โ€บ opencv thresholding ( cv2.threshold )
OpenCV Thresholding ( cv2.threshold ) - PyImageSearch
June 8, 2023 - The cv2.threshold function then returns a tuple of 2 values: the first, T, is the threshold value. In the case of simple thresholding, this value is trivial since we manually supplied the value of T in the first place.
๐ŸŒ
Medium
medium.com โ€บ @manik.butex41 โ€บ an-introduction-to-thresholding-operations-in-opencv-64aff0cb2826
An Introduction to Thresholding Operations in OpenCV | by Manik Hossain | Medium
November 6, 2023 - The cv2.ADAPTIVE_THRESH_GAUSSIAN_C method calculates the threshold value for each pixel by taking a weighted mean of the pixelโ€™s neighbourhood, with pixels closer to the centre of the neighbourhood given more weight.
๐ŸŒ
Scaler
scaler.com โ€บ home โ€บ topics โ€บ opencv cv2.threshold() function
OpenCV cv2.threshold() Function - Scaler Topics
June 19, 2023 - The cv2.threshold() method is used to apply a fixed-level threshold to an input image, which means that it will convert all pixel values below a certain threshold value to a specified value and all pixel values above that threshold value to another specified value.
Find elsewhere
๐ŸŒ
LearnOpenCV
learnopencv.com โ€บ home โ€บ image processing โ€บ image thresholding in opencv
Image Thresholding in OpenCV
March 1, 2024 - This is the most common and simplest type of thresholding. ... # import opencv import cv2 # Read image src = cv2.imread("threshold.png", cv2.IMREAD_GRAYSCALE) # Set threshold and maxValue thresh = 0 maxValue = 255 # Basic threshold example th, dst = cv2.threshold(src, thresh, maxValue, cv2.THRESH_BINARY);
๐ŸŒ
GeeksforGeeks
geeksforgeeks.org โ€บ python-thresholding-techniques-using-opencv-set-1-simple-thresholding
Python | Thresholding techniques using OpenCV | Set-1 (Simple Thresholding) - GeeksforGeeks
January 4, 2023 - Syntax: cv2.threshold(source, thresholdValue, maxVal, thresholdingTechnique) Parameters: -> source: Input Image array (must be in Grayscale). -> thresholdValue: Value of Threshold below and above which pixel values will change accordingly. -> ...
๐ŸŒ
PyImageSearch
pyimagesearch.com โ€บ home โ€บ blog โ€บ adaptive thresholding with opencv ( cv2.adaptivethreshold )
Adaptive Thresholding with OpenCV ( cv2.adaptiveThreshold ) - PyImageSearch
June 9, 2023 - Here we supply a value of cv2.ADAPTIVE_THRESH_MEAN_C to indicate that we are using the arithmetic mean of the local pixel neighborhood to compute our threshold value of T. We could also supply a value of cv2.ADAPTIVE_THRESH_GAUSSIAN_C (which weโ€™ll do next) to indicate we want to use the Gaussian average โ€” which method you choose is entirely dependent on your application and situation, so youโ€™ll want to play around with both methods.
๐ŸŒ
Ccoderun
ccoderun.ca โ€บ programming โ€บ doxygen โ€บ opencv โ€บ tutorial_py_thresholding.html
OpenCV: Image Thresholding
For every pixel, the same threshold value is applied. If the pixel value is smaller than the threshold, it is set to 0, otherwise it is set to a maximum value. The function cv.threshold is used to apply the thresholding. The first argument is the source image, which should be a grayscale image.
๐ŸŒ
GitHub
github.com โ€บ jagracar โ€บ OpenCV-python-tests โ€บ blob โ€บ master โ€บ OpenCV-tutorials โ€บ imageProcessing โ€บ threshold.py
OpenCV-python-tests/OpenCV-tutorials/imageProcessing/threshold.py at master ยท jagracar/OpenCV-python-tests
# Apply the different thresholds ยท ret, thresh1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) ret, thresh2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV) ret, thresh3 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC) ret, thresh4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO) ret, thresh5 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV) ret, thresh6 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) thresh7 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2) thresh8 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2) ยท
Author: jagracar
๐ŸŒ
Medium
medium.com โ€บ @madhavarajumaddimani โ€บ exploring-thresholding-techniques-in-opencv-7e92dcb70502
Exploring Thresholding Techniques in OpenCV | by Madhavaraju | Medium
October 27, 2025 - Otsuโ€™s method is an automatic thresholding technique that calculates the optimal threshold value by maximizing the between-class variance. It is particularly effective for bimodal images (images with two distinct pixel value distributions).
Top answer
1 of 2
2

Your question and comment are contradictory: Keep everything (significantly) brighter/darker than the mean (+/- constant) of the neighbourhood (question) vs. keep everything within mean +/- constant (comment). I assume the first one to be the correct, and I'll try to give an answer.

Using cv2.adaptiveThreshold is certainly useful; parameterization might be tricky, especially given the example image. First, let's have a look at the output:

We see, that the intensity value range in the given image is small. The upper-halfs of the third and sixth' squares don't really differ from their neighbourhood. It's quite unlikely to find a proper difference there. The upper-halfs of squares #8 and #12 (or also the lower-half of square #10) are more likely to be found.

Top row now shows some more "global" parameters (blocksize = 151, c = 25), bottom row more "local" parameters (blocksize = 51, c = 5). Middle column is everything darker than the neighbourhood (with respect to the paramters), right column is everything brighter than the neighbourhood. We see, in the more "global" case, we get the proper upper-halfs, but there are mostly no "significant" darker areas. Looking, at the more "local" case, we see some darker areas, but we won't find the complete upper-/lower-halfs in question. That's just because how the different triangles are arranged.

On the technical side: You need two calls of cv2.adaptiveThreshold, one using the cv2.THRESH_BINARY_INV mode to find everything darker and one using the cv2.THRESH_BINARY mode to find everything brighter. Also, you have to provide c or -c for the two different cases.

Here's the full code:

import cv2
from matplotlib import pyplot as plt
from skimage import io          # Only needed for web grabbing images

plt.figure(1, figsize=(15, 10))

img = cv2.cvtColor(io.imread('https://i.sstatic.net/dA1Vt.png'), cv2.COLOR_RGB2GRAY)

plt.subplot(2, 3, 1), plt.imshow(img, cmap='gray'), plt.colorbar()

# More "global" parameters
bs = 151
c = 25
img_le = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, bs, c)
img_gt = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, bs, -c)
plt.subplot(2, 3, 2), plt.imshow(img_le, cmap='gray')
plt.subplot(2, 3, 3), plt.imshow(img_gt, cmap='gray')

# More "local" parameters
bs = 51
c = 5
img_le = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, bs, c)
img_gt = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, bs, -c)
plt.subplot(2, 3, 5), plt.imshow(img_le, cmap='gray')
plt.subplot(2, 3, 6), plt.imshow(img_gt, cmap='gray')

plt.tight_layout()
plt.show()

Hope that helps โ€“ somehow!

-----------------------
System information
-----------------------
Python:      3.8.1
Matplotlib:  3.2.0rc1
OpenCV:      4.1.2
-----------------------
2 of 2
1

Another way to look at this is that where abs(mean - image) <= c, you want that to become white, otherwise you want that to become black. In Python/OpenCV/Scipy/Numpy, I first compute the local uniform mean (average) using a uniform 51x51 pixel block averaging filter (boxcar average). You could use some weighted averaging method such as the Gaussian average, if you want. Then I compute the abs(mean - image). Then I use Numpy thresholding. Note: You could also just use one simple threshold (cv2.threshold) on the abs(mean-image) result in place of two numpy thresholds.

Input:

import cv2
import numpy as np
from scipy import ndimage

# read image as grayscale
# convert to floats in the range 0 to 1 so that the difference keeps negative values
img = cv2.imread('squares.png',0).astype(np.float32)/255.0

# get uniform (51x51 block) average
ave = ndimage.uniform_filter(img, size=51)

# get abs difference between ave and img and convert back to integers in the range 0 to 255
diff = 255*np.abs(ave - img)
diff = diff.astype(np.uint8)

# threshold
# Note: could also just use one simple cv2.Threshold on diff
c = 5
diff_thresh = diff.copy()
diff_thresh[ diff_thresh <= c ] = 255
diff_thresh[ diff_thresh != 255 ] = 0


# view result
cv2.imshow("img", img)
cv2.imshow("ave", ave)
cv2.imshow("diff", diff)
cv2.imshow("threshold", diff_thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()

# save result
cv2.imwrite("squares_2way_thresh.jpg", diff_thresh)


Result:

๐ŸŒ
Physics and Math Tutor
pmt.physicsandmathstutor.com โ€บ download โ€บ Physics โ€บ A-level โ€บ Past-Papers โ€บ AQA โ€บ Data Sheet.pdf pdf
A-level Physics data and formulae
threshold of hearing ยท ๐ผ0 = 1.0 ร— 10โˆ’12 W mโˆ’2 ยท intensity level ยท ๐‘–๐‘–๐‘–๐‘–๐‘–๐‘–๐‘–๐‘–๐‘– ๐‘™๐‘™๐‘™๐‘™๐‘™ = 10 log ๐ผ ยท ๐ผ0 ยท absorption ยท ๐ผ = ๐ผ0๐‘’โ€“๐œ‡๐œ‡ ยท ๐œ‡m = ๐œ‡ ยท ๐œŒ ยท ultrasound imaging ยท ๐‘ = ๐‘ ๐‘ ยท