C#描述-计算机视觉OpenCV(5):直方图算法

C#描述-计算机视觉OpenCV(5):直方图算法

    • 前文链接
    • 图像直方图
    • 灰度直方图的计算
    • 灰度直方图的绘制
    • BGR三通道的直方图
    • 直方图的均衡化算法
    • 相似图像检测

前文链接

文中没提到的东西,很可能都在前文描述过
C#描述-计算机视觉OpenCV(1):基础操作
C#描述-计算机视觉OpenCV(2):图像处理
C#描述-计算机视觉OpenCV(3):重映射
C#描述-计算机视觉OpenCV(4):图像分割

图像直方图

图像由各种数值的像素构成。例如在单通道灰度图像中,每个像素都有一个 0(黑色)~255(白色)的整数。对于每个灰度,都有不同数量的像素分布在图像内,具体取决于图片内容。直方图是一个简单的表格,表示一幅图像(有时是一组图像)中具有某个值的像素的数量。
因此,灰度图像的直方图有 256 个项目,也叫箱子(bin)。0 号箱子提供值为 0 的像素的数量,1 号箱子提供值为 1 的像素的数量,以此类推。很明显,如果把直方图的所有箱子进行累加,得到的结果就是像素的总数。你也可以把直方图归一化,即所有箱子的累加和等于 1。这时,每个箱子的数值表示对应的像素数量占总数的百分比。
直方图或者说对色彩的统计,是很多算法的基础。本文将以此图为例,来生成在直方图:
在这里插入图片描述

灰度直方图的计算

首先,我们要把原图通过CvtColor函数转换为一个灰度图像:
在这里插入图片描述
然后我们拿着这张图,代入CalcHist函数。
这个直方图生成函数的参数为:
int histSize[1]; // 直方图中箱子的数量
float hranges[2]; // 值范围
const float* ranges[1]; // 值范围的指针,C#中,我们直接使用rangef
int channels[1]; // 要检查的通道数量
以及一个转换为Mat[] 的Mat灰度图像,以及一个hist作为结果输出
对于这张图,由于是一个0-255的单通道灰度图像,hist是一个Mat一维数组,大小为256,值域根据原图像素,心里也可以有个估计,每个色块的总数大约占比多少,然后可以直接输出出来检视结果。

public void GetHistogram(Mat img)
        {
            Mat hist = new Mat();
            Mat grayImg = new Mat();
            
            Cv2.CvtColor(img, grayImg, ColorConversionCodes.BGR2GRAY);
            Mat[] vimg = new Mat[] { grayImg };
            int[] channels = new int[] { 0 };// 要检查的通道数量
            Rangef[] ranges = new Rangef[] { new Rangef(0, 256) };// 值范围与指针
            int[] histSize = new int[] { 256 };// 直方图中箱子的数量
            int dims = 1; //需要统计的特征数目(只统计灰度图单通道)
            Cv2.CalcHist(vimg, channels, new Mat(), hist, dims,histSize, ranges);
            Cv2.ImShow("grayImg", grayImg);
            for(int i=0;i<256;i++)
            {
                float h = hist.At<float>(i);
                textBox1.Text +="value "+Convert.ToString(i)+" : " +Convert.ToString(h) + "\r\n";//打印结果来检视下
            }
            GetImageOfHistogram(hist);//用计算结果去生成直方图
        }

到这一步,我们得到了统计数组,然后就开始写直方图绘制函数

灰度直方图的绘制

我们可以同时生成一个直方图和一个Winform自带的Chart函数来进行结果比对

public void GetImageOfHistogram(Mat hist)
        {
            for (int i = 0; i < 256; i++)
            {
                XList1.Add(i);
                YList1.Add(Convert.ToInt32(hist.At<float>(i)));
                chart1.Series["Hist"].Points.DataBindXY(XList1, YList1);
            }
            double maxVal = 0;
            double minVal = 0;
            Cv2.MinMaxLoc(hist, out minVal, out maxVal);
            int histSize = hist.Rows;
            Mat histImg = new Mat(histSize, histSize, MatType.CV_8UC1);
            int hpt = Convert.ToInt32(0.9 * histSize);
            for (int h = 0; h < histSize; h++)
            {
                float binVal = hist.At<float>(h);
                if (binVal > 0)
                {
                    int intensity = Convert.ToInt32(binVal * hpt / maxVal);
                    Cv2.Line(histImg, h, histSize, h, histSize - intensity, 255, 1);
                        }
            }
            Cv2.ImShow("histImg", histImg);
            }

