ubuntu环境下opencv学习+踩坑

2023-05-16

opencv学习+踩坑

环境

  • ubuntu 19.04
  • vscode 1.37.0
  • opencv 3.4.7
  • cmake 3.13.4

自己的blog在持续更新中
https://blog.csdn.net/sasasatori/article/details/99671579

拜一下julao的数字图像处理提纲
https://bitlecture.github.io/notes/数字图像处理/

然后开始跟着毛星云的blog跑demo来学opencv
实际上如果论实用性的话,以下的系列blog可能还会更好一些?
https://blog.csdn.net/morewindows/article/category/1291764

https://www.cnblogs.com/long5683/p/10094122.html

实际上学一会就会发现RM里面使用到的视觉(仅仅看这篇开源的话)并不困难

https://blog.csdn.net/u010750137/article/details/91344986

https://blog.csdn.net/qq_31669419/article/details/53053321

反而是去年的神符里面涉及到了一些类似机器学习一样的东西,更有研究的空间在

那么让我们开始视觉学习之路

文件读取和输出
https://blog.csdn.net/poem_qianmo/article/details/20537737

定义图像

Mat image = imread("Filename");
namedWindow("Windowname");
imshow("Windowname",image);

需要注意的是图片要放到build的文件夹里面,如果没能成功imread的话,会报错——

error: (-215:Assertion failed) size.width>0 && size.height>0 in function 'imshow'

视频读取

VideoCapture cap;
cap.open("Filename");
打开摄像头
cap.open(0);

检测是否读取到的方法:

//方法1
if(!image.data){printf("未能读取")};
//方法2
if(image.empty()){printf("未能读取")};

划定特定区域(ROI)
https://blog.csdn.net/poem_qianmo/article/details/20911629

 Mat imageROI;
//方法一
imageROI= srcImage4(Rect(200,250,logoImage.cols,logoImage.rows));
//方法二
imageROI= srcImage4(Range(250,250+logoImage.rows),Range(200,200+logoImage.cols));

图像变换应该也挺重要的
https://blog.csdn.net/xiaowei_cqu/article/details/7616044

图像线性混合
使用addWeighted可以直接混合两张图片,

int main()
{
    double alphavalue = 0.5;
    double betavalue;
    
    Mat satori = imread("satori.jpg");
    Mat name = imread("name.png");

    if(satori.empty()){cout << "未能成功读取图片satori" << endl;exit;};
    if(name.empty()){cout << "未能成功读取图片satori2" << endl;exit;};

    betavalue = 1 - alphavalue;
    //在satori上划出ROI
    Mat ROI = satori(Rect(0,0,name.cols,name.rows));
    //将划出了ROI的satori和name做合并
    addWeighted(ROI,alphavalue,name,betavalue,0.,ROI);

    namedWindow("混合效果");
    imshow("混合效果",satori);

    waitKey();
    return 0;
}

分离/合并颜色通道
split()/merge()

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "iostream"

using namespace cv;
using namespace std;

int main()
{    
    Mat satori = imread("satori.jpg");
    Mat name = imread("name.png",0);
    vector<Mat>channels;
    Mat blue_channel;

    if(satori.empty()){cout << "未能成功读取图片satori" << endl;exit;};
    if(name.empty()){cout << "未能成功读取图片satori2" << endl;exit;};

    //分割成几个颜色通道
    split(satori,channels);
    blue_channel = channels.at(0);

    addWeighted(blue_channel(Rect(0,0,name.cols,name.rows)),1.0,name,0.5,0,blue_channel(Rect(0,0,name.cols,name.rows)));
    //混合通道
    merge(channels,satori);

    namedWindow("混合效果");
    imshow("混合效果",satori);

    waitKey();
    return 0;
}

从颜色通道的角度来说,可以扒掉另外两个通道,只留一个通道做合成来形成单色图片
opencv里面可以设置图片类型,比如CV_8UC1,就是unsigned int8+channel_1,所以这里的操作还是挺简单的,就是用black来取代掉另外两个通道(black意味着灰度值为0),把它给另外两个通道即可

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "iostream"

using namespace cv;
using namespace std;

int main()
{    
    Mat satori = imread("satori.jpg");
    vector<Mat> channels(satori.channels());
    vector<Mat> channels_mix(satori.channels());
    Mat mixed;

    if(satori.empty()){cout << "未能成功读取图片satori" << endl;exit;}

    int w = satori.cols;
    int h = satori.rows;

    split(satori,channels);

    Mat black;
    black.create(h,w,CV_8UC1);
    black = Scalar(0);

    channels_mix[0] = channels[0];
    channels_mix[1] = black;
    channels_mix[2] = black;

    merge(channels_mix,mixed);

     imshow("mixed",mixed);

    waitKey();
    return 0;
}

颜色通道和ROI,以及线性混合的内容再补充一个画矩形?
https://blog.csdn.net/wc781708249/article/details/78518447

会用rectangle就行了

这个是边缘查找,感觉也是个有意思的demo
https://www.cnblogs.com/skyfsm/p/6890863.html

还是继续跑demo,拿小圆当看板是有点东西的,tracebar的话,应该相当于提供了类似嵌入式开发中的在线debug一样的功能?
https://blog.csdn.net/poem_qianmo/article/details/21479533

关于向量这个数据类型
https://www.cnblogs.com/mr-wid/archive/2013/01/22/2871105.html

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "iostream"

using namespace cv;
using namespace std;

Mat satori;
int threval = 160;

