OpenCvSharp学习笔记13--使用InRange进行HSV阈值过滤、渐变色生成

这篇具有很好参考价值的文章主要介绍了OpenCvSharp学习笔记13--使用InRange进行HSV阈值过滤、渐变色生成。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目标

HSV色彩空间介绍
使用InRange操作阈值
基于HSV色彩空间的像素值范围检测对象
生成渐变色

HSV色彩空间

HSV(Hue, Saturation, Value)是根据颜色的直观特性由A. R. Smith在1978年创建的一种颜色空间, 也称六角锥体模型(Hexcone Model)。HSV色系对用户来说是一种直观的颜色模型,对于颜色,人们直观的会问”什么颜色?深浅如何?明暗如何?“,而HSV色系则直观的表示了这些信息。
每一种颜色都是由色相(Hue,简H),饱和度(Saturation,简S)和色明度(Value,简V)所表示的。这个模型中颜色的参数分别是:色调(H),饱和度(S),亮度(V)。
色调H参数表示色彩信息,即所处的光谱颜色的位置。该参数用一角度量来表示,取值范围为0°~360°。若从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,紫色为300°;
饱和度S:取值范围为0.0~1.0;
亮度V:取值范围为0.0(黑色)~1.0(白色)。
H:(Hue色调、色相)、S:(Saturation饱和度)、V:(Value高度)。HSV对于需要按颜色分割对象的场合十分有用。饱和度(S)的范围从不饱和到代表灰色的阴影和完全饱和(没有白色成分)。亮度(V)通道描述的是亮度或强度。如图(图片来源官网)

亮度阈值过滤,OpenCVSharp学习,opencv,c#,图像处理

相对于RGB色彩空间,通过颜色是很难分割图像中的对象的。

亮度阈值过滤,OpenCVSharp学习,opencv,c#,图像处理

InRange范围判断

函数说明:检查数组元素是否位于其他两个数组的元素之间。
对单通道的每个元素
dst(I)=lowerb(I)0≤src(I)0≤upperb(I)0

对于三通道
dst(I)=lowerb(I)0≤src(I)0≤upperb(I)0 ∧ lowerb(I)1≤src(I)1≤upperb(I)1 ∧ lowerb(I)2≤src(I)2≤upperb(I)2

//函数原型1
void InRange(InputArray src,
	InputArray lowerb,
	InputArray upperb,
	OutputArray dst)

//函数原型2
void InRange(InputArray src,
	Scalar lowerb,
	Scalar upperb,
	OutputArray dst)
参数 说明                            
InputArray src 输入图像
InputArray lowerb
Scalar lowerb
范围下限(包含)
InputArray upperb
Scalar upperb
范围上限(包含)
OutputArray dst 输出图像(在lowerb与upperb范围内的值为255,否则为0)

OpenCV中的HSV

OpenCV中使用的HSV的取值范围是H:[0,180],S:[0,255],V:[0,255],所以,各颜色的范围如下图所示:亮度阈值过滤,OpenCVSharp学习,opencv,c#,图像处理

将HSV中各颜色的边界值绘制成颜色块

1、将上表定义为矩阵

/// <summary>
/// HSV颜色范围
/// </summary>
/// <returns></returns>
private Mat GetHSVRange()
{
    //使用HSV定义的各颜色范围 6个byte为一组,
    //前三个是对应颜色的最小值,后三个是对应颜色的最大值
    var HSVMinMaxValue = new byte[] {0,0,0,  //黑min
                                    180,255,46,//黑max
                                    0,0,46,//灰
                                    180,43,220,
                                    0,0,221,//白
                                    180,30,255,
                                    0,43,46,//红1
                                    10,255,255,
                                    156,43,46,//红2
                                    180,255,255,
                                    11,43,46,//橙
                                    25,255,255,
                                    26,43,46,//黄
                                    34,255,255,
                                    35,43,46,//绿
                                    77,255,255,
                                    78,43,46,//青
                                    99,255,255,
                                    100,43,46,//蓝
                                    124,255,255,
                                    125,43,46,//紫
                                    155,255,255};
    var hsvRange = new Mat(HSVMinMaxValue.Length / 3, 1, MatType.CV_8UC3, HSVMinMaxValue);
    return hsvRange;
}