结果:
在这里插入图片描述
大多数情况下,直方图是单个的单通道或三通道图像,但也可以在这个函数中指定一个分布在多幅图像(即多个 Mat)上的多通道图像。这也是把输入图像数组作为函数第一个参数的原因。参数 dims 指明了直方图的维数,例如 1 表示一维直方图。在分析多通道图像时,可以只把它的部分通道用于计算直方图,将需要处理的通道放在维数确定的数组 channel 中。
在这个类的实现中只有一个通道,默认为 0。直方图用每个维度上的箱子数量(即整数数组histSize)以及每个维度(由 ranges 数组提供,数组中每个元素又是一个二元素数组)上的最小值(含)和最大值(不含)来描述。

BGR三通道的直方图

针对彩色图像,我们通过修改函数参数,来执行三通道的统计:
histSize[0]= histSize[1]= histSize[2]= 256;
hranges[0]= 0.0; // BGR 范围为 0~256
hranges[1]= 256.0;
ranges[0]= hranges; // 这个类中
ranges[1]= hranges; // 所有通道的范围都相等
ranges[2]= hranges;
channels[0]= 0; // 三个通道:B
channels[1]= 1; // G
channels[2]= 2; // R
对于执行结果,由于三维直方图很难画,我们可以生成三个单独的通道直方图。

直方图的均衡化算法

很多时候,图像的视觉缺陷并不因为它使用的强度值范围太窄,而是因为部分强度值的使用频率远高于其他强度值。有时候图像中等灰度的强度值非常多,而较暗和较亮的像素值则非常稀少,而均衡对所有像素强度值的使用频率可以作为提高图像质量的一种手段。这正是直方图均衡化这一概念背后的思想,也就是让图像的直方图尽可能地平稳。
对于直方图的均衡化,我们可以运用自带函数:

Mat res = new Mat();
Cv2.EqualizeHist(grayImg, res);
Cv2.ImShow("res", res);

效果图:
在这里插入图片描述
经常P图的小伙伴能看出来,这种算法增强了图像的对比度,也增加了图像的清晰度与细节呈现。
直方图均衡化后的统计结果:
在这里插入图片描述

相似图像检测

基于内容的图像检索是计算机视觉的一个重要课题。它包括根据一个已有的基准图像,找出一批内容相似的图像。直方图是标识图像内容的一种有效方式,因此值得研究一下能否用它来解决基于内容的图像检索问题。
这里的关键是,要仅靠比较它们的直方图就测量出两幅图像的相似度。我们需要定义一个测量函数,来评估两个直方图之间的差异程度或相似程度。人们已经提出了很多测量方法,OpenCV中compareHist 函数的实现过程中使用了其中的一些方法,在C#中,该函数调用方法为:

CompareHist(InputArray h1, InputArray h2, HistCompMethods method);

写个Demo测试下

public void HistCompTest(Mat img1, Mat img2)
        {

            Mat hist1 = new Mat();
            Mat hist2 = new Mat();
            Mat grayImg1 = new Mat();
            Mat grayImg2 = new Mat();
            Cv2.CvtColor(img1, grayImg1, ColorConversionCodes.BGR2GRAY);
            Cv2.CvtColor(img2, grayImg2, ColorConversionCodes.BGR2GRAY);
            Mat[] vimg1 = new Mat[] { grayImg1 };
            Mat[] vimg2 = new Mat[] { grayImg2 };
            int[] channels = new int[] { 0 };
            Rangef[] ranges = new Rangef[] { new Rangef(0, 256) };
            int[] histSize = new int[] { 256 };
            int dims = 1;
            Cv2.CalcHist(vimg1, channels, new Mat(), hist1, dims, histSize, ranges);
            Cv2.CalcHist(vimg2, channels, new Mat(), hist2, dims, histSize, ranges);
            for (int i = 0; i < 256; i++)
            {
                XList1.Add(i);
                YList1.Add(Convert.ToInt32(hist1.At<float>(i)));
                chart1.Series["Green"].Points.DataBindXY(XList1, YList1);
            }
            for (int i = 0; i < 256; i++)
            {
                XList2.Add(i);
                YList2.Add(Convert.ToInt32(hist2.At<float>(i)));
                chart2.Series["Green"].Points.DataBindXY(XList2, YList2);
            }
            hist1.ConvertTo(hist1, MatType.CV_32FC1);
            hist2.ConvertTo(hist2, MatType.CV_32FC1);
            Cv2.Normalize(hist1, hist1, 0, 1, NormTypes.MinMax, -1, null);
            Cv2.Normalize(hist2, hist2, 0, 1, NormTypes.MinMax, -1, null);//将两张图片统一格式,避免报错
            double x;
            x = Cv2.CompareHist(hist1, hist2, HistCompMethods.Correl);
            textBox1.Text = Convert.ToString(x);
        }

