0
点赞
收藏
分享

微信扫一扫

【OpenCV C++&Python】(四)图像阈值处理


文章目录

  • ​​图像阈值处理​​
  • ​​Python​​
  • ​​简单阈值处理​​
  • ​​自适应阈值处理​​
  • ​​大津(Otsu)法​​
  • ​​C++​​
  • ​​简单阈值处理​​
  • ​​自适应阈值处理​​
  • ​​大津(Otsu)法​​


图像阈值处理

Python

简单阈值处理

OpenCV的​​cv.threshold​​​用于简单阈值处理,它的第一个参数是灰度源图像​​src​​​;第二个参数是阈值​​thresh​​​;第三个参数是赋值给超过阈值的像素的最大值​​maxval​​;第四个参数则是阈值处理的类型:

【OpenCV C++&Python】(四)图像阈值处理_python

​cv.threshold​​​返回两个输出。第一个是使用的阈值,第二个输出是阈值图像​​dst​​。下面比较不同类型的阈值处理方法:

import cv2 as cv
from matplotlib import pyplot as plt
import numpy as np

img = cv.imread('threshold.jpg', 0)
ret, thresh1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
ret, thresh2 = cv.threshold(img, 127, 255, cv.THRESH_BINARY_INV)
ret, thresh3 = cv.threshold(img, 127, 255, cv.THRESH_TRUNC)
ret, thresh4 = cv.threshold(img, 127, 255, cv.THRESH_TOZERO)
ret, thresh5 = cv.threshold(img, 127, 255, cv.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()

【OpenCV C++&Python】(四)图像阈值处理_python_02

自适应阈值处理

简单阈值处理在图像全局都使用同一个阈值,如果图像在不同区域有不同的照明条件,这可能就不适用了。在这种情况下,自适应阈值处理更适合。自适应阈值处理根据像素周围的一个小区域来确定阈值。可以使用OpenCV的​​cv.adaptiveThreshold​​实现这个功能。

​cv.adaptiveThreshold​​​的第一个参数是灰度源图像​​src​​​;第二个参数是赋值给超过阈值的像素的最大值​​maxval​​;

第三个参数​​adaptiveMethod​​决定如何计算阈值:

  • ​cv.ADAPTIVE_THRESH_MEAN_C​​​:阈值是邻域内像素的平均值减去常数​​C​​。
  • ​cv.ADAPTIVE_THRESH_GAUSSIAN_C​​​:阈值是邻域内像素的高斯加权和减去常数​​C​​。

第四个参数则是阈值处理的类型;第五个参数​​ blockSize​​​决定邻域区域的大小;第六个参数是常数​​C​​。

下面对比一下简单阈值处理和自适应阈值处理:

import cv2 as cv
from matplotlib import pyplot as plt

img = cv.imread('adaptiveThreshold.jpg', 0)
ret, th1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
th2 = cv.adaptiveThreshold(img, 255, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY, 11, 2)
th3 = cv.adaptiveThreshold(img, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 11, 2)

titles = ['Original Image', 'BINARY(v=127)',
'ADAPTIVE_THRESH_MEAN_C', 'ADAPTIVE_THRESH_GAUSSIAN_C']
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()

【OpenCV C++&Python】(四)图像阈值处理_阈值处理_03

大津(Otsu)法

在普通阈值处理中,我们需要指定阈值。大津法则能够从图像直方图中自动确定一个最优的全局阈值,即使用大津法不用人工挑选阈值。

大津法确定阈值的方法是找到一个阈值【OpenCV C++&Python】(四)图像阈值处理_计算机视觉_04,使类间方差最大或类内方差最小。设图像像素数为【OpenCV C++&Python】(四)图像阈值处理_计算机视觉_05,灰度范围为【OpenCV C++&Python】(四)图像阈值处理_方差_06。对应灰度级【OpenCV C++&Python】(四)图像阈值处理_计算机视觉_07的像素数为【OpenCV C++&Python】(四)图像阈值处理_方差_08,灰度级【OpenCV C++&Python】(四)图像阈值处理_计算机视觉_07的频率为:

【OpenCV C++&Python】(四)图像阈值处理_计算机视觉_10

总和为1:
【OpenCV C++&Python】(四)图像阈值处理_opencv_11

把图像中像素按灰度值用阈值【OpenCV C++&Python】(四)图像阈值处理_计算机视觉_04分成两类【OpenCV C++&Python】(四)图像阈值处理_计算机视觉_13【OpenCV C++&Python】(四)图像阈值处理_方差_14【OpenCV C++&Python】(四)图像阈值处理_计算机视觉_13由灰度值在【OpenCV C++&Python】(四)图像阈值处理_python_16之间的像素组成,【OpenCV C++&Python】(四)图像阈值处理_方差_14由灰度值在【OpenCV C++&Python】(四)图像阈值处理_opencv_18之间的像素组成。整幅图像的均值为:
【OpenCV C++&Python】(四)图像阈值处理_opencv_19


【OpenCV C++&Python】(四)图像阈值处理_方差_20

【OpenCV C++&Python】(四)图像阈值处理_方差_21

【OpenCV C++&Python】(四)图像阈值处理_计算机视觉_13【OpenCV C++&Python】(四)图像阈值处理_方差_14的均值为:

【OpenCV C++&Python】(四)图像阈值处理_python_24

【OpenCV C++&Python】(四)图像阈值处理_计算机视觉_25

由上面式子可得:

【OpenCV C++&Python】(四)图像阈值处理_计算机视觉_26

类内方差(within-class variance) 定义为:

【OpenCV C++&Python】(四)图像阈值处理_阈值处理_27

反映每一类样本方差的大小。(值越小,各类的样本越集中,越好分割)

类间方差(between-class variance) 定义为:

【OpenCV C++&Python】(四)图像阈值处理_计算机视觉_28

相当于以每一类的均值作为该类的代表值,以总体均值为均值计算方差。类间方差反映阈值两侧数据之间的差异程度。(值越大,类别越明显,越好分割)

【OpenCV C++&Python】(四)图像阈值处理_方差_29,得到:

【OpenCV C++&Python】(四)图像阈值处理_opencv_30

【OpenCV C++&Python】(四)图像阈值处理_方差_31

【OpenCV C++&Python】(四)图像阈值处理_python_32

再由:

【OpenCV C++&Python】(四)图像阈值处理_python_33

得到:

【OpenCV C++&Python】(四)图像阈值处理_python_34

即类内方差与类间方差之和为定值,最大化类间方差与最小化类内方差等价。

使用​​cv.threshold()​​​函数,阈值处理的类型可以任意选择,​​THRESH_OTSU​​​作为一个额外的​​flag​​即可使用大津法。下面以一个直方图包含两个峰的图像(双峰图像)为例:

import cv2 as cv
from matplotlib import pyplot as plt

img = cv.imread('coins.png', 0)
# 普通二值化
ret1, th1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
# Otsu法二值化
ret2, th2 = cv.threshold(img, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU) # 随便给个0为阈值

images = [img, [], th1,
img, [], th2]
titles = ['Original Image', 'Histogram', 'BINARY (v=127)',
'Original Image', 'Histogram', 'OTSU(v=0)']
for i in range(2):
plt.subplot(2, 3, i * 3 + 1), plt.imshow(images[i * 3], 'gray')
plt.title(titles[i * 3]), plt.xticks([]), plt.yticks([])
plt.subplot(2, 3, i * 3 + 2), plt.hist(images[i * 3].ravel(), 256)
plt.title(titles[i * 3 + 1]), plt.xticks([]), plt.yticks([])
plt.subplot(2, 3, i * 3 + 3), plt.imshow(images[i * 3 + 2], 'gray')
plt.title(titles[i * 3 + 2]), plt.xticks([]), plt.yticks([])
plt.show()

【OpenCV C++&Python】(四)图像阈值处理_阈值处理_35

大津法无需人工指定阈值即可达到较为理想的效果。

C++

简单阈值处理

#include <opencv2\opencv.hpp>
using namespace cv;
int main()
{
Mat img = imread("threshold.jpg",0);
Mat thresh1, thresh2, thresh3, thresh4, thresh5;

threshold(img, thresh1, 127, 255, THRESH_BINARY);
threshold(img, thresh2, 127, 255, THRESH_BINARY_INV);
threshold(img, thresh3, 127, 255, THRESH_TRUNC);
threshold(img, thresh4, 127, 255, THRESH_TOZERO);
threshold(img, thresh5, 127, 255, THRESH_TOZERO_INV);

imshow("Original Image", img);
imshow("BINARY", thresh1);
imshow("BINARY_INV", thresh2);
imshow("TRUNC", thresh3);
imshow("TOZERO", thresh4);
imshow("TOZERO_INV", thresh5);
waitKey(0);
return 0;
}

自适应阈值处理

#include <opencv2\opencv.hpp>
using namespace cv;
int main()
{
Mat img = imread("adaptiveThreshold.jpg",0);
Mat thresh1, thresh2, thresh3;
threshold(img, thresh1, 127, 255, THRESH_BINARY);
adaptiveThreshold(img, thresh2,255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 11, 2);
adaptiveThreshold(img, thresh3,255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 11, 2);

imshow("Original Image", img);
imshow("BINARY", thresh1);
imshow("ADAPTIVE_THRESH_MEAN_C", thresh2);
imshow("ADAPTIVE_THRESH_GAUSSIAN_C", thresh3);
waitKey(0);
return 0;
}

大津(Otsu)法

#include <opencv2\opencv.hpp>
using namespace cv;
int main()
{
Mat img = imread("coins.png",0);
Mat thresh1, thresh2;
threshold(img, thresh1, 127, 255, THRESH_BINARY);
threshold(img, thresh2, 0, 255, THRESH_BINARY + THRESH_OTSU);

imshow("Original Image", img);
imshow("BINARY", thresh1);
imshow("OTSU", thresh2);
waitKey(0);
return 0;
}

代码:
​​​ https://gitee.com/BinaryAI/open-cv-c–and-python​​

参考:

[1]https://docs.opencv.org/4.6.0/

[2]https://zhuanlan.zhihu.com/p/384457101

[3]数字图像处理(MATLAB版)(第2版),张德丰, 人民邮电出版社


举报

相关推荐

0 条评论