上一篇博客记录了在旭日x3派上运行opencv进行颜色识别和在图像中的定位。在处理实际问题时颜色的阈值调整时会遇到一些问题,比如要识别的颜色不一定是经典红或绿或黄,此时对于HSV色域的阈值选择则需要实时调整了。
另外这里补充下为什么要选用HSV色域而不用RGB色域,比如黄色在HSV色域中描述色彩值H为35-77,但是在RGB中便不好描述一个范围。HSV色域H代表色彩,S代表深度,V代表亮度,如下图圆锥体表示出来的色彩空间。
了解了色域问题,接下来描述下遇到的问题。
采用如上表的颜色阈值进行识别绿色和红色的模型时效果良好,但是识别黄色时就出现问题了:识别得到红色区域面积最大,因而判定为红,但是明明颜色的阈值都是按照阈值表来设定的,甚至黄色识别部分一点都识别不到。
//黄色阈值设定
Scalar lower_yellow(26,43,46);
Scalar upper_yellow(34,255,255);
//黄色阈值设定 Scalar lower_yellow(26,43,46); Scalar upper_yellow(34,255,255);
//黄色阈值设定 Scalar lower_yellow(26,43,46); Scalar upper_yellow(34,255,255);
复制
有了问题就需要解决,这种问题一般是因为理论上的阈值和实际的颜色有偏差导致的,因此需要一个工具可以让我们根据自己要识别的物体具体实时调整,来获取对应的阈值。编写这个小工具需要用到滑块控件:
滑块控件
要实现阈值的选择,我们不可能一个数值一个数值的输入再运行看效果,然后再试。所以要使用到滑块控件,在图片中实时调节并可以观察到效果。
int cv::createTrackbar(const String & trackbarname,
const String & winname,
int * value,
int count,
TrackbarCallback onChange = 0,
void * userdata = 0
)
int cv::createTrackbar(const String & trackbarname, const String & winname, int * value, int count, TrackbarCallback onChange = 0, void * userdata = 0 )
int cv::createTrackbar(const String & trackbarname, const String & winname, int * value, int count, TrackbarCallback onChange = 0, void * userdata = 0 )
复制
int cv::createTrackbar(const String & trackbarname, const String & winname, int * value, int count, TrackbarCallback onChange = 0, void * userdata = 0 )
int cv::createTrackbar(const String & trackbarname, const String & winname, int * value, int count, TrackbarCallback onChange = 0, void * userdata = 0 )
复制
int cv::createTrackbar(const String & trackbarname, const String & winname, int * value, int count, TrackbarCallback onChange = 0, void * userdata = 0 )
int cv::createTrackbar(const String & trackbarname, const String & winname, int * value, int count, TrackbarCallback onChange = 0, void * userdata = 0 )
int cv::createTrackbar(const String & trackbarname, const String & winname, int * value, int count, TrackbarCallback onChange = 0, void * userdata = 0 )
opencv官方提供了以上滑块控件api。第一个输入参数滑块的名称,第二个参数是滑块显示在的窗口名称,第三个参数是滑块的值赋予的变量的地址,第四个输入参数是滑块的最大值(该api默认最小值为0),第五个输入参数回调函数名,第六输入参数会直接原样输入给回调函数。
创建工程
老样子在旭日x3派跑destop系统,需要提高速度可以试试交叉编译。在qtcreator新建一个Non-Qt Project——Plain C++ Application的项目,其余默认即可。打开 .pro文件配置参数。首先添加头文件路径:
INCLUDEPATH += /usr/include/opencv4 \
/usr/include/opencv2 \
/usr/include/opencv
INCLUDEPATH += /usr/include/opencv4 \ /usr/include/opencv2 \ /usr/include/opencv
INCLUDEPATH += /usr/include/opencv4 \ /usr/include/opencv2 \ /usr/include/opencv
复制
INCLUDEPATH += /usr/include/opencv4 \ /usr/include/opencv2 \ /usr/include/opencv
INCLUDEPATH += /usr/include/opencv4 \ /usr/include/opencv2 \ /usr/include/opencv
复制
INCLUDEPATH += /usr/include/opencv4 \ /usr/include/opencv2 \ /usr/include/opencv
INCLUDEPATH += /usr/include/opencv4 \ /usr/include/opencv2 \ /usr/include/opencv
INCLUDEPATH += /usr/include/opencv4 \ /usr/include/opencv2 \ /usr/include/opencv
再添加相应的库,使用旭日x3派,如果是使用apt install libopencv-dev安装编译好的opencv库,那么其库文件路径就是/usr/lib/x86_64-linux-gnu/.如果是另外编译安装的可以在终端中输入 dpkg -L libopencv-dev 查看包的安装路径。
LIBS += -L/usr/lib/x86_64-linux-gnu -lopencv_core -lopencv_highgui -lopencv_imgcodecs \
-lopencv_calib3d -lopencv_features2d -lopencv_imgproc -lopencv_photo -lopencv_superres \
-lopencv_stitching
LIBS += -L/usr/lib/x86_64-linux-gnu -lopencv_core -lopencv_highgui -lopencv_imgcodecs \ -lopencv_calib3d -lopencv_features2d -lopencv_imgproc -lopencv_photo -lopencv_superres \ -lopencv_stitching
LIBS += -L/usr/lib/x86_64-linux-gnu -lopencv_core -lopencv_highgui -lopencv_imgcodecs \ -lopencv_calib3d -lopencv_features2d -lopencv_imgproc -lopencv_photo -lopencv_superres \ -lopencv_stitching
复制
LIBS += -L/usr/lib/x86_64-linux-gnu -lopencv_core -lopencv_highgui -lopencv_imgcodecs \ -lopencv_calib3d -lopencv_features2d -lopencv_imgproc -lopencv_photo -lopencv_superres \ -lopencv_stitching
LIBS += -L/usr/lib/x86_64-linux-gnu -lopencv_core -lopencv_highgui -lopencv_imgcodecs \ -lopencv_calib3d -lopencv_features2d -lopencv_imgproc -lopencv_photo -lopencv_superres \ -lopencv_stitching
复制
LIBS += -L/usr/lib/x86_64-linux-gnu -lopencv_core -lopencv_highgui -lopencv_imgcodecs \ -lopencv_calib3d -lopencv_features2d -lopencv_imgproc -lopencv_photo -lopencv_superres \ -lopencv_stitching
LIBS += -L/usr/lib/x86_64-linux-gnu -lopencv_core -lopencv_highgui -lopencv_imgcodecs \ -lopencv_calib3d -lopencv_features2d -lopencv_imgproc -lopencv_photo -lopencv_superres \ -lopencv_stitching
LIBS += -L/usr/lib/x86_64-linux-gnu -lopencv_core -lopencv_highgui -lopencv_imgcodecs \ -lopencv_calib3d -lopencv_features2d -lopencv_imgproc -lopencv_photo -lopencv_superres \ -lopencv_stitching
说明:
- LIBS += :表示将后面的库文件添加到已有库文件的列表中。
- -L/usr/lib/x86_64-linux-gnu:表示告诉链接器在 /usr/lib/x86_64-linux-gnu 目录下查找库文件。
- -lopencv_core -lopencv_highgui等:表示要链接的库文件分别是libopencv_core.a、libopencv_highgui.a等。简单来说,这个指令的作用是将 OpenCV 的核心库、高级图形库等链接到你的程序中,以便你可以使用它们的函数和类。
效果
代码以无回调函数形式应用滑块控件。
#include <iostream>
#include<opencv4/opencv2/opencv.hpp>
using namespace cv;
using namespace std;
/* 定义HSV的阈值
* lh、uh分别为色调h的低阈值和高阈值
* ls、us分别为饱和度s的低阈值和高阈值
* lv、uv分别为亮度v的低阈值和高阈值
* hsv储存将RGB色域图像转为HSV色域的图像
*/
int lh,ls,lv,uh,us,uv; Mat hsv;
Mat dst;
int main()
{
//建立窗口命名为dst
namedWindow("dst", WINDOW_AUTOSIZE);
Mat img = imread("/home/wch/模板/cv_1/yellow.jpg");
//RGB转HSV色域
cvtColor(img, hsv, COLOR_RGB2HSV);
//创建控件
//没有回调函数时,回调函数名那个参数设置为0
createTrackbar("lh","dst",&lh,255,0);
createTrackbar("ls","dst",&ls,255,0);
createTrackbar("lv","dst",&lv,255,0);
createTrackbar("uh","dst",&uh,255,0);
createTrackbar("us","dst",&us,255,0);
createTrackbar("uv","dst",&uv,255,0);
while(1)
{
inRange(hsv, Scalar(lh,ls,lv), Scalar(uh,us,uv), dst);
imshow("dst", dst);
//延时等待,按回车退出
if(waitKey(2) == 13) //27==esc 13==enter
break;
}
destroyAllWindows();
return 0;
}
#include <iostream> #include<opencv4/opencv2/opencv.hpp> using namespace cv; using namespace std; /* 定义HSV的阈值 * lh、uh分别为色调h的低阈值和高阈值 * ls、us分别为饱和度s的低阈值和高阈值 * lv、uv分别为亮度v的低阈值和高阈值 * hsv储存将RGB色域图像转为HSV色域的图像 */ int lh,ls,lv,uh,us,uv; Mat hsv; Mat dst; int main() { //建立窗口命名为dst namedWindow("dst", WINDOW_AUTOSIZE); Mat img = imread("/home/wch/模板/cv_1/yellow.jpg"); //RGB转HSV色域 cvtColor(img, hsv, COLOR_RGB2HSV); //创建控件 //没有回调函数时,回调函数名那个参数设置为0 createTrackbar("lh","dst",&lh,255,0); createTrackbar("ls","dst",&ls,255,0); createTrackbar("lv","dst",&lv,255,0); createTrackbar("uh","dst",&uh,255,0); createTrackbar("us","dst",&us,255,0); createTrackbar("uv","dst",&uv,255,0); while(1) { inRange(hsv, Scalar(lh,ls,lv), Scalar(uh,us,uv), dst); imshow("dst", dst); //延时等待,按回车退出 if(waitKey(2) == 13) //27==esc 13==enter break; } destroyAllWindows(); return 0; }
#include <iostream> #include<opencv4/opencv2/opencv.hpp> using namespace cv; using namespace std; /* 定义HSV的阈值 * lh、uh分别为色调h的低阈值和高阈值 * ls、us分别为饱和度s的低阈值和高阈值 * lv、uv分别为亮度v的低阈值和高阈值 * hsv储存将RGB色域图像转为HSV色域的图像 */ int lh,ls,lv,uh,us,uv; Mat hsv; Mat dst; int main() { //建立窗口命名为dst namedWindow("dst", WINDOW_AUTOSIZE); Mat img = imread("/home/wch/模板/cv_1/yellow.jpg"); //RGB转HSV色域 cvtColor(img, hsv, COLOR_RGB2HSV); //创建控件 //没有回调函数时,回调函数名那个参数设置为0 createTrackbar("lh","dst",&lh,255,0); createTrackbar("ls","dst",&ls,255,0); createTrackbar("lv","dst",&lv,255,0); createTrackbar("uh","dst",&uh,255,0); createTrackbar("us","dst",&us,255,0); createTrackbar("uv","dst",&uv,255,0); while(1) { inRange(hsv, Scalar(lh,ls,lv), Scalar(uh,us,uv), dst); imshow("dst", dst); //延时等待,按回车退出 if(waitKey(2) == 13) //27==esc 13==enter break; } destroyAllWindows(); return 0; }
复制
#include <iostream> #include<opencv4/opencv2/opencv.hpp> using namespace cv; using namespace std; /* 定义HSV的阈值 * lh、uh分别为色调h的低阈值和高阈值 * ls、us分别为饱和度s的低阈值和高阈值 * lv、uv分别为亮度v的低阈值和高阈值 * hsv储存将RGB色域图像转为HSV色域的图像 */ int lh,ls,lv,uh,us,uv; Mat hsv; Mat dst; int main() { //建立窗口命名为dst namedWindow("dst", WINDOW_AUTOSIZE); Mat img = imread("/home/wch/模板/cv_1/yellow.jpg"); //RGB转HSV色域 cvtColor(img, hsv, COLOR_RGB2HSV); //创建控件 //没有回调函数时,回调函数名那个参数设置为0 createTrackbar("lh","dst",&lh,255,0); createTrackbar("ls","dst",&ls,255,0); createTrackbar("lv","dst",&lv,255,0); createTrackbar("uh","dst",&uh,255,0); createTrackbar("us","dst",&us,255,0); createTrackbar("uv","dst",&uv,255,0); while(1) { inRange(hsv, Scalar(lh,ls,lv), Scalar(uh,us,uv), dst); imshow("dst", dst); //延时等待,按回车退出 if(waitKey(2) == 13) //27==esc 13==enter break; } destroyAllWindows(); return 0; }
#include <iostream> #include<opencv4/opencv2/opencv.hpp> using namespace cv; using namespace std; /* 定义HSV的阈值 * lh、uh分别为色调h的低阈值和高阈值 * ls、us分别为饱和度s的低阈值和高阈值 * lv、uv分别为亮度v的低阈值和高阈值 * hsv储存将RGB色域图像转为HSV色域的图像 */ int lh,ls,lv,uh,us,uv; Mat hsv; Mat dst; int main() { //建立窗口命名为dst namedWindow("dst", WINDOW_AUTOSIZE); Mat img = imread("/home/wch/模板/cv_1/yellow.jpg"); //RGB转HSV色域 cvtColor(img, hsv, COLOR_RGB2HSV); //创建控件 //没有回调函数时,回调函数名那个参数设置为0 createTrackbar("lh","dst",&lh,255,0); createTrackbar("ls","dst",&ls,255,0); createTrackbar("lv","dst",&lv,255,0); createTrackbar("uh","dst",&uh,255,0); createTrackbar("us","dst",&us,255,0); createTrackbar("uv","dst",&uv,255,0); while(1) { inRange(hsv, Scalar(lh,ls,lv), Scalar(uh,us,uv), dst); imshow("dst", dst); //延时等待,按回车退出 if(waitKey(2) == 13) //27==esc 13==enter break; } destroyAllWindows(); return 0; }
复制
#include <iostream> #include<opencv4/opencv2/opencv.hpp> using namespace cv; using namespace std; /* 定义HSV的阈值 * lh、uh分别为色调h的低阈值和高阈值 * ls、us分别为饱和度s的低阈值和高阈值 * lv、uv分别为亮度v的低阈值和高阈值 * hsv储存将RGB色域图像转为HSV色域的图像 */ int lh,ls,lv,uh,us,uv; Mat hsv; Mat dst; int main() { //建立窗口命名为dst namedWindow("dst", WINDOW_AUTOSIZE); Mat img = imread("/home/wch/模板/cv_1/yellow.jpg"); //RGB转HSV色域 cvtColor(img, hsv, COLOR_RGB2HSV); //创建控件 //没有回调函数时,回调函数名那个参数设置为0 createTrackbar("lh","dst",&lh,255,0); createTrackbar("ls","dst",&ls,255,0); createTrackbar("lv","dst",&lv,255,0); createTrackbar("uh","dst",&uh,255,0); createTrackbar("us","dst",&us,255,0); createTrackbar("uv","dst",&uv,255,0); while(1) { inRange(hsv, Scalar(lh,ls,lv), Scalar(uh,us,uv), dst); imshow("dst", dst); //延时等待,按回车退出 if(waitKey(2) == 13) //27==esc 13==enter break; } destroyAllWindows(); return 0; }
经过阈值调节选择后获得一个较为理想的阈值Scalar(86,62,110),Scalar(105,255,255);这个值作为识别黄色模型苗就较理论上黄色阈值Scalar(26,43,46),Scalar(34,255,255)更加优。
然后识别程序中的阈值修改为该程序调节选取获得的阈值,可以看到黄色的识别问题得到了解决。
Scalar lower_yellow(86,62,110);
Scalar upper_yellow(105,255,255);
Scalar lower_yellow(86,62,110); Scalar upper_yellow(105,255,255);
Scalar lower_yellow(86,62,110); Scalar upper_yellow(105,255,255);
复制
另外,这种方式的识别受光线影响大,因此要尽可能避免光线的影响。同时考虑到这是室外作业,因此通常是强光条件,所以以光亮的情景下选取阈值,同时在摄像头旁装上照明灯,哪怕室外阴天光线较弱,照明灯也可以保证获取的图像与光线充足时相近。
#include <iostream> #include<opencv4/opencv2/opencv.hpp> using namespace cv; using namespace std; /* 定义HSV的阈值 * lh、uh分别为色调h的低阈值和高阈值 * ls、us分别为饱和度s的低阈值和高阈值 * lv、uv分别为亮度v的低阈值和高阈值 * hsv储存将RGB色域图像转为HSV色域的图像 */ int lh,ls,lv,uh,us,uv; Mat hsv; Mat dst; //void callback(int lh, void*) //{ // inRange(hsv, Scalar(lh,ls,lv), Scalar(uh,us,uv), dst); // imshow("red", dst); //} int main() { //建立窗口命名为dst namedWindow("dst", WINDOW_AUTOSIZE); Mat img = imread("/home/wch/模板/cv_1/yellow.jpg"); //RGB转HSV色域 cvtColor(img, hsv, COLOR_RGB2HSV); //创建控件 createTrackbar("lh","dst",&lh,255,0); createTrackbar("ls","dst",&ls,255,0); createTrackbar("lv","dst",&lv,255,0); createTrackbar("uh","dst",&uh,255,0); createTrackbar("us","dst",&us,255,0); createTrackbar("uv","dst",&uv,255,0); while(1) { inRange(hsv, Scalar(lh,ls,lv), Scalar(uh,us,uv), dst); imshow("dst", dst); //延时等待,按回车退出 if(waitKey(2) == 13) //27==esc 13==enter break; } destroyAllWindows(); return 0; }
#include <iostream> #include<opencv4/opencv2/opencv.hpp> using namespace cv; using namespace std; /* 定义HSV的阈值 * lh、uh分别为色调h的低阈值和高阈值 * ls、us分别为饱和度s的低阈值和高阈值 * lv、uv分别为亮度v的低阈值和高阈值 * hsv储存将RGB色域图像转为HSV色域的图像 */ int lh,ls,lv,uh,us,uv; Mat hsv; Mat dst; //void callback(int lh, void*) //{ // inRange(hsv, Scalar(lh,ls,lv), Scalar(uh,us,uv), dst); // imshow("red", dst); //} int main() { //建立窗口命名为dst namedWindow("dst", WINDOW_AUTOSIZE); Mat img = imread("/home/wch/模板/cv_1/yellow.jpg"); //RGB转HSV色域 cvtColor(img, hsv, COLOR_RGB2HSV); //创建控件 createTrackbar("lh","dst",&lh,255,0); createTrackbar("ls","dst",&ls,255,0); createTrackbar("lv","dst",&lv,255,0); createTrackbar("uh","dst",&uh,255,0); createTrackbar("us","dst",&us,255,0); createTrackbar("uv","dst",&uv,255,0); while(1) { inRange(hsv, Scalar(lh,ls,lv), Scalar(uh,us,uv), dst); imshow("dst", dst); //延时等待,按回车退出 if(waitKey(2) == 13) //27==esc 13==enter break; } destroyAllWindows(); return 0; }
评论(0)
您还未登录,请登录后发表或查看评论