【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. 程序挂起等待用户输入,一旦有输入,程序结束。

/**
 * @file BasicLinearTransforms.cpp
 * @brief 简单的程序用于调整图像的对比度和亮度
 * @author OpenCV团队
 */


// 引入头文件
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp" //<-- 用于图像显示
#include <iostream> //<-- C++标准库中的输入/输出头文件


// 在此处,我们没有在全局范围内使用 "using namespace std;",是为了避免在 C++17 中 beta 变量与 std::beta产生冲突
using std::cin;
using std::cout;
using std::endl;
using namespace cv;    // <-- 使用OpenCV中被封装在cv::下的所有的类和函数




/**
 * @function main
 * @brief 主函数
 */
int main( int argc, char** argv )
{
    /// 读取用户输入的图像文件
    //! [basic-linear-transform-load]
    CommandLineParser parser( argc, argv, "{@input | lena.jpg | input image}" );
    Mat image = imread( samples::findFile( parser.get<String>( "@input" ) ) );
    if( image.empty() )  //<-- 判断图像是否读取成功
    {
      // 如果图像未被成功读取,则显示错误信息并返回-1,程序结束
      cout << "Could not open or find the image!\n" << endl;
      cout << "Usage: " << argv[0] << " <Input image>" << endl;
      return -1;
    }
    //! [basic-linear-transform-load]


    // 创建新的图像,准备存放调整对比度和亮度之后的图像
    //! [basic-linear-transform-output]
    Mat new_image = Mat::zeros( image.size(), image.type() );
    //! [basic-linear-transform-output]


    // 定义调整对比度和亮度的参数
    //! [basic-linear-transform-parameters]
    double alpha = 1.0; /*< 用于控制对比度的简单参数 */
    int beta = 0;       /*< 用于控制亮度的简单参数 */


    /// 初始化值
    cout << " Basic Linear Transforms " << endl;
    cout << "-------------------------" << endl;
    cout << "* Enter the alpha value [1.0-3.0]: "; cin >> alpha;  //<-- 从控制台获取alpha值
    cout << "* Enter the beta value [0-100]: ";    cin >> beta;   //<-- 从控制台获取beta值
    //! [basic-linear-transform-parameters]


    /// 执行操作 new_image(i,j) = alpha*image(i,j) + beta
    /// 我们本可以简单的使用函数:image.convertTo(new_image, -1, alpha, beta); 来实现上述操作,
    /// 但我们希望通过这个例子,展示出如何访问像素 :)
    //! [basic-linear-transform-operation]
    for( int y = 0; y < image.rows; y++ ) {      //<-- 遍历图像的所有行
        for( int x = 0; x < image.cols; x++ ) {  //<-- 遍历图像的所有列
            for( int c = 0; c < image.channels(); c++ ) {  //<-- 遍历图像的每个通道
                new_image.at<Vec3b>(y,x)[c] =
                  saturate_cast<uchar>( alpha*image.at<Vec3b>(y,x)[c] + beta ); //<-- 对原图像的每一个像素值执行操作调整对比度和亮度,saturate_cast<uchar>是为了确保计算结果落在uchar的取值范围
            }
        }
    }
    //! [basic-linear-transform-operation]


    // 展示图像
    //! [basic-linear-transform-display]
    imshow("Original Image", image);  // 显示原始图像
    imshow("New Image", new_image);   // 显示调整后的图像


    // 等待用户按键,这样做是为了防止程序运行完后立即退出,无法显示图像
    waitKey();
    //! [basic-linear-transform-display]
    return 0;  //正常结束程序
}

637d9aafcdbe579f7331ede9bc76721c.png

11.2 Morphology_1.cpp

13ec57550ae6ba55dff6a7f5cba8d8b5.png

f5994fd2d6b3ba464ef0fe7e5f2d7302.png

9a7d60ff015c6d51d9874d544181c978.png

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

/**
 * @file Morphology_1.cpp
 * @brief Erosion and Dilation sample code 腐蚀与膨胀的样例代码
 * @author OpenCV team
 */


#include "opencv2/imgproc.hpp"  //包含OpenCV库头文件
#include "opencv2/highgui.hpp"  //包含OpenCV高级用户界面的头文件
#include <iostream>


