当前位置:   article > 正文

【opencv】教程代码 —ImgProc (11)

【opencv】教程代码 —ImgProc (11)

11.1 BasicLinearTransforms.cpp

29d7bd73db9166758d1cbf1662b69039.png

调整输入图像的对比度和亮度,并显示原始图像和调整后的图像。程序中主要包含以下步骤:

  1. 读取用户输入的图像并做相应的错误处理。

  2. 创建一个新的图像矩阵用于存放调整对比度和亮度后的图像。

  3. 获取用户从命令行输入的对比度控制参数 alpha 和亮度控制参数 beta。

  4. 对原始图像中的每一个像素进行运算 new_image(i,j) = alpha*image(i,j) + beta 调整对比度和亮度。获取新的图像。

  5. 展示原始图像和新的图像。

  6. 程序挂起等待用户输入,一旦有输入,程序结束。

  1. /**
  2. * @file BasicLinearTransforms.cpp
  3. * @brief 简单的程序用于调整图像的对比度和亮度
  4. * @author OpenCV团队
  5. */
  6. // 引入头文件
  7. #include "opencv2/imgcodecs.hpp"
  8. #include "opencv2/highgui.hpp" //<-- 用于图像显示
  9. #include <iostream> //<-- C++标准库中的输入/输出头文件
  10. // 在此处,我们没有在全局范围内使用 "using namespace std;",是为了避免在 C++17 中 beta 变量与 std::beta产生冲突
  11. using std::cin;
  12. using std::cout;
  13. using std::endl;
  14. using namespace cv;    // <-- 使用OpenCV中被封装在cv::下的所有的类和函数
  15. /**
  16. * @function main
  17. * @brief 主函数
  18. */
  19. int main( int argc, char** argv )
  20. {
  21. /// 读取用户输入的图像文件
  22. //! [basic-linear-transform-load]
  23. CommandLineParser parser( argc, argv, "{@input | lena.jpg | input image}" );
  24. Mat image = imread( samples::findFile( parser.get<String>( "@input" ) ) );
  25. if( image.empty() ) //<-- 判断图像是否读取成功
  26. {
  27. // 如果图像未被成功读取,则显示错误信息并返回-1,程序结束
  28. cout << "Could not open or find the image!\n" << endl;
  29. cout << "Usage: " << argv[0] << " <Input image>" << endl;
  30. return -1;
  31. }
  32. //! [basic-linear-transform-load]
  33. // 创建新的图像,准备存放调整对比度和亮度之后的图像
  34. //! [basic-linear-transform-output]
  35. Mat new_image = Mat::zeros( image.size(), image.type() );
  36. //! [basic-linear-transform-output]
  37. // 定义调整对比度和亮度的参数
  38. //! [basic-linear-transform-parameters]
  39. double alpha = 1.0; /*< 用于控制对比度的简单参数 */
  40. int beta = 0; /*< 用于控制亮度的简单参数 */
  41. /// 初始化值
  42. cout << " Basic Linear Transforms " << endl;
  43. cout << "-------------------------" << endl;
  44. cout << "* Enter the alpha value [1.0-3.0]: "; cin >> alpha; //<-- 从控制台获取alpha值
  45. cout << "* Enter the beta value [0-100]: "; cin >> beta; //<-- 从控制台获取beta值
  46. //! [basic-linear-transform-parameters]
  47. /// 执行操作 new_image(i,j) = alpha*image(i,j) + beta
  48. /// 我们本可以简单的使用函数:image.convertTo(new_image, -1, alpha, beta); 来实现上述操作,
  49. /// 但我们希望通过这个例子,展示出如何访问像素 :)
  50. //! [basic-linear-transform-operation]
  51. for( int y = 0; y < image.rows; y++ ) { //<-- 遍历图像的所有行
  52. for( int x = 0; x < image.cols; x++ ) { //<-- 遍历图像的所有列
  53. for( int c = 0; c < image.channels(); c++ ) { //<-- 遍历图像的每个通道
  54. new_image.at<Vec3b>(y,x)[c] =
  55. saturate_cast<uchar>( alpha*image.at<Vec3b>(y,x)[c] + beta ); //<-- 对原图像的每一个像素值执行操作调整对比度和亮度,saturate_cast<uchar>是为了确保计算结果落在uchar的取值范围
  56. }
  57. }
  58. }
  59. //! [basic-linear-transform-operation]
  60. // 展示图像
  61. //! [basic-linear-transform-display]
  62. imshow("Original Image", image); // 显示原始图像
  63. imshow("New Image", new_image); // 显示调整后的图像
  64. // 等待用户按键,这样做是为了防止程序运行完后立即退出,无法显示图像
  65. waitKey();
  66. //! [basic-linear-transform-display]
  67. return 0; //正常结束程序
  68. }