在这里插入图片描述
输出的结果相似度越靠近1,也就越相似,越靠近0,差别越大。由于是直方图对比,所以图像的旋转并不会影响对比的结果。暂时这是一个很初级的算法测试,实际工程意义不大,所以不做更多测试。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/784741.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C++深度探索】继承机制详解(二)

hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#xff1a;大耳朵土土垚的博客 &#x1…

深入解析:抖音视频标题的Python爬虫提取方法

引言 随着短视频的兴起&#xff0c;抖音已经成为全球最受欢迎的社交媒体平台之一。对于数据分析师、市场研究人员以及内容创作者来说&#xff0c;能够从抖音上抓取数据是一项宝贵的技能。本文将深入解析如何使用Python编写爬虫程序来提取抖音视频的标题。 爬虫基础 在开始编…

K8S 上部署大数据相关组件

文章目录 一、前言二、Redis 一、前言 Artifact Hub 是一个专注于云原生应用的集中式搜索和发布平台。它旨在简化开发者在 CNCF&#xff08;Cloud Native Computing Foundation&#xff09;项目中寻找、安装和分享包与配置的过程。用户可以通过这个平台方便地发现、安装各类云原…

为什么需要重写equals和如何重写equals

首先先看Java中的 &#xff0c;比较的两个对象的地址值。 如果是基本数据类型&#xff0c;那么就是比较的是值。 如果是引用数据类型&#xff0c;比较的就是地址. object类中的equals方法也是用的&#xff1b; 所以要比较两个对象的大小&#xff0c;去调用默认的equals方法…

Apache Spark分布式计算框架架构介绍

目录 一、概述 二、Apache Spark架构组件栈 2.1 概述 2.2 架构图 2.3 架构分层组件说明 2.3.1 支持数据源 2.3.2 调度运行模式 2.3.3 Spark Core核心 2.3.3.1 基础设施 2.3.3.2 存储系统 2.3.3.3 调度系统 2.3.3.4 计算引擎 2.3.4 生态组件 2.3.4.1 Spark SQL 2.…

关系型数据库MySQL和时序数据库的区别?

时序数据库和关系型数据库是两种不同类型的数据库系统&#xff0c;它们在设计理念、存储结构、性能优化等方面有显著差异&#xff0c;以适应不同的应用场景和需求。具体对比如下&#xff1a; 数据存储结构 时序数据库&#xff1a;使用列式存储&#xff0c;每条记录通常包含时间…

MySQL资源组的使用方法

MySQL支持创建和管理资源组&#xff0c;并允许将服务器内运行的线程分配给特定的组&#xff0c;以便线程根据组可用的资源执行。组属性允许控制其资源&#xff0c;以启用或限制组中线程的资源消耗。DBA可以针对不同的工作负载适当地修改这些属性。 目前&#xff0c;CPU时间是一…

田地行走-美团2023笔试(codefun2000)

题目链接 田地行走-美团2023笔试(codefun2000) 题目内容 塔子哥是一个农民&#xff0c;他有一片 nm 大小的田地&#xff0c;共 n 行 m 列&#xff0c;其中行和列都用从 1 开始的整数编号&#xff0c;田地中有 k 个格子中埋有土豆。我们记第 a 行第 b 列的格子为 (a,b) 。塔子哥…

LM2596/LM2596S多路降压稳压DC-DC开关电源芯片详解(第二部分:电路设计)(12V转5V、12V转3.3V、任意电压转任意电压)

目录 一、固定电压&#xff08;3.3/5/12V&#xff09;模块设计实例 1.设计条件&#xff1a;VOUT5V&#xff0c;VIN(MAX)12V&#xff0c;ILOAD(MAX)3A 2.设计步骤&#xff1a; &#xff08;1&#xff09;电感的选择&#xff08;L1&#xff09; &#xff08;2&#xff09;输…

