Opencv-C++笔记 (11) : opencv-图像二值化与LUB查找表

2023-10-30

一、概述

我们在上一节程序中生成了一张只有黑色和白色的图像,这种“非黑即白”的图像像素的灰度值无论在什么数据类型中只有最大值和最小值两种取值,因此称其为二值图像。二值图像色彩种类少,可以进行高度的压缩,节省存储空间,将非二值图像经过计算变成二值图像的过程称为图像的二值化。在OpenCV4中提供了threshold()和adaptiveThreshold()两个函数用于实现图像的二值化,我们首先介绍threshold()函数的使用方法,该函数的函数原型在代码清单3-17中给出。

double cv::threshold(InputArray src,OutputArray dst,double  thresh,double  maxval,int  type)
  • src:待二值化的图像,图像只能是CV_8U和CV_32F两种数据类型。对于图像通道数目的要求和选择的二值化方法相关。
  • dst:二值化后的图像,与输入图像具有相同的尺寸、数据类型和通道数。
  • thresh:二值化的阈值。
  • maxval:二值化过程的最大值,此函数只在THRESH_BINARY和THRESH_BINARY_INV两种二值化方法中才使用,但是在使用其他方法是也需要输入。
  • type:选择图像二值化方法的标志。

该函数是众多二值化方法的集成,所有的方法都实现了一个功能,就是给定一个阈值,计算所有像素灰度值与这个阈值关系,得到最终的比较结果。函数中有些阈值比较方法输出结果的灰度值并不是二值的,而是具有一个取值范围,不过为了体现其最常用的功能,我们仍然称其为二值化函数或者阈值比较函数。函数的部分参数和返回值都是针对特定的算法才有用,但是即使不使用这些算法在使用函数时也需要明确的给出,不可缺省。函数的最后一个参数是选择二值化计算方法的标志,可以选择二值化方法以及控制哪些参数对函数的计算结果产生影响,该标志可以选择的范围及含义在表3-2中给出。
在这里插入图片描述

接下来将详细的介绍每种标志对应的二值化原理和需要的参数。

二、THRESH_BINARY和THRESH_BINARY_INV

这两个标志是相反的二值化方法,THRESH_BINARY是将灰度值与阈值(第三个参数thresh)进行比较,如果灰度值大于阈值就将灰度值改为函数中第四个参数maxval的值,否则将灰度值改成0。THRESH_BINARY_INV标志正好与这个过程相反,如果灰度值大于阈值就将灰度值改为0,否则将灰度值改为maxval的值。这两种标志的计算公式在式(3.7)中给出。
在这里插入图片描述

三、THRESH_TRUNC

这个标志相当于重新给图像的灰度值设定一个新的最大值,将大于新的最大值的灰度值全部重新设置为新的最大值,具体逻辑为将灰度值与阈值thresh进行比较,如果灰度值大于thresh则将灰度值改为thresh,否则保持灰度值不变。这种方法没有使用到函数中的第四个参数maxval的值,因此maxval的值对本方法不产生影响。这种标志的计算公式在式(3.8)中给出。

在这里插入图片描述

四、THRESH_TOZERO和THRESH_TOZERO_INV

这两个标志是相反的阈值比较方法, THRESH_TOZERO表示将灰度值与阈值thresh进行比较,如果灰度值大于thresh则将保持不变,否则将灰度值改为0。THRESH_TOZERO_INV方法与其相反,将灰度值与阈值thresh进行比较,如果灰度值小于等于thresh则将保持不变,否则将灰度值改为0。这种两种方法都没有使用到函数中的第四个参数maxval的值,因此maxval的值对本方法不产生影响。这两个标志的计算公式在式(3.9)中给出。
在这里插入图片描述
前面五种标志都支持输入多通道的图像,在计算时分别对每个通道进行阈值比较。为了更加直观的理解上述阈值比较方法,我们假设图像灰度值是连续变化的信号,将阈值比较方法比做滤波器,绘制连续信号通过滤波器后的信号形状,结果如图3-14所示,图中红线为设置的阈值,黑线为原始信号通过滤波器后的信号形状。
在这里插入图片描述

五、THRESH_OTSU和THRESH_TRIANGLE