637d9aafcdbe579f7331ede9bc76721c.png

11.2 Morphology_1.cpp

13ec57550ae6ba55dff6a7f5cba8d8b5.png

f5994fd2d6b3ba464ef0fe7e5f2d7302.png

9a7d60ff015c6d51d9874d544181c978.png

这段由OpenCV团队编写,对一张图片进行腐蚀和膨胀操作的C++代码。腐蚀操作会减小图像区域,膨胀操作会增大图像区域。主要通过设定腐蚀元素和膨胀元素的类型以及内核大小,然后调用库函数进行腐蚀或者膨胀操作,来改变图像的性质。可以视为一种提取图像特征的方法,常应用于图像二值化、边缘检测、消除噪声等图像处理操作中。

  1. /**
  2. * @file Morphology_1.cpp
  3. * @brief Erosion and Dilation sample code 腐蚀与膨胀的样例代码
  4. * @author OpenCV team
  5. */
  6. #include "opencv2/imgproc.hpp" //包含OpenCV库头文件
  7. #include "opencv2/highgui.hpp" //包含OpenCV高级用户界面的头文件
  8. #include <iostream>
  9. using namespace cv;
  10. using namespace std;
  11. /// Global variables 定义全局变量
  12. Mat src, erosion_dst, dilation_dst;
  13. int erosion_elem = 0;
  14. int erosion_size = 0;
  15. int dilation_elem = 0;
  16. int dilation_size = 0;
  17. int const max_elem = 2;
  18. int const max_kernel_size = 21;
  19. /** Function Headers 函数的声明(函数的原型) */
  20. void Erosion( int, void* ); // 腐蚀函数
  21. void Dilation( int, void* ); // 膨胀函数
  22. /**
  23. * @function main 主函数
  24. */
  25. int main( int argc, char** argv )
  26. {
  27. /// Load an image, 加载图片
  28. CommandLineParser parser( argc, argv, "{@input | LinuxLogo.jpg | input image}" );
  29. src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR );
  30. if( src.empty() )
  31. {
  32. cout << "Could not open or find the image!\n" << endl; //若图片为空,打印错误信息
  33. cout << "Usage: " << argv[0] << " <Input image>" << endl;
  34. return -1;
  35. }
  36. /// Create windows 创建窗口
  37. namedWindow( "Erosion Demo", WINDOW_AUTOSIZE );
  38. namedWindow( "Dilation Demo", WINDOW_AUTOSIZE );
  39. moveWindow( "Dilation Demo", src.cols, 0 );
  40. /// Create Erosion Trackbar 创建腐蚀的滑动条
  41. createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Erosion Demo",
  42. &erosion_elem, max_elem,
  43. Erosion );
  44. createTrackbar( "Kernel size:\n 2n +1", "Erosion Demo",
  45. &erosion_size, max_kernel_size,
  46. Erosion );
  47. /// Create Dilation Trackbar 创建膨胀的滑动条
  48. createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Dilation Demo",
  49. &dilation_elem, max_elem,
  50. Dilation );
  51. createTrackbar( "Kernel size:\n 2n +1", "Dilation Demo",
  52. &dilation_size, max_kernel_size,
  53. Dilation );
  54. /// Default start 默认开始
  55. Erosion( 0, 0 );
  56. Dilation( 0, 0 );
  57. waitKey(0); //等待用户按键操作
  58. return 0;
  59. }
  60. /**
  61. * @function Erosion 腐蚀函数
  62. */
  63. void Erosion( int, void* )
  64. {
  65. int erosion_type = 0;
  66. if( erosion_elem == 0 ){ erosion_type = MORPH_RECT; } //如果元素类型为0,腐蚀的类型为MORPH_RECT
  67. else if( erosion_elem == 1 ){ erosion_type = MORPH_CROSS; } //如果元素类型为1,腐蚀的类型为MORPH_CROSS
  68. else if( erosion_elem == 2) { erosion_type = MORPH_ELLIPSE; } //如果元素类型为2,腐蚀的类型为MORPH_ELLIPSE
  69. //![kernel]
  70. Mat element = getStructuringElement( erosion_type, //生成预定义形状的结构元素
  71. Size( 2*erosion_size + 1, 2*erosion_size+1 ),
  72. Point( erosion_size, erosion_size ) );
  73. //![kernel]
  74. /// Apply the erosion operation 进行腐蚀操作
  75. erode( src, erosion_dst, element );
  76. imshow( "Erosion Demo", erosion_dst ); //显示腐蚀的效果
  77. }
  78. /**
  79. * @function Dilation 膨胀函数
  80. */
  81. void Dilation( int, void* )
  82. {
  83. int dilation_type = 0;
  84. if( dilation_elem == 0 ){ dilation_type = MORPH_RECT; } //如果元素类型为0,膨胀的类型为MORPH_RECT
  85. else if( dilation_elem == 1 ){ dilation_type = MORPH_CROSS; } //如果元素类型为1,膨胀的类型为MORPH_CROSS
  86. else if( dilation_elem == 2) { dilation_type = MORPH_ELLIPSE; } //如果元素类型为2,膨胀的类型为MORPH_ELLIPSE
  87. Mat element = getStructuringElement( dilation_type, //生成预定义形状的结构元素
  88. Size( 2*dilation_size + 1, 2*dilation_size+1 ),
  89. Point( dilation_size, dilation_size ) );
  90. /// Apply the dilation operation 进行膨胀操作
  91. dilate( src, dilation_dst, element );
  92. imshow( "Dilation Demo", dilation_dst ); //显示膨胀的效果
  93. }

e0ebadb1a27274c4b329a313c5a8d96b.png

11.3 Morphology_2.cpp

5e521e18e9043bc31a2daaef8148f3a3.png

e5689583dd574c9839b98a507cbfc378.png

这段代码的主要功能是实现形态学操作,如:开运算、闭运算、形态学梯度、顶帽运算和黑帽运算,用户可以自行选择操作类型及形状和大小的结构元素,以观察不同设置下的形态学操作效果

  1. /**
  2. * @file Morphology_2.cpp
  3. * @brief 封装了一些形态学变换的高级示例代码
  4. * @author OpenCV团队
  5. */
  6. #include "opencv2/imgproc.hpp" //包含了图像处理方面的函数和类
  7. #include "opencv2/imgcodecs.hpp" //包含了图像编码/解码方面的函数和类
  8. #include "opencv2/highgui.hpp" //包含了图像显示方面的函数和类
  9. #include <iostream> //标准输入输出库
  10. using namespace cv; //使用OpenCV的命名空间
  11. /// 全局变量
  12. Mat src, dst; //定义两个Mat类型的变量,src和dst,分别用来存储原图和处理后的图像
  13. int morph_elem = 0; //定义一个变量,用于切换形态学处理的内核类型
  14. int morph_size = 0; //定义一个变量,用于改变形态学处理的内核大小
  15. int morph_operator = 0; //定义一个变量,用于切换不同的形态学操作
  16. int const max_operator = 4; //定义操作类型的最大值
  17. int const max_elem = 2; //定义内核类型的最大值
  18. int const max_kernel_size = 21; //定义内核大小的最大值
  19. const char* window_name = "Morphology Transformations Demo"; //定义程序窗口的标题
  20. /** 函数声明 */
  21. void Morphology_Operations( int, void* );
  22. /**
  23. * @function main
  24. */
  25. int main( int argc, char** argv )
  26. {
  27. //![load]
  28. CommandLineParser parser( argc, argv, "{@input | baboon.jpg | 输入图片}" );
  29. //使用parser获取的参数,从文件加载图像到src,加载方式为色彩图像
  30. src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR );
  31. //如果原图为空,则输出错误信息并返回
  32. if (src.empty())
  33. {
  34. std::cout << "无法打开或找到图像!\n" << std::endl;
  35. std::cout << "使用方法: " << argv[0] << " <输入图像>" << std::endl;
  36. return EXIT_FAILURE;
  37. }
  38. //![load]
  39. //![window]
  40. namedWindow( window_name, WINDOW_AUTOSIZE ); //创建一个名为window_name的窗口,窗口大小自适应图像大小
  41. //![window]
  42. //![create_trackbar1]
  43. /// 创建滚动条来选择形态学操作类型
  44. createTrackbar("Operator:\n 0: 开运算 - 1: 闭运算 \n 2: 形态学梯度 - 3: 顶帽运算 \n 4: 黑帽运算", window_name, &morph_operator, max_operator, Morphology_Operations );
  45. //![create_trackbar1]
  46. //![create_trackbar2]
  47. /// 创建滚动条来选择内核类型
  48. createTrackbar( "Element:\n 0: 矩形 - 1: 十字形 - 2: 椭圆形", window_name,
  49. &morph_elem, max_elem,
  50. Morphology_Operations );
  51. //![create_trackbar2]
  52. //![create_trackbar3]
  53. /// 创建滚动条来选择内核大小
  54. createTrackbar( "Kernel size:\n 2n +1", window_name,
  55. &morph_size, max_kernel_size,
  56. Morphology_Operations );
  57. //![create_trackbar3]
  58. /// 默认开始
  59. Morphology_Operations( 0, 0 ); //调用形态学操作处理函数
  60. waitKey(0); //等待用户按键
  61. return 0; //程序正常退出
  62. }
  63. //![morphology_operations]
  64. /**
  65. * @function Morphology_Operations
  66. */
  67. void Morphology_Operations( int, void* )
  68. {
  69. // 在MORPH_X中:2,3,4,5和6
  70. //![operation]
  71. int operation = morph_operator + 2; //根据用户选择,确定形态学操作的类型
  72. //![operation]
  73. //构建指定类型和大小的内核元素
  74. Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );
  75. /// 应用指定的形态学操作
  76. morphologyEx( src, dst, operation, element ); //执行形态学操作
  77. imshow( window_name, dst ); //在窗口中显示处理后的图像
  78. }
  79. //![morphology_operations]

40a7fa1ce046594b5ee8f4099ca58a91.png

11.4 Threshold.cpp

25900e352f558b61fa917c55dd01af7f.png

1f2328359d1d2276055f93cf724ab6c7.png

代码主要实现了在给定图像上应用不同类型的阈值处理,并通过创建的滑动条手动选择阈值类型以及阈值大小,进而观察不同阈值类型和大小对图像处理效果的影响。阈值处理通常应用于图像分割、特征提取等操作,是计算机视觉领域的基本技术之一。

  1. /**
  2. * @file Threshold.cpp
  3. * @brief 这是一个示例代码,演示如何使用OpenCV提供的各种阈值策略
  4. * @author OpenCV团队
  5. */
  6. #include "opencv2/imgproc.hpp" // 引用OpenCV库处理图像处理部分
  7. #include "opencv2/imgcodecs.hpp" // 用于处理图像编码和解码带
  8. #include "opencv2/highgui.hpp" // 提供创建窗口,加载图像等与高级图形用户界面相关的功能
  9. #include <iostream> // 引用标准输入输出库
  10. using namespace cv; // 使用命名空间cv,方便代码中直接用OpenCV函数
  11. using std::cout; // 使用标准命名空间中的cout
  12. /// 全局变量
  13. int threshold_value = 0; // 阈值
  14. int threshold_type = 3; // 阈值类型
  15. int const max_value = 255; // 最大值常数
  16. int const max_type = 4; // 最大类型常数
  17. int const max_binary_value = 255; // 二值图像最大像素值
  18. Mat src, src_gray, dst; // 声明原图、灰度图和目标图像矩阵
  19. const char* window_name = "Threshold Demo"; // 窗口名称
  20. const char* trackbar_type = "Type: \n 0: Binary \n 1: Binary Inverted \n 2: Truncate \n 3: To Zero \n 4: To Zero Inverted";
  21. const char* trackbar_value = "Value"; // 滑动条上显示的提示文字
  22. //![阈值处理函数]
  23. /**
  24. * @function Threshold_Demo
  25. */
  26. static void Threshold_Demo( int, void* )
  27. {
  28. /* 0: Binary 二值阈值
  29. 1: Binary Inverted 反转二值阈值
  30. 2: Threshold Truncated 截断阈值
  31. 3: Threshold to Zero 零设置阈值
  32. 4: Threshold to Zero Inverted 反转零设置阈值
  33. */
  34. threshold( src_gray, dst, threshold_value, max_binary_value, threshold_type ); // 应用阈值进行图像分割
  35. imshow( window_name, dst ); // 显示处理后的图像
  36. }
  37. //![阈值处理函数]
  38. /**
  39. * @function main
  40. */
  41. int main( int argc, char** argv )
  42. {
  43. //! [load]
  44. String imageName("stuff.jpg"); // 默认图像文件名
  45. if (argc > 1)
  46. {
  47. imageName = argv[1]; // 如果有输入参数,第一个参数作为图像文件名
  48. }
  49. src = imread( samples::findFile( imageName ), IMREAD_COLOR ); // 读取图像文件
  50. if (src.empty())
  51. {
  52. cout << "Cannot read the image: " << imageName << std::endl; // 如果图像读取失败,打印错误信息
  53. return -1;
  54. }
  55. cvtColor( src, src_gray, COLOR_BGR2GRAY ); // 将图像转化为灰度图
  56. //! [load]
  57. //! [window]
  58. namedWindow( window_name, WINDOW_AUTOSIZE ); // 创建窗口用以显示结果
  59. //! [window]
  60. //! [trackbar]
  61. createTrackbar( trackbar_type,
  62. window_name, &threshold_type,
  63. max_type, Threshold_Demo ); // 创建滑动条以选择阈值类型
  64. createTrackbar( trackbar_value,
  65. window_name, &threshold_value,
  66. max_value, Threshold_Demo ); // 创建滑动条以选择阈值大小
  67. //! [trackbar]
  68. Threshold_Demo( 0, 0 ); // 初始化并调用阈值处理函数
  69. /// 等待用户操作结束程序
  70. waitKey();
  71. return 0;
  72. }

