OpenCV计算机视觉开发实践:基于Qt C++ - 商品搜索 - 京东
灰度图像大多通过算子寻找边缘和区域生长融合来分割图像。彩色图像增加了色彩信息,可以通过不同的色彩值来分割图像,常用彩色空间HSV/HIS、RGB、LAB等都可以用于分割。本节使用inRange函数来实现阈值化,跟前面的阈值化方法一样,只不过在实现时用阈值范围来替代固定阈值。inRange函数提供了一种物体检测的手段,用基于像素值范围的方法,在HSV色彩空间检测物体,从而达到分割的效果。
9-12所示是HSV圆柱体,表示HSV的颜色空间。HSV(Hue、Saturation、Value的首字母,分别表示颜色的色相、饱和度、强度)色彩空间是一种类似于RGB的颜色表示方式。Hue通道是颜色类型,在需要根据颜色来分割物体的应用中非常有效。Saturation的变化从不饱和到完全饱和,对应图9-12所示的灰色过度到阴影(没有白色成分)。Value描述了颜色的强度或者说亮度。

图9-12
HSV是一种比较直观的颜色模型,所以在许多图像编辑工具中应用比较广泛,这个模型中颜色的参数分别是:色调(H, Hue)、饱和度(S, Saturation)、明度(V, Value)。色调H用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,品红为300°;饱和度S表示颜色接近光谱色的程度。一种颜色,可以看成是某种光谱色与白色混合的结果。其中光谱色所占的比例愈大,颜色接近光谱色的程度就愈高,颜色的饱和度也就愈高。饱和度高,颜色则深而艳。光谱色的白光成分为0,饱和度达到最高。通常取值范围为0%~100%,值越大,颜色越饱和。明度V表示颜色明亮的程度。对于光源色,明度值与发光体的光亮度有关;对于物体色,此值和物体的透射比或反射比有关。通常取值范围为0%(黑)到100%(白)。HSV颜色空间模型(圆锥模型)如图9-13所示。
图9-13
由于RGB色彩空间是由三个通道来编码颜色,所以难以根据颜色来分割物体,而HSV中只有Hue一个通道表示颜色。此时可以用函数cvtColor将BGR转换到HSV色彩空间,然后用函数inRange根据HSV设置的范围检测目标。该函数声明如下:
void inRange (InputArray src, InputArray lowerb, InputArray upperb, OutputArray dst);
其中src表示输入图像;lowerb 表示H、S、V的最小值;upperb表示H、S、V的最大值;dst表示输出图像,要和输入图像有相同的尺寸且为CV_8U类。
OpenCV中的inRange()函数可实现二值化功能(这点类似threshold函数),更关键的是可以同时针对多通道进行操作。通俗来讲,这个函数就是判断src中每一个像素是否在[lowerb,upperb]之间,注意集合的开闭。如果结果为是,那么在dst相应像素位置填上255,反之则是0。一般我们把dst当作一个mask来用。对于单通道图像,如果一幅灰度图像的某个像素的灰度值在指定的高、低阈值范围之内,则在dst图像中令该像素值为255,否则令其为0,这样就生成了一幅二值化的输出图像。对于三通道图像,每个通道的像素值都必须在规定的阈值范围内。下面我们看一个用函数inRange进行颜色分割的例子。
【例9.5】直接用HSV体系进行颜色分割
(1)新建一个控制台工程,工程名是test。
(2)打开main.cpp,输入代码如下:
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <opencv2/imgproc/types_c.h>
#include <iostream>
using namespace std;
using namespace cv;
//输入图像
Mat img;
//灰度值归一化
Mat bgr;
//HSV图像
Mat hsv;
//色相
int hmin = 0;
int hmin_Max = 360;
int hmax = 180;
int hmax_Max = 180;
//饱和度
int smin = 0;
int smin_Max = 255;
int smax = 255;
int smax_Max = 255;
//亮度
int vmin = 106;
int vmin_Max = 255;
int vmax = 255;
int vmax_Max = 255;
//显示原图的窗口
string windowName = "src";
//输出图像的显示窗口
string dstName = "dst";
//输出图像
Mat dst;
void callBack(int, void*)//回调函数
{
dst = Mat::zeros(img.size(), img.type()); //输出图像分配内存
Mat mask; //掩码
inRange(hsv, Scalar(hmin, smin, vmin), Scalar(hmax, smax, vmax), mask);
//掩模到原图的转换
for (int r = 0; r < bgr.rows; r++)
for (int c = 0; c < bgr.cols; c++)
if (mask.at<uchar>(r, c) == 255)
dst.at<Vec3b>(r, c) = bgr.at<Vec3b>(r, c);
//输出图像
imshow(dstName, dst);
//保存图像
//dst.convertTo(dst, CV_8UC3, 255.0, 0);
imwrite("HSV_inRange.jpg", dst);
}
int main(int argc, char** argv)
{
//输入图像
img = imread("myclr.jpg");
if (!img.data || img.channels() != 3)
return -1;
imshow(windowName, img);
bgr = img.clone();
//颜色空间转换
cvtColor(bgr, hsv, CV_BGR2HSV);
//定义输出图像的显示窗口
namedWindow(dstName, WINDOW_GUI_EXPANDED);
//调节色相 H
createTrackbar("hmin", dstName, &hmin, hmin_Max, callBack);
createTrackbar("hmax", dstName, &hmax, hmax_Max, callBack);
//调节饱和度 S
createTrackbar("smin", dstName, &smin, smin_Max, callBack);
createTrackbar("smax", dstName, &smax, smax_Max, callBack);
//调节亮度 V
createTrackbar("vmin", dstName, &vmin, vmin_Max, callBack);
createTrackbar("vmax", dstName, &vmax, vmax_Max, callBack);
callBack(0, 0);
waitKey(0);
return 0;
}
代码中,我们首先载入图像,然后利用函数cvtColor进行颜色空间转换,转换为HSV颜色空间。接着我们为调节色相H、饱和度 S和亮度V分别创建了滑块,这样可以通过调节不同的颜色、饱和度、亮度区间来定义信息。最后调用了自定义函数callBack,callBack函数中调用inRange进行颜色分割。
(3)保存工程并运行,运行结果如图9-14所示。

图9-14