2、将HSV转为BGR,然后绘制

var HSVRange = GetHSVRange();
var BGRRange = new Mat();
Cv2.CvtColor(HSVRange, BGRRange, ColorConversionCodes.HSV2BGR);
            
int cWidth = 100;
int perRow = width / cWidth - 2;

Mat canvasBGR = new Mat(height, width, MatType.CV_8UC3, Scalar.White);
//将各颜色的上下限画出来
for (int i = 0; i < BGRRange.Rows; i++)
{
    var bgr = BGRRange.At<Vec3b>(i, 0);
    var color = Scalar.FromVec3b(bgr);
    var rect = new Rect(cWidth * ((i) % perRow + 1), cWidth * ((i) / perRow + 1), cWidth, cWidth);
    Cv2.Rectangle(canvasBGR, rect, color, -1);
}
Utils.ShowWaitDestroy(canvasBGR, "HSV ColorSpace");//显示canvasBGR

生成的效果如下(对黑、灰、白、红1、红2、橙、黄、绿、青、蓝、紫的上下边界值):
亮度阈值过滤,OpenCVSharp学习,opencv,c#,图像处理
按HSV各值生成渐变色:
亮度阈值过滤,OpenCVSharp学习,opencv,c#,图像处理
HSV转BGR后,再生成渐变色:
亮度阈值过滤,OpenCVSharp学习,opencv,c#,图像处理
两种方式生成的结果略有不同。

官网示例

将官网的代码用OpenCvSharp跑一边,效果如下:
亮度阈值过滤,OpenCVSharp学习,opencv,c#,图像处理
如上图所示,简单的根据颜色边界范围来提取对象的效果一般都不能满足生产要求!

代码示例

const int maxValueH = 360 / 2;
const int maxValue = 255;
const string winNameSrc = "源图";
const string winNameDst = "HSV对象掩膜";
const string winNameResult = "根据掩膜提取的对象";
//int lowH = 0, lowS = 0, lowV = 0;
//int highH=maxValueH, highS = maxValue, highV = maxValue;
//红色区域
//int lowH = 156, lowS = 43, lowV = 46;
//int highH = 180, highS = 255, highV = 255;
//蓝色区域
int lowH = 100, lowS = 43, lowV = 46;
int highH = 124, highS = 255, highV = 255;

public void Run()
{
    DrawingHSVRange();

    HSVThreshold();
}

#region HSV Threshold Sample
private void HSVThreshold()
{
    var fileName = ImagePath.WindowsLogo;
    using var src = new Mat(fileName, ImreadModes.Color);

    Cv2.NamedWindow(winNameSrc);
    Cv2.NamedWindow(winNameDst);
    Cv2.NamedWindow(winNameResult);
    // Trackbars to set thresholds for HSV values
    Cv2.CreateTrackbar("Low H", winNameDst, ref lowH, maxValueH, OnLowHThreshTrackbar);
    Cv2.CreateTrackbar("High H", winNameDst, ref highH, maxValueH, OnHighHTreshTrackbar);
    Cv2.CreateTrackbar("Low S", winNameDst, ref lowS, maxValue, OnLowSThreshTrackbar);
    Cv2.CreateTrackbar("High S", winNameDst, ref highS, maxValue, OnHighSThreshTrackbar);
    Cv2.CreateTrackbar("Low V", winNameDst, ref lowV, maxValue, OnLowVThreshTrackbar);
    Cv2.CreateTrackbar("High V", winNameDst, ref highV, maxValue, OnHighVThreshTrackbar);
    Cv2.ImShow(winNameSrc, src);
    using var dstHSV = new Mat();
    // 将BGR 转为 HSV
    Cv2.CvtColor(src, dstHSV, ColorConversionCodes.BGR2HSV);            
    using var dstThreshold = new Mat();
    while (true)
    {
        // 使用InRange根据HSV的最小、最大值检测对象
        Cv2.InRange(dstHSV, new Scalar(lowH, lowS, lowV), new Scalar(highH, highS, highV), dstThreshold);

        // 对象掩膜
        Cv2.ImShow(winNameDst, dstThreshold);

        var copyImg = new Mat();
        src.CopyTo(copyImg, dstThreshold);
        
        Cv2.ImShow(winNameResult, copyImg);
        char key = (char)Cv2.WaitKey(50);
        if (key == 'q' || key == 27)
        {
            break;
        }
    }
    Cv2.DestroyAllWindows();
}