static void trace_bar(int,void*)
{
    Mat image = threval > 128? (satori < threval) : (satori > threval);
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;

    findContours(image,contours,hierarchy,CV_RETR_CCOMP,CV_CHAIN_APPROX_SIMPLE);

    Mat dst = Mat::zeros(satori.size(),CV_8UC3);

    if(!contours.empty() && !hierarchy.empty())
    {
        for (int i = 0; i >=0; i=hierarchy[i][0])
        {
            Scalar color((rand()&255),(rand()&255),(rand()&255));
            drawContours(dst,contours,i,color,CV_FILLED,8,hierarchy);
        }
    }

    imshow("satori",dst);
}

int main()
{    
    satori = imread("satori.jpg",0);

    if(satori.empty()){cout << "未能成功读取图片satori" << endl;exit;}

    namedWindow("satori");
    createTrackbar("treashould","satori",&threval,255,trace_bar);

    trace_bar(threval,0);

    waitKey();
    return 0;
}

所以实际上主要就是找轮廓+填色,有点意思

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "iostream"

using namespace cv;
using namespace std;

int Contrast,Bright;
Mat srcImage,dstImage;

static void trace_bar(int,void *)
{
    for (int i = 0; i < srcImage.cols; i++)
    {
        for (int j = 0; j < srcImage.rows; j++)
        {
            for (int k = 0; k < 3; k++)
            {
                dstImage.at<Vec3b>(j,i)[k] = saturate_cast<uchar>((Contrast*0.01)*srcImage.at<Vec3b>(j,i)[k] + Bright);
            }   
        }
    }
    
    imshow("satori",dstImage);
};

int main()
{    
   srcImage = imread("satori.jpg");

    if(srcImage.empty()){cout << "未能成功读取图片satori" << endl;return -1;}

    dstImage = Mat::zeros(srcImage.size(),srcImage.type());

    Contrast = 80;
    Bright = 80;

    namedWindow("satori");
    createTrackbar("contrast","satori",&Contrast,255,trace_bar);
    createTrackbar("bright","satori",&Bright,255,trace_bar);

    trace_bar(Contrast,0);
    trace_bar(Bright,0);

    waitKey();
    return 0;
}

这个demo主要是试了一下针对像素调bright和contrast,我没想到居然就是这么简单的线性运算关系,另外就是对单独的像素操作
其实我们已经看出来了,图片的一种表现方式就是每个Image.at(width,height)[channel]的集合,这个值的大小包含了像素的位置,色度这两个关键信息

到滤波了
https://blog.csdn.net/poem_qianmo/article/details/22745559
https://blog.csdn.net/xiaowei_cqu/article/details/7785365

方框滤波——boxblur函数
均值滤波(邻域平均滤波)——blur函数
高斯滤波——GaussianBlur函数
中值滤波——medianBlur函数
双边滤波——bilateralFilter函数

https://wenku.baidu.com/view/f55e1bc6f90f76c661371ac5.html
二维卷积挺有用的,包括之后做边沿检测用的sobel算子等

https://blog.csdn.net/dang_boy/article/details/76150067

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "iostream"

using namespace cv;
using namespace std;


int main()
{    
    Mat srcImage = imread("satori.jpg");
    if(srcImage.empty()){cout << "未能成功读取图片satori" << endl;return -1;}

    Mat dstImage1,dstImage2,dstImage3,dstImage4,dstImage5;

    dstImage1 = srcImage.clone();
    dstImage2 = srcImage.clone();
    dstImage3 = srcImage.clone();
    dstImage4 = srcImage.clone();
    dstImage5 = srcImage.clone();

    imshow("原图",srcImage);

     boxFilter(srcImage,dstImage1,-1,Size(5,5));
     imshow("方框滤波",dstImage1);

    blur(srcImage,dstImage2,Size(5,5));
    imshow("均值滤波",dstImage2);
 
    GaussianBlur(srcImage,dstImage3,Size(3,3),0,0);
    imshow("高斯滤波",dstImage3);

    medianBlur(srcImage,dstImage4,5);
    imshow("中值滤波",dstImage4);

    bilateralFilter(srcImage,dstImage5,25,25*2,25/2);
    imshow("双边滤波",dstImage5);

    waitKey();
    destroyAllWindows();

    return 0;
}

简单的滤波跑了一下而已,Size(w,h)规定了卷积核的大小,卷积核的大小会影响模糊的效果

然后是非线性滤波
中值滤波和双线性滤波

双线性滤波的效果非常神奇,把原图上一些类似于陈旧的纹理一样的效果给修没了,非常6p(磨皮?)

膨胀腐蚀

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "iostream"

using namespace cv;
using namespace std;

int Elem_Size = 3;
int value1;

Mat srcImage,dstImage;

static void tracebar(int,void*)
{
    Mat element = getStructuringElement(MORPH_RECT,Size(2*Elem_Size+1,2*Elem_Size+1),Point(Elem_Size,Elem_Size));
    
    if(value1 == 0)
    {
        erode(srcImage,dstImage,element);
    }
   else
   {
       dilate(srcImage,dstImage,element);
   }
   
    
    imshow("satori",dstImage);
}

int main()
{    
    srcImage = imread("satori.jpg");
    if(srcImage.empty()){cout << "未能成功读取图片satori" << endl;return -1;}

    Mat element = getStructuringElement(MORPH_RECT,Size(2*Elem_Size+1,2*Elem_Size+1),Point(Elem_Size,Elem_Size));
    erode(srcImage,dstImage,element);

    imshow("satori",dstImage);

    createTrackbar("腐蚀/膨胀","satori",&value1,1,tracebar);
    createTrackbar("内核尺寸","satori",&Elem_Size,21,tracebar);

    tracebar(value1,0);
    tracebar(Elem_Size,0);

    while(char(waitKey(1)) != 'q');
    
    return 0;
}

