OpenCV - C++实战(05) — 颜色检测

news2024/11/17 3:39:37

目录

第5章  颜色检测

5.1 实现原理

5.2 Lab颜色模型

5.3 cv :: threshold() 阈值函数 

5.4 计算图像之间的距离

5.4.1 公式计算

5.4.2 cv::absdiff()

5.4.3 cv::floodFill() 

 5.5 完整代码


Github代码地址:GitHub - Qinong/OpenCV

第5章  颜色检测

        颜色检测用来识别图像中所有像素的某种颜色。这个算法必领输人一幅图像和一个颜色,并且返回一个二值图像,显示具有指定颜色的像素。在运行算法前,还要指定一个阈值,即能接受的颜色的公差。

5.1 实现原理

         算法的核心对每个像素进行循环扫描,把像素颜色和目标颜色做比较。可以这样写这个循环:

cv::Mat ColorDetector::process(const cv::Mat &image) {

	  result.create(image.size(),CV_8U);

	  // 转换成Lab色彩空间
	  if (useLab)
		  cv::cvtColor(image, converted, CV_BGR2Lab);

	  // 获取迭代器
	  cv::Mat_<cv::Vec3b>::const_iterator it= image.begin<cv::Vec3b>();
	  cv::Mat_<cv::Vec3b>::const_iterator itend= image.end<cv::Vec3b>();
	  cv::Mat_<uchar>::iterator itout= result.begin<uchar>();

	  // 获取转换图像的迭代器
	  if (useLab) {
		  it = converted.begin<cv::Vec3b>();
		  itend = converted.end<cv::Vec3b>();
	  }

	  // 遍历每个像素
	  for ( ; it!= itend; ++it, ++itout) {
          // 通过像素与阈值比较 
          // getDistanceToTargetColor()见完整代码
		  if (getDistanceToTargetColor(*it)<maxDist) {
			  *itout= 255;
		  } 
		  else {
			  *itout= 0;
		  }
	  }

	  return result;
}

        要先创建迭代器实现扫描循环,在每个迭代步骤中计算当前像素的颜色与目标颜色的距离,检查它是否在公差(maxDist )范围之内。如果是,就在输出图像中赋值 255( 白色),否则就赋值 0( 黑色)。

5.2 Lab颜色模型

        Lab颜色模型弥补了RGB和CMYK两种色彩模式的不足。它是一种设备无关的颜色模型,也是一种基于生理特征的颜色模型。 Lab颜色模型由三个要素组成,一个要素是亮度(L),a 和b是两个颜色通道。a包括的颜色是从深绿色(低亮度值)到灰色(中亮度值)再到亮粉红色(高亮度值);b是从亮蓝色(低亮度值)到灰色(中亮度值)再到黄色(高亮度值)。因此,这种颜色混合后将产生具有明亮效果的色彩。

        Lab模式既不依赖光线,也不依赖于颜料,它是CIE组织确定的一个理论上包括了人眼可以看见的所有色彩的色彩模式。Lab模式弥补了RGB和CMYK两种色彩模式的不足。同RGB颜色空间相比,Lab是一种不常用的色彩空间。它是在1931年国际照明委员会(CIE)制定的颜色度量国际标准的基础上建立起来的。

        Lab颜色空间中的取值范围:

  • L分量用于表示像素的亮度,取值范围是[0,100],表示从纯黑到纯白;
  • a表示从红色到绿色的范围,取值范围是[127,-128];
  • b表示从黄色到蓝色的范围,取值范围是[127,-128]。

        下图所示为Lab颜色空间的图示:

5.3 cv :: threshold() 阈值函数 

        cv::threshold()函数创建一个二值图像。这个函数通常用于将所有像素与某个阈值(第三个参数)进行比较,在常规阈值化模式 (CV::THRESH_BINARY)下,将所有大于指定阈值的像素赋值为预定的最大值(第四个参数),将其他像素赋值为 0。

double cv::threshold(  
    cv::InputArray src, 
    cv::OutputArray dst, 
    double thresh,  
    double maxValue,  
    int thresholdType 
);  
  • src: 多通道的输入图像,8bits或者32bits float 
  • dst: 与src的同尺寸、类型、通道数的输出图像
  • thresh: 阈值(介于最大值和最小值之间的一个值)
  • maxval: 与两种阈值类型(THRESH_BINARY & THRESH_BINARY_INV)结合使用,用于指定最大的值
  • type: 阈值的类型

阈值的类型