这两种标志是获取阈值的方法,并不是阈值的比较方法的标志,这两个标志可以和前面5种标志一起使用,例如“THRESH_BINARY|THRESH_OTSU”。前面5种标志在调用函数时都需要人为的设置阈值,如果对图像不了解设置的阈值不合理,会对处理后的效果造成严重的影响,这两个标志分别表示利用**大津法(OTSU)和三角形法(TRIANGLE)**结合图像灰度值分布特性获取二值化的阈值,并将阈值以函数返回值的形式给出。因此如果函数最后一个参数设置了这两个标志中的任何一个,那么函数第三个参数thresh将由系统自动给出,但是在调用函数的时候仍然不能缺省,只是程序不会使用这个数值。需要注意的是,目前为止OpenCV
4中针对这两个标志只支持输入CV_8UC1类型的图像。

threshold()函数全局只使用一个阈值,在实际情况中由于光照不均匀以及阴影的存在,全局只有一个阈值会使得在阴影处的白色区域也会被函数二值化成黑色,因此adaptiveThreshold()函数提供了两种局部自适应阈值的二值化方法,该函数的函数原型在代码清单3-18中给出。

void cv::adaptiveThreshold(InputArray src,
                                 OutputArray dst,
                                double  maxValue,
                                int  adaptiveMethod,
                                 int  thresholdType,
                                 int  blockSize,
                                double   C
                                  )
  • src:待二值化的图像,图像只能是CV_8UC1数据类型。
  • dst:二值化后的图像,与输入图像具有相同的尺寸、数据类型。
  • maxValue:二值化的最大值。
  • adaptiveMethod:自制应确定阈值的方法,分为均值法ADAPTIVE_THRESH_MEAN_C和高斯法ADAPTIVE_THRESH_GAUSSIAN_C这两种。
  • thresholdType:选择图像二值化方法的标志,只能是THRESH_BINARY和THRESH_BINARY_INV。
  • blockSize:自适应确定阈值的像素邻域大小,一般为3,5,7的奇数。 C:从平均值或者加权平均值中减去的常数,可以为正,也可以为负。

该函数将灰度图像转换成二值图像,通过均值法和高斯法自适应的计算blockSize* blockSize邻域内的阈值,之后进行二值化,其原理与前面的相同,这里就不再赘述。

为了直观的体会到图像二值化的效果,在代码清单3-19中给出了分别对彩色图像和灰度图像进行二值化的示例程序

#include<iostream>
#include<vector>
#include<string>
#include <opencv2/opencv.hpp>
#include "opencv/highgui.h"

using namespace std;
using namespace cv;

int main(int argc,char** argv) {
    cout<<"OpenCv Version: "<<CV_VERSION<<endl;
    Mat img=imread("699342568.jpg");
    if(img.empty()){
        cout<<"请确认输入的图片的路径是否正确"<<endl;
        return -1;
    }

    Mat gray;
    cvtColor(img,gray,COLOR_BGR2GRAY);
    Mat img_B,img_B_V,gray_B,gray_B_V,gray_T,gray_T_V,gray_TRUNC;

    //彩色图像二值化
    threshold(img,img_B,125,255,THRESH_BINARY);
    threshold(img,img_B_V,125,255,THRESH_BINARY_INV);
    imshow("img_B",img_B);
    imshow("img_B_V",img_B_V);

    //灰度图像二值化
    threshold(gray,gray_B,125,255,THRESH_BINARY);
    threshold(gray,gray_B_V,125,255,THRESH_BINARY_INV);
    imshow("gray_B",gray_B);
    imshow("gray_B_V",gray_B_V);

    //灰度图像TOZERO变换
    threshold(gray,gray_T,125,255,THRESH_TOZERO);
    threshold(gray,gray_T_V,125,255,THRESH_TOZERO_INV);
    imshow("gray_T",gray_T);
    imshow("gray_T_V",gray_T_V);

    //灰度图像TRUNC变换
    threshold(gray,gray_TRUNC,125,255,THRESH_TRUNC);
    imshow("gray_TRUNC",gray_TRUNC);

    //灰度图像大津法和三角法二值化
    Mat img_Thr=imread("threshold.jpg",IMREAD_GRAYSCALE);
    Mat img_Thr_0,img_Thr_T;
    threshold(img_Thr,img_Thr_0,100,255,THRESH_BINARY|THRESH_OTSU);
    threshold(img_Thr,img_Thr_T,125,255,THRESH_BINARY|THRESH_TRIANGLE);
    imshow("img_Thr",img_Thr);
    imshow("img_Thr_0",img_Thr_0);
    imshow("img_Thr_T",img_Thr_T);

    //灰度图像自适应二值化
    Mat adaptive_mean,adaptive_gauss;
    adaptiveThreshold(img_Thr,adaptive_mean,255,ADAPTIVE_THRESH_MEAN_C,THRESH_BINARY,55,0);
    adaptiveThreshold(img_Thr,adaptive_gauss,255,ADAPTIVE_THRESH_GAUSSIAN_C,THRESH_BINARY,55,0);

    imshow("adaptive_mean",adaptive_mean);
    imshow("adaptive_gauss",adaptive_gauss);
    waitKey(0);
    return 0;
}