腐蚀是将暗的像素扩大,膨胀是将亮的像素扩大

在这个基础上还有开运算,闭运算,黑帽运算…
开运算其实就是分开细微链接的像素,闭运算是填平小的裂痕
https://blog.csdn.net/hanshanbuleng/article/details/80657148

Mat element = getStructuringElement(MORPH_RECT,Size(2*Elem_Size+1,2*Elem_Size+1),Point(Elem_Size,Elem_Size));

    morphologyEx(srcImage,dstImage,MORPH_OPEN,element);

更改第三个参数即可

终于到快乐的算子环节了

在具体介绍之前,先来一起看看边缘检测的一般步骤吧。

  • 1)滤波:边缘检测的算法主要是基于图像强度的一阶和二阶导数,但导数通常对噪声很敏感,因此必须采用滤波器来改善与噪声有关的边缘检测器的性能。常见的滤波方法主要有高斯滤波,即采用离散化的高斯函数产生一组归一化的高斯核(具体见“高斯滤波原理及其编程离散化实现方法”一文),然后基于高斯核函数对图像灰度矩阵的每一点进行加权求和(具体程序实现见下文)。

  • 2)增强:增强边缘的基础是确定图像各点邻域强度的变化值。增强算法可以将图像灰度点邻域强度值有显著变化的点凸显出来。在具体编程实现时,可通过计算梯度幅值来确定。

  • 3)检测:经过增强的图像,往往邻域中有很多点的梯度值比较大,而在特定的应用中,这些点并不是我们要找的边缘点,所以应该采用某种方法来对这些点进行取舍。实际工程中,常用的方法是通过阈值化方法来检测。

边缘检测应该是RM里面非常常用的算法了,识别装甲板应该主要就用了这个,识别到边缘之后solvepnp

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "iostream"

using namespace cv;
using namespace std;

int Elem_Size = 1;
int value1;

Mat srcImage,dstImage;

int main()
{    
    srcImage = imread("satori.jpg");
    if(srcImage.empty()){cout << "未能成功读取图片satori" << endl;return -1;}

    Canny(srcImage,dstImage,300,100);

    imshow("satori",dstImage);
    
    while (char(waitKey(1)) != 'q');
    
    return 0;
}

用canny很容易就可以看到效果,调节一下两个阈值则可以起到抑制噪声的作用

sobel算子可以计算x方向和y方向各自的梯度方向,相比canny而言,可以在一些相对比较特定(特征在x/y方向)的场景起到作用

    #include "opencv2/core/core.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/opencv.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    #include "iostream"

    using namespace cv;
    using namespace std;

    int Elem_Size = 1;
    int value1;

    Mat srcImage,dstImage,dstImage2,dstImage3;

    int main()
    {    
        Mat satori;

        satori = imread("satori.jpg");
        if(satori.empty()){cout << "未能成功读取图片satori" << endl;return -1;}

        imshow("image",satori);

        bilateralFilter(satori,srcImage,25,25*2,25/2);

        cvtColor(srcImage,srcImage,CV_RGB2GRAY);


        Sobel(srcImage,dstImage,srcImage.depth(),1,0,3,1,0,BORDER_DEFAULT);

        Sobel(srcImage,dstImage2,srcImage.depth(),0,1,3,1,0,BORDER_DEFAULT);

        imshow("satori",dstImage);
        imshow("satori2",dstImage2);
        
        addWeighted(dstImage,1,dstImage2,1,1,dstImage3);

        imshow("satori3",dstImage3);

        while (char(waitKey(1)) != 'q');
        
        return 0;
    }

结合了双边滤波后在x,y方向做sobel检测,然后合成,效果还行

结果试了一下双边滤波后做laplace检测,效果更好,啧啧

    #include "opencv2/core/core.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/opencv.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    #include "iostream"

    using namespace cv;
    using namespace std;

    int Elem_Size = 1;
    int value1;

    Mat srcImage,dstImage,dstImage2,dstImage3;

    int main()
    {    
        Mat satori;

        satori = imread("satori.jpg");
        if(satori.empty()){cout << "未能成功读取图片satori" << endl;return -1;}

        imshow("image",satori);

        bilateralFilter(satori,srcImage,25,25*2,25/2);

        cvtColor(srcImage,srcImage,CV_RGB2GRAY);

        Laplacian(srcImage,dstImage,srcImage.depth());

        imshow("satori",dstImage);
        
        while (char(waitKey(1)) != 'q');
        
        return 0;
    }

但是还有Scharr,可以看成对sobel的进一步优化?试试看效果

    #include "opencv2/core/core.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/opencv.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    #include "iostream"

    using namespace cv;
    using namespace std;

    int Elem_Size = 1;
    int value1;

    Mat srcImage,dstImage,dstImage2,dstImage3;

    int main()
    {    
        Mat satori;

        satori = imread("satori.jpg");
        if(satori.empty()){cout << "未能成功读取图片satori" << endl;return -1;}

        bilateralFilter(satori,srcImage,25,25*2,25/2);
       imshow("image",srcImage);

        cvtColor(srcImage,srcImage,CV_RGB2GRAY);


        Scharr(srcImage,dstImage,srcImage.depth(),1,0,1,0,BORDER_DEFAULT);

        Scharr(srcImage,dstImage2,srcImage.depth(),0,1,1,0,BORDER_DEFAULT);

        imshow("satori",dstImage);
        imshow("satori2",dstImage2);
        
        addWeighted(dstImage,1,dstImage2,1,1,dstImage3);

        imshow("satori3",dstImage3);

        while (char(waitKey(1)) != 'q');
        
        return 0;
    }