C++入门基础

前言 本篇博客讲解一下c得入门基础 &#x1f493; 个人主页&#xff1a;普通young man-CSDN博客 ⏩ 文章专栏&#xff1a;C_普通young man的博客-CSDN博客 ⏩ 本人giee:普通小青年 (pu-tong-young-man) - Gitee.com 若有问题 评论区见&#x1f4dd; &#x1f389;欢迎大家点赞&…

掌握计算机网络基础:从零开始的指南

计算机网络是现代信息社会的重要基石。本文将以简洁明了的方式为基础小白介绍计算机网络的基本概念、分类、以及其在信息时代中的重要作用。 计算机网络在信息时代中的作用 21世纪是以数字化、网络化、信息化为重要特征的信息时代。 计算机网络作为信息的最大载体和传输媒介&…

微信自动加好友工具

批量导入数据到后台&#xff0c;可设置添加速度、间隔时间、验证信息和自动备注等&#xff0c;任务执行时间&#xff0c;后台会自动执行操作。

ubuntu 分区情况

ubuntu系统安装与分区指南 - Philbert - 博客园 (cnblogs.com)https://www.cnblogs.com/liangxuran/p/14872811.html 详解安装Ubuntu Linux系统时硬盘分区最合理的方法-腾讯云开发者社区-腾讯云 (tencent.com)https://cloud.tencent.com/developer/article/1711884

基于flask的猫狗图像预测案例

&#x1f4da;博客主页&#xff1a;knighthood2001 ✨公众号&#xff1a;认知up吧 &#xff08;目前正在带领大家一起提升认知&#xff0c;感兴趣可以来围观一下&#xff09; &#x1f383;知识星球&#xff1a;【认知up吧|成长|副业】介绍 ❤️如遇文章付费&#xff0c;可先看…

uni-app 封装http请求

1.引言 前面一篇文章写了使用Pinia进行全局状态管理。 这篇文章主要介绍一下封装http请求&#xff0c;发送数据请求到服务端进行数据的获取。 感谢&#xff1a; 1.yudao-mall-uniapp: 芋道商城&#xff0c;基于 Vue Uniapp 实现&#xff0c;支持分销、拼团、砍价、秒杀、优…

2024年6月总结 | 软件开发技术月度回顾(第一期)

最新技术资源&#xff08;建议收藏&#xff09; https://www.grapecity.com.cn/resources/ Hello&#xff0c;大家好啊&#xff01;随着欧洲杯和奥运会的临近&#xff0c;2024 年下半年的序幕也随之拉开。回顾 2024 年上半年的技术圈&#xff0c;我们看到了一系列令人振奋的进展…

ELfK logstash filter模块常用的插件 和ELFK部署

ELK之filter模块常用插件 logstash filter模块常用的插件&#xff1a; filter&#xff1a;表示数据处理层&#xff0c;包括对数据进行格式化处理、数据类型转换、数据过滤等&#xff0c;支持正则表达式 grok 对若干个大文本字段进行再分割成一些小字段 (?<字段名…

51单片机嵌入式开发:5、按键、矩阵按键操作及protues仿真

按键、矩阵按键操作及protues仿真 1 按键介绍1.1 按键种类1.2 按键应用场景 2 按键电路3 按键软件设计3.1 按键实现3.2 按键滤波方法3.3 矩阵按键软件设计3.4 按键Protues 仿真 4 按键操作总结 提示 1 按键介绍 1.1 按键种类 按键是一种用于控制电子设备或电路连接和断开的按…

LLM之RAG实战(四十一)| 使用LLamaIndex和Gemini构建高级搜索引擎

Retriever 是 RAG&#xff08;Retrieval Augmented Generation&#xff09;管道中最重要的部分。在本文中&#xff0c;我们将使用 LlamaIndex 实现一个结合关键字和向量搜索检索器的自定义检索器&#xff0c;并且使用 Gemini大模型来进行多个文档聊天。 通过本文&#xff0c;我…

Face_recognition实现人脸识别

这里写自定义目录标题 欢迎使用Markdown编辑器一、安装人脸识别库face_recognition1.1 安装cmake1.2 安装dlib库1.3 安装face_recognition 二、3个常用的人脸识别案例2.1 识别并绘制人脸框2.2 提取并绘制人脸关键点2.3 人脸匹配及标注 欢迎使用Markdown编辑器 本文基于face_re…