在这里插入图片描述

六、LUT查找表

阈值比较方法中只有一个阈值,如果需要与多个阈值进行比较,就需要用到显示查找表(Look-Up-Table,LUT)。LUT查找表简单来说就是一个像素灰度值的映射表,它以像素灰度值作为索引,以灰度值映射后的数值作为表中的内容。例如我们有一个长度为5的存放字符的数组,LUT查找表就是通过这个数组将0映射成a,将1映射成b,依次类推,其映射关系为。在OpenCV
4中提供了LUT()函数用于实现图像像素灰度值的LUT查找表功能,在代码清单3-20中给出了该函数的原型。

 void cv::LUT(InputArray src,
                   InputArray lut,
                   OutputArray dst
                  )
  • src:输入图像矩阵,其数据类型只能是CV_8U。

  • lut:256个像素灰度值的查找表,单通道或者与src通道数相同。

-dst:输出图像矩阵,其尺寸与src相同,数据类型与lut相同。

该函数的第一个输入参数要求的数据类型必须是CV_8U类型,但是可以是多通道的图像矩阵。第二个参数根据其参数说明可以知道输入量是一个1×256的矩阵,其中存放着每个像素灰度值映射后的数值,其形式如图3-17所示。如果第二个参数是单通道,则输入变量中的每个通道都按照一个LUT查找表进行映射;如果第二个参数是多通道,则输入变量中的第i个通道按照第二个参数的第i个通道LUT查找表进行映射。与之前的函数不同,函数输出图像的数据类型不与原图像的数据类型保持一致,而是和LUT查找表的数据类型保持一致,这是因为将原灰度值映射到新的空间中,因此需要与新空间中的数据类型保持一致。
在这里插入图片描述

为了体会LUT查找表处理图像后的效果,在代码清单3-21中给出通过LUT()函数将灰度图像和彩色图像分别处理的示例程序,程序中分别应用单通道和三通道的查找表对彩色图像进行映射

#include<iostream>
#include<vector>
#include<string>
#include <opencv2/opencv.hpp>
#include "opencv/highgui.h"

using namespace std;
using namespace cv;