using namespace cv;
using namespace std;


/// Global variables 定义全局变量
Mat src, erosion_dst, dilation_dst;


int erosion_elem = 0;
int erosion_size = 0;
int dilation_elem = 0;
int dilation_size = 0;
int const max_elem = 2;
int const max_kernel_size = 21;


 /** Function Headers 函数的声明(函数的原型) */
void Erosion( int, void* ); // 腐蚀函数
void Dilation( int, void* ); // 膨胀函数


/**
 * @function main 主函数
 */
int main( int argc, char** argv )
{
  /// Load an image,    加载图片
  CommandLineParser parser( argc, argv, "{@input | LinuxLogo.jpg | input image}" );
  src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR );
  if( src.empty() )
  {
    cout << "Could not open or find the image!\n" << endl;   //若图片为空,打印错误信息
    cout << "Usage: " << argv[0] << " <Input image>" << endl;
    return -1;
  }


  /// Create windows   创建窗口
  namedWindow( "Erosion Demo", WINDOW_AUTOSIZE );
  namedWindow( "Dilation Demo", WINDOW_AUTOSIZE );
  moveWindow( "Dilation Demo", src.cols, 0 );


  /// Create Erosion Trackbar 创建腐蚀的滑动条
  createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Erosion Demo",
          &erosion_elem, max_elem,
          Erosion );


  createTrackbar( "Kernel size:\n 2n +1", "Erosion Demo",
          &erosion_size, max_kernel_size,
          Erosion );


  /// Create Dilation Trackbar 创建膨胀的滑动条
  createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Dilation Demo",
          &dilation_elem, max_elem,
          Dilation );


  createTrackbar( "Kernel size:\n 2n +1", "Dilation Demo",
          &dilation_size, max_kernel_size,
          Dilation );


  /// Default start 默认开始
  Erosion( 0, 0 );
  Dilation( 0, 0 );


  waitKey(0);  //等待用户按键操作
  return 0;
}


/**
 * @function Erosion 腐蚀函数
 */
void Erosion( int, void* )
{
  int erosion_type = 0;
  if( erosion_elem == 0 ){ erosion_type = MORPH_RECT; }   //如果元素类型为0,腐蚀的类型为MORPH_RECT
  else if( erosion_elem == 1 ){ erosion_type = MORPH_CROSS; } //如果元素类型为1,腐蚀的类型为MORPH_CROSS
  else if( erosion_elem == 2) { erosion_type = MORPH_ELLIPSE; } //如果元素类型为2,腐蚀的类型为MORPH_ELLIPSE


  //![kernel]
  Mat element = getStructuringElement( erosion_type,          //生成预定义形状的结构元素
                       Size( 2*erosion_size + 1, 2*erosion_size+1 ),
                       Point( erosion_size, erosion_size ) );
  //![kernel]


  /// Apply the erosion operation   进行腐蚀操作
  erode( src, erosion_dst, element );
  imshow( "Erosion Demo", erosion_dst );   //显示腐蚀的效果
}


/**
 * @function Dilation 膨胀函数
 */
void Dilation( int, void* )
{
  int dilation_type = 0;
  if( dilation_elem == 0 ){ dilation_type = MORPH_RECT; }   //如果元素类型为0,膨胀的类型为MORPH_RECT
  else if( dilation_elem == 1 ){ dilation_type = MORPH_CROSS; } //如果元素类型为1,膨胀的类型为MORPH_CROSS
  else if( dilation_elem == 2) { dilation_type = MORPH_ELLIPSE; } //如果元素类型为2,膨胀的类型为MORPH_ELLIPSE


  Mat element = getStructuringElement( dilation_type,   //生成预定义形状的结构元素
                       Size( 2*dilation_size + 1, 2*dilation_size+1 ),
                       Point( dilation_size, dilation_size ) );


  /// Apply the dilation operation  进行膨胀操作
  dilate( src, dilation_dst, element );
  imshow( "Dilation Demo", dilation_dst );  //显示膨胀的效果
}

e0ebadb1a27274c4b329a313c5a8d96b.png

11.3 Morphology_2.cpp

5e521e18e9043bc31a2daaef8148f3a3.png