void OnLowHThreshTrackbar(int pos, IntPtr userData)
{
    lowH = Math.Min(highH - 1, pos);
    Cv2.SetTrackbarPos("Low H", winNameDst, lowH);
}

void OnHighHTreshTrackbar(int pos, IntPtr userData)
{
    highH = Math.Max(pos, lowH + 1);
    Cv2.SetTrackbarPos("High H", winNameDst, highH);
}

void OnLowSThreshTrackbar(int pos, IntPtr userData)
{
    lowS = Math.Min(highS - 1, pos);
    Cv2.SetTrackbarPos("Low S", winNameDst, lowS);
}
void OnHighSThreshTrackbar(int pos, IntPtr userData)
{
    highS = Math.Max(pos, lowS + 1);
    Cv2.SetTrackbarPos("High S", winNameDst, highS);
}
void OnLowVThreshTrackbar(int pos, IntPtr userData)
{
    lowV = Math.Min(highV - 1, pos);
    Cv2.SetTrackbarPos("Low V", winNameDst, lowV);
}
void OnHighVThreshTrackbar(int pos, IntPtr userData)
{
    highV = Math.Max(pos, lowV + 1);
    Cv2.SetTrackbarPos("High V", winNameDst, highV);
}
#endregion

/// <summary>
/// 按HSV绘制
/// </summary>
private void DrawingHSVRange()
{
    var width = 1600;
    var height = 800;

    string ColorName = "黑、灰、白、红1、红2、橙、黄、绿、青、蓝、紫";
    var HSVRange = GetHSVRange();
    var BGRRange = new Mat();
    Cv2.CvtColor(HSVRange, BGRRange, ColorConversionCodes.HSV2BGR);
                
    int cWidth = 100;
    int perRow = width / cWidth - 2;

    Mat canvasBGR = new Mat(height, width, MatType.CV_8UC3, Scalar.White);
    //将各颜色的上下限画出来
    for (int i = 0; i < BGRRange.Rows; i++)
    {
        var bgr = BGRRange.At<Vec3b>(i, 0);
        var color = Scalar.FromVec3b(bgr);
        var rect = new Rect(cWidth * ((i) % perRow + 1), cWidth * ((i) / perRow + 1), cWidth, cWidth);
        Cv2.Rectangle(canvasBGR, rect, color, -1);
    }
    Utils.ShowWaitDestroy(canvasBGR, "HSV ColorSpace");

    //初始化一张HSV,白底画布,注意HSV中白色是(0,0,255)
    Mat canvasHSV = new Mat(height, width, MatType.CV_8UC3, new Scalar(0, 0, 255));
    //通过HSV的递增生成渐变色
    GenerateGradientColor(canvasHSV, HSVRange, "HSV");
    //将HSV转成BGR显示
    Cv2.CvtColor(canvasHSV, canvasHSV, ColorConversionCodes.HSV2BGR);
    Utils.ShowWaitDestroy(canvasHSV, "HSV:"+ ColorName);
    
    //初始化一张BGR,白底画布
    canvasBGR = new Mat(height, width, MatType.CV_8UC3, Scalar.White);
    GenerateGradientColor(canvasBGR, BGRRange, "BGR");
    Utils.ShowWaitDestroy(canvasBGR, "BGR:"+ ColorName);
    Cv2.DestroyAllWindows();
}
/// <summary>
/// 根据颜色范围生成渐变色
/// </summary>
/// <param name="canvas">画布</param>
/// <param name="colorRange">颜色范围(最小值、最大值)</param>
/// <param name="colorSpace"></param>
private void GenerateGradientColor(Mat canvas,Mat colorRange,string colorSpace)
{
    //两边的边距
    int edgeDistance = 160;
    //各渐变色的跨度
    int stepWdith = canvas.Cols - edgeDistance * 2;
    //各渐变色的高度
    int rowHeight = 60;
    //通过颜色递增生成渐变色
    for (int i = 0; i < colorRange.Rows - 1; i += 2)
    {
        var minVal = colorRange.At<Vec3b>(i, 0);//颜色最小值
        var maxVal = colorRange.At<Vec3b>(i + 1, 0);//颜色最大值 转BGR时,各通道的值大、小有变
        var stepB = Math.Abs(1.0D * (maxVal.Item0 - minVal.Item0) / stepWdith);
        var stepG = Math.Abs(1.0D * (maxVal.Item1 - minVal.Item1) / stepWdith);
        var stepR = Math.Abs(1.0D * (maxVal.Item2 - minVal.Item2) / stepWdith);
        int rowOffset = rowHeight * (i / 2 + 1);
        for (int step = 0; step <= stepWdith; step++)
        {
            //生成渐变色
            var colorVec = new Vec3b((byte)(Math.Min(minVal.Item0,maxVal.Item0) + stepB * step),
                                     (byte)(Math.Min(minVal.Item1, maxVal.Item1) + stepG * step),
                                     (byte)(Math.Min(minVal.Item2, maxVal.Item2) + stepR * step));
            //生成rowHeigh行
            for (int row = 0; row < rowHeight; row++)
            {
                canvas.At<Vec3b>(row + rowHeight * (i / 2 + 1), edgeDistance + step) = colorVec;
            }
            //两边加文字
            if (step == 0)
            {
                PutText(canvas, new Point(5, rowOffset + rowHeight / 2), colorSpace, colorVec);
            }
            else if (step == stepWdith)
            {
                PutText(canvas, new Point((canvas.Cols - edgeDistance) + 5, rowOffset + rowHeight / 2), colorSpace, colorVec);
            }
        }                
    }    
}