作用
THRESH_BINARY 最常见的作法,设置一个阈值,大于的时候取最大值(maxval是threshold的参数),否则取0。
 THRESH_BINARY_INV 
 
与THRESH_BINARY相反,大于阈值取0,否则取最大值,maxval对于THRESH_BINARY和THRESH_BINARY_INV有意义。
THRESH_TRUNC 
 
截断型,大于阈值的统一将为阈值,其余不变。
THRESH_TOZERO 
 
过滤型,大于阈值的保持不变,其余设置为0。
THRESH_TOZERO_INV 
 
大于阈值0,其他不变。
THRESH_MASK 
 
THRESH_OTSU 
 
大津阈值
 THRESH_TRIANGLE 矩形阈值

5.4 计算图像之间的距离

5.4.1 公式计算

        要计算两个颜色向量间的距离,可使用这个简单的公式:

return abs (color [0]-target [0])+
            abs (color [1]-target [1])+
            abs (color [2]-target [2]);

 (1)公式计算颜色检测

// 计算两个颜色之间的城区距离
int getColorDistance(const cv::Vec3b& color1, const cv::Vec3b& color2) const {

return abs(color1[0]-color2[0])+
		   abs(color1[1]-color2[1])+
		   abs(color1[2]-color2[2]);
	  }

5.4.2 cv::absdiff()

        计算两个数组(图像)差的绝对值: dst(I)c = abs(src1(I) - src2(I)c),所有数组(图像)必须有相同的数据类型、相同的大小(或ROI大小)。

void cv::absdiff( const CvArr* src1, const CvArr* src2, CvArr* dst );  
  • src1:第一个原数组  
  • src2:第二个原数组  
  • dst:输出数组 

5.4.3 cv::floodFill() 

        cv::floodFill()在判断一个像素时,要检查附近像素的状态,这是为了识别某种颜色的相关区域。用户只需指定一起始位置和允许的误差,就可以找出颜色接近的连续区域。

// 构造函数1
int floodFill(InputArray image, 
              Point seedPoint, 
              Scalar newVal, 
              Rect* rect=0,
              Scalar ioDiff=Scalar(), 
              Scalar upDiff=Scalar(), 
              int flags=4)
// 构造函数2
int floodFill(InputArray image, 
              InputArray mask, 
              Point seedPoint, 
              Scalar newVal, 
              Rect* rect=0,
              Scalar ioDiff=Scalar(), 
              Scalar upDiff=Scalar(), 
              int flags=4)
  • image:输入图像,1个或3个通道的8位或浮点图像。
  • mask:只有第二个版本才有该参数,表示作为掩膜。它应该是单通道,8位,长和宽都比输入图像大两个点的图像。因为漫水填充需要使用以及更细掩膜,所以对这个mask参数,我们一定要将其准本好并填在此处。需要注意的是,漫水填充不会填充mask的非零像素区域。例如,一个边缘检测算子的输出可以用来作为掩膜,以防止填充到边缘。同样的,也可以在多次的函数调用中使用同一个掩膜,以保证填充的区域不会重叠。需要注意的是,mask会比需填充的图像大,所以mask与输入图像(x, y)像素点对应的坐标为(x,+1 y+1)。
  • seedPoint:漫水填充算法的起始点。
  • newVal:像素被染色的值。
  • rect:默认值是0,可选参数,用于设置floodFill函数将要重绘区域的最小边界矩形区域。
  • loDiff:默认值是Scalar(),表示当前观察像素值与其部件邻域像素值或者待加入该部件的种子像素之间的亮度或颜色之负差的最大值。
  • upDiff:默认值是Scalar(),表示当前观察像素值与其部件邻域像素值或者待加入该部件的种子像素之间的亮度或颜色之正差的最大值。
  • flags:操作标志符。

(1) cv::absdiff()检测颜色

	// 使用cv::floodFill函数
    ColorDetector colordetector(230, 190, 130, 45, true);  // 第3个构造函数
	cv::floodFill(image,            // 输入/输出图像
		cv::Point(100, 50),         // 起始点
		cv::Scalar(255, 255, 255),  // 填充颜色
		(cv::Rect*)0,               // 填充颜色的边界距离
		cv::Scalar(35, 35, 35),     // 偏差的最小/最大阈值
		cv::Scalar(35, 35, 35),     // 正差阈值,两个阈值通常相等
		cv::FLOODFILL_FIXED_RANGE); // 与起始点像素比较
	cv::namedWindow("Image2");
	cv::Mat image2 = colordetector(image);
	cv::imshow("Image2", image2);
	cv::waitKey();

 5.5 完整代码