结果没看出了什么优化,反而引入了更多的噪声…可能是我参数没继续调吧(另一层面上来说更加敏锐?

好了,我们到了快乐的resize阶段,还有pryUp,pryDown这两个金字塔放大缩小函数
https://blog.csdn.net/poem_qianmo/article/details/26157633

我觉得没啥特别好说的,就是研究怎么样尽可能合理的采样或者插值,然后高斯函数的优势又一次被体现出来了。不得不这是个非常伟大的函数(我刚学到大数定律时就被这个函数的神奇性给吓到了)

    #include "opencv2/core/core.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/opencv.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    #include "iostream"

    using namespace cv;
    using namespace std;

    int main()
    {    
        Mat tmpImage,dstImage;

        tmpImage = imread("satori.jpg");
        if(tmpImage.empty()){cout << "未能成功读取图片satori" << endl;return -1;}

        dstImage = tmpImage;
        while (1)
        {
            char key = waitKey(1);

            switch (key)
            {
            case 'q':
                return 0;
                break;
            
            case 'w':
                resize(tmpImage,dstImage,Size(tmpImage.cols*2,tmpImage.rows*2));
                break;

            case 's':
                resize(tmpImage,dstImage,Size(tmpImage.cols/2,tmpImage.rows/2));
                break;

            default:
                break;
            }

            tmpImage = dstImage;
            imshow("satori",dstImage);
        }
    }

不出意外的,缩小之后再放大之后会上天

霍夫线/圆检测算法

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "iostream"

using namespace cv;
using namespace std;

int main()
{    
    Mat srcImage,dstImage,midImage;

    srcImage = imread("test.jpg");
    if(srcImage.empty()){cout << "未能成功读取图片" << endl;return -1;}

    Canny(srcImage,midImage,400,100,3);

    cvtColor(midImage,dstImage,CV_GRAY2BGR);

    vector<Vec2f> lines;
    HoughLines(midImage,lines,1,CV_PI/180,150,0,0);    

    for (size_t i = 0; i < lines.size(); i++)
    {
        float rho = lines[i][0] , theta = lines[i][1];
        Point pt1,pt2;
        double a = cos(theta),b = sin(theta);
        double x0 = a*rho,y0 = b*rho;
        pt1.x = cvRound(x0 + 1000 * (-b));
        pt1.y = cvRound(y0 + 1000 * (a));
        pt2.x = cvRound(x0 - 1000 * (-b));
        pt2.y = cvRound(y0 - 1000 * (a));

        line(dstImage,pt1,pt2,Scalar(55,100,95),1,CV_AA);
    }    

    imshow("dst",dstImage);

    while(char(waitKey(1)) != 'q');
    return 0;
}

这个检测看得我头大
另外还有HoughLinesP这个检测方法,有点意思

试验了一下,HoughLinesP应该是相比HoughLine更优秀的一种检测方法,因为他是可以检测出线的起始的,而且有更多实用的可调参数(可以显示的最小/最大线段长度等)

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "iostream"

using namespace cv;
using namespace std;

int main()
{    
    Mat srcImage,dstImage,midImage;

    srcImage = imread("test.jpg");
    if(srcImage.empty()){cout << "未能成功读取图片" << endl;return -1;}

    Canny(srcImage,midImage,400,100,3);

    cvtColor(midImage,dstImage,CV_GRAY2BGR);

    vector<Vec4i> lines;
    HoughLinesP(midImage,lines,1,CV_PI/180,150,0,0);    

    for (size_t i = 0; i < lines.size(); i++)
    {
        Vec4i l = lines[i];
        line(dstImage,Point(l[0],l[1]),Point(l[2],l[3]),Scalar(0,100,0),5,CV_AA);
    }    

    imshow("dst",dstImage);

    while(char(waitKey(1)) != 'q');
    return 0;
}

还有HoughCircles
RM上倒是不太用得上圆检测,除非要检测弹丸(谁没事干检测弹丸),RC这边倒是应该用得上?毕竟有几次赛题要扔球来着

HoughCircles里面,第5个参数规定了可检测的最大半径的圆形,有筛选作用,6,7参数则起到规定阈值的作用,也挺有用的

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "iostream"

using namespace cv;
using namespace std;

int main()
{    
    Mat srcImage,dstImage,midImage;

    srcImage = imread("circle.jpeg");
    if(srcImage.empty()){cout << "未能成功读取图片" << endl;return -1;}

    cvtColor(srcImage,midImage,CV_BGR2GRAY);
    GaussianBlur(midImage,midImage,Size(3,3),1,1);

    vector<Vec3f> circles;
    HoughCircles(midImage,circles,CV_HOUGH_GRADIENT,1.5,20,300,100,0,0);

    for (size_t i = 0; i < circles.size(); i++)
    {
        Point center(cvRound(circles[i][0]),cvRound(circles[i][1]));
        int radius = cvRound(circles[i][2]);

        circle(srcImage,center,radius,Scalar(0,100,0),3);
    }
    

    imshow("dst",srcImage);

    while(char(waitKey(1)) != 'q');
    return 0;
}

调了一下参数,能够比较好的检测到图中想找的圆

去研究源码实现的话会发现其实HoughCircle,HoughCircleP其实都是基于HoughCircle2(旧的霍夫圆检测)实现的,这里就不去太扣底层的东西,来日方长

到快乐的漫水填充算法了

floodFill从功能上去理解就是和ps的魔术棒一样,总的来说是非常重要的一个功能

https://blog.csdn.net/poem_qianmo/article/details/28261997

算法原理其实挺好理解的,就是先选中一个点作为种子,以这个种子作为起点去计算周边像素差值,在阈值范围内的像素作为下一批种子

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "iostream"

using namespace cv;
using namespace std;

int main()
{    
    Mat srcImage,dstImage,midImage;

    srcImage = imread("satori.jpg");
    if(srcImage.empty()){cout << "未能成功读取图片" << endl;return -1;}

    Rect ccomp;
    floodFill(srcImage,Point(0,0),Scalar(0,0,0),&ccomp,Scalar(10,10,10),Scalar(10,10,10));

    imshow("dst",srcImage);

    while(char(waitKey(1)) != 'q');
    return 0;
}

最后两个Scalar参数用来框定选取阈值,调了一下之后可以比较好的把人物抠出来

floodFill还可以设置掩膜模式,避免漫水填充到掩膜内的非0像素

最后floodFill的最后参数还有一个32位操作数,高8位,中8位,低8位都有含义,相当的复杂,这里就不去过于细致的研究了

emmmm到角点检测了

角点检测应该是比较重要的,不管是RC还是RM,像RM里面识别到灯柱之后,要给灯柱四个角上的关键点都标注出来,然后才能做pnp结算

图像特征类型可以被分为如下三种:

  • <1>边缘
  • <2>角点 (感兴趣关键点)
  • <3>斑点(Blobs)(感兴趣区域)

在当前的图像处理领域,角点检测算法可归纳为三类:

  • <1>基于灰度图像的角点检测
  • <2>基于二值图像的角点检测
  • <3>基于轮廓曲线的角点检测

角点检测算法又是梯度运算的一大应用场景(这个想想就能知道)

配合角点检测的还有一个知名度非常高的方法那就是二值化——一共有5种方法(茴香豆的茴字有几种写法啊?)

先试一下二值化

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "iostream"

using namespace cv;
using namespace std;

int thresholdvalue;
Mat src_image,dst_image1,dst_image2,dst_image3,dst_image4,dst_image5;

static void tracebar(int,void*)
{
    threshold(src_image,dst_image1,thresholdvalue,255,THRESH_BINARY);

    imshow("test",dst_image1);
}

int main()
{    

    src_image = imread("satori.jpg");
    if(src_image.empty()){cout << "未能成功读取图片" << endl;return -1;}

    cvtColor(src_image,src_image,CV_BGR2GRAY);

    namedWindow("test");

    createTrackbar("threshold","test",&thresholdvalue,255,tracebar);

    tracebar(thresholdvalue,0);

    while(char(waitKey(1)) != 'q');
    return 0;
}

我就不尝试每一种方法了,总之还是挺立竿见影的

值得一提的是connerHarris之后的图像只有通过一个很小阈值的threshold之后才能显现出来

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "iostream"

using namespace cv;
using namespace std;

int thresholdvalue;
Mat src_image,dst_image1,dst_image2,dst_image3,dst_image4,dst_image5;
Mat mid_image;

int main()
{    
    src_image = imread("satori.jpg");
    if(src_image.empty()){cout << "未能成功读取图片" << endl;return -1;}

    cvtColor(src_image,src_image,CV_BGR2GRAY);

    namedWindow("test");

    cornerHarris(src_image,mid_image,5,3,0.01);

   threshold(mid_image,dst_image1,0.0001,255,THRESH_BINARY);

    imshow("test",dst_image1);
    
    while(char(waitKey(1)) != 'q');
    return 0;
}

可以知道经过harris检测之后的图像值被放的很小,不符合我们常用的0-255的灰度规定,所以如果要用的话,一般来说还得经过操作,比如二值化,比如normolize

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "iostream"

using namespace cv;
using namespace std;

int thresholdvalue;
Mat src_image,dst_image1,dst_image2,dst_image3,dst_image4,dst_image5;
Mat mid_image;

static void tracebar(int,void*)
{
    threshold(dst_image1,dst_image2,thresholdvalue,255,THRESH_BINARY);

    imshow("test",dst_image2);
}

int main()
{    
    src_image = imread("satori.jpg");
    if(src_image.empty()){cout << "未能成功读取图片" << endl;return -1;}

    cvtColor(src_image,src_image,CV_BGR2GRAY);

    namedWindow("test");

    cornerHarris(src_image,mid_image,5,3,0.01);
    normalize(mid_image,dst_image1,0,255,NORM_MINMAX,CV_32FC1,Mat());

    createTrackbar("threshold","test",&thresholdvalue,255,tracebar);

    tracebar(thresholdvalue,0);

    while(char(waitKey(1)) != 'q');
    return 0;
}

这下拖条总算有点用了

重映射和surf特征点检测,surf应该是比较通用的特征点检测算法了,总体而言

https://www.cnblogs.com/dengxiaojun/p/5302778.html

重映射主要是使用remap这个函数,remap的数学定义如下
d s t ( x , y ) = s r c ( m a p 1 ( x , y ) , m a p 2 ( x , y ) ) dst(x,y) = src(map_1(x,y),map_2(x,y)) dst(x,y)=src(map1(x,y),map2(x,y))
其中 m a p 1 map_1 map1 m a p 2 map_2 map2都是作为参数输入remap函数的(值得一提的是map的类型不是随意的,create的时候要创建CV_32FC1)

这里有个简单的应用,比如将一个图片给镜像翻转

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "iostream"

using namespace cv;
using namespace std;

int thresholdvalue;
Mat src_image,dst_image1,dst_image2;
Mat mid_image;

int main()
{    
    src_image = imread("satori.jpg");
    if(src_image.empty()){cout << "未能成功读取图片" << endl;return -1;}

    Mat map_x,map_y;

    map_x.create(src_image.size(),CV_32FC1);
    map_y.create(src_image.size(),CV_32FC1);

    for (size_t i = 0; i < src_image.cols; i++)
    {
        for (size_t j = 0; j < src_image.rows; j++)
        {
            map_x.at<float>(j,i) = static_cast<float>(i);
            map_y.at<float>(j,i) = static_cast<float>(src_image.rows-j);
        }
    }
    
    remap(src_image,dst_image1,map_x,map_y,CV_INTER_LINEAR,BORDER_CONSTANT,Scalar(0,0,0));

    imshow("dst1",dst_image1);

    while(char(waitKey(1)) != 'q');
    return 0;
}

这个没有什么难度的,像素操作的时候稍微注意一点就行了

然后我们来关注一下重点——我们亲爱的SURF特征检测算法,在opencv里面SURF被封成了一个类,有一堆可以执行的乱七八糟的操作

我们跑个drawKeypoints试试

然后发现好玩的事情

https://blog.csdn.net/zhounanzhaode/article/details/50302385

所以为了使用SURF我还得再操作一下,真的烦人

直接使用apt-get的方法失败了,会在添加ppa时报错——

没有 Release 文件。 N: 无法安全地用该源进行更新,所以默认禁用该源。

采用自己摸索的方法操作成功,具体的操作方法为:

去github上面下载这个库

https://github.com/opencv/opencv_contrib/tree/3.4

然后checkout到和opencv版本一致的tag下面

将opencv_contrib/modules/…下面所需要的模组复制粘贴到opencv/modules/下面去

然后修改opencv/modules下的cmakelist中的

set(FIXED_ORDER_MODULES core imgproc imgcodecs videoio highgui video calib3d features2d objdetect dnn ml flann photo stitching xfeatures2d)

添加自己需要的新的模组即可

然后就是和安装时一样的cmake,make,make install三连,成功的话就可以正常include nonfree.hpp了

emmmmmm

看了一下,更新到opencv3之后,SURF的使用和opencv2完全不一样了,除了drawKeypoint这个api仍然保留之外,其他的好像都变化了

随手找了个教程,结果又找到RM相关的了,哈哈哈哈哈哈

https://www.cnblogs.com/long5683/p/9692987.html

结果发现之前的操作没弄干净…编译的时候出现了这个

error: (-213:The function/feature is not implemented) This algorithm is patented and is excluded in this configuration; Set OPENCV_ENABLE_NONFREE CMake option and rebuild the library in function 'create'

结果就是还得在cmake的时候操作一下…干啊

https://blog.csdn.net/zhoukehu_CSDN/article/details/83145026

按照这个博客走,记得在cmake-gui里面把OPENCV_ENABLE_NONFREE这个勾上…唉,这个是真的恶心,又要make整整20分钟了

重新make完之后就一切ok了

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/xfeatures2d/nonfree.hpp"
#include "iostream"

using namespace cv;
using namespace cv::xfeatures2d;
using namespace std;

int thresholdvalue;
Mat src_image,dst_image1,dst_image2;
Mat mid_image;

int main()
{    
    src_image = imread("satori.jpg");
    if(src_image.empty()){cout << "未能成功读取图片" << endl;return -1;}

    int minHessian = 400;

    Ptr<SURF> detector = SURF::create(minHessian);
    vector<KeyPoint> keypoints;
    detector->detect(src_image,keypoints,Mat());

    drawKeypoints(src_image,keypoints,dst_image1,Scalar(0,0,0));

    imshow("dst",dst_image1);

    while(char(waitKey(1)) != 'q');
    return 0;
}

暂时跟着demo跑的内容就先这么多吧

实际上还需要学习的内容包括相机有关的一些知识(内参矩阵标定),solvePnp的使用等等,包括video模块下的一些有用的功能我也还没去做了解

后面就开始看几个实际项目研究研究,一边巩固已经学习的东西,一边积累工作经验吧

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ubuntu环境下opencv学习+踩坑 的相关文章

  • sample.html

    目录 一 Html使用注意 1 二 界面设计 xff08 css xff09 9 三 表单使用 15 Jquery判断表单是否修改 15 表单序列化为json对象 16 json对象转为json字符串 16 json对象赋值到表单 xff0
  • Cgi使用

    目录 一 CGI xff08 通用网关接口 xff09 是外部扩展应用程序与 Web 服务器交互的一个标准接口 1 二 web服务器配置 3 三 Cgi编程 xff08 根据CGI标准 xff0c 编写Web服务器运行时的外部扩展应用程序
  • mmap内存映射在应用和内核/驱动交互,进程间交互,大规模数据传输/大文件读写中的使用

    目录 一 Mmap用途 步骤实例 细节 及相关函数 2 1 mmap函数主要用途有三个 xff08 应用和内核 驱动交互 xff0c 进程间交互 xff0c 大规模数据传输 大文件读写 xff09 2 2 使用步骤 xff1a 所有对mma
  • PID控制器整理分享

    概述 日常开发中 xff0c 常常需要对速度 温度等物理量进行稳态控制 xff0c 而在目前的自动化控制原理中 xff0c 使用最为广泛的方法就是PID控制算法 本文简要整理分享PID控制器的使用 正文 PID控制器 xff0c 即比例 积
  • Markdown插入图片的方法

    Markdown插入图片的方法 Markdown插入图片的语法 xff1a alt 属性文本 图片地址 alt 属性文本 图片地址 34 可选标题 34 说明 xff1a 以感叹号开头 接着一个中括号 接着一个小括号 xff0c 里面放上图
  • SLA的基本概念

    SLA的基本概念 现在的产品和系统都非常的复杂 xff0c 彼此连接依赖越来越复杂 xff0c 为了整体的高速运转 xff0c 对每个部件的稳定性越来越高 xff0c 越来越精密 xff0c 发展到一定程度 xff0c 人力已经无法掌控 x
  • ssh远程登录可能出现的问题以及解决办法

    首先 确保server端的ssh服务是开的 xff08 service shhd start xff09 然后在client端输入 xff1a ssh usrname 64 serverip xff08 远程登录 xff09 scp fil
  • 各种滤波原理

    1 维纳滤波 维纳滤波是一种平稳随机过程的最佳滤波理论 xff0c 换句话说就是在滤波过程中系统的状态参数 xff08 或信号的波形参数 xff09 是稳定不变的 它将所有时刻的采样数据用来计算互相关矩阵 xff0c 涉及到解维纳 xff0
  • MTCNN算法提速应用(ARM测试结果评估)

    经博主测试 xff0c mtcnn原三层网络如果用于工程测试 xff0c 误检情况严重 xff0c 在fddb上测试结果也是 xff0c 经常将手或者耳朵 检测为人脸 xff0c 这个很头疼 xff08 因为标注数据 xff01 xff09
  • 国外知名音频库一站式资料和简介

    Speex Speex http blog csdn net xyz lmn article details 8013490 简介 Speex是一套主要针对语音的开源免费 xff0c 无专利保护的音频压缩格式 Speex工程着力于通过提供一
  • vs2010调试工具栏不可用(变灰)解决办法

    菜单中页不能进行调试 使用vs的命令行工具 在命令行中运行 xff1a devenv ResetSkipPkgs 重新打开vs xff08 注 xff1a 第一次没有关闭vs xff0c 运行这条命令后 xff0c 会新打开vs没有变化 x
  • STM32 抗锯齿中文显示另类方案分享

    STM32 抗锯齿中文显示另类方案分享 最近在玩车载液晶仪表 xff0c 之前用Arduino 43 USART液晶 xff0c 后来发现Arduino性能不够 xff0c 就转向STM32 由于需要在液晶屏上显示手机信息 xff0c 先是
  • [Pixhawk] ardupilot源码windows编译教程

    声明 此教程最开始取自于淘宝商家 xff0c 后查找资料发现淘宝商家取自 怒飞垂云 的教程 xff0c 但原教程中有些冗余和错误导致无法通过编译 xff0c 我做更改整理后发出 xff0c 如有疑问请各位一起讨论 目录 一 建立飞控固件编译
  • pixhawk MP地面站 NTF_LED参数相关

    NeoPixel Copter documentation 这一部分里面的NTF相关参数 xff0c 对应MP地面站里的这几项 不是给外接WS2812设置的 我折腾了一下午 xff0c 再更改了其他一些参数后 xff0c 导致飞控上那个最大
  • 无人机betaflight 地面站遥控器通道最低点不是1000,最高点不是2000,中点不是1500的调整办法

    参考老外的视频 How to fix the endpoints of your Rx channels with RXRANGE in Betaflight or Cleanflight EASY FIX 需要进入CLI界面 xff0c
  • 八皇后详解

    历史 八皇后问题是一个古老而著名的问题 xff0c 是回溯算法的典型例题 该问题是十九世纪著名的数学家高斯1850年提出 xff1a 在8X8格的国际象棋上摆放八个皇后 xff0c 使其不能互相攻击 xff0c 即任意两个皇后都不能处于同一
  • KVM虚拟机创建功能详细讲解

    KVM虚拟机创建功能详细讲解 一 KVM虚拟机创建的用户操作 对于用户或者管理员来说 xff0c 虚拟机的创建有着很多的方法 xff0c 例如 xff1a kvm自带命令行工 具 使用virsh命令来创建 使用具有图形界面的virt man
  • JS中堆和栈

    什么是栈和堆 栈和堆的主要作用存储变量 xff0c 根据变量值得不同存储的位置也不同 在JS中 xff0c 栈主要是存储基本类型的变量 xff0c 包括String Number Boolean Undefined Null 和 对象类型的
  • 实例:Python调用c++文件(参数为指针和数组指针)

    本文作为Python调用c 43 43 的进阶实现 xff0c 简单的实现参考文章Python调用c 43 43 高级 xff08 swig xff09 1 一个小小的工作目标 通过Python调用c 43 43 文件 xff0c 生成一个
  • RT-Thread学习笔记——信号量

    前言 本文讲RT Thread的线程间同步之信号量 xff0c 包括为什么要进行线程间同步 信号量创建与删除 信号量获取与释放以及基于STM32的二值信号量示例和计算型信号量示例 xff0c 采用RTT amp 正点原子联合出品潘多拉开发板

随机推荐

  • ssh Connection closed by remote host解决

    描述 最近某台服务器隔一段时间就会有ssh连接失败的告警 xff0c 但是上去排查的时候会发现ssh并没有连接失败的错误 后来有在ssh的地方添加日志 xff0c 打印错误的输出 xff0c 最终得到的报错Stderr ssh exchan
  • linux-2.6.32.6源码目录结构分析及其driver所占比重

    find maxdepth 1 type d name exec du sh 未编译个目录文件大小 xff1a 423M linux 2 6 32 6 5 8M firmware 1 3M lib 140K init 144K virt 2
  • Formatter(格式化)和 Converter(格式化)

    功能 xff1a Spring框架的功能 xff0c Formatter和Converter均可以将一种对象类型转换成另一种对象类型 区别 xff1a Converter是通用元件 xff0c 可以在应用程序的任意层中使用 Formatte
  • 牛人 就是这样锤炼出来的,中兴资深工程师,嵌入式专家韦东山是这样炼成的!

    导读 xff1a 还在大学的你 xff0c 不知道未来何去何从 xff1f 都说迷茫是大事做不了 xff0c 小事不想做 还在做程序袁的你 xff0c 是不是也想创业 xff1f 程序袁好创业吗 xff1f Linux好学吗 xff1f 学
  • Windows下编译PX4源码并连接simulink

    目录 一 安装UAV支持PX4飞行器支持包二 下载工具链对于2019a对于2021a 三 克隆PX4源码四 构建PX4固件固件的选择编译指令报错的处理 五 MATLAB的Test Connection 一 安装UAV支持PX4飞行器支持包
  • ubuntu树莓派3/4B+的GPIO硬件串口通信

    目录 一 概要二 树莓派硬件常识三 交换硬件串口和mini串口映射关系3 1 查看原始映射3 2 编辑config文件 xff0c 置换硬件映射3 3 禁用蓝牙 四 串口功能配置4 1 编辑cmdline文件4 2 禁用蓝牙4 3 串口功能
  • JS运行原理,宏任务微任务的任务loop驱动模式

    要拎清运行原理 xff0c 就要知道js有什么无理限制和主要解决什么问题 首先 xff0c js是单线程的 xff0c 不能开辟多线程 这决定了它支持并发 xff0c 不支持并行 并发 强调的是可以一起 出 发 xff0c 并行 强调的是可
  • 关于CPU C-States 省电模式,你需要知道的事情

    为了在CPU空闲的时候降低功耗 xff0c CPU可以被命令进入low power模式 每个CPU都有几种power模式 xff0c 这些模式被统称为C states或者C modes lower power模式最早在486DX4处理器上被
  • nginx如何部署多个web应用(vue)

    服务器资源与域名资源都是有限的 xff0c 如何在同一个域名下部署多个vue项目 xff08 nginx反向代理 xff09 xff0c 通过域名后面的不同的URI来访问不同的应用 xff0c 来解决资源不足的问题 xff0c 在此期间遇到
  • 【博客8】缤果PyQt5串口调试助手V1.1(初级篇)

    超级好用的Python QT GUI串口调试助手 目录 前言 一 软件概要 xff1a 二 软件界面 xff1a 1 App动态演示 2 其他扩展展示 三 main py源码 xff1a 1 PyQt5 Serial Debug Assis
  • 主机ubuntu通过SSH访问TX2

    因为最近实验室搭建好了装有激光雷达的ros小车 xff0c 笔者想通过主机对TX2的文件进行管理和下载 但由于笔者之前使用TX2都是直接连接在显示屏上使用 xff0c 并未使用过远程访问 xff0c 为此笔者查了较多资料走了不少弯路 xff
  • IDEA快捷创建Servlet项目

    IDEA快捷创建Servlet项目 1 右键单击 要创建Servlet项目 所在的包 注 xff1a 若没有Servlet选项 打开File gt project structure 进入如下页面 xff0c 选择你使用的工程 xff0c
  • 【基础教程】Python转义字符及用法

    ASCII 编码为每个字符都分配了唯一的编号 xff0c 称为编码值 在 Python 中 xff0c 一个 ASCII 字符除了可以用它的实体 xff08 也就是真正的字符 xff09 表示 xff0c 还可以用它的编码值表示 这种使用编
  • SAP 实施新的金融工具 IFRS17规则解析

    在实施新的金融工具 IFRS 规则的过程中 xff0c 保险公司现在看到了保险负债的新标准 经过多年的长期讨论 xff0c IASB 于 2016 年 11 月承诺在 2021 年 1 月 1 日生效 xff0c 并明确表示不会考虑进一步推
  • FreeRTOS中任务控制块中关于堆栈的三个变量pxTopOfStack、pxStack、pxEndOfStack的分析

    这里写自定义目录标题 FreeRTOS中任务控制块中关于堆栈的定义 typedef struct tskTaskControlBlock volatile StackType t pxTopOfStack lt Points to the
  • 在keil中添加c文件时的一些常见错误

    近期在做项目时 xff0c 计划添加一个测距模块 xff0c 因此要新建一个c文件封装一些函数 xff0c 方便调用 xff0c 在实际操作时遇到了以下两个问题 1 编译时报错 error no such file or dictionar
  • stm32f1驱动HC-SR04超声波测距模块

    这段时间算是比较系统的学习了一点stm32相关的东西 xff0c 驱动超声波测距模块这个简单的小任务综合了定时器 xff0c 中断 xff0c 串口三个知识点 xff0c 拿来练手还是挺不错的 首先先介绍一下HC SR04模块 xff1a
  • HC05/HC06蓝牙模块驱动(1)

    花了点时间熟悉了一下蓝牙模块 xff0c 发现意料之外的简单 先说蓝牙模块的三种工作模式 xff08 这三种工作模式是HC05 06共有的 xff0c 并且通过模块上的LED反映出来 xff09 1 待连接模式 HC05 快速闪灯 HC06
  • Raft的PreVote实现机制

    Raft的PreVote实现机制 1 背景 在Basic Raft算法中 xff0c 当一个Follower与其他节点网络隔离 xff0c 如下图所示 xff1a Follower 2在electionTimeout没收到心跳之后 会发起选
  • ubuntu环境下opencv学习+踩坑

    opencv学习 43 踩坑 环境 ubuntu 19 04vscode 1 37 0opencv 3 4 7cmake 3 13 4 自己的blog在持续更新中 https blog csdn net sasasatori article