int main(int argc,char** argv) {
    cout<<"OpenCv Version: "<<CV_VERSION<<endl;
    //LUT查找表第一层
    uchar lutFirst[256];
    for(int i=0;i<256;++i){
        if(i<=100)lutFirst[i]=0;
        else if(i>100&&i<=200)lutFirst[i]=100;
        else lutFirst[i]=255;
    }
    Mat lutOne(1,256,CV_8UC1,lutFirst);

    //LUT查找表第二层
    uchar lutSecond[256];
    for(int i=0;i<256;++i){
        if(i<=100)lutSecond[i]=0;
        else if(i>100&&i<=150)lutSecond[i]=100;
        else if(i>150&&i<=200)lutSecond[i]=150;
        else lutSecond[i]=255;
    }
    Mat lutTwo(1,256,CV_8UC1,lutSecond);

    //LUT查找表第三层
    uchar lutThird[256];
    for(int i=0;i<256;++i){
        if(i<=100)lutThird[i]=0;
        else if(i>100&&i<=200)lutThird[i]=100;
        else lutThird[i]=255;
    }
    Mat lutThree(1,256,CV_8UC1,lutThird);

    //拥有三通道的LUT查找表矩阵
    vector<Mat>mergeMats;
    mergeMats.push_back(lutOne);
    mergeMats.push_back(lutTwo);
    mergeMats.push_back(lutThree);
    Mat LutTree;
    merge(mergeMats,LutTree);

    //计算图像的查找表
    Mat img=imread("lena.jpeg");
    if(img.empty()){
        cout<<"请确认输入的图片路径是否正确"<<endl;
        return -1;
    }
    Mat gray,out0,out1,out2;
    cvtColor(img,gray,COLOR_BGR2GRAY);
    LUT(gray,lutOne,out0);
    LUT(img,lutOne,out1);
    LUT(img,LutTree,out2);
    imshow("out0",out0);
    imshow("out1",out1);
    imshow("out2",out2);
    waitKey(0);
    return 0;
}

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Opencv-C++笔记 (11) : opencv-图像二值化与LUB查找表 的相关文章

  • 使用内部构造函数实例化类

    我有一个类 其构造函数被定义为内部 这意味着我无法实例化它 虽然这可能有道理 但出于调试和研究目的 我仍然愿意做一次 是否可以通过反射来做到这一点 我知道我可以访问私有 内部成员 但是我可以调用内部构造函数吗 或者 由于构造函数没有做任何重
  • 实体框架中的重复键异常?

    我试图捕获当我将具有给定用户名的现有用户插入数据库时 引发的异常 正如标题所说 我正在使用 EF 当我尝试将用户插入数据库时 引发的唯一异常是 UpdateException 如何提取此异常以识别其是否是重复异常或其他异常 catch Up
  • 从 .Net 将简单数据插入 Excel 文件的最简单方法

    我有一个 Excel 文件 大约有 10 列和 1 20 行 我需要插入 1 20 行包含各种数据元素 我想知道是否有一种方法可以将一些标签放入 Excel 文件中 以便可以找到并替换它们 将列标记为 名称 的东西 这样我就可以在代码中说
  • 此插件导致 Outlook 启动缓慢

    我正在使用 C NET 4 5 开发 Outlook Addin 项目 但部署后 有时 Outlook 会禁用我的插件 并显示此消息 这个插件导致 Outlook 启动缓慢 我不知道我的插件出了什么问题 这只有很少的代码 并且ThisAdd
  • 浏览器收集哪些值作为回发数据?

    当页面被发送回服务器时 浏览器收集每个控件的当前值并将其粘贴到一个字符串中 然后 该回发数据通过 HTTP POST 发送回服务器 Q1 除了控件的 Text 属性和 SelectedIndexchanged 因此除了用户输入数据 之外 控
  • 隐形打开的弹出窗口

    第二天就解决这个问题 要重现 请创建新的 WPF 应用程序 xaml
  • 将占位符文本添加到文本框

    我正在寻找一种将占位符文本添加到文本框的方法 就像在 html5 中使用文本框一样 IE 如果文本框没有文本 则会添加文本Enter some text here 当用户单击它时 占位符文本消失并允许用户输入自己的文本 如果文本框失去焦点并
  • 实体框架7审计日志

    我正在将一个旧项目移植到 ASP NET 5 和 Entity Framework 7 我使用数据库优先方法 DNX 脚手架 来创建模型 旧项目基于Entity Framework 4 审计跟踪是通过重写实现的SaveChanges的方法D
  • CMake 警告:无法为目标生成安全的链接器搜索路径

    在为 pcl 项目运行 CMake 时 我收到一条警告消息 Configuring done CMake Warning at CMakeLists txt 12 add executable Cannot generate a safe
  • Windows 程序如何临时更改其时区?

    我写了一个函数来返回time t与给定日期的午夜相对应的值 当给定日期没有午夜时 它返回最早可用的时间 例如 当埃及进入夏令时时 这种情况就可能发生 今年 时间更改于 4 月 29 日晚上午夜生效 因此时钟直接从 23 59 转到 01 0
  • Type_traits *_v 变量模板实用程序顺序无法编译

    看过了这个答案 https stackoverflow com a 31763111 7151494 我试图想出一个变量模板从中获取代码的实用程序 template
  • IBM Watson 对话服务错误:无法从“方法组”转换为“conversation.onMessage”

    我正在尝试运行 IBM Watson会话服务团结和下面是代码片段 https github com watson developer cloud unity sdk conversation private Conversation m C
  • ASP.NET Web API Swagger(Swashbuckle)重复OperationId

    I have a web api controller like below In swagger output I am having the below image And when I want to consume it in my
  • IEnumerable.比带中断的 for 循环更快吗?

    我们的代码打开表单时遇到了一些缓慢的情况 这可能是由于for循环与break这需要很长时间才能执行 我把它切换到IEnumerable Any 并看到表格很快打开 我现在试图弄清楚是否单独进行此更改会提高性能 或者是否正在访问Product
  • 如果数组为空,LINQ 返回 null

    public class Stuff public int x other stuff 我有一个IEnumerable
  • printf 参数不足

    我的问题是关于缺少参数的 printf 之后的行为 printf s blah blah d int integer was given as argument and not int written 我已经知道 如果格式参数不足 则行为是
  • 为什么从绑定返回的对象会忽略额外的参数?

    假设我有一个带有两个参数的函数 void f int x int y 我想绑定其中之一 我可以用std bind如下 auto partiallyBoundF std bind f 10 1 partiallyBoundF仅需要一个参数 但
  • 统一;随机物体移动[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我正在制作一款机器人战斗游戏 我希望敌人随机移动 然后有时会向敌人移动 我希望运动包含在其中的代码 else if avoid fal
  • 获取大于某个数字的元素个数

    我正在尝试解决以下问题 数字被插入到容器中 每次插入数字时 我需要知道容器中有多少元素大于或等于当前插入的数字 我相信这两个操作都可以以对数复杂度完成 我的问题 C 库中有标准容器可以解决这个问题吗 我知道std multiset可以在对数
  • Visual Studio 2015默认附加库

    当我在 VS 2015 中创建一个空项目时 它会自动将这些库放入 附加依赖项 中 kernel32 lib user32 lib gdi32 lib winspool lib comdlg32 lib advapi32 lib shell3

随机推荐

  • Vue3 (computed函数,watch函数,watchEffect函数)

    1 computed函数 与vue2中computed配置功能一致 变化 需要引入 组合式的API
  • 树莓派第一讲:入门那些事(系统烧录、外设连接)

    目录 基本了解 系统烧录 连接外设 基本了解 树莓派4B是一款单板计算机 采用ARM架构处理器 配备4GB内存 Gigabit以太网口 多个USB接口 HDMI输出接口等 它具备1 5Ghz运行的64位四核处理器 最高支持以60fps速度刷
  • 因果推断 - 基础知识

    目录 因果关系之梯 因果图的路径结构 阻断 d 分离 混杂 结构因果模型 SCM 版权 转载前请联系作者获得授权 声明 部分内容出自因果关系之梯 已获得原作者授权 参考书籍 The Book of Why Judea Pearl 因果关系之
  • 什么是回调函数Callback----自己的一点理解

    何为回调函数 若把函数的指针作为函数参数传递给一个函数 当这个指着被用来调用它所指向的函数时 我们将该指针所指向的函数称为回调函数 回调函数与普通函数最大区别在于函数的调用 对普通函数而言 函数实现者可以直接拿来用 可以直接将它放在main
  • 金山逍遥网 sersync 服务器实时镜像同步方案

    金山逍遥网 sersync 服务器实时镜像同步方案 1 sersync rsync原理 2 inotify和sersync同步的区别 3 配置sersync rsync实现实时同步 2台centos7 4 一台装sersync一台装rsyn
  • 『网络安全』蜜罐到蜜网入门指南(三)蜜罐内部组成分析

    原创不易 点个赞呗 如果喜欢 关注 收藏不迷路 前言 大家好 网络安全 蜜罐到蜜网入门指南 进入第三篇 通过前面的内容 我们知道了什么是蜜罐以及蜜罐的作用和分类等 点击下方链接 可快速查看 相关文章 网络安全 蜜罐到蜜网入门指南 一 蜜罐初
  • php执行命令的函数和方法

    PHP提供4个专门的执行外部命令的函数 exec system passthru shell exec 1 exec 原型 string exec string command array output int return var 说明
  • Win10+Ubuntu双系统 使用EasyUEFI修复Ubuntu引导启动项

    某同事安装了Win10 Ubuntu双系统 有一天电脑突然坏了开不了机 把硬盘拆下放到别的机器上 发现是直接进入Win10系统 而不是grub选择界面 进F12也找不到ubuntu的启动项 最开始我先使用 Win10 Ubuntu双系统修复
  • 在项目中添加天气预报功能

    查看当地的天气情况 调用七日的天气预报情况 天气预报 注册账号获取appid和APPSecret使用 https www tianqiapi com api 图标可以直接在网站上下载
  • CoreDNS 惊现诡异 bug,导致服务大面积中断

    Sealos 公众号已接入了 GPT 4 完全免费 欢迎前来调戏 原文链接 https juejin cn post 7277471908417110053 我是 LEE 老李 一个在 IT 行业摸爬滚打 17 年的技术老兵 事件背景 某天
  • Flutter开发(十二)—— 页面跳转与返回

    示例代码 以下代码展示页面跳转与返回 抛开所有复杂因素 只展示最简单的跳转 第一个页面 点击按钮时onPressed 进行相应 通过 Navigator push 和 MaterialPageRoute 进行页面跳转功能实现 第二个页面 点
  • python中百分数如何表达_Python转换百分数表示法

    YAML中任何以百分号结尾的数字序列都将 通常作为字符串标量加载 因为 不会 标量匹配任何其他模式 尤其是不匹配整数的模式 或浮动 当然 您可以递归地遍历数据结构 从YAML和patch加载 但是如果在 数据结构 用于构造特定对象 递归到这
  • passwd: Authentication token manipulation error----linux

    author skatetime 2009 04 11 更改centos4 7 linux的root密码 报 passwd Authentication token manipulation error 如下所示 root ticket A
  • react移动端适配

    一 自定义方法配置 1 使用JavaScript动态设置根元素的字体大小 为了实现响应式的自适应效果 可以结合JavaScript根据屏幕大小动态设置根元素的字体大小 可以使用window innerWidth获取窗口的宽度 然后根据需要的
  • Q_PROPERTY

    一 定义 Qt提供了一个成熟的属性系统 Q PROPERTY是一个宏 用来在类中声明一个属性 由于该特性是Qt所特有的 需要moc进行编译 故必须继承QObject 查看Qt助手 我们可以看到如下定义 看不懂 其实 这是一个正则表达式 我们
  • Selenium Web自动化测试框架实践

    功能实现 自动运行用例 自动生成测试报告 自动断言与截图 自动将最新测试报告发送到指定邮箱 自动生成测试报告 数据 页面元素 测试用例分离 执行日志 分布式执行 配置化文件 元素 数据 实践功能 https passport csdn ne
  • AUC,ROC我看到的最透彻的讲解

    面试的时候 一句话说明AUC的本质和计算规则 AUC 一个正例 一个负例 预测为正的概率值比预测为负的概率值还要大的可能性 所以根据定义 我们最直观的有两种计算AUC的方法 1 绘制ROC曲线 ROC曲线下面的面积就是AUC的值 2 假设总
  • 前端异步编程的进化之旅 promise generator aysnc/await

    promise generator aysnc await 1 三者都是异步编程的解决方案 不同的是 promise为较早出来的 其次generator 最后为async await 三者象征了前端进行解决异步编程的进化路程 promise
  • 接口的概念及接口的方法是怎么使用的

    接口 一个完成某些特定功能的类 是一个功能的集合 语法为 接口修饰符 interface 接口名称 成员变量和方法的声明 成员变量默认为public static final类型 类不能多继承 但可通过接口实现多层继承 接口既可以多层继承
  • Opencv-C++笔记 (11) : opencv-图像二值化与LUB查找表

    文章目录 一 概述 二 THRESH BINARY和THRESH BINARY INV 三 THRESH TRUNC 四 THRESH TOZERO和THRESH TOZERO INV 五 THRESH OTSU和THRESH TRIANG