简单阈值处理一般处理的是灰度图像,输出是二值图像。简单阈值处理,就是遍历图像中的每一个像素,判断像素与阈值大小,大于阈值或者小于阈值,都会赋予一个新值。
- '''
- 简单阈值
- 像素值高于阈值时 我们给这个像素 赋予一个新值, 可能是白色 ,
- 否则我们给它赋予另外一种颜色, 或是黑色 。
- 这个函数就是 cv2.threshhold()。
- 这个函数的第一个参数就是原图像
- 原图像应是灰度图。
- 第二个参数就是用来对像素值进行分类的阈值。
- 第三个参数 就是当像素值高于, 有时是小于 阈值时应该被赋予的新的像素值。
- OpenCV 提供了多种不同的阈值方法 , 是由第四个参数来决定的。
- 些方法包括
- • cv2.THRESH_BINARY
- • cv2.THRESH_BINARY_INV • cv2.THRESH_TRUNC
- • cv2.THRESH_TOZERO
- • cv2.THRESH_TOZERO_INV
- '''
-
- import cv2
- import numpy as np
- from matplotlib import pyplot as plt
-
- img = cv2.imread('img1.png',0)
-
- 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)
-
- titles = ['Original Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
- images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
-
- for i in range(6):
- plt.subplot(2, 3, i + 1), plt.imshow(images[i], 'gray')
- plt.title(titles[i])
- plt.xticks([]), plt.yticks([])
- plt.show()

上面使用的全局阈值,整幅图片采用同一个数作为阈值。但是这种方法不适应所有情况,尤其是同一幅图片的不同部分的具有不同亮度的时候。这时需要根据图像上的一个小区域计算对应的阈值。

- '''
- 自适应阈值
- Adaptive Method- 指定 算阈值的方法。
- – cv2.ADPTIVE_THRESH_MEAN_C 值取自相邻区域的平均值
- – cv2.ADPTIVE_THRESH_GAUSSIAN_C 值取值相邻区域 的加权和 ,权重为一个高斯窗口
- '''
-
- import cv2
- import numpy as np
- from matplotlib import pyplot as plt
-
- img = cv2.imread('img1.png', 0)
- # 中值滤波
- img = cv2.medianBlur(img, 5)
- ret, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
-
- # 11 为 Block size 邻域大小 用来计算阈值的区域大小 ,
- # 2 为 C值,常数, 阈值就等于的平均值或者加权平均值减去这个常数。
- th2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)
- th3 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)
-
- titles = ['Original Image', 'Global Thresholding (v = 127)',
- 'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
- images = [img, th1, th2, th3]
-
- for i in range(4):
- plt.subplot(2, 2, i + 1), plt.imshow(images[i], 'gray')
- plt.title(titles[i])
- plt.xticks([]), plt.yticks([])
-
- plt.show()
全局阈值,是随便使用一个数做阈值,但是怎么判断阈值的好坏呢?
Otsu二值化,同样使用cv2.threshold(),但是需要传入一个flag (cv2.THRESH_OTSU),把阈值设为0.然后算法会找到最优的阈值。这种方法对于双峰图像效果比较好,但是对于非双峰图像,这种方法效果不好。
- '''
- Otsu's 二值化
- 简单来说,就是对一副双峰图像自动根据其直方图计算出一个阈值。
- 对于非双峰图像 这 种方法 得到的结果可能会不理想 。
- 这里 用到的函数 是 cv2.threshold() 但是 需要多传入一个参数 flag cv2.THRESH_OTSU。
- 这时 把 值 为 0。然后算法会找到最 优阈值 ,这 个最优 值就是 回值 retVal。
- 如果不使用 Otsu 二值化 返回的retVal 值与 设定的 阈值相等。
- 下 的例子中 输入图像是一副带有噪声的图像。
- 第一种方法 我们 设127 为全局 阈值。
- 第二种方法 我们直接使用 Otsu 二值化。
- 第三种方法 我 们 先使用一个 5x5 的 高斯核 去噪 然后再使用 Otsu 二值化。
- 看看噪音 去除对结果的影响有多大吧。
- '''
-
- import cv2
- import numpy as np
- from matplotlib import pyplot as plt
-
- def gasuss_noise(image, mean=0, var=0.01):
- '''
- 添加高斯噪声
- mean : 均值
- var : 方差
- '''
- image = np.array(image/255, dtype=float)
- ##生成均值,方差的类似Image矩阵
- noise = np.random.normal(mean, var ** 0.5, image.shape)
- out = image + noise
- if out.min() < 0:
- low_clip = -1.
- else:
- low_clip = 0.
- out = np.clip(out, low_clip, 1.0)
- out = np.uint8(out*255)
- return out
-
- img = cv2.imread('img1.png', 0)
- # global thresholding
- ret1, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
- # Otsu's thresholding 这里这个0不管用,这里使用的OTSU寻找最优阈值。这个对于原始图像的自适应阈值处理
- ret2, th2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
- # Otsu's thresholding after Gaussian filtering
- # 5,5 为 斯核的大小 0 为标准差
- blur = cv2.GaussianBlur(img, (5, 5), 0)
- ret4,th4= cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
- # 阀值一定为 0
- ret3, th3 = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
- # plot all the images and their histograms
- images = [img, 0, th1,
- img, 0, th2,
- blur,0,th4,
- blur, 0, th3]
- titles = ['Original Noisy Image', 'Histogram', 'Global Thresholding (v=127)',
- 'Original Noisy Image', 'Histogram', "Otsu's Thresholding",
- 'Noisy Image','Histogram',"Otsu's Thresholding",
- 'Gaussian filtered Image', 'Histogram', "Otsu's Thresholding"]
- # 使用了 pyplot 中画直方图的方法 plt.hist,
- # 注意的是它的参数是一维数组
- # 所以使用了 numpy ravel 方法 将多维数组 换成一维 也可以使用 flatten 方法
-
- for i in range(4):
- plt.subplot(4, 3, i * 3 + 1), plt.imshow(images[i * 3], 'gray')
- plt.title(titles[i * 3]), plt.xticks([]), plt.yticks([])
- plt.subplot(4, 3, i * 3 + 2), plt.hist(images[i * 3].ravel(), 256)
- plt.title(titles[i * 3 + 1]), plt.xticks([]), plt.yticks([])
- plt.subplot(4, 3, i * 3 + 3), plt.imshow(images[i * 3 + 2], 'gray')
- plt.title(titles[i * 3 + 2]), plt.xticks([]), plt.yticks([])
- plt.show()

OTSU的原理
- import cv2
- import numpy as np
-
- img = cv2.imread('img1.png', 0)
- blur = cv2.GaussianBlur(img, (5, 5), 0)
- # find normalized_histogram, and its cumulative distribution function
- # 算归一化直方图
- # CalcHist(image, accumulate=0, mask=NULL)
-
- hist = cv2.calcHist([blur], [0], None, [256], [0, 256])
- hist_norm = hist.ravel() / hist.max()
- Q = hist_norm.cumsum()
-
- bins = np.arange(256)
- fn_min = np.inf
- thresh = -1
-
- for i in range(1, 256):
- p1, p2 = np.hsplit(hist_norm, [i]) # probabilities
- q1, q2 = Q[i], Q[255] - Q[i] # cum sum of classes
- b1, b2 = np.hsplit(bins, [i]) # weights
-
- # finding means and variances
- m1, m2 = np.sum(p1 * b1) / q1, np.sum(p2 * b2) / q2
- v1, v2 = np.sum(((b1 - m1) ** 2) * p1) / q1, np.sum(((b2 - m2) ** 2) * p2) / q2
-
- # calculates the minimization function
- fn = v1 * q1 + v2 * q2
- if fn < fn_min:
- fn_min = fn
- thresh = i
-
- # find otsu's threshold value with OpenCV function
- ret, otsu = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
-
- print(thresh, ret)