//绘制渐变色两字的文字
private void PutText(Mat src, Point org, string text, Vec3b colorVec)
{
    Cv2.PutText(src, $"{text}({colorVec.Item0},{colorVec.Item1},{colorVec.Item2})", org, HersheyFonts.HersheyTriplex, 0.5, Scalar.Black);
}

/// <summary>
/// HSV颜色范围
/// </summary>
/// <returns></returns>
private Mat GetHSVRange()
{
    //使用HSV定义的各颜色范围 6个byte为一组,
    //前三个是对应颜色的最小值,后三个是对应颜色的最大值
    var HSVMinMaxValue = new byte[] {0,0,0,  //黑min
                                    180,255,46,//黑max
                                    0,0,46,//灰
                                    180,43,220,
                                    0,0,221,//白
                                    180,30,255,
                                    0,43,46,//红1
                                    10,255,255,
                                    156,43,46,//红2
                                    180,255,255,
                                    11,43,46,//橙
                                    25,255,255,
                                    26,43,46,//黄
                                    34,255,255,
                                    35,43,46,//绿
                                    77,255,255,
                                    78,43,46,//青
                                    99,255,255,
                                    100,43,46,//蓝
                                    124,255,255,
                                    125,43,46,//紫
                                    155,255,255};
    var hsvRange = new Mat(HSVMinMaxValue.Length / 3, 1, MatType.CV_8UC3, HSVMinMaxValue);
    return hsvRange;
}

参考
https://docs.opencv.org/4.7.0/da/d97/tutorial_threshold_inRange.html文章来源地址https://www.toymoban.com/news/detail-760764.html

