利用HSV中的常见色彩区分度比RGB中的色彩区分度更明显的优势,再结合inRange()函数可以将图片中不同的颜色给提取出来。
使用 OpenCV 中的 cvtColor 函数,并设置参数为CV_BGR2HSV,那么所得的H、S、V值范围分别是[0,180),[0,255),[0,255),这时我们可以查下面的表格来确定颜色的大致区间。
OpenCV中的inRange()函数可实现二值化功能(这点类似threshold()函数),更关键的是可以同时针对多通道进行操作,使用起来非常方便!主要是将在两个阈值内的像素值设置为白色(255),而不在阈值区间内的像素值设置为黑色(0)。
要特别注意的是:该函数输出的是一幅二值化之后的图像。
程序如下:
#include <iostream>
#include "12_opencv_mat.h"
using namespace std;
void QuickDemo::inrange_demo(Mat &image)
{
Mat hsv = Mat::zeros(image.size(), image.type());
//我们这里输入的是一张白底图像
cvtColor(image,hsv,COLOR_BGR2HSV);
/*
* inRange
* 函数功能:
* inRange()函数可实现二值化功能(这点类似threshold()函数),
* 更关键的是可以同时针对多通道进行操作,使用起来非常方便!
* 主要是将在两个阈值内的像素值设置为白色(255),而不在阈值区间内的像素值设置为黑色(0)。
*
* void inRange(InputArray src, InputArray lowerb, InputArray upperb, OutputArray dst)
*
* 参数1:输入要处理的图像,可以为单通道或多通道。
* 参数2:包含下边界(最小值)的数组或标量。
* 参数3:包含上边界(最大值)的数组或标量。
* 参数4:输出图像,与输入图像src其大小和通道数量且为 CV_8U 类型。
*
* 要特别注意的是:该函数输出的DST是一幅二值化之后的图像。
*/
Mat mask;
inRange(hsv, Scalar(0,0,221), Scalar(180,30,255), mask);//注意集合的开闭
imshow("输出图像", mask);
}
运行程序:
理论上,输入图像中除了白色部分都应该变成黑色。但从输入图像和输出图像对比来看,输入图像中的左半部分的颜色有点偏粉红并没有被识别出来。所以这种处理有些粗糙。
下面程序要求弄懂对mask的理解。
12_opencv_mat.h
#pragma once
#ifndef _12_OPENCV_MAT_H
#define _12_OPENCV_MAT_H
#include <opencv2/opencv.hpp>
using namespace cv;
class QuickDemo {
public:
void inrange_demo(Mat& image);
};
#endif
12_opencv_mat.cpp
#include <iostream>
#include "12_opencv_mat.h"
using namespace std;
void QuickDemo::inrange_demo(Mat &image)
{
Mat hsv = Mat::zeros(image.size(), image.type());
cvtColor(image,hsv,COLOR_BGR2HSV);
/*
* inRange
* 函数功能:
* inRange()函数可实现二值化功能(这点类似threshold()函数),
* 更关键的是可以同时针对多通道进行操作,使用起来非常方便!
* 主要是将在两个阈值内的像素值设置为白色(255),而不在阈值区间内的像素值设置为黑色(0)。
*
* void inRange(InputArray src, InputArray lowerb, InputArray upperb, OutputArray dst)
*
* 参数1:输入要处理的图像,可以为单通道或多通道。
* 参数2:包含下边界(最小值)的数组或标量。
* 参数3:包含上边界(最大值)的数组或标量。
* 参数4:输出图像,与输入图像src其大小和通道数量且为 CV_8U 类型。
*
* 要特别注意的是:该函数输出是一幅二值化之后的图像。
*/
Mat mask;
//我们这里输入的是一张白底图像
inRange(hsv, Scalar(0,0,221), Scalar(180,30,255), mask);//注意集合的开闭
//mask中白色背景的值为255,人物部分的值为0
//创建一个空白图像
Mat redback = Mat::zeros(image.size(), image.type());
redback = Scalar(22,22,222);//红色
bitwise_not(mask,mask);//此时mask中白色背景的值为0,人物部分的值为255
/*
* copyTo的函数功能:将输入图像复制到输出图像。
* copyTo是图像(Mat)对象的成员方法,有两种
* void Mat::copyTo(OutputArray m)
* 和
* void Mat::copyTo(OutputArray m, InputArray mask)
*
* 参数:
* m – 输出图像。 如果在操作之前它没有适当的大小或类型,则重新分配它。
* mask – 掩码操作(标记操作). 它的非零元素表示需要复制哪些矩阵元素。
*
* 使用第二个函数时必须要提供mask参数,没有不能写NULL,否则程序报错。
*/
/*
该函数把输入图像(数组)image中被选中的(mask不为0对应的部分)元素
拷贝到输出图像(redback)中,需要注意的是mask是一个和dst or src大小完全一致的数组(图像)。
*/
image.copyTo(redback,mask);
/*
* 能够进行下述操作的前提是,mask是一个和dst or src大小完全一致的数组(图像)。
if (mask(I) ! = 0)
dst(I) = src(I);
else
dst(I) = dst(I);
*/
imshow("输出图像", redback);
}
main.cpp
#include <iostream>
#include "12_opencv_mat.h"
using namespace std;
int main(int argc,char** argv)
{
//使用Mat(matrix -- 矩阵)这种类型来读取通过指定路径的图像信息并存放到变量picture中。
//图像从本质上来说都是二维的数组(矩阵)
Mat picture = imread("鱼头人.jpeg", IMREAD_COLOR);
if (picture.empty())
{
cout << "could not Load image." << endl;
system("pause");
return -1;
}
// namedWindow("输入窗口",WINDOW_FREERATIO);
imshow("输入窗口", picture);
QuickDemo qd;
qd.inrange_demo(picture);
waitKey(0);
destroyAllWindows();
system("pause");
return 0;
}
最后运行结果如下:
可以看出来处理后的结果非常粗糙,这种方式只适合背景与人物颜色相差非常明显(区分度很大)的图像的处理。