目录
第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
颜色检测用来识别图像中所有像素的某种颜色。这个算法必领输人一幅图像和一个颜色,并且返回一个二值图像,显示具有指定颜色的像素。在运行算法前,还要指定一个阈值,即能接受的颜色的公差。
算法的核心对每个像素进行循环扫描,把像素颜色和目标颜色做比较。可以这样写这个循环:
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_::const_iterator it= image.begin();
cv::Mat_::const_iterator itend= image.end();
cv::Mat_::iterator itout= result.begin();
// 获取转换图像的迭代器
if (useLab) {
it = converted.begin();
itend = converted.end();
}
// 遍历每个像素
for ( ; it!= itend; ++it, ++itout) {
// 通过像素与阈值比较
// getDistanceToTargetColor()见完整代码
if (getDistanceToTargetColor(*it)
要先创建迭代器实现扫描循环,在每个迭代步骤中计算当前像素的颜色与目标颜色的距离,检查它是否在公差(maxDist )范围之内。如果是,就在输出图像中赋值 255( 白色),否则就赋值 0( 黑色)。
Lab颜色模型弥补了RGB和CMYK两种色彩模式的不足。它是一种设备无关的颜色模型,也是一种基于生理特征的颜色模型。 Lab颜色模型由三个要素组成,一个要素是亮度(L),a 和b是两个颜色通道。a包括的颜色是从深绿色(低亮度值)到灰色(中亮度值)再到亮粉红色(高亮度值);b是从亮蓝色(低亮度值)到灰色(中亮度值)再到黄色(高亮度值)。因此,这种颜色混合后将产生具有明亮效果的色彩。
Lab模式既不依赖光线,也不依赖于颜料,它是CIE组织确定的一个理论上包括了人眼可以看见的所有色彩的色彩模式。Lab模式弥补了RGB和CMYK两种色彩模式的不足。同RGB颜色空间相比,Lab是一种不常用的色彩空间。它是在1931年国际照明委员会(CIE)制定的颜色度量国际标准的基础上建立起来的。
Lab颜色空间中的取值范围:
下图所示为Lab颜色空间的图示:
cv::threshold()函数创建一个二值图像。这个函数通常用于将所有像素与某个阈值(第三个参数)进行比较,在常规阈值化模式 (CV::THRESH_BINARY)下,将所有大于指定阈值的像素赋值为预定的最大值(第四个参数),将其他像素赋值为 0。
double cv::threshold(
cv::InputArray src,
cv::OutputArray dst,
double thresh,
double maxValue,
int thresholdType
);
阈值的类型 |
作用 |
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 | 矩形阈值 |
要计算两个颜色向量间的距离,可使用这个简单的公式:
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]);
}
计算两个数组(图像)差的绝对值: dst(I)c = abs(src1(I) - src2(I)c),所有数组(图像)必须有相同的数据类型、相同的大小(或ROI大小)。
void cv::absdiff( const CvArr* src1, const CvArr* src2, CvArr* dst );
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)
(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();
/*
colordetector.h头文件
*/
#if !defined COLORDETECT
#define COLORDETECT
#include
#include
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 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(0, 0) = cv::Vec3b(blue, green, red);
// 转换成Lab色彩空间
cv::cvtColor(tmp, tmp, CV_BGR2Lab);
target = tmp.at(0, 0);
}
}
// 设置需要检测的颜色
void setTargetColor(cv::Vec3b color) {
target= color;
}
// 获取需要检测的颜色
cv::Vec3b getTargetColor() const {
return target;
}
};
#endif
/*
颜色检测处理函数
*/
#include "colordetector.h"
#include
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_::const_iterator it= image.begin();
cv::Mat_::const_iterator itend= image.end();
cv::Mat_::iterator itout= result.begin();
// 获取转换图像的迭代器
if (useLab) {
it = converted.begin();
itend = converted.end();
}
// 遍历每个像素
for ( ; it!= itend; ++it, ++itout) {
// 通过像素与阈值比较
if (getDistanceToTargetColor(*it)
/*
主函数
*/
#include
#include
#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;
}