(21条消息) 自适应阈值(adaptiveThreshold)分割原理及实现

背景介绍及原理

前面介绍了OTSU算法和最大熵算法,但这两种算法都属于全局阈值法,所以对于某些光照不均的图像,这种全局阈值分割的方法会显得苍白无力,如下图:

显然,这样的阈值处理结果不是我们想要的,那么就需要一种方法来应对这样的情况。

这种办法就是自适应阈值法(adaptiveThreshold),它的思想不是计算全局图像的阈值,而是根据图像不同区域亮度分布,计算其局部阈值,所以对于图像不同区域,能够自适应计算不同的阈值,因此被称为自适应阈值法。(其实就是局部阈值法)

如何确定局部阈值呢?可以计算某个邻域(局部)的均值、中值、高斯加权平均(高斯滤波)来确定阈值。值得说明的是:如果用局部的均值作为局部的阈值,就是常说的移动平均法(听起来挺高大上,其实......逃)。

OpenCV提供的API:

  1. void adaptiveThreshold(InputArray src, OutputArray dst, double maxValue,
  2. int adaptiveMethod, int thresholdType, int blockSize, double C)

说明下各参数:

InputArray src:源图像

OutputArray dst:输出图像,与源图像大小一致

int adaptiveMethod:在一个邻域内计算阈值所采用的算法,有两个取值,分别为 ADAPTIVE_THRESH_MEAN_C 和 ADAPTIVE_THRESH_GAUSSIAN_C 。

ADAPTIVE_THRESH_MEAN_C的计算方法是计算出领域的平均值再减去第七个参数double C的值。

ADAPTIVE_THRESH_GAUSSIAN_C的计算方法是计算出领域的高斯均值再减去第七个参数double C的值。

int thresholdType:这是阈值类型,只有两个取值,分别为 THRESH_BINARY 和THRESH_BINARY_INV  具体的请看官方的说明,这里不多做解释。

int blockSize:adaptiveThreshold的计算单位是像素的邻域块,这是局部邻域大小,3、5、7等。

double C:这个参数实际上是一个偏移值调整量,用均值和高斯计算阈值后,再减或加这个值就是最终阈值。

注:相比OpenCV的API,我多用了一个中值法确定阈值。

 

基于OpenCV实现

  1. #include <iostream>
  2. #include <opencv2/core.hpp>
  3. #include <opencv2/highgui.hpp>
  4. #include <opencv2/imgproc.hpp>
  5. enum adaptiveMethod{meanFilter,gaaussianFilter,medianFilter};
  6. void AdaptiveThreshold(cv::Mat& src, cv::Mat& dst, double Maxval, int Subsize, double c, adaptiveMethod method = meanFilter){
  7. if (src.channels() > 1)
  8. cv::cvtColor(src, src, CV_RGB2GRAY);
  9. cv::Mat smooth;
  10. switch (method)
  11. {
  12. case meanFilter:
  13. cv::blur(src, smooth, cv::Size(Subsize, Subsize)); //均值滤波
  14. break;
  15. case gaaussianFilter:
  16. cv::GaussianBlur(src, smooth, cv::Size(Subsize, Subsize),0,0); //高斯滤波
  17. break;
  18. case medianFilter:
  19. cv::medianBlur(src, smooth, Subsize); //中值滤波
  20. break;
  21. default:
  22. break;
  23. }
  24. smooth = smooth - c;
  25. //阈值处理
  26. src.copyTo(dst);
  27. for (int r = 0; r < src.rows;++r){
  28. const uchar* srcptr = src.ptr<uchar>(r);
  29. const uchar* smoothptr = smooth.ptr<uchar>(r);
  30. uchar* dstptr = dst.ptr<uchar>(r);
  31. for (int c = 0; c < src.cols; ++c){
  32. if (srcptr[c]>smoothptr[c]){
  33. dstptr[c] = Maxval;
  34. }
  35. else
  36. dstptr[c] = 0;
  37. }
  38. }
  39. }
  40. int main(){
  41. cv::Mat src = cv::imread("I:\\Learning-and-Practice\\2019Change\\Image process algorithm\\Img\\Fig1049(a)(spot_shaded_text_image).tif");
  42. if (src.empty()){
  43. return -1;
  44. }
  45. if (src.channels() > 1)
  46. cv::cvtColor(src, src, CV_RGB2GRAY);
  47. cv::Mat dst, dst2;
  48. double t2 = (double)cv::getTickCount();
  49. AdaptiveThreshold(src, dst, 255, 21, 10, meanFilter); //
  50. t2 = (double)cv::getTickCount() - t2;
  51. double time2 = (t2 *1000.) / ((double)cv::getTickFrequency());
  52. std::cout << "my_process=" << time2 << " ms. " << std::endl << std::endl;
  53. cv::adaptiveThreshold(src, dst2, 255, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, 21, 10);
  54. cv::namedWindow("src", CV_WINDOW_NORMAL);
  55. cv::imshow("src", src);
  56. cv::namedWindow("dst", CV_WINDOW_NORMAL);
  57. cv::imshow("dst", dst);
  58. cv::namedWindow("dst2", CV_WINDOW_NORMAL);
  59. cv::imshow("dst2", dst2);
  60. //cv::imwrite("I:\\Learning-and-Practice\\2019Change\\Image process algorithm\\Image Filtering\\MeanFilter\\TXT.jpg",dst);
  61. cv::waitKey(0);
  62. }

效果

这个效果就好很多了

(0)

相关推荐