e5689583dd574c9839b98a507cbfc378.png

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

/**
 * @file Morphology_2.cpp
 * @brief 封装了一些形态学变换的高级示例代码
 * @author OpenCV团队
 */


#include "opencv2/imgproc.hpp" //包含了图像处理方面的函数和类
#include "opencv2/imgcodecs.hpp" //包含了图像编码/解码方面的函数和类
#include "opencv2/highgui.hpp" //包含了图像显示方面的函数和类
#include <iostream> //标准输入输出库


using namespace cv; //使用OpenCV的命名空间


/// 全局变量
Mat src, dst; //定义两个Mat类型的变量,src和dst,分别用来存储原图和处理后的图像


int morph_elem = 0; //定义一个变量,用于切换形态学处理的内核类型
int morph_size = 0; //定义一个变量,用于改变形态学处理的内核大小
int morph_operator = 0; //定义一个变量,用于切换不同的形态学操作
int const max_operator = 4; //定义操作类型的最大值
int const max_elem = 2; //定义内核类型的最大值
int const max_kernel_size = 21; //定义内核大小的最大值


const char* window_name = "Morphology Transformations Demo"; //定义程序窗口的标题


/** 函数声明 */
void Morphology_Operations( int, void* );


/**
 * @function main
 */
int main( int argc, char** argv )
{
  //![load]
  CommandLineParser parser( argc, argv, "{@input | baboon.jpg | 输入图片}" );
  
  //使用parser获取的参数,从文件加载图像到src,加载方式为色彩图像
  src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR ); 
  
  //如果原图为空,则输出错误信息并返回
  if (src.empty())
  {
    std::cout << "无法打开或找到图像!\n" << std::endl;
    std::cout << "使用方法: " << argv[0] << " <输入图像>" << std::endl;
    return EXIT_FAILURE;
  }
  //![load]


  //![window]
  namedWindow( window_name, WINDOW_AUTOSIZE ); //创建一个名为window_name的窗口,窗口大小自适应图像大小
  //![window]


  //![create_trackbar1]
  /// 创建滚动条来选择形态学操作类型
  createTrackbar("Operator:\n 0: 开运算 - 1: 闭运算  \n 2: 形态学梯度 - 3: 顶帽运算 \n 4: 黑帽运算", window_name, &morph_operator, max_operator, Morphology_Operations );
  //![create_trackbar1]


  //![create_trackbar2]
  /// 创建滚动条来选择内核类型
  createTrackbar( "Element:\n 0: 矩形 - 1: 十字形 - 2: 椭圆形", window_name,
                  &morph_elem, max_elem,
                  Morphology_Operations );
  //![create_trackbar2]


  //![create_trackbar3]
  /// 创建滚动条来选择内核大小
  createTrackbar( "Kernel size:\n 2n +1", window_name,
                  &morph_size, max_kernel_size,
                  Morphology_Operations );
  //![create_trackbar3]


  /// 默认开始
  Morphology_Operations( 0, 0 ); //调用形态学操作处理函数


  waitKey(0); //等待用户按键
  return 0; //程序正常退出
}


//![morphology_operations]
/**
 * @function Morphology_Operations
 */
void Morphology_Operations( int, void* )
{
  // 在MORPH_X中:2,3,4,5和6
  //![operation]
  int operation = morph_operator + 2; //根据用户选择,确定形态学操作的类型
  //![operation]


  //构建指定类型和大小的内核元素
  Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );


  /// 应用指定的形态学操作
  morphologyEx( src, dst, operation, element ); //执行形态学操作
  imshow( window_name, dst );  //在窗口中显示处理后的图像
}
//![morphology_operations]

40a7fa1ce046594b5ee8f4099ca58a91.png

11.4 Threshold.cpp

25900e352f558b61fa917c55dd01af7f.png

1f2328359d1d2276055f93cf724ab6c7.png

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

/**
 * @file Threshold.cpp
 * @brief 这是一个示例代码,演示如何使用OpenCV提供的各种阈值策略
 * @author OpenCV团队
 */