到了这里,关于OpenCvSharp学习笔记13--使用InRange进行HSV阈值过滤、渐变色生成的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【学习笔记】在Android使用Frida进行https抓包

    最近需要在Android进行https抓包,对数据解密,找了很多方法,终于成功了,不过原文一些步骤对于我这个小白还是有点不理解的地方,在此记录一下。 一台root手机 frida环境 该步骤全程参考: [Frida入门教程] 一文搞定Frida环境搭建,基于逍遥模拟器(虚拟设备)和ADB(Android Debug

    2024年02月16日
    浏览(43)
  • ASIC设计学习笔记——使用Design Compiler进行综合

    综合是ASIC的前端设计中极为重要的步骤,所谓的综合过程,是指将行为级描述的电路、RTL级的电路转换到门级网表的过程。本文介绍使用Synopsys公司的Design Compiler作为工具完成综合的过程。 在ASIC开发中,当使用verilog等硬件描述语言完成对所需要的功能的代码编写和仿真后,

    2024年02月07日
    浏览(40)
  • Opencv中inRange函数使用介绍

    inRange是OpenCV中的图像阈值函数,用于将图像中的像素值限制在指定的范围内。它的输入和输出如下所述: 输入值: src:输入图像,可以是灰度图像或彩色图像。 lowerb:表示下界的阈值,可以是一个标量值或与输入图像通道数相同的数组。对于灰度图像,lowerb是一个标量值。

    2024年02月05日
    浏览(49)
  • C++中键盘响应结合OpenCV库进行图像灰度图、HSV图转换和亮度调整

    QuickDemo.cpp quick_opencv.h main.cpp 按键esc退出程序。 注意必须点击原图上再输入1或2或3才有响应。

    2024年02月20日
    浏览(66)
  • unity学习笔记13

    一、常用物理关节 Unity中的物理关节(Physics Joints)是用于在游戏中模拟和控制物体之间的连接。物理关节允许你在对象之间应用各种约束,例如旋转、移动或固定连接,以模拟真实世界中的物理交互。 物理关节类型: 1.Fixed Joint(固定关节): 固定关节将两个物体连接在一

    2024年02月04日
    浏览(34)
  • 【机器学习笔记】13 降维

    维数灾难(Curse of Dimensionality):通常是指在涉及到向量的计算的问题中,随着维数的增加,计算量呈指数倍增长的一种现象。在很多机器学习问题中,训练集中的每条数据经常伴随着上千、甚至上万个特征。要处理这所有的特征的话,不仅会让训练非常缓慢,还会极大增加搜寻

    2024年02月20日
    浏览(39)
  • 《使用Unreal Engine Python插件进行UE4中的Python开发》学习笔记1

    ·本文为B站系列教学视频 《使用 UnrealEnginPython 插件进行UE4中的Python开发》 ——《 Day 01 初识 UnrealEnginePython 》的学习笔记,UP主为腾讯游戏策划、虚幻社区贡献者 世欺子。 本节课对基于虚幻引擎( Unreal Engine )的 Python 集成开发插件 UnrealEnginePython 进行简要介绍,接着在虚幻

    2024年04月11日
    浏览(67)
  • 企业架构LNMP学习笔记13

    上线商城项目: 1)上传项目文件到数据库:   入口文件位置的设计是为了让应用部署更安全,public目录为web可访问目录,其他的文件都可以放到非web访问目录下面。 nginx 默认访问index.html。没有index.html,就会列出目录结构,没有权限列出,所以就显示403。   需要配置数据库

    2024年02月09日
    浏览(42)
  • 吴恩达机器学习笔记:第 8 周-13 聚类(Clustering)13.1-13.2

    在这个视频中,我将开始介绍聚类算法。这将是一个激动人心的时刻,因为这是我们学习的第一个非监督学习算法。我们将要让计算机学习无标签数据,而不是此前的标签数据。 那么,什么是非监督学习呢?在课程的一开始,我曾简单地介绍过非监督学习,然而,我们还是有

    2024年04月22日
    浏览(52)
  • java对时间序列根据阈值进行连续性分片

    问题描述:我需要对一个连续的时间戳list进行分片,分片规则是下一个数据比当前数据要大于某一个阈值则进行分片; 解决方式: 1、输入的有顺序的list ,和需要进行分片的阈值 2、调用方法,填入该排序的list和阈值 检验: 结果:(应该是成功了)

    2024年02月10日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包