/*
    colordetector.h头文件
*/
#if !defined COLORDETECT
#define COLORDETECT

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>

class ColorDetector {

  private:
	  int maxDist;     // 允许的最小差距
	  cv::Vec3b target;    // 目标颜色
	  cv::Mat converted;   // 转换的图像
	  bool useLab;         // 是否使用Lab色彩空间
	  cv::Mat result;  // 存储二值映射结果的图像

  public:
	  // 不同的构造函数
	  // 在此初始化默认参数
	  ColorDetector() : maxDist(100), target(0,0,0), useLab(false) {}
	  ColorDetector(bool useLab) : maxDist(100), target(0,0,0), useLab(useLab) {}
	  ColorDetector(uchar blue, uchar green, uchar red, int mxDist=100, bool useLab=false): maxDist(mxDist), useLab(useLab) { 
		  // 设置需要检测的颜色
		  setTargetColor(blue, green, red);
	  }

	  // 计算与目标颜色的距离
	  int getDistanceToTargetColor(const cv::Vec3b& color) const {
		  return getColorDistance(color, target);
	  }

	  // 计算两个颜色之间的城区距离
	  int getColorDistance(const cv::Vec3b& color1, const cv::Vec3b& color2) const {

		  return abs(color1[0]-color2[0])+
					abs(color1[1]-color2[1])+
					abs(color1[2]-color2[2]);
	  }

	  cv::Mat process(const cv::Mat &image);
      
	  cv::Mat operator()(const cv::Mat &image) {
		  cv::Mat input;
		 
		  if (useLab) { 
			  cv::cvtColor(image, input, CV_BGR2Lab);    // 转换成Lab色彩空间
		  }
		  else {
			  input = image;
		  }

		  cv::Mat output;
		  cv::absdiff(input,cv::Scalar(target),output);   // 计算目标图像的绝对距离
	      std::vector<cv::Mat> images;
	      cv::split(output,images);    // 分割图像的通道
	      output= images[0]+images[1]+images[2];   // 合并3个通道
	      
		  // 设定阈值
          cv::threshold(output,  // 输入图像
                        output,  // 输出图像
                        maxDist, // 阈值
                        255,     // 最大值
                        cv::THRESH_BINARY_INV); // 阈值类型
	
	      return output;
	  }


	  //设置颜色距离的阈值
	  void setColorDistanceThreshold(int distance) {
		  if (distance<0)
			  distance=0;
		  maxDist= distance;
	  }

	  // 获取颜色距离的阈值
	  int getColorDistanceThreshold() const {
		  return maxDist;
	  }
	  
	  // 设置需要检测的颜色
	  void setTargetColor(uchar blue, uchar green, uchar red) {
		  // BGR的次序
		  target = cv::Vec3b(blue, green, red);

		  if (useLab) {
			  // 创建临时的单像素图像
			  cv::Mat tmp(1, 1, CV_8UC3);
			  tmp.at<cv::Vec3b>(0, 0) = cv::Vec3b(blue, green, red);

			  // 转换成Lab色彩空间
			  cv::cvtColor(tmp, tmp, CV_BGR2Lab);
			  target = tmp.at<cv::Vec3b>(0, 0);
		  }
	  }

	  // 设置需要检测的颜色
	  void setTargetColor(cv::Vec3b color) {
		  target= color;
	  }

	  // 获取需要检测的颜色
	  cv::Vec3b getTargetColor() const {
		  return target;
	  }
};


#endif
/*
    颜色检测处理函数
*/
#include "colordetector.h"
#include <vector>
	
cv::Mat ColorDetector::process(const cv::Mat &image) {

	  result.create(image.size(),CV_8U);

	  // 转换成Lab色彩空间
	  if (useLab)
		  cv::cvtColor(image, converted, CV_BGR2Lab);

	  // 获取迭代器
	  cv::Mat_<cv::Vec3b>::const_iterator it= image.begin<cv::Vec3b>();
	  cv::Mat_<cv::Vec3b>::const_iterator itend= image.end<cv::Vec3b>();
	  cv::Mat_<uchar>::iterator itout= result.begin<uchar>();

	  // 获取转换图像的迭代器
	  if (useLab) {
		  it = converted.begin<cv::Vec3b>();
		  itend = converted.end<cv::Vec3b>();
	  }

	  // 遍历每个像素
	  for ( ; it!= itend; ++it, ++itout) {
          // 通过像素与阈值比较 
		  if (getDistanceToTargetColor(*it)<maxDist) {
			  *itout= 255;
		  } 
		  else {
			  *itout= 0;
		  }
	  }

	  return result;
}
/*
    主函数
*/
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "colordetector.h"