#include "opencv2/imgproc.hpp" // 引用OpenCV库处理图像处理部分
#include "opencv2/imgcodecs.hpp" // 用于处理图像编码和解码带
#include "opencv2/highgui.hpp" // 提供创建窗口,加载图像等与高级图形用户界面相关的功能
#include <iostream> // 引用标准输入输出库


using namespace cv; // 使用命名空间cv,方便代码中直接用OpenCV函数
using std::cout; // 使用标准命名空间中的cout


/// 全局变量
int threshold_value = 0; // 阈值
int threshold_type = 3; // 阈值类型
int const max_value = 255; // 最大值常数
int const max_type = 4;  // 最大类型常数
int const max_binary_value = 255; // 二值图像最大像素值


Mat src, src_gray, dst; // 声明原图、灰度图和目标图像矩阵
const char* window_name = "Threshold Demo"; // 窗口名称


const char* trackbar_type = "Type: \n 0: Binary \n 1: Binary Inverted \n 2: Truncate \n 3: To Zero \n 4: To Zero Inverted";
const char* trackbar_value = "Value"; // 滑动条上显示的提示文字


//![阈值处理函数]
/**
 * @function Threshold_Demo
 */
static void Threshold_Demo( int, void* )
{
    /* 0: Binary 二值阈值
     1: Binary Inverted 反转二值阈值
     2: Threshold Truncated 截断阈值
     3: Threshold to Zero 零设置阈值
     4: Threshold to Zero Inverted 反转零设置阈值
    */
    threshold( src_gray, dst, threshold_value, max_binary_value, threshold_type ); // 应用阈值进行图像分割
    imshow( window_name, dst ); // 显示处理后的图像
}
//![阈值处理函数]


/**
 * @function main
 */
int main( int argc, char** argv )
{
    //! [load]
    String imageName("stuff.jpg"); // 默认图像文件名
    if (argc > 1)
    {
        imageName = argv[1]; // 如果有输入参数,第一个参数作为图像文件名
    }
    src = imread( samples::findFile( imageName ), IMREAD_COLOR ); // 读取图像文件


    if (src.empty())
    {
        cout << "Cannot read the image: " << imageName << std::endl; // 如果图像读取失败,打印错误信息
        return -1;
    }


    cvtColor( src, src_gray, COLOR_BGR2GRAY ); // 将图像转化为灰度图
    //! [load]


    //! [window]
    namedWindow( window_name, WINDOW_AUTOSIZE ); // 创建窗口用以显示结果
    //! [window]


    //! [trackbar]
    createTrackbar( trackbar_type,
                    window_name, &threshold_type,
                    max_type, Threshold_Demo ); // 创建滑动条以选择阈值类型


    createTrackbar( trackbar_value,
                    window_name, &threshold_value,
                    max_value, Threshold_Demo ); // 创建滑动条以选择阈值大小
    //! [trackbar]


    Threshold_Demo( 0, 0 ); // 初始化并调用阈值处理函数


    /// 等待用户操作结束程序
    waitKey();
    return 0;
}

3268b4219a3722e2b8b04a57e8f35ad9.png

78053c8cb1f69361f0098b90278ed48b.png

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

ca13ce266b1a87c6a656562342a0a240.png

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

223f2e0deaa684ac46efab247eabd672.png

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

9274685b431d8df178a1a08e37fdbe6d.png

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

cc535b5fbe69ebf46f2d4937c9a820c3.png

11.5 Threshold_inRange.cpp

0e7867e852e6b16b27beb1efe602838d.png

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

ffce378e85e3bf0b25f2cf8e9f762fd3.png

#include "opencv2/imgproc.hpp" // 引入opencv图像处理库
#include "opencv2/highgui.hpp" // 引入opencv高级GUI库
#include "opencv2/videoio.hpp" // 引入opencv的视频输入输出库
#include <iostream> // 引入标准输入输出库


using namespace cv; // 使用命名空间cv,opencv核心库中的所有函数和方法都被定义在cv的命名空间下(包含图像处理、数据结构等主要模块)