3268b4219a3722e2b8b04a57e8f35ad9.png

78053c8cb1f69361f0098b90278ed48b.png

如何确定使用哪种阈值处理方式来处理图像?

ca13ce266b1a87c6a656562342a0a240.png

阈值处理是图像处理中的哪一步骤?

223f2e0deaa684ac46efab247eabd672.png

阈值处理可以在哪些应用场景中使用?

9274685b431d8df178a1a08e37fdbe6d.png

阈值处理如何在目标检测中应用?

cc535b5fbe69ebf46f2d4937c9a820c3.png

11.5 Threshold_inRange.cpp

0e7867e852e6b16b27beb1efe602838d.png

这段代码的功能是通过设定HSV色彩空间的阈值来检测颜色,并利用滑动条可实时调整阈值,用以监测摄像头(或者视频)中特定颜色的物体,用于实时颜色追踪的应用。

ffce378e85e3bf0b25f2cf8e9f762fd3.png

  1. #include "opencv2/imgproc.hpp" // 引入opencv图像处理库
  2. #include "opencv2/highgui.hpp" // 引入opencv高级GUI库
  3. #include "opencv2/videoio.hpp" // 引入opencv的视频输入输出库
  4. #include <iostream> // 引入标准输入输出库
  5. using namespace cv; // 使用命名空间cv,opencv核心库中的所有函数和方法都被定义在cv的命名空间下(包含图像处理、数据结构等主要模块)
  6. /** 全局变量 */
  7. const int max_value_H = 360/2; // 定义 HSV 色调的最大值
  8. const int max_value = 255; // 定义 HSV 饱和度和亮度的最大值
  9. const String window_capture_name = "Video Capture"; // 定义视频捕获窗口的名称
  10. const String window_detection_name = "Object Detection"; // 定义物体检测窗口的名称
  11. int low_H = 0, low_S = 0, low_V = 0; // 定义 HSV 颜色空间分别对应色调、饱和度和亮度的最低阈值
  12. int high_H = max_value_H, high_S = max_value, high_V = max_value; // 定义 HSV 颜色空间分别对应色调、饱和度和亮度的最高阈值
  13. //! [low]
  14. static void on_low_H_thresh_trackbar(int, void *) // 当滑动条的值改变时,调用此回调函数,更新低阈值
  15. {
  16. low_H = min(high_H-1, low_H); // 确保低阈值总是小于高阈值
  17. setTrackbarPos("Low H", window_detection_name, low_H); // 设置滑动条的位置
  18. }
  19. //! [low]
  20. //! [high]
  21. static void on_high_H_thresh_trackbar(int, void *) // 当滑动条的值改变时,调用此回调函数,更新高阈值
  22. {
  23. high_H = max(high_H, low_H+1); // 确保高阈值总是大于低阈值
  24. setTrackbarPos("High H", window_detection_name, high_H); // 设置滑动条的位置
  25. }
  26. static void on_low_S_thresh_trackbar(int, void *) // 更新饱和度的低阈值
  27. {
  28. low_S = min(high_S-1, low_S);
  29. setTrackbarPos("Low S", window_detection_name, low_S);
  30. }
  31. static void on_high_S_thresh_trackbar(int, void *) // 更新饱和度的高阈值
  32. {
  33. high_S = max(high_S, low_S+1);
  34. setTrackbarPos("High S", window_detection_name, high_S);
  35. }
  36. static void on_low_V_thresh_trackbar(int, void *) // 更新亮度的低阈值
  37. {
  38. low_V = min(high_V-1, low_V);
  39. setTrackbarPos("Low V", window_detection_name, low_V);
  40. }
  41. static void on_high_V_thresh_trackbar(int, void *) // 更新亮度的高阈值
  42. {
  43. high_V = max(high_V, low_V+1);
  44. setTrackbarPos("High V", window_detection_name, high_V);
  45. }
  46. int main(int argc, char* argv[]) // 主函数
  47. {
  48. //! [cap]
  49. VideoCapture cap(argc > 1 ? atoi(argv[1]) : 0); // 创建视频捕获对象,如果程序有参数,使用参数作为视频源,否则使用默认摄像头
  50. //! [cap]
  51. //! [window]
  52. namedWindow(window_capture_name); // 创建视频捕获窗口
  53. namedWindow(window_detection_name); // 创建物体检测窗口
  54. //! [window]
  55. //! [trackbar]
  56. // 创建滑动条,并设置HSV值的阈值
  57. createTrackbar("Low H", window_detection_name, &low_H, max_value_H, on_low_H_thresh_trackbar);
  58. createTrackbar("High H", window_detection_name, &high_H, max_value_H, on_high_H_thresh_trackbar);
  59. createTrackbar("Low S", window_detection_name, &low_S, max_value, on_low_S_thresh_trackbar);
  60. createTrackbar("High S", window_detection_name, &high_S, max_value, on_high_S_thresh_trackbar);
  61. createTrackbar("Low V", window_detection_name, &low_V, max_value, on_low_V_thresh_trackbar);
  62. createTrackbar("High V", window_detection_name, &high_V, max_value, on_high_V_thresh_trackbar);
  63. //! [trackbar]
  64. Mat frame, frame_HSV, frame_threshold; // 定义三个Mat对象,用于存储原始帧,HSV色彩空间帧和阈值帧
  65. while (true) { // 循环处理每一帧
  66. //! [while]
  67. cap >> frame; // 读取一帧
  68. if(frame.empty()) // 如果帧为空说明视频已经结束
  69. {
  70. break;
  71. }
  72. cvtColor(frame, frame_HSV, COLOR_BGR2HSV); // 将原始帧从BGR色彩空间转换到HSV色彩空间
  73. inRange(frame_HSV, Scalar(low_H, low_S, low_V), Scalar(high_H, high_S, high_V), frame_threshold); // 通过HSV阈值,对帧进行二值化处理
  74. //! [while]
  75. //! [show]
  76. imshow(window_capture_name, frame); // 显示原始帧
  77. imshow(window_detection_name, frame_threshold); // 显示二值化处理后的帧
  78. //! [show]
  79. char key = (char) waitKey(30); // 等待键盘输入,如果按下'q'或者ESC键,跳出循环
  80. if (key == 'q' || key == 27)
  81. {
  82. break;
  83. }
  84. }
  85. return 0;
  86. }

c36ce1acf2570de9853fbf9389d2c8cb.png

cde7b557ad698131256231fedb1dfb15.png

Threshold_Tutorial_Theory_Base_Figure

7d61eb92741a6549334371780f3128cd.png

Threshold_Tutorial_Theory_Binary.png

e29378910c922b8c7d7f6b4586e6d99c.png

Threshold_Tutorial_Theory_Binary_Inverted.png

a6a54371e339734b87f6d977b6ae1321.png

Threshold_Tutorial_Theory_Truncate.png

ab7e109ffb35a3961ed7141538ee7fd2.png

Threshold_Tutorial_Theory_Zero.png

f4bb698ac613ca5f9f80b11101da9cf7.png

Threshold_Tutorial_Theory_Zero_Inverted.png

bcd48729a6cda73941f01860770bb4fd.png

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/344984
推荐阅读
相关标签
  

闽ICP备14008679号