int main()
{
    // step1
    // 创建检测图像的对象
	ColorDetector cdetect;

    // step2
    // 读取原图像
	cv::Mat image= cv::imread("Ferrar_F8.png");
	if (image.empty())
		return 0; 
	cv::namedWindow("Image");
	cv::imshow("Image", image);

    // step3
    // 设置输入参数与输出图像处理效果
	cdetect.setTargetColor(230,190,130);
	cv::namedWindow("Image1");
	cv::Mat image1 = cdetect.process(image);
	cv::imshow("Image1",image1);
    

	// 使用cv::floodFill函数
    ColorDetector colordetector(230, 190, 130, 45, true);  // 第3个构造函数
	cv::floodFill(image,            // 输入/输出图像
		cv::Point(100, 50),         // 起始点
		cv::Scalar(255, 255, 255),  // 填充颜色
		(cv::Rect*)0,               // 填充颜色的边界距离
		cv::Scalar(35, 35, 35),     // 偏差的最小/最大阈值
		cv::Scalar(35, 35, 35),     // 正差阈值,两个阈值通常相等
		cv::FLOODFILL_FIXED_RANGE); // 与起始点像素比较
	cv::namedWindow("Image2");
	cv::Mat image2 = colordetector(image);
	cv::imshow("Image2", image2);
	cv::waitKey();

	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

腾讯云轻量级服务器哪个镜像比较好?

腾讯云轻量应用服务器镜像是什么&#xff1f;镜像就是操作系统&#xff0c;轻量服务器镜像系统怎么选择&#xff1f;如果是用来搭建网站腾讯云百科txybk.com建议选择选择宝塔Linux面板腾讯云专享版&#xff0c;镜像系统根据实际使用来选择&#xff0c;腾讯云百科来详细说下腾讯…

Express框架开发接口之实现分页功能

1、是什么&#xff1f; 在我们做数据查询的时候&#xff0c;如果数据量很大&#xff0c;比如几万条数据&#xff0c;放在一个页面显示的话显然不友好&#xff0c;这时候就需要采用分页显示的形式&#xff0c;如每次只显示10条数据 要实现分页功能&#xff0c;实际上就是从结…

代码随想录Day33 LeetCode T62不同路径 LeetCode T63 不同路径II

前言 动规五部曲 1.确定dp数组含义 2.确定递推公式 3.初始化数组 4.确定遍历方式 5.打印dp数组查看分析问题 LeetCode T62 不同路径 题目链接:62. 不同路径 - 力扣&#xff08;LeetCode&#xff09; 题目思路: 注:n行m列而不是m行n列 1.确定dp数组含义 代表到达此下标有多少条…

云尘-AI-Web-1.0

继续&#xff01; 开扫 继续先测试web sql注入 直接sqlmap跑 通过注入 &#xff08;sqlmap查询方式省略&#xff09; 存在systemuser 不知道会不会是电脑的密码 我们解密一下然后直接试试看 然后失败 这里就没有思路了 但是我们刚刚存在一个目录 我们再扫扫看 无果 换另一个…

C# Onnx DBNet 检测条形码

效果 项目 代码 using Microsoft.ML.OnnxRuntime; using Microsoft.ML.OnnxRuntime.Tensors; using OpenCvSharp; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Numerics; using System.Runtime.InteropServices.…

【广州华锐互动】风景区规划VR虚拟现实教学系统

风景区规划VR虚拟现实教学系统是一种新兴的教学工具&#xff0c;它可以通过虚拟现实技术&#xff0c;为学生提供一种沉浸式的、实时的、全方位的景区规划体验。 在风景区规划VR虚拟现实教学系统中&#xff0c;学生可以通过虚拟现实技术&#xff0c;实时地与景区进行交互。他们可…

人大金仓助力中国人民银行征信中心业务系统异地容灾优化升级

日前&#xff0c;人大金仓助力中国人民银行应收账款融资服务平台异地容灾项目顺利上线&#xff0c;保证了平台系统运行的连续性和数据安全&#xff0c;为充分发挥平台的融资功能&#xff0c;缓解中小微企业融资难提供了强有力的保障。 缓解中小微企业融资难 中国人民银行构于2…

「Qt中文教程指南」如何创建基于Qt Widget的应用程序(四)

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写&#xff0c;所有平台无差别运行&#xff0c;更提供了几乎所有开发过程中需要用到的工具。如今&#xff0c;Qt已被运用于超过70个行业、数千家企业&#xff0c;支持数百万设备及应用。 本文描述了如何使用…

【音视频 | wav】wav音频文件格式详解——包含RIFF规范、完整的各个块解析、PCM转wav代码

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

Kafka - 3.x 消费者 生产经验不完全指北

文章目录 生产经验之Consumer事务生产经验—数据积压&#xff08;消费者如何提高吞吐量&#xff09; 生产经验之Consumer事务 Kafka引入了消费者事务&#xff08;Consumer Transactions&#xff09;来确保在消息处理期间维护端到端的数据一致性。这使得消费者能够以事务的方式…

springboot2.x使用@RestControllerAdvice实现通用异常捕获

文章目录 demo地址实现效果引入基础类准备1.通用枚举与错误状态枚举2.定义通用返回结果3.自定义业务异常 统一异常捕获测试 demo地址 demo工程地址 实现效果 当我们输入1时&#xff0c;正常的返回通用的响应结果当我们输入2时&#xff0c;抛出异常&#xff0c;被捕获然后返回…

43基于matlab针对压缩重构感知中的稀疏优化问题,实现L1范数最小化问题求解。

基于matlab针对压缩重构感知中的稀疏优化问题&#xff0c;实现L1范数最小化问题求解&#xff0c;首先构造信号&#xff0c;并进行离散余弦变换&#xff0c;保证稀疏度&#xff0c;采用多个方法进行稀疏重构&#xff0c;分别有&#xff0c;&#xff08;1&#xff09;基于L1正则的…

代数结构上的泛型算法

一&#xff0c;半群算法 //半群 class SemiGroup { public://枚举只去掉1个数&#xff08;v.size()>1&#xff09;&#xff0c;剩下的数做p累积运算的结果template<typename T, typename Tfunc>static vector<T> allExceptOne(vector<T>& v, Tfunc p…

SOLIDWORKS 2024最新版价格:SW专业版|白金版多少钱一套?

从一开始&#xff0c;SOLIDWORKS 就一直站在让设计对每位设计师和工程师来说都触手可及的最前沿。我们的任务是通过功能强大且易于使用的产品开发解决方案&#xff0c;在创造、协作和提供创新的产品体验方面助您一臂之力。SOLIDWORKS 2024 延续了这一期望&#xff0c;同时开启了…

带你从0开始学习自动化框架Airtest

现在市面上做UI自动化的框架很多&#xff0c;包括我们常用的Web自动化框架Selenium&#xff0c;移动端自动化框架Appium。 虽然Selenium和Appium分属同源&#xff0c;而且API都有很多相同的地方&#xff0c;可以无损耗切换&#xff0c;但是还是需要引入不同的库&#xff0c;而…

PO- array数据只能接收到一条的问题处理

问题描述&#xff1a; 发送方明明是array多条数据&#xff0c;接收方通过PO接收后只有一条数据 原因分析&#xff1a; SAP AAE类适配器是按照XML格式识别&#xff0c;虽然设置为[0unbound]&#xff0c;但是由于JSON的array[]格式过来后不会自动变成多组XML&#xff0c;所以需…

【探索AI潜能,连结现代通讯】相隔万里,我们与AI一同赏月。

1️⃣写在前面 近年来&#xff0c;AI得到了迅猛的发展&#xff0c;尤其是大模型的出现受到了广泛的关注和讨论。ChatGPT、文心一言等纷纷登场&#xff0c;可谓是百家争鸣。 而AI大模型所延申出的子项目如AI绘画、AI写作等&#xff0c;在各自的领域展示出了惊人的潜力。 最圆…

风格化角色渲染方法

一、前言 二、基础结构 种类较多的风格化渲染风格 解帧分析 三、光照 漫反射和高光 增加卡通风 头发的高光 环状高光&#xff0c;物理性质的不同 解决高光形状不可控的问题 瞳孔的焦散效应 四、阴影 五、描边 六、Other

全网最详细的centos中修改tomact的端口号

&#x1f3c5;我是默&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《Linux》。&#x1f3af;&#x1f3af; &#x1f680;无论你是编程小白&#xff0c;还是有一定基础的程序员&#xff0c;这个专…

linux--线程共享内存

Linux线程共享内存空间是指多个线程可以访问同一个内存区域&#xff0c;这些线程共享该内存区域的内容。 代码&#xff1a; #include <stdio.h> #include <pthread.h>// share memoryint data 0; //定义一个全局变量datavoid *fun1(void *arg) {printf("t1:…