/** 全局变量 */
const int max_value_H = 360/2; // 定义 HSV 色调的最大值
const int max_value = 255; // 定义 HSV 饱和度和亮度的最大值
const String window_capture_name = "Video Capture"; // 定义视频捕获窗口的名称
const String window_detection_name = "Object Detection"; // 定义物体检测窗口的名称
int low_H = 0, low_S = 0, low_V = 0; // 定义 HSV 颜色空间分别对应色调、饱和度和亮度的最低阈值
int high_H = max_value_H, high_S = max_value, high_V = max_value; // 定义 HSV 颜色空间分别对应色调、饱和度和亮度的最高阈值


//! [low]
static void on_low_H_thresh_trackbar(int, void *) // 当滑动条的值改变时,调用此回调函数,更新低阈值
{
    low_H = min(high_H-1, low_H); // 确保低阈值总是小于高阈值
    setTrackbarPos("Low H", window_detection_name, low_H); // 设置滑动条的位置
}
//! [low]


//! [high]
static void on_high_H_thresh_trackbar(int, void *) // 当滑动条的值改变时,调用此回调函数,更新高阈值
{
    high_H = max(high_H, low_H+1); // 确保高阈值总是大于低阈值
    setTrackbarPos("High H", window_detection_name, high_H); // 设置滑动条的位置
}


static void on_low_S_thresh_trackbar(int, void *) // 更新饱和度的低阈值
{
    low_S = min(high_S-1, low_S);
    setTrackbarPos("Low S", window_detection_name, low_S);
}


static void on_high_S_thresh_trackbar(int, void *) // 更新饱和度的高阈值
{
    high_S = max(high_S, low_S+1);
    setTrackbarPos("High S", window_detection_name, high_S);
}


static void on_low_V_thresh_trackbar(int, void *) // 更新亮度的低阈值
{
    low_V = min(high_V-1, low_V);
    setTrackbarPos("Low V", window_detection_name, low_V);
}


static void on_high_V_thresh_trackbar(int, void *) // 更新亮度的高阈值
{
    high_V = max(high_V, low_V+1);
    setTrackbarPos("High V", window_detection_name, high_V);
}


int main(int argc, char* argv[]) // 主函数
{
    //! [cap]
    VideoCapture cap(argc > 1 ? atoi(argv[1]) : 0); // 创建视频捕获对象,如果程序有参数,使用参数作为视频源,否则使用默认摄像头
    //! [cap]


    //! [window]
    namedWindow(window_capture_name); // 创建视频捕获窗口
    namedWindow(window_detection_name); // 创建物体检测窗口
    //! [window]


    //! [trackbar]
    // 创建滑动条,并设置HSV值的阈值
    createTrackbar("Low H", window_detection_name, &low_H, max_value_H, on_low_H_thresh_trackbar);
    createTrackbar("High H", window_detection_name, &high_H, max_value_H, on_high_H_thresh_trackbar);
    createTrackbar("Low S", window_detection_name, &low_S, max_value, on_low_S_thresh_trackbar);
    createTrackbar("High S", window_detection_name, &high_S, max_value, on_high_S_thresh_trackbar);
    createTrackbar("Low V", window_detection_name, &low_V, max_value, on_low_V_thresh_trackbar);
    createTrackbar("High V", window_detection_name, &high_V, max_value, on_high_V_thresh_trackbar);
    //! [trackbar]


    Mat frame, frame_HSV, frame_threshold; // 定义三个Mat对象,用于存储原始帧,HSV色彩空间帧和阈值帧
    while (true) { // 循环处理每一帧
        //! [while]
        cap >> frame; // 读取一帧
        if(frame.empty()) // 如果帧为空说明视频已经结束
        {
            break;
        }


        cvtColor(frame, frame_HSV, COLOR_BGR2HSV); // 将原始帧从BGR色彩空间转换到HSV色彩空间
        inRange(frame_HSV, Scalar(low_H, low_S, low_V), Scalar(high_H, high_S, high_V), frame_threshold); // 通过HSV阈值,对帧进行二值化处理
        //! [while]


        //! [show]
        imshow(window_capture_name, frame); // 显示原始帧
        imshow(window_detection_name, frame_threshold); // 显示二值化处理后的帧
        //! [show]


        char key = (char) waitKey(30); // 等待键盘输入,如果按下'q'或者ESC键,跳出循环
        if (key == 'q' || key == 27)
        {
            break;
        }
    }
    return 0;
}

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

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值