opencv学习笔记

2023-11-13

#include "quickdemo.h"
#include "shuzu.h"
#include <vector>
#include <cmath>
#include <opencv2/features2d.hpp>
#include <direct.h>
#include<io.h>
using namespace cv;
using namespace std;
using namespace dnn;
QuickDemo::QuickDemo()
{

}
//copyto mask用来限制原图给别人多少,接受图的(roi)用来表示接受的区域是哪里
void QuickDemo::colorSpace_Demo(Mat &change){
    Mat one,two;
    cvtColor(change,one,COLOR_BGR2HSV);//转成hsv
    cvtColor(change,two,COLOR_BGR2GRAY);//转成hsv

    imshow("hsv",one);
    imshow("黑白",two);
    imwrite("E:\\BaiduNetdiskDownload\\hsv.jpg",one);
    imwrite("E:\\BaiduNetdiskDownload\\heibai.jpg",two);



}
void QuickDemo::mat_creation_demo(cv::Mat &change){//di3节//创建的时候宽高,高row=行
    Mat m1,m2;//克隆和拷贝都是值拷贝,赋值=是指向同一份数据,类似于指针
    m1.copyTo(change);
    m2=change.clone();//克隆
    //创建空白图像
    Mat m3=Mat::zeros(Size(5,4),CV_8UC3);//8位得无符号int单通道,最后数字表示几通道,值全是0
//    Mat m3=Mat::ones(Size(3,8),CV_8UC1);//使用ones创建空白图像得时候单通道全是1,多通道只有第一个通道是1
    //给像素点通道赋值
//    m3=255;//给单通道赋值
    m3=Scalar(88,66,77);//给多通道赋值
    cout <<"宽度"<<m3.cols<<"高度"<<m3.rows<<"通道数"<<m3.channels()<<endl;

    imshow("dd",m3);
    Mat m4=m3;//共用同一份图像数据
    Mat m5=(Mat_<char>(3,3)<<1,5,6,8,7,9,9,8,7);//默认通道数1

}

void QuickDemo::xiangshuduxie_demo(cv::Mat &change){//像素读写操作//高行
    int tongdao=change.channels();
    int hang=change.rows;
    int lie=change.cols;
    for (int row=0;row<hang;++row)
    {
        for (int col=0;col<lie;++col)
        {
            if(tongdao==1)
            {
                int xiangsudian=change.at<uchar>(row,col);
                change.at<uchar>(row,col)=255-xiangsudian;
            }
            if (tongdao==3) {
                Vec3b xiangsudian=change.at<Vec3b>(row,col);  //Vec3b相当于一个存放3个uchar类型得数组,int类型Vec3i,浮点类型Vec3f
                change.at<Vec3b>(row,col)[0]=255-xiangsudian[0];
                change.at<Vec3b>(row,col)[1]=255-xiangsudian[1];
                change.at<Vec3b>(row,col)[2]=255-xiangsudian[2];
            }
        }
    }
//    for (int row=0;row<hang;++row)//指针版本
//    {   uchar *xiangsudian=change.ptr<uchar>(row);//将第一行赋给指针,数组赋给指针,指针指向数组首地址,也就是该行第一个像素点
//        for (int col=0;col<lie;++col)
//        {
//            if(tongdao==1)
//            {
//                *xiangsudian=255-*xiangsudian;
//                ++xiangsudian;
//            }
//            if (tongdao==3) {
//                for (int i=0;i<3;i++,++xiangsudian) {
//                    *xiangsudian=255-*xiangsudian;//一行有col个像素点,每个像素点有3个值
//                }

//            }
//        }
//    }
    namedWindow("测试窗口",WINDOW_AUTOSIZE);
    imshow("测试窗口",change);
}
void QuickDemo::tupianjisuan_demo(cv::Mat &change){//图片加减
//    saturate_cast<uchar>,当使用它时会保证BGR图像的像素值在[0,255] saturate_cast<uchar>(p1[0]+p2[0]),可以是别的符号,可以保证数值在有效范围内
//    Mat ceshi=change+Scalar(50,50,50);
//    add(change,Scalar(50,50,50),ceshi);  //多种用法,最好用函数,加标准定义,如下
    Mat scr=Mat::zeros(change.size(),change.type());
    Mat zhongjian=Mat::zeros(change.size(),change.type());
    zhongjian=Scalar(50,50,50);
//    add(change,zhongjian,scr);//加法
//  subtract(change,zhongjian,scr);//减法
//    multiply(change,zhongjian,scr);//乘法
    divide(change,zhongjian,scr);//除法
    namedWindow("结果",WINDOW_FREERATIO);
    imshow("结果",scr);
}
//void on_treak(int chushzhi,void *p){/*传统版本亮度*/
//    Mat src=*((Mat*)p);
//    Mat zhongjian=Mat::zeros(src.size(),src.type());
//    Mat jieguo=Mat::zeros(src.size(),src.type());
//    zhongjian=Scalar(chushzhi,chushzhi,chushzhi);
//    add(src,zhongjian,jieguo);
//    imshow("亮度和对比度窗口",jieguo);
//}
void on_treak(int chushzhi,void *p){/*ddWeighted版本亮度*/
    Mat src=*((Mat*)p);
    Mat zhongjian=Mat::zeros(src.size(),src.type());
    Mat jieguo=Mat::zeros(src.size(),src.type());
    double fanwei=chushzhi-50;

    addWeighted(src,1,zhongjian,0,fanwei,jieguo);
    imshow("亮度和对比度窗口",jieguo);
}
void duibidu(int chushzhi,void *p){
//    addWeighted
//        共7个参数
//            第1个参数 第一个输入 src1
//            第2个参数 第一个输入的权重 alpha
//            第3个参数 第二个输入 src2
//            第4个参数 第二个输入的权重 beta
//            第5个参数 每个数要增加的标量 gamma
//            第6个参数 输出 dst图片

//            第7个参数 输出的可选深度(一般用不到)

//        公式
//            dst = src1 * alpha + src2 * beta + gamma
//            范围截断在[0,255]
    Mat src=*((Mat*)p);
    Mat zhongjian=Mat::zeros(src.size(),src.type());
    Mat jieguo=Mat::zeros(src.size(),src.type());
    double quanzhong=chushzhi/100.0;
    addWeighted(src,quanzhong,zhongjian,0,0,jieguo);
    imshow("亮度和对比度窗口",jieguo);
}
void QuickDemo::gundongtiaoliangdu_demo(cv::Mat &change){
    Mat src=change;
    namedWindow("亮度和对比度窗口",WINDOW_FREERATIO);
    int maxliangdu=100;
    int l_chushzhi=50;
    int maxduibidu=200;
    int d_chushzhi=50;
    createTrackbar("亮度:","亮度和对比度窗口",&l_chushzhi,maxliangdu,on_treak,(void*)&change);
    createTrackbar("对比度:","亮度和对比度窗口",&d_chushzhi,maxduibidu,duibidu,(void*)&change);

}
void QuickDemo::jianpanshijian(cv::Mat &change){
   Mat src=Mat::zeros(change.size(),change.type());
   namedWindow("结果窗口",WINDOW_FREERATIO);
   while (true)
   {
       int c=waitKey(1000);
       if (c==27) {
           break;
       }else if (c==49)
       {
           cvtColor(change,src,COLOR_BGR2GRAY);
           imshow("结果窗口",src);
        }else if (c==50)
       {
       cvtColor(change,src,COLOR_BGR2HSV);
       imshow("结果窗口",src);
        }else if (c==51)
       {
    src=change+Scalar(50,50,50);
    imshow("结果窗口",src);
      }
   }

}
void QuickDemo::zidaiyansebiao(cv::Mat &change){
    int colormap[] = {
         COLORMAP_AUTUMN,
         COLORMAP_BONE,
         COLORMAP_JET,
         COLORMAP_WINTER,
         COLORMAP_RAINBOW,
         COLORMAP_OCEAN,
         COLORMAP_SUMMER,
         COLORMAP_SPRING,
         COLORMAP_COOL,
         COLORMAP_HSV,//10
         COLORMAP_PINK,
         COLORMAP_HOT,
         COLORMAP_PARULA,
         COLORMAP_MAGMA,
         COLORMAP_INFERNO,
         COLORMAP_PLASMA,
         COLORMAP_VIRIDIS,
         COLORMAP_CIVIDIS,
         COLORMAP_TWILIGHT,
         COLORMAP_TWILIGHT_SHIFTED,//20
         COLORMAP_TURBO,
         COLORMAP_DEEPGREEN
        };
    Mat src=Mat::zeros(change.size(),change.type());
    int index{};
    namedWindow("结果窗口",WINDOW_FREERATIO);
    while (true) {
        int c=waitKey(1000);
        if (c==27) {
            break;
        }
        applyColorMap(change,src,colormap[index%22]);
        ++index;
        imshow("结果窗口",src);
    }

}
void QuickDemo::luojiweiyunsun(cv::Mat &change){
//    rectangele
//        绘制矩形
//            共7个参数
//                第1个参数 输入
//                第2个参数 矩形左上坐标
//                第3个参数 矩形右下坐标
//                第4个参数 矩形颜色
//                第5个参数 线宽
//                                如果参数 >=0,则表示绘制矩形(如为1,表示绘制的矩形边为1个像素)
//                                如果参数 < 0,则表示填充矩形(如-1,表示填充整个矩形)
//                第6个参数 lineType
//                                关于图像锯齿,有几种方式处理
//                                    不管不顾,就用LINE_4 或者 LINE_8
//                                    消除锯齿,就用LINE_AA (AA就是反锯齿)
//                第7个参数  缩小图像,同时缩短矩形左上顶点与(0,0)位置的距离
//                          0表示不变
//                          1表示图像*1/2,同时距离(0,0)的x方向和y方向距离*1/2
//                          2表示图像*(1/2)^2,同时距离(0,0)的x方向和y方向距离*(1/2)^2

    Mat m1=Mat::zeros(Size(250,250),CV_8UC3);
    Mat m2=Mat::zeros(Size(250,250),CV_8UC3);
    rectangle(m1,Point(120,200),Point(150,250),Scalar(3,88,99),-1,LINE_8,0);
    rectangle(m2,Rect(88,120,130,140),Scalar(35,88,250),-1,LINE_8,0);//用左上角下标,后面两个是矩形大小Rect
    imshow("m1",m1);
    imshow("m2",m2);
    Mat m4;
//    bitwise_and(m1,m2,m4);//交及
//        bitwise_or(m1,m2,m4);//并及
//    bitwise_not(change,m4);//取反相当于255减每个像素点
      bitwise_xor(m1,m2,m4);//异或
    imshow("m4",m4);


}
void QuickDemo::tongdaofenoli(cv::Mat &change){
    vector<Mat> tuxiang;
    split(change,tuxiang);//通道分离
//    imshow("蓝色",tuxiang[0]);
//    imshow("绿色",tuxiang[1]);
//    imshow("红色",tuxiang[2]);
    Mat jieguo=Mat::zeros(change.size(),change.type());//使用mixChannels得时候得初始化
    //    tuxiang[0]=0;
    //    tuxiang[2]=0;//可以得到单一一种彩色得图像,合并起来后
    merge(tuxiang,jieguo);//合并
//    imshow("结果",jieguo);
//    mixChannels
//        混合通道
//            共6个参数
//                第1个参数 输入
//                第2个参数 输入的矩阵数
//                第3个参数 输出
//                第4个参数 输出的矩阵数
//                第5个参数 从哪个通道 变成 哪个通道
//                第6个参数 要变的对数
//    这个混合的意思是,彩色图像本来是bgr的顺序,经过通道混合就变成了rgb。
    int jiaohuan[]={0,2,1,2,2,0};
    mixChannels(&change,1,&jieguo,1,jiaohuan,3);//混合,也可以把个4通道分为一个三通道和一个一同道,输出改成一个数组,数量改成2,对数改一下就可以了
    imshow("结果",jieguo);

}
void QuickDemo::genhuanbeijing(cv::Mat &change){
//    在opencv中,我们提取指定色彩范围的区域,采用inRange实现,这样的一块区域,学名叫做ROI(region of interest),感兴趣区域。
//    关于inRange的提取原理
//        图像中,对于在指定色彩范围内的颜色,将置为255(白色),不在的则置为0(黑色)
//        对于多通道的输入,输出结果是各个通道的结果相与,当各通道结果都在上下限之内时,输出为255,否则为0。
//        因此也有人将输出理解为mask掩码模板,作为mask使用。inRange
//    提取指定色彩范围内的区域
//		共4个参数
//			第1个参数 输入
//			第2个参数 色彩下界
//			第3个参数 色彩上界
//			第4个参数 输出
    Mat src;
    cvtColor(change,src,COLOR_BGR2HSV);
    inRange(src,Scalar(35,43,46),Scalar(77,255,255),src);
    Mat beijing=Mat::zeros(change.size(),change.type());
    beijing=Scalar(20,20,200);
    bitwise_not(src,src);
    imshow("inRange",src);
    change.copyTo(beijing,src);//将src中不为0得像素点扣过来beijing里,再把change中赋值到src扣过来得部分,其余beijing不变
    imshow("jieguo",beijing);

}
void QuickDemo::daxiaopingjun(cv::Mat &change){
//    minMaxLoc
//        求取单通道图像像素的最小值,最大值
//            共6个参数
//                第1个参数 输入单通道图像
//                第2个参数 输出最小值
//                第3个参数 输出最大值
//                第4个参数 输出最小值点的坐标
//                第5个参数 输出最大值点的坐标

//                第6个参数 输入图像的子数组(有时候我们会求取ROI区域的最小/最大值,就会传入mask图像)
//                        (这里的子数组,是一种图像掩模,可以实现加东西/扣东西)
//    meanStdDev
//        求取平均值,标准差
//            共4个参数
//                第1个参数 输入
//                第2个参数 输出图像像素的平均值,每个通道都会输出一个
//                第3个参数 输出图像像素的标准差,每个通道都会输出一个

//                第4个参数 输入图像的子数组(有时候我们会求取ROI区域的平均值/标准差,就会传入mask图像)
//                        (这里的子数组,是一种图像掩模,可以实现加东西/扣东西)
    Point minp,maxp;
    double min,max;
    vector <Mat>fenli;
    split(change,fenli);
    int i{1};
    for(auto dantongdao:fenli){
        minMaxLoc(dantongdao,&min,&max,&minp,&maxp);
        cout <<"第"<<i++<<"个通道最大值为:"<<max<<"最小值为:"<<min<<endl;
    }
    Mat pjz,fangcha;
//    cvtColor(change,change,COLOR_BGR2GRAY);
    meanStdDev(change,pjz,fangcha);
    cout <<"平均值:"<<pjz<<endl;
    cout <<"方差:"<<fangcha<<endl;


}
void QuickDemo::huizhituxing(cv::Mat &change){
//    circle
//        绘制圆形
//            共7个参数
//                第1个参数 输入
//                第2个参数 圆心点
//                第3个参数 圆形半径
//                第4个参数 圆形颜色
//                第5个参数 线宽
//                第6个参数 lineType
//                第7个参数  缩小图像,同时缩短圆心与(0,0)位置的距离
//                          0表示不变
//                          1表示图像*1/2,同时距离(0,0)的x方向和y方向距离*1/2
//                          2表示图像*(1/2)^2,同时距离(0,0)的x方向和y方向距离*(1/2)^2
//    line
//        绘制线段
//            共7个参数
//                第1个参数 输入
//                第2个参数 起点
//                第3个参数 终点
//                第4个参数 线段颜色
//                第5个参数 线宽(注意,这个时候线宽只能>=0)
//                第6个参数 lineType
//                第7个参数  缩短线段左上顶点与(0,0)位置的距离
//                          0表示不变
//                          1表示图像*1/2,同时距离(0,0)的x方向和y方向距离*1/2
//                          2表示图像*(1/2)^2,同时距离(0,0)的x方向和y方向距离*(1/2)^2
//    ellipse
//        绘制椭圆
//            共5个参数
//                第1个参数 输入
//                第2个参数 RotatedRect
//                第3个参数 椭圆颜色
//                第4个参数 线宽
//                第5个参数 lineType
    Mat tuya=Mat::zeros(change.size(),change.type());
    Rect rec;
    rec.x=555;
    rec.y=555;
    rec.width=100;
    rec.height=100;
    rectangle(tuya,rec,Scalar(0,0,200),2,LINE_8,0);
    //等价于rectangle(change,rec(55,55,100,100),Scalar(0,0,2000),2,LINE_8,0);
    circle(tuya,Point(55,55),200,Scalar(255,200,200),2,LINE_AA,0);//圆形
    line(tuya,Point(55,100),Point(105,100),Scalar(222,255,200),2,LINE_AA,0);
    RotatedRect yuan;
    yuan.center=Point(80,100);
    yuan.size=Size(50,20);
    yuan.angle=0;//旋转角度
    ellipse(tuya,yuan,Scalar(22,25,20),2,LINE_AA);
    addWeighted(change,0.7,tuya,0.5,0,tuya);
    imshow("矩形",tuya);

}
void QuickDemo::suijishu(cv::Mat &change){
    RNG rng(12345);
    int w=change.cols;
    int h=change.rows;
    while (true) {
        int c=waitKey(255);
        if (c==27) {
            break;
        }
        int x1=rng.uniform(0,w);
        int x2=rng.uniform(0,w);
        int y1=rng.uniform(0,h);
        int y2=rng.uniform(0,h);
        int b=rng.uniform(0,255);
        int g=rng.uniform(0,255);
        int r=rng.uniform(0,255);
        line(change,Point(x1,y1),Point(x2,y2),Scalar(b,g,r),2,LINE_AA,0);
        namedWindow("随机数",WINDOW_FREERATIO);
        imshow("随机数",change);
    }
}
void QuickDemo::duobianxinghuizhi(cv::Mat &change){
//    polylines
//        绘制多条多边形曲线
//            共7个参数
//                第1个参数 输入
//                第2个参数 输入多边形点集(绘制边的顺序,与点集数组中点的顺序有关)
//                第3个参数 isclosed(bool类型)
//                第4个参数 多边形颜色
//                第5个参数 线宽
//                第6个参数 lineType
//                第7个参数 shift(左顶点缩小)
//    fillPoly
//        填充绘制的多边形
//            共6个参数
//                第1个参数 输入
//                第2个参数 点集数组
//                第3个参数 填充颜色
//                第4个参数 lineType
//                第5个参数 shift
//                第6个参数 轮廓所有点的可选偏移
//    drawContours
//        绘制轮廓轮廓/填充轮廓
//            共9个参数,这里我们只介绍前5个(剩下的有缺省值)
//                第1个参数 输入
//                第2个参数 多边形轮廓的数组
//                第3个参数 选择的多边形轮廓(在数组中的编号,-1为全选)
//                第4个参数 多边形颜色
//                第5个参数 填充/绘制(-1为填充)

    Mat beijing=Mat::zeros(Size(500,500),CV_8UC3);
    vector<Point>duobianxing;
    RNG rng(12345);
    for (int i{};i<5;i++) {
        int x=rng.uniform(0,500);
        int y=rng.uniform(0,500);
        duobianxing.push_back(Point(x,y));
    }
//    int b=rng.uniform(0,255);
//    int g=rng.uniform(0,255);
//    int r=rng.uniform(0,255);
    /*polylines(beijing,duobianxing,true,Scalar(b,g,r),1,LINE_AA,0);
    fillPoly(beijing,duobianxing,Scalar(rng.uniform(0,255),rng.uniform(0,255),rng.uniform(0,255)),LINE_AA,0);*///如果先填充再画线,就有边框
    vector<vector<Point>> jihe;
    jihe.push_back(duobianxing);
    drawContours(beijing,jihe,-1,Scalar(rng.uniform(0,255),rng.uniform(0,255),rng.uniform(0,255)),-1);//jihe是一个多边形得集合,这个函数可以同时画多个多边形
    imshow("多边形",beijing);
}
Point s(0,0),e(0,0);Mat temp;
void shubiaohuidiao(int shijian,int x, int y,int jian,void* tupain){//jian这个参数用到得类型是按着不放那种,6种类型看官方文档,shijian类型是点击
//    MouseCallback类型的函数(鼠标事件回调函数)
//        共5个参数,这5个参数全是针对鼠标的捕捉
//            第1个参数 捕捉的鼠标事件(查阅文档可知,都有什么类型)
//            第2个参数 捕捉的鼠标事件的x坐标
//            第3个参数 捕捉的鼠标事件的y坐标
//            第4个参数 标志捕捉的事件是哪一个键(有5种,查阅文档可知)
//            第5个参数 void*类型的可选传入数据
    Mat src=*((Mat*)tupain);
    if (shijian==EVENT_LBUTTONDOWN) {//按下左键
        s.x=x;
        s.y=y;
        cout <<"起点"<<s<<endl;
    }
    if (jian==EVENT_FLAG_LBUTTON&&shijian==EVENT_MOUSEMOVE) {//按住并且拖动//画之前用原图替换现图,可以去掉之前画的,就永远是最新得那个框了
        e.x=x;
        e.y=y;
        temp.copyTo(src);
        rectangle(src,s,e,Scalar(0,0,255),2,LINE_AA,0);
        imshow("鼠标绘制矩形",src);
    }
    if (shijian==EVENT_LBUTTONUP) {//松开
        e.x=x;
        e.y=y;
        cout <<"终点"<<e<<endl;
        rectangle(src,s,e,Scalar(0,0,255),2,LINE_AA,0);
        imshow("鼠标绘制矩形",src);
        Rect rec(e,s);
        imshow("ROI",src(rec));


    }

}
//画圆形截取
//void shubiaohuidiao(int shijian,int x, int y,int jian,void* tupain){//jian这个参数用到得类型是按着不放那种,6种类型看官方文档,shijian类型是点击

//    Mat src=*((Mat*)tupain);
//    if (shijian==EVENT_LBUTTONDOWN) {//按下左键
//        s.x=x;
//        s.y=y;
//        cout <<"起点"<<s<<endl;
//    }
//    if (jian==EVENT_FLAG_LBUTTON&&shijian==EVENT_MOUSEMOVE) {//按住并且拖动//画之前用原图替换现图,可以去掉之前画的,就永远是最新得那个框了
//        e.x=x;
//        e.y=y;
//        double banjing=(pow((e.x-s.x)*(e.x-s.x)+(e.y-s.y)*(e.y-s.y),1.0f/2))/2;
//        Point yuanxin((e.x+s.x)/2,(e.y+s.y)/2);
//        temp.copyTo(src);
//        circle(src,yuanxin,banjing,Scalar(0,0,255),10,LINE_AA,0);
//        imshow("鼠标绘制矩形",src);
//    }
//    if (shijian==EVENT_LBUTTONUP) {//松开
//        e.x=x;
//        e.y=y;
//        cout <<"终点"<<e<<endl;
//        double banjing=(pow((e.x-s.x)*(e.x-s.x)+(e.y-s.y)*(e.y-s.y),1.0f/2))/2;
//        Point yuanxin((e.x+s.x)/2,(e.y+s.y)/2);
//        circle(src,yuanxin,banjing,Scalar(0,0,255),10,LINE_AA,0);
//        imshow("鼠标绘制矩形",src);
//        Mat lingshi=Mat::zeros(src.size(),CV_8UC1);
//        circle(lingshi,yuanxin,banjing,Scalar(255,255,255),-1,LINE_AA,0);
//        Mat roi=Mat::zeros(src.size(),src.type());
//        temp.copyTo(roi,lingshi);
//        imshow("roi",roi);


//    }

//}
void QuickDemo::shubiaoshijian(cv::Mat &change){
//    setMouseCallback
//        设置指定窗口的鼠标处理程序
//            共3个参数
//                第1个参数 窗口名称
//                第2个参数 处理鼠标事件的回调函数
//                第3个参数 传递给回调函数的可选参数
    namedWindow("鼠标绘制矩形",WINDOW_FREERATIO);
    setMouseCallback("鼠标绘制矩形",shubiaohuidiao,(void*)&change);
    imshow("鼠标绘制矩形",change);
    temp=change.clone();
}

void QuickDemo::tuxiangleixingzhuanhuanyuguiyi(cv::Mat &change){
//    convertTO
//        数据类型转换
//            本文采用了第一种传参方式
//            共2个参数
//                第1个参数 输入
//                第2个参数 将要转换的数据类型
//    normalize
//        归一化,归一指归为同一范围
//            共7个参数
//                第1个参数 输入
//                第2个参数 输出

//                第3个参数 alpha 规范值/归一化范围下限
//                第4个参数 beta 归一化范围上限
//                        默认规范值为1,即数值规范为1,此时beta = 0
//                        (所以这两处有两种传参方式
//                            第一种 1.0 0,默认的这种,取值规范为[0,1]
//                            第二种 0,b 下限上限的这种。取值规范为指定范围
//                        两种方式都可以使用)

//                第5个参数 归一化类型(查阅文档可知)
//                第6个参数 默认类型与src一直
//                            负数,则类型与src一直
//                            否则,通道数和src一致,depth=指定的图像深度(图像深度= 图像列数*通道数)
//                第7个参数 可选mask
//    关于归一化类型,常用的有4种:

//        NORM_L1——求和归一
//        NORM_L2——三维向量转单位向量归一
//        NORM_INF——根据最大值归一
//        NORM_MINMAX——根绝最大最小值差值归一(最为常用)

    cout <<change.type()<<endl;
    change.convertTo(change,CV_32FC3);
    cout <<change.type()<<endl;
    Mat dit;
    normalize(change,dit,1.0,0,NORM_MINMAX);
    imshow("jgg",dit);
}
void QuickDemo::fangsuo(cv::Mat &change){
//    resize
//        重设图像宽长
//            共6个参数
//                第1个参数 输入
//                第2个参数 输出

//                第3个参数 输出图像的size
//                第4个参数 fx 沿水平的比例因子
//                第5个参数 fy 沿垂直的比例因子

//                        可以使用size做放缩插值,也可以使用fx,fy卷积做放缩插值

//                第6个参数 插值方法(其中前两种比较快,后两种比较慢)
//    INTER_NEAREST = 0 ——最近邻插值
//    INTER_LINEAR = 1 ——线性插值//fangda
//    INTER_CUBIC = 2 ——立方插值
//    INTER_LANCZOS4 = 4 ——Lanczos插值
    //    INTER_AREA xiao
    Mat a1,a2;
    int w=change.cols;
    int h=change.rows;
//    resize(change,a1,Size(w*2,h*2),0,0,INTER_NEAREST);//放大
//    resize(change,a1,Size(0,0),0.5,0.5,INTER_NEAREST);//缩小0.5
   resize(change,a1,Size(w/2,h/2),0,0,INTER_NEAREST);//缩小0.5
    imshow("缩小",a1);

}
void QuickDemo::fanzhaun(cv::Mat &change){
    Mat src;
//    flip(change,src,0);//上下翻转
    flip(change,src,-1);//上下左右翻转//180du
//    flip(change,src,1);//左右翻转
    namedWindow("fanzhuan",WINDOW_FREERATIO);
    imshow("fanzhuan",src);
}
void QuickDemo::tupainxuanzuan(cv::Mat &change){
//    warpAffine
//        仿射变换
//            共7个参数
//                第1个参数 输入
//                第2个参数 输出
//                第3个参数 输入的变换矩阵(2行3列)
//                第4个参数 输出图像的size
//                第5个参数 插值方法
//                第6个参数 边界模式
//                第7个参数 边界颜色
//    getRotationMatrix2D
//        计算二维旋转的仿射矩阵(变换矩阵)
//            共3个参数
//                第1个参数 图像的旋转中心
//                第2个参数 旋转角度
//                            (规定坐标原点左上角,正值表示逆时针旋转)
//                第3个参数 各向同性比例因子(对图像本身大小的放缩
//      旋转后:
//    新宽度nw = wcosΘ + hsinΘ
//    新宽度nh = wsinΘ + hcosΘ
//    新旋转中心x +=( nw/2 - w/2)
//    新旋转中心y +=( nh/2 - h/2))
    Mat src,get;
    int w=change.cols;
    int h=change.rows;
    get=getRotationMatrix2D(Point2f(w/2,h/2),45,1.0);//xy是浮点类型
    double cos=abs(get.at<double>(0,0));
    double sin=abs(get.at<double>(0,1));
    int nw=w*cos+h*sin;
    int nh=w*sin+h*cos;
    get.at<double>(0,2)+=(nw/2-w/2);//这里一定要这样字,不能直接赋值nw/2
    get.at<double>(1,2)+=(nh/2-h/2);

    warpAffine(change,src,get,Size(nw,nh),INTER_NEAREST,0,Scalar(255,255,255));

    imshow("旋转",src);
}
void QuickDemo::shipingcaozuo(cv::Mat &change){
    VideoCapture video("E:\\BaiduNetdiskDownload\\opencv\\sources\\samples\\data\\Megamind.avi");
    if (video.isOpened())
    {
        Mat tupian;
        while (true) {
       video.read(tupian);//读没了返回false并且传入空图像
            if (tupian.empty()) {
                break;
            }
            imshow("shiping",tupian);
            if(waitKey(16)==27){
                break;
            }
        }
    }
    video.release();
}
void QuickDemo::shipingshuxing(cv::Mat &change){
//    VideoWriter (const String &filename, int fourcc, double fps, Size frameSize, bool isColor=true)
//        视频写入对象
//            共5个参数
//                第1个参数 视频文件路径
//                第2个参数 视频编码方式(我们可以通过VideoCapture::get(CAP_PROP_FOURCC)获得)
//                第3个参数 fps
//                第4个参数 size
//                第5个参数 是否为彩色
     VideoCapture video("E:\\BaiduNetdiskDownload\\opencv\\sources\\samples\\data\\Megamind.avi");
     int zhenkuandu=video.get(CAP_PROP_FRAME_WIDTH);
     int zhengaodu=video.get(CAP_PROP_FRAME_HEIGHT);
     int zongzhenshu=video.get(CAP_PROP_FRAME_COUNT);
     int fps=video.get(CAP_PROP_FPS);
     cout <<zhenkuandu<<endl;
     cout <<zhengaodu<<endl;
     cout <<zongzhenshu<<endl;
     cout <<fps<<endl;
     VideoWriter wri("E:\\BaiduNetdiskDownload\\488.avi",video.get(CAP_PROP_FOCUS),fps,Size(zhenkuandu,zhengaodu),true);
     Mat tupian;
     if (video.isOpened())
     {
         while (true) {
             video.read(tupian);
             if (tupian.empty()) {
                 break;
             }
             wri.write(tupian);
             imshow("tup",tupian);
             int c=waitKey(17);
             if (c==27) {
                 break;
             }


         }
     }
     video.release();
     wri.release();
}
void QuickDemo::tupianzhifangtu(cv::Mat &change){
//    calcHist
//        计算一维数组的直方图(输入图像可以有多通道)
//            共10个参数
//                第1个参数 图像数组
//                第2个参数 输入图像数量
//                第3个参数 通道数组(单通道0,多通道可指定通道BGR)
//                第4个参数 可选mask

//                第5个参数 输出直方图数据(值与对应频次)的n维数组
//                第6个参数 直方图维数

//                        当通道为1个时,我们选择维度为1维,此时直方图数据就为一维数组
//                        当维度为2个时,我们选择维度为2维,此时直方图数据就为二维数组
//                        ………………
//                        也就是说,n张图像 每张图像m个通道 也可以计算出相应的直方图数据

//                        但对于绘制来说,一般都只绘制到2维,3维及以上就很复杂了

//                第7个参数 histSize( bins数组,x轴长度,是直方图图像得X轴长度,在最终显示图像中实际是有多少个区间多少个点,最终图像宽度除以区间数量等于区间大小)
//                第8个参数 ranges(取值范围数组)//根据维度,有时候有多个范围,所以这里多个范围时候多个范围数组,再把这些范围数组放到一个储存指针得数组里

//                //以下参数暂时用不到
//                第9个参数 指示直方图bin间隔是否一致
//                            默认为true,即等间隔取值
//                            如果为false,则range不能写{0,255}这种,就要写{1,1,……,1}这种

//                第10个参数 累计标志(默认为false)
//                            当多张图像的时候,
//                                如果为true,则绘制直每张方图的时候,不会从头清空
//                                会在前者直方图的基础上继续
//    cvRound
//        将浮点数四舍五入到最近的整数
//            共1个参数
//                第1个参数 要处理的浮点数
    vector<Mat> fenli;
    split(change,fenli);
    const int channels[]={0};
    int changdu[]={256};
    const float fanwei[]={0,255};
    Mat b_zft,g_zft,r_zft;
    const float* fanweizongjie[]={fanwei};
    calcHist(&fenli[0],1,channels,Mat(),b_zft,1,changdu,fanweizongjie);
    calcHist(&fenli[1],1,channels,Mat(),g_zft,1,changdu,fanweizongjie);
    calcHist(&fenli[2],1,channels,Mat(),r_zft,1,changdu,fanweizongjie);
    int zftw=512;
    int zfth=400;
    int bin_w=cvRound((double)zftw/changdu[0]);//区间,两个点之间得距离横坐标
    Mat xianshitu=Mat::zeros(Size(zftw,zfth),CV_8UC3);
    normalize(b_zft,b_zft,0,xianshitu.rows,NORM_MINMAX,-1,Mat());
    normalize(g_zft,g_zft,0,xianshitu.rows,NORM_MINMAX,-1,Mat());
    normalize(r_zft,r_zft,0,xianshitu.rows,NORM_MINMAX,-1,Mat());
    for (int i{};i<256;i++) {
        Point p11(bin_w*i,zfth-cvRound(b_zft.at<float>(i)));
        Point p12(bin_w*i+bin_w,zfth-cvRound(b_zft.at<float>(i+1)));
        Point p21(bin_w*i,zfth-cvRound(g_zft.at<float>(i)));
        Point p22(bin_w*i+bin_w,zfth-cvRound(g_zft.at<float>(i+1)));
        Point p31(bin_w*i,zfth-cvRound(r_zft.at<float>(i)));
        Point p32(bin_w*i+bin_w,zfth-cvRound(r_zft.at<float>(i+1)));
        line(xianshitu,p11,p12,Scalar(255,0,0),1,LINE_AA,0);
        line(xianshitu,p21,p22,Scalar(0,255,0),1,LINE_AA,0);
        line(xianshitu,p31,p32,Scalar(0,0,255),1,LINE_AA,0);
    }
    imshow("直方图",xianshitu);


}
void QuickDemo::tupianzhifangtuerwei(cv::Mat &change){
    Mat zhifangtu,hsv,jieguo;
    cvtColor(change,hsv,COLOR_BGR2HSV);
    int h_bin=30;
    int s_bin=32;
    int arr_bin[]={h_bin,s_bin};
    float h_fanwei[]={0,180};//范围是float
    float s_fanwei[]={0,255};
    const float* range[]={h_fanwei,s_fanwei};//必须是const
    int channels[]={0,1};
    calcHist(&hsv,1,channels,Mat(),zhifangtu,2,arr_bin,range);
    double max{};
    minMaxLoc(zhifangtu,0,&max,0,0);
    int diejialiang{10};
    jieguo=Mat::zeros(Size(s_bin*diejialiang,h_bin*diejialiang),CV_8UC3);
    for (int i=0;i<zhifangtu.rows;i++)
    {
        for (int j=0;j<zhifangtu.cols;j++) {
            int pingci=zhifangtu.at<float>(i,j);
            int guiyizhi=cvRound(pingci/max*255);
            Point p1(i*diejialiang,j*diejialiang);
            Point p2((i+1)*diejialiang,(j+1)*diejialiang);
            rectangle(jieguo,p1,p2,Scalar::all(guiyizhi),-1,LINE_AA);

        }
    }
    applyColorMap(jieguo,jieguo,COLORMAP_DEEPGREEN);
    imshow("zhifangtu",jieguo);
}
void QuickDemo::zhifangtujunheng(cv::Mat &change){
//    equalizeHist
//        均衡灰度图像的直方图
//            共2个参数
//                第1个参数 输入
//                第2个参数 输出

//    Mat huidu;
//    cvtColor(change,huidu,COLOR_BGR2GRAY);
//    Mat jieguo;
//    equalizeHist(huidu,jieguo);
//    imshow("huidu",huidu);
//    imshow("jieguo",jieguo);//灰度图像均衡
    //彩色图像均衡
        Mat hsv,jieguo;
        cvtColor(change,hsv,COLOR_BGR2HSV);
        vector<Mat> fenli;
        split(hsv,fenli);
        equalizeHist(fenli[2],fenli[2]);
        merge(fenli,jieguo);
        cvtColor(jieguo,jieguo,COLOR_HSV2BGR);
        imshow("zftjh",jieguo);

}
void QuickDemo::tuxiangjuanji(cv::Mat &change){
//    blur
//        使用归一化框滤镜模糊图像
//            共5个参数
//                第1个参数 输入
//                第2个参数 输出
//                第3个参数 卷积核size(卷积核越大,图像模糊程度越高)
//                第4个参数 锚点
//                        (默认值Point(-1,-1)表示锚点位于卷积后的内核中心,卷积后的值在这里更新)
//                第5个参数 图像边缘处理方式
//                        (默认参数 BORDER_DEFAULT 边缘有很多处理方式,查阅官方文档可知)
    Mat jieguo;
    blur(change,jieguo,Size(13,13));
//    blur(change,jieguo,Siz e(1,13));//列卷积
//    blur(change,jieguo,Size(13,1));//行卷积
    imshow("jieguo",jieguo);

}
void QuickDemo::gaosimohu(cv::Mat &change){
//    GaussianBlur
//        使用高斯滤镜模糊图像
//            共6个参数
//                第1个参数 输入
//                第2个参数 输出
//                第3个参数 高斯卷积核size(必须是整数和奇数,可以是0,然后根据sigma计算他们)

//                第4个参数 x方向的高斯核标准差
//                第5个参数 y方向的高斯核标准差(如果sigmaY = 0,则设置其 = sigmaX)
//                        (如果两个标准差都为0,则根据高斯卷积核size计算)
//                        (官方文档建议:三个参数最好都指定)

//                         虽然size和sigma都可以实现模糊,但sigma的影响更大

//                第6个参数 图像边缘处理方式
    Mat jieguo;
    GaussianBlur(change,jieguo,Size(5,5),5,5);
    imshow("jieguo",jieguo);

}
void QuickDemo::gaosimohushuangbian(cv::Mat &change){
//    bilateralFilter  通常情况下sigmaColor取100 ~ 150,sigmaSpace取10 ~ 25
//        将双边过滤器应用于图像
//            共6个参数
//                第1个参数 输入
//                第2个参数 输出
//                第3个参数 过滤期间使用的每个像素邻域的直径(如为非正数,则根据sigmaSpac计算)一般为0

//                第4个参数 sigmaColor(在颜色空间中的过滤标准差)

//                            sigmaColor一般取值大一点,
//                            大一点的话根据二维高斯函数计算所得的值越小,越趋近于0,影响越低

//                第5个参数 sigmaSpace(在坐标空间中的过滤标准差)

//                第6个参数  图像边缘处理方式
    Mat jieguo;
    bilateralFilter(change,jieguo,0,100,10);
//    namedWindow("jieguo",WINDOW_FREERATIO);
    imshow("jieguo",jieguo);

}
void QuickDemo::renlianshibie(cv::Mat &change){
    string path="E:\\BaiduNetdiskDownload\\opencv\\sources\\samples\\dnn\\face_detector\\";
    Net net=readNetFromTensorflow(path+"opencv_face_detector_uint8.pb",path+"opencv_face_detector.pbtxt");//读取模型进来
    VideoCapture video("E:\\BaiduNetdiskDownload\\opencv\\sources\\samples\\data\\Megamind.avi");
    Mat tupian;
    if (video.isOpened())
    {
        while (true) {
            video.read(tupian);
            if (tupian.empty()) {
                break;
            }
            Mat bandian=blobFromImage(tupian,1.0,Size(300, 300),Scalar(104, 177, 123),false,false);
            net.setInput(bandian);
            Mat huoqu=net.forward();
            Mat dectectionMat(huoqu.size[2],huoqu.size[3],CV_32F,huoqu.ptr<float>());
            for (int i{0};i<dectectionMat.rows;++i) {
                double zhi=dectectionMat.at<float>(i,2);
                if (zhi>0.5) {
                    int x1=static_cast<int>(dectectionMat.at<float>(i,3)*tupian.cols);
                    int y1=static_cast<int>(dectectionMat.at<float>(i,4)*tupian.rows);
                    int x2=static_cast<int>(dectectionMat.at<float>(i,5)*tupian.cols);
                    int y2=static_cast<int>(dectectionMat.at<float>(i,6)*tupian.rows);
                    rectangle(tupian,Point(x1,y1),Point(x2,y2),Scalar(0,0,255),1,LINE_AA,0);
                }
            }
            imshow("tup",tupian);
            int c=waitKey(17);
            if (c==27) {
                break;
            }


        }
    }
    video.release();
}
void QuickDemo::wenzihuizhi(cv::Mat &change){
//    putText
//        共9个参数
//            第1个参数 输入输出
//            第2个参数 字符串
//            第3个参数 字符串左下角的点
//            第4个参数 字体类型
//            第5个参数 字体大小
//            第6个参数 字体颜色
//            第7个参数 线宽
//            第8个参数 lineType

//            第9个参数 默认false,图像数据原点位于左上角
//                      如果true,图像数据原点位于左下角
        putText(change,"kkpp",Point(200,200),FONT_HERSHEY_COMPLEX,2.0,Scalar(45,18,77),2,LINE_AA);
        namedWindow("jieguo",WINDOW_FREERATIO);
        imshow("jieguo",change);
}
void QuickDemo::hezimohu(cv::Mat &change){
//    boxfilter
//        使用方框滤镜模糊图像
//            共7个参数
//                第1个参数 输入
//                第2个参数 输出
//                第3个参数 输出图像深度(-1表示使用输入图像的深度)
//                第4个参数 ksize(模糊内核大小)
//                第5个参数 锚点(默认Point(-1,-1)表示锚点位于内核中心)
//                第6个参数 是否归一化(默认为true)
//               第7个参数 borderType
    Mat jieguo;
    boxFilter(change,jieguo,-1,Size(30,30));//Size越大越模糊
    namedWindow("jieguo",WINDOW_FREERATIO);
    imshow("jieguo",jieguo);
}
void QuickDemo::zidingyilvbo(cv::Mat &change){
//    filter2D
//        将图像与内核卷积
//            共7个参数
//                第1个参数 输入
//                第2个参数 输出
//                第3个参数 输出图像的深度(-1表示和输入图像一直)
//                第4个参数 卷积核
//                第5个参数 锚点
//                        (指示内核中过滤点的相对位置;
//                          锚点应位于内核中;
//                          默认值Point(-1,-1),表示锚点位于内核中心)
//                第6个参数 delta变量(可以调整亮度)
//                第7个参数 borderType

    //均值滤波
//    int k=15;
//    Mat shuchu=Mat::ones(k,k,CV_32F)/(float)(k*k);//类似于归一化,都成小数,后面imshow得时候会把小数*255
//    Mat src;
//    filter2D(change,src,-1,shuchu);


    //自定滤波
    Mat juanji=(Mat_<int>(2,2)<<0,1,8,5);
    Mat src;
    filter2D(change,src,CV_32F,juanji,Point(-1,-1));
//    convertScaleAbs(src,src);
    namedWindow("src",WINDOW_FREERATIO);
    imshow("src",src);

}
void QuickDemo::tuxiangtidu(cv::Mat &change){
    //梯度,卷积得一种,图片得轮廓,色差值大得地方
    //robot算子x:1,0,0,-1 y:0,1,-1,0(轮廓不明显)
    //CV_32F
    Mat jx=(Mat_<int>(2,2)<<1,0,0,-1);
    Mat jy=(Mat_<int>(2,2)<<0,1,-1,0);
    Mat sx,sy;
    filter2D(change,sx,CV_32F,jx,Point(-1,-1),0);
    filter2D(change,sy,CV_32F,jy,Point(-1,-1),0);
    convertScaleAbs(sx,sx);
    convertScaleAbs(sy,sy);
    Mat reslult;
    add(sx,sy,reslult);
    imshow("jieguo",reslult);

    //Sobel算子:1:输入,2.输出3.深度,4.x方向 5.y方向6.ksize越大越明显
    Sobel(change,sx,CV_32F,1,0);
    Sobel(change,sy,CV_32F,0,1);
    convertScaleAbs(sx,sx);
    convertScaleAbs(sy,sy);
    add(sx,sy,reslult);
    imshow("Sobel",reslult);

    //Scharr算子:1:输入,2.输出3.深度,4.x方向 5.y方向
    Scharr(change,sx,CV_32F,1,0);
    Scharr(change,sy,CV_32F,0,1);
    convertScaleAbs(sx,sx);
    convertScaleAbs(sy,sy);
    add(sx,sy,reslult);
    imshow("Scharr",reslult);

}
void QuickDemo::tuxiangbianyuanfaxian(cv::Mat &change){
//    拉普拉斯算子,二阶导数算子,提取图像轮廓,噪声对拉普拉斯影响较大
//    不用convertScaleAbs 深度用原图-1

    Mat src;
    Laplacian(change,src,-1,1);

    imshow("lapulasi",src);
    //图片锐化 算子:0,-1,0
    //            -1,5,-1
    //             0,-1,0
    Mat ruihua=(Mat_<int>(3,3)<<0,-1,0,-1,5,-1,0,-1,0);
    filter2D(change,src,CV_32F,ruihua,Point(-1,-1));
    convertScaleAbs(src,src);
    imshow("ruihua",src);


}
void QuickDemo::ruihua(cv::Mat &change){
//    gaosi模糊-lapulasi
//    lapu 得权重越大越锐化
    Mat gaosi,lapu;
    GaussianBlur(change,gaosi,Size(3,3),0);
    GaussianBlur(change,change,Size(3,3),0);//高斯降噪锐化效果好
    Laplacian(change,lapu,-1,1);
    imshow("Laplacian",lapu);
    Mat result;
    addWeighted(gaosi,1.0,lapu,-2.0,0,result);
//    namedWindow("ruihua",WINDOW_FREERATIO);
    imshow("ruihua",result);
}
void QuickDemo::tuxiangzaosheng(cv::Mat &change){
    //椒盐噪声
    RNG rng(12345);
    Mat yangcong=change.clone();
    int num=100000;
    for (int i=0;i<num;++i) {
        int x=rng.uniform(0,yangcong.rows);
        int y=rng.uniform(0,yangcong.cols);
        if (i%2==0) {
            yangcong.at<Vec3b>(x,y)=Vec3b(255,255,255);
        }
        else {
            yangcong.at<Vec3b>(x,y)=Vec3b(0,0,0);
        }
    }
    imshow("yangcong",yangcong);
    //高斯噪声
    Mat gaosi=Mat::zeros(change.size(),change.type());
    //参数,均值(亮度)和方差(噪声程度)
    randn(gaosi,Scalar(10,10,10),Scalar(100,100,100));
    add(change,gaosi,gaosi);
    imshow("gaosi",gaosi);
    
}
void QuickDemo::quzaosheng(cv::Mat &change){
    RNG rng(12345);
    Mat yangcong=change.clone();
    int num=100000;
    for (int i=0;i<num;++i) {
        int x=rng.uniform(0,yangcong.rows);
        int y=rng.uniform(0,yangcong.cols);
        if (i%2==0) {
            yangcong.at<Vec3b>(x,y)=Vec3b(255,255,255);
        }
        else {
            yangcong.at<Vec3b>(x,y)=Vec3b(0,0,0);
        }
    }
    imshow("yangcong",yangcong);
    //中值滤波对椒盐噪声比较有效,原理抽取一块像素区,将里面像素值排列,取中间值
//    中值滤波函数-medianBlur
//    -参数InputArray表示输入图像Mat对象
//    -参数OutputArray表示模糊之后输出Mat对象
//    -参数ksize表示卷积核大小,必须是正数而且必须是大于1,如:3、5、7等。
    medianBlur(yangcong,yangcong,3);
    imshow("去噪声",yangcong);
}
void QuickDemo::quzaoshenggs(cv::Mat &change){
    //高斯噪声
    Mat gaosi=Mat::zeros(change.size(),change.type());
    //参数,均值(亮度)和方差(噪声程度)
    randn(gaosi,Scalar(10,10,10),Scalar(50,50,50));
    add(change,gaosi,gaosi);
    imshow("噪声图像",gaosi);
    //高斯双边去噪声、
    Mat reault;
    bilateralFilter(gaosi,reault,0,100,10);
    imshow("高斯双边去噪声",reault);

    //非局部均值滤波(对小噪声比较有效)
    //7是卷积核大小,21是局部范围大小 h越大越模糊,最后一个参数一般是倒数第二得3到5倍
    Mat fjbjzlb;
    fastNlMeansDenoisingColored(gaosi,fjbjzlb,3,3,7,21);
    imshow("非局部均值滤波",fjbjzlb);
    cvtColor(gaosi,gaosi,COLOR_BGR2GRAY);
    fastNlMeansDenoising(gaosi,fjbjzlb);
    imshow("黑白非局部均值滤波",fjbjzlb);

}
void on_candy(int t1,void* tupian){
    Mat src=*((Mat*)tupian);
    //低于低阈值得像素点去除,高于高阈值得像素点保存
    //1.输入 2.输出 3.t1低阈值 4.t2高阈值(为低阈值得2——3倍) 5.用默认值表示 Sobel 算子的孔径大小 6.默认用L1,快一点 为false
    Mat result;
    Canny(src,result,t1,t1*3,3,false);
//    bitwise_and(src,src,dit,result);
    namedWindow("结果",WINDOW_FREERATIO);
    imshow("结果",result);

}
void QuickDemo::bianyuantiqu(cv::Mat &change){
    int chushizhi=50;
    int max=100;
    createTrackbar("边缘程度:","原始",&chushizhi,max,on_candy,(void*)&change);
}

void QuickDemo::erzhituxiang(cv::Mat &change){
    //二值图像就是像素点为0或者255得图像
    //二值分割有5种方法(手动阈值)
//    1、THRESH_BINARY:将大于阈值的设为255,小于阈值的设为0
//    2、THRESH_BINARY_INV:将大于阈值的设为0,小于阈值的设为255
//    3、THRESH_TOZERO:将小于阈值的设为0,大于阈值的保留
//    4、THRESH_TOZERO_INV:将大于阈值的设为0,小于阈值的保留
//    5、THRESH_TRUNC:将大于阈值的值设置为阈值得值,小于阈值的保留
    Mat gray,src;
    cvtColor(change,gray,COLOR_BGR2GRAY);
    //输入 输出 阈值 最大值 分割方法(要用灰度图像)
    threshold(gray,src,171,255,THRESH_BINARY);
    imshow("THRESH_BINARY",src);
    threshold(gray,src,171,255,THRESH_BINARY_INV);
    imshow("THRESH_BINARY_INV",src);
    threshold(gray,src,171,255,THRESH_TOZERO);
    imshow("THRESH_TOZERO",src);
    threshold(gray,src,171,255,THRESH_TOZERO_INV);
    imshow("THRESH_TOZERO_INV",src);
    threshold(gray,src,171,255,THRESH_TRUNC);
    imshow("THRESH_TRUNC",src);

}
void QuickDemo::erzhituxiangquanjuyuzhi(cv::Mat &change){
//    全局分割方法:1.均值法
//                2.直方图法(最优法)THRESH_OTSU
//                3.三角法(对单峰直方图效果好)THRESH_TRIANGLE(2,3自动阈值)
    Mat gray,src;
    cvtColor(change,gray,COLOR_BGR2GRAY);
    Scalar m=mean(gray);//平均值
    threshold(gray,src,m[0],255,THRESH_BINARY);
    imshow("均值法",src);
    threshold(gray,src,0,255,THRESH_OTSU);
    imshow("THRESH_OTSU",src);
    threshold(gray,src,0,255,THRESH_TRIANGLE);
    imshow("THRESH_TRIANGLE",src);
//     cout <<m[0]<<"\t"<<zft<<"\t"<<sjx<<endl;
}
void QuickDemo::zishiyingyuzhi(cv::Mat &change){
//    用到的阈值是自适应的,而不是整张图共用一个固定值。当同一幅图像上的不同部分的具有不同亮度时。这种情况下我们需要采用自适应阈值。
//    此时的阈值是根据图像上的每一个小区域计算与其对应的阈值。因此在同一幅图像上的不同区域采用的是不同的阈值,从而使我们能在亮度不同的情况下得到更好的结果。
//    adaptiveThreshold
//    第一个参数(img):源图像
//    th2:输出图像,与源图像大小一致
//    第三个参数(255):超过阈值的部分取值是多少(对于cv.THRESH_BINARY而言)
//    第四个参数(cv.ADAPTIVE_THRESH_MEAN_C):
//    (1)在一个邻域内计算阈值所采用的算法,有两个取值,分别为 ADAPTIVE_THRESH_MEAN_C 和 ADAPTIVE_THRESH_GAUSSIAN_C
//    (2)ADAPTIVE_THRESH_MEAN_C的计算方法是计算出领域的平均值再减去第七个参数2的值。
//    (3)ADAPTIVE_THRESH_GAUSSIAN_C的计算方法是计算出领域的高斯均值再减去第七个参数2的值
//    第五个参数(cv.THRESH_BINARY):这是阈值类型,只有两个取值,分别为 THRESH_BINARY 和THRESH_BINARY_INV
//    第六个参数(11):adaptiveThreshold的计算单位是像素的邻域块大小选择,这是局部邻域大小,3、5、7等(越大白纹理越多)
//    第七个参数(2):这个参数实际上是一个偏移值调整量,用均值和高斯计算阈值后,再减或加这个值就是最终阈值。
     Mat gray,src;
     cvtColor(change,gray,COLOR_BGR2GRAY);
     adaptiveThreshold(gray,src,255,ADAPTIVE_THRESH_MEAN_C,THRESH_BINARY,3,-5);
     imshow("ADAPTIVE_THRESH_MEAN_C",src);
     adaptiveThreshold(gray,src,255,ADAPTIVE_THRESH_GAUSSIAN_C,THRESH_BINARY,3,-5);
     imshow("ADAPTIVE_THRESH_GAUSSIAN_C",src);
}
void QuickDemo::tiquqianjing(cv::Mat &change){
//    函数的返回值是标签总数(包括背景),输出的是标记图像,所谓标记图像就是图像中每一个连通域(前景)都拥有一个标签(背景的标签是0),该标签即代表了此连通域的灰度值。
//    例如,一幅图像中存在3个连通域(背景除外),那么他们的标签分别是1、2、3,他们的灰度值分别是1、2、3。
//    connectedComponents(
//            InputArray    image,             // 输入二值图像
//            OutputArray   labels,            // 输出的标记图像,背景index=0
//            int           connectivity = 8,  // 连通域,默认是8连通
//            int           ltype = CV_32S     // 输出的labels类型,默认是CV_32S
    GaussianBlur(change,change,Size(3,3),0);//高斯降噪
    //转为二值图像
    Mat gray,src,qianjingtu;
    cvtColor(change,gray,COLOR_RGB2GRAY);
    threshold(gray,src,0,255,THRESH_BINARY|THRESH_OTSU);
    imshow("二值",src);
    int qianjing=connectedComponents(src,qianjingtu,8,CV_32S,CCL_DEFAULT);
    vector<Vec3b> qianjingheji(qianjing);
    qianjingheji[0]=Vec3b(0,0,0);
    RNG rng(12345);
    for (int i=1;i<qianjing;++i) {
        qianjingheji[i]=Vec3b(rng.uniform(0,256),rng.uniform(0,256),rng.uniform(0,256));
    }
    Mat result=Mat::zeros(change.size(),change.type());
    for (int i=0;i<result.rows;++i) {
        for (int j=0;j<result.cols;++j) {
            int huiduzhi=qianjingtu.at<int>(i,j);
            result.at<Vec3b>(i,j)=qianjingheji[huiduzhi];
        }
    }

    putText(result,format("number:%d",qianjing),Point(50,50),FONT_HERSHEY_COMPLEX,1.0,Scalar(255,0,255),2,LINE_AA);
    imshow("result",result);


}
void QuickDemo::tiquqianjingdaitongjixinxi(cv::Mat &change){
//    connectedComponentsWithStats
//            InputArray   image,        // 输入二值图像
//            OutputArray  labels,       // 输出的标记图像,背景index=0
//            OutputArray  stats,        // 统计信息,包括每个组件的位置、宽、高与面积//像素值为int
//            OutputArray  centroids,    // 每个组件的中心位置坐标cx, cy//像素值为double
//            int          connectivity, // 连通域,默认是8连通
//            int          ltype,        // 输出的labels类型,默认是CV_32S
//            int          ccltype       // 连通组件算法
//    其中stats包括以下枚举类型数据信息:
//       CC_STAT_LEFT   组件的左上角点像素点坐标的X位置
//       CC_STAT_TOP    组件的左上角点像素点坐标的Y位置
//       CC_STAT_WIDTH  组件外接矩形的宽度
//       CC_STAT_HEIGHT 组件外接矩形的高度
//       CC_STAT_AREA   当前连通组件的面积(像素单位)

    //stats centroids得每一行相当于行数是连通域得值
    GaussianBlur(change,change,Size(3,3),0);//高斯降噪
    //转为二值图像
    Mat gray,src,qianjingtu,stats,centroids;
    cvtColor(change,gray,COLOR_RGB2GRAY);
    threshold(gray,src,0,255,THRESH_BINARY|THRESH_OTSU);
    imshow("二值",src);
    int qianjing=connectedComponentsWithStats(src,qianjingtu,stats,centroids,8,CV_32S,CCL_DEFAULT);
    vector<Vec3b> qianjingheji(qianjing);
    qianjingheji[0]=Vec3b(0,0,0);
    RNG rng(12345);
    for (int i=1;i<qianjing;++i) {
        qianjingheji[i]=Vec3b(rng.uniform(0,256),rng.uniform(0,256),rng.uniform(0,256));
    }
    Mat result=Mat::zeros(change.size(),change.type());
    for (int i=0;i<result.rows;++i) {
        for (int j=0;j<result.cols;++j) {
            int huiduzhi=qianjingtu.at<int>(i,j);
            result.at<Vec3b>(i,j)=qianjingheji[huiduzhi];
        }
    }
    for (int i=1;i<qianjing;++i) {
        //第一行表示连通域值为1得连通域得中心,第一列为x,第二列为y
        int px=centroids.at<double>(i,0);
        int py=centroids.at<double>(i,1);
        int zsjx=stats.at<int>(i,CC_STAT_LEFT);
        int zsjy=stats.at<int>(i,CC_STAT_TOP);
        int w=stats.at<int>(i,CC_STAT_WIDTH);
        int h=stats.at<int>(i,CC_STAT_HEIGHT);
        int area=stats.at<int>(i,CC_STAT_AREA);
        circle(result,Point(px,py),2,Scalar(0,0,255),1,LINE_AA);
        rectangle(result,Rect(zsjx,zsjy,w,h),Scalar(0,0,255),1,LINE_AA);
        putText(result,format("%d",area),Point(zsjx,zsjy),FONT_HERSHEY_COMPLEX,0.5,Scalar(255,0,255),1,LINE_AA);

    }
     imshow("result",result);
}
void QuickDemo::lunkuofaxian(cv::Mat &change){
//   findContours(
//    InputArray          image,
//    OutputArrayOfArrays contours,
//    OutputArray         hierarchy,
//    int                 mode,
//    int                 method,
//    Point               offset = Point()
//                      )
//参数一: image,输入图像、八位单通道的,背景为黑色的二值图像。(一般是经过Canny、拉普拉斯等边缘检测算子处理过的二值图像)
//参数二:contours,输出轮廓图像。是一个向量,向量的每个元素都是一个轮廓。因此,这个向量的每个元素仍是一个向量。即:
//           vector<vector<Point> > contours;
//参数三:hierarchy,输出各个轮廓的继承关系。hierarchy也是一个向量,长度和contours相等,每个元素和contours的元素对应。
//      hierarchy的每个元素是一个包含四个整型数的向量。即:分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,则该值为负数。
//           vector<Vec4i> hierarchy;
//参数四:mode,检测轮廓的方法。有四种方法:
//    RETR_EXTERNAL:只检测外轮廓。忽略轮廓内部的洞。
//    RETR_LIST:检测所有轮廓,但不建立继承(包含)关系。
//    RETR_TREE:检测所有轮廓,并且建立所有的继承(包含)关系。
//    RETR_CCOMP:检测所有轮廓,但是仅仅建立两层包含关系。
//参数五:method,每个轮廓的编码信息。也有四种(常用前两种)
//    CHAIN_APPROX_NONE:把轮廓上所有的点存储。
//    CHAIN_APPROX_SIMPLE:只存储轮廓上的拐点。
//    CHAIN_APPROX_TC89_L1,CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法

//    drawContours(
//          InputOutputArray  binImg, // 输出图像
//          OutputArrayOfArrays  contours,//  全部发现的轮廓对象
//          Int contourIdx// 轮廓索引号,-1表示绘制所有轮廓
//          const Scalar & color,// 绘制时候颜色
//          int  thickness,// 绘制线宽,-1表示填充轮廓内部
//          int  lineType,// 线的类型LINE_8
//          InputArray hierarchy,// 拓扑结构图
//          int maxlevel,// 最大层数,0只绘制当前的,1表示绘制绘制当前和他的子轮廓 2再画一个子轮廓
//          Point offset = Point()// 轮廓位移,可选
//                )
    Mat gray,src;
    GaussianBlur(change,change,Size(3,3),0);
    cvtColor(change,gray,COLOR_BGR2GRAY);
    threshold(gray,src,0,255,THRESH_BINARY_INV|THRESH_OTSU);
    imshow("THRESH_OTSU",src);
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
//    Mat cc=Mat::zeros(change.size(),change.type());
    findContours(src,contours,hierarchy,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);
    drawContours(change,contours,-1,Scalar(0,0,255),3,LINE_AA/*,hierarchy,1*/);
    imshow("jieguo",change);

}
void QuickDemo::lunkuojisuan(cv::Mat &change){
    Mat gray,src;
    GaussianBlur(change,change,Size(3,3),0);
    cvtColor(change,gray,COLOR_BGR2GRAY);
    threshold(gray,src,0,255,THRESH_BINARY|THRESH_OTSU);
    imshow("THRESH_OTSU",src);
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
     findContours(src,contours,hierarchy,RETR_TREE,CHAIN_APPROX_SIMPLE);
     for (vector<vector<Point>>::size_type i{};i<contours.size();++i) {
         //轮廓面积
         double mianji=contourArea(contours[i]);
//         轮廓周长
         double zhouchang=arcLength(contours[i],true);//第二个参数是否是闭合区域
         cout <<"面积:"<<setw(8)<<setiosflags(ios::left)<<mianji<<"\t"<<"周长:"<<zhouchang<<endl;
//         最大矩形轮廓
//         Rect maxbox=boundingRect(contours[i]);
//         rectangle(change,maxbox,Scalar(0,0,255),2,LINE_AA);

//         RotatedRect 旋转矩形
//                 矩形中心点(质心)
//                 边长(长和宽)
//                 旋转角度
//         //最小矩形轮廓
         RotatedRect minbox=minAreaRect(contours[i]);
         //用椭圆画出来最小矩形轮廓
//         ellipse(change,minbox,Scalar(0,0,255),2,LINE_AA);

         //用矩形画出来最小矩形轮廓
         Point2f arr[4];//创建一个储存point得数组
         minbox.points(arr);//将矩形轮廓得四个点输入到数组
         for (size_t t{};t<4;++t) {
             line(change,arr[t],arr[(t+1)%4],Scalar(0,0,255),2,LINE_AA);
         }//%4保证不超过4

         imshow("maxbox",change);
     }

}
vector<vector<Point>> geshizhengli(cv::Mat &change){
    Mat gray,src;
    GaussianBlur(change,change,Size(3,3),0);
    cvtColor(change,gray,COLOR_BGR2GRAY);
    threshold(gray,change,0,255,THRESH_BINARY|THRESH_OTSU);
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(change,contours,hierarchy,RETR_EXTERNAL,CHAIN_APPROX_NONE,Point());
    return contours;
}
void QuickDemo::lunkuopipei(cv::Mat &change){
   Mat change2=imread("E:\\BaiduNetdiskDownload\\1.png",IMREAD_ANYCOLOR);
   auto contours=geshizhengli(change);
   auto contours2=geshizhengli(change2);
   imshow("1",change);
   imshow("2",change2);
   //几何矩计算//几何矩可以用来计算hu矩,也可以求轮廓中心点
   Moments jh2=moments(contours2[0]);
   //hu矩计算
   Mat hu2;
   HuMoments(jh2,hu2);
   for (vector<vector<Point>>::size_type i{};i<contours.size();++i) {
       Moments jh=moments(contours[i]);
       Mat hu;
       HuMoments(jh,hu);
       //计算两个轮廓hu矩的差值,越小匹配度越高,可以直接两个轮廓比,但效果没有两个hu矩好
       double chazhi=matchShapes(hu2,hu,CONTOURS_MATCH_I1,0);
       if (chazhi<1.0) {
           cvtColor(change,change,COLOR_GRAY2BGR);
           drawContours(change,contours,i,Scalar(0,0,255),-1,LINE_AA);

       }
       //轮廓中心点计算
       double yx=jh.m10/jh.m00;//x坐标位置
       double yy=jh.m01/jh.m00;//y坐标位置
       circle(change,Point(yx,yy),2,Scalar(55,88,99),1,LINE_AA);
   }

   imshow("jieguo",change);
}
void QuickDemo::lunkuobijinyunihe(cv::Mat &change){
//    approxPolyDP(InputArray curve,OutputArray approxCurve,double epsilon,bool closed)//算轮廓顶点
//    Curve:表示轮廓曲线,通常可以是findContours()中参数Contours里的元素,即一个由点集组成的轮廓。
//    approxCurve: 可以设为vector<Point>保存轮廓逼近输出的折点。
//    epsilon: 轮廓逼近的顶点距离真实轮廓曲线的最大距离,该值越小表示越逼近真实轮廓。
//    close: 表示是否为闭合区域。
    Mat jieguo=change.clone();
    auto contours=geshizhengli(change);
    imshow("geshi",change);
    vector<vector<Point>> approxCurve(contours.size());
    for (vector<vector<Point>>::size_type i=0;i<contours.size();++i)
    {
        approxPolyDP(contours[i],approxCurve[i],4,true);
        for (int j=0;j<approxCurve[i].size();++j) {
            circle(jieguo,approxCurve[i][j],2,Scalar(0,0,255),1,LINE_AA);
        }
        //轮廓中心点计算
        Moments jh=moments(contours[i]);
        double yx=jh.m10/jh.m00;//x坐标位置
        double yy=jh.m01/jh.m00;//y坐标位置
        circle(change,Point(yx,yy),2,Scalar(55,88,99),1,LINE_AA);
//        if (approxCurve[i].size()==3) {
//            putText(jieguo,"3",Point(yx,yy),FONT_HERSHEY_COMPLEX,2,Scalar(88,88,88),1);
//        }
//        if (approxCurve[i].size()==4) {
//            putText(jieguo,"4",Point(yx,yy),FONT_HERSHEY_COMPLEX,2,Scalar(88,88,88),1);
//        }
//        if (approxCurve[i].size()>10) {
//            putText(jieguo,"yuan",Point(yx,yy),FONT_HERSHEY_COMPLEX,2,Scalar(88,88,88),1);
//        }

    }
    imshow("jieguo",jieguo);
}
void QuickDemo::nihe(cv::Mat &change){
    //最小外接圆拟合
//    void minEnclosingCircle( InputArray points,Point2f& center,
//                     float& radius );
//        1points:由点集组成的轮廓。
//        2center:输出圆心坐标。
//        3radius:输出圆的半径。
    Mat waijie=change.clone();
    Mat neijie=change.clone();
    auto contours=geshizhengli(change);
    imshow("geshi",change);

    for (vector<vector<Point>>::size_type i=0;i<contours.size();++i)
    {  Point2f yuanxin;
        float banjing{};
       minEnclosingCircle(contours[i],yuanxin,banjing);
       circle(waijie,yuanxin,banjing,Scalar(0,0,245),2,LINE_AA);
    }

    imshow("waijie",waijie);

//    //轮廓最大内接圆
//    double pointPolygonTest( InputArray contour, Point2f pt,
//                            bool measureDist );
//  该函数可以准确计算出一个点距离某个轮廓的距离,如果该点在轮廓上,返回的距离就是0;如果是在外部或者内部,则分别返回负数和正数表示距离。它的参数解释如下:
//    contour:点集组成的轮廓。
//    pt:要判断的点。
//    measureDist:设为True时返回实际点到轮廓的距离,设为False返回0(表示点在轮廓上)或者1(表示点在轮廓外)或者-1(表示点在轮廓内)三者中的一个。

    for (vector<vector<Point>>::size_type x=0;x<contours.size();++x)
    {
        Mat julichucun(change.size(),CV_32F);//用图像灰度值储存各个像素点到轮廓得最短距离,再算出距离最长得像素值就是圆的半径,该点就是圆心
        for (int i=0;i<change.rows;++i)
        {
            for (int j=0;j<change.cols;++j)
            {
             julichucun.at<float>(i,j)=(float)pointPolygonTest(contours[x],Point2f((float)j,(float)i),true);//点得x对应图像得列也就是宽度
            }
        }
        double max{};
        double min{};
        Point maxp,minp;
        minMaxLoc(julichucun,&min,&max,&minp,&maxp);
        circle(neijie,maxp,max,Scalar(255,255,255),2,LINE_AA);
    }
    imshow("neijie",neijie);

}
void QuickDemo::tuoyuannihe(cv::Mat &change){
    //椭圆拟合
    Mat caise=change.clone();
    auto contours=geshizhengli(change);
    imshow("2z",change);
    for (vector<vector<Point>>::size_type x=0;x<contours.size();++x) {
        if (contours[x].size()>5) {
            drawContours(caise,contours,x,Scalar(0,0,255),2,LINE_AA);
            RotatedRect xzjx=fitEllipse(contours[x]);//fitEllipse函数要求轮廓至少要有5个点
           ellipse(caise,xzjx,Scalar(99,99,99),2,LINE_AA);

        }

    }
    imshow("caise",caise);
}
void QuickDemo::huofuzhixian(cv::Mat &change){
//    void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0 )
//    参数:
//    image:边缘检测的输出图像. 它应该是个灰度图 (但事实上是个二值化图)
//    lines:储存着检测到的直线的参数对 的容器  // vector<Vec3f>类型,分别是距离角度累加值
//    rho:参数极径 以像素值为单位的分辨率. 我们使用 1 像素.
//    theta:参数极角 以弧度为单位的分辨率. 我们使用 1度 (即CV_PI/180)
//    theta:要”检测” 一条直线所需最少的的曲线交点,直线交点得阈值,也就是vec3f得最后一个值,累加值,越大直线越少
//    srn and stn: 参数默认为0.
    Mat xian=change.clone(),gray;
    cvtColor(change,gray,COLOR_BGR2GRAY);
    threshold(gray,change,0,255,THRESH_BINARY|THRESH_OTSU);
    imshow("erzhi",change);
    vector<Vec3f> lines;
    HoughLines(change,lines,1,CV_PI/180.0,150);
    Point p1,p2;//画直线得两个顶点
    for (vector<Vec3f>::size_type i{};i<lines.size();++i) {
        float r=lines[i][0];
        float hudu=lines[i][1];
        float leijiazhi=lines[i][2];
        cout <<"1:"<<r<<",2:"<<hudu<<",3:"<<leijiazhi<<endl;
        double c=cos(hudu);
        double s=sin(hudu);
        double x0=r*c,y0=r*s;
        p1.x=cvRound(x0+1000*(-s));
        p1.y=cvRound(y0+1000*c);
        p2.x=cvRound(x0-1000*(-s));
        p2.y=cvRound(y0-1000*c);
        int jiaodu=cvRound((hudu/CV_PI)*180);
        cout <<jiaodu<<endl;
        if (r>0) {
            line(xian,p1,p2,Scalar(0,0,255),2,LINE_AA);
            if (jiaodu==90) {//水平线
            line(xian,p1,p2,Scalar(25,18,25),2,LINE_AA);

            }
            if(jiaodu<=1){//垂直线
                line(xian,p1,p2,Scalar(0,255,0),2,LINE_AA);

            }
        }else {//长度为负得数是往左边倾斜得
            line(xian,p1,p2,Scalar(255,0,0),2,LINE_AA);
        }

    }
    imshow("结果",xian);

}
void QuickDemo::huofuzhixian2(cv::Mat &change){
//    HoughLinesP(
//    InputArray src, // 输入图像,必须8-bit的灰度图像
//    OutputArray lines, // 输出的极坐标来表示直线//vector<Vec4i>
//    double rho, // 生成极坐标时候的像素扫描步长,一般取1
//    double theta, //生成极坐标时候的角度步长,一般取值CV_PI/180
//    int threshold, // 阈值,要”检测” 一条直线所需最少的的曲线交点,直线交点得阈值
//    double minLineLength=0;// 最小直线长度
//    double maxLineGap=0;// 两交点间允许的最大间隔,(经canny的梯度可能不连续,间隔调大)
    //霍夫直线前可以传统方法转换为二值图像,也可以用canny转化为二值图像
    Mat jieguo=change.clone(),bianyuan;
    Canny(change,bianyuan,80,160,3,false);
    imshow("bianyuan",bianyuan);
    vector<Vec4i> lines;
    HoughLinesP(bianyuan,lines,1,CV_PI/180.0,100,100,20);
    for (vector<Vec4i>::size_type i{};i<lines.size();++i) {
        line(jieguo,Point(lines[i][0],lines[i][1]),Point(lines[i][2],lines[i][3]),Scalar(0,0,255),1,LINE_AA);
    }
    imshow("结果",jieguo);

}
void QuickDemo::huofuyuan(cv::Mat &change){
//    HoughCircles(
//        InputArray image, // 输入图像 ,必须是8位的单通道灰度图像,先进行降噪
//        OutputArray circles, // 输出结果,发现的圆信息//圆心x,圆心y,半径vector<Vec3f>
//        Int method, // 方法 - HOUGH_GRADIENT
//        Double dp, // dp = 1; 越大检测越多
//        Double mindist, // 10 最短距离-可以分辨是两个圆的,否则认为是同心圆- src_gray.rows/8//圆心之间得最小距离
//        Double param1, // canny高阈值低阈值默认为它的一半
//        Double param2, // 中心点累加器阈值 ? 候选圆心,累加交点阈值
//        Int minradius, // 最小半径
//        Int maxradius//最大半径
//    )
    //传入灰度图像并降噪//霍夫直线和圆都对噪声敏感,降噪效果更好
    Mat gaosi,gray;
    GaussianBlur(change,gaosi,Size(3,3),5);
    cvtColor(gaosi,gray,COLOR_BGR2GRAY);
    imshow("zhuanhuan",gray);
    vector<Vec3f> circles;
    HoughCircles(gray,circles,HOUGH_GRADIENT,2,60,130,130,20,70);
    for (vector<Vec3f>::size_type i{};i<circles.size();++i) {
        circle(change,Point(circles[i][0],circles[i][1]),circles[i][2],Scalar(0,0,255),3,LINE_AA);
        circle(change,Point(circles[i][0],circles[i][1]),2,Scalar(0,0,255),-1,LINE_AA);
    }
    imshow("jieguo",change);

}
void QuickDemo::fushiyupengzhang(cv::Mat &change){
//    结构元形状构造函数getStructuringElement( int shape, Size ksize, Point anchor )
//   其参数解释如下:
//       shape:  1)MORPH_RECT 表示产生矩形的结构元
//                2)MORPH_ELLIPSEM 表示产生椭圆形的结构元
//                3)MORPH_CROSS 表示产生十字交叉形的结构元
//       ksize:表示结构元的尺寸,即(宽,高),必须是奇数
//       anchor:表示结构元的锚点,即参考点。默认值Point(-1, -1)代表中心像素为锚点

//    dilate()//膨胀
//                其参数解释如下:
//    src 输入图像
//    dst 输出与src相同大小和类型的图像。
//    kernle 用于膨胀的核结构元素 内核可以使用getStructuringElement创建,上面函数得返回值
//    anchor 元素中锚的锚定位置; 默认值(-1,-1)表示锚位于元素中心。
//    iterations 迭代次数
//    borderType 像素外推方法
//    borderValue 当边界为常数时的边界值

//    erode()//腐蚀
//    src 输入图像
//    dst 输出与src相同大小和类型的图像。
//    kernle 用于腐蚀的核结构元素 内核可以使用getStructuringElement创建
//    anchor 元素中锚的锚定位置; 默认值(-1,-1)表示锚位于元素中心。
//    iterations 迭代次数
//    borderType 像素外推方法
//    borderValue 当边界为常数时的边界值
    Mat erzhi,fushi,pengzhang;
     cvtColor(change,erzhi,COLOR_BGR2GRAY);
     threshold(erzhi,erzhi,0,255,THRESH_BINARY|THRESH_OTSU);
     imshow("erzhi",erzhi);
     Mat kernle=getStructuringElement(MORPH_RECT,Size(5,5));
     dilate(erzhi,pengzhang,kernle);
     imshow("pengzhang",pengzhang);
     erode(erzhi,fushi,kernle);
     imshow("fushi",fushi);

//彩色图像也可以用
}
void QuickDemo::kaibicaozuo(cv::Mat &change){
    //开操作 去掉不必要的点  先腐蚀后膨胀
    //闭操作 填充必和区域  先膨胀后腐蚀

//    morphologyEx(形态学操作)
//    src:源图像
//    dst:目标图像
//    op:操作代号
//代号	含义	作用
//0	MORPH_ERODE	腐蚀运算(Erode operation)
//1	MORPH_DILATE	膨胀运算(Dilate operation)
//2	MORPH_OPEN	开运算(Opening operation)
//3	MORPH_CLOSE	闭运算(Closing operation)
//4	MORPH_GRADIENT	形态学梯度(Morphological gradient)
//5	MORPH_TOPHAT	“顶帽”(Top hat)
//6	MORPH_BLACKHAT	“黑帽”(Black hat)
//7	MORPH_HITMISS
//    kernel:核(用于膨胀操作的结构元素),可使用getStructuringElement()方法创建
//    anchor:锚点坐标,为负代表核的中心坐标
//    iterations:迭代次数
//    borderType:像素外推方法,具有默认值 BORDER_CONSTANT
//    borderValue:边界为常数时的边界值,有默认值morphologyDefaultBorderValue()
    Mat jieguob,jieguok,kernel=getStructuringElement(MORPH_RECT,Size(5,5)),erzhi;//size越大效果越明显
    cvtColor(change,erzhi,COLOR_BGR2GRAY);
    threshold(erzhi,erzhi,0,255,THRESH_BINARY_INV|THRESH_OTSU);
    imshow("erzhi",erzhi);

//    morphologyEx(erzhi,jieguob,MORPH_CLOSE,kernel,Point(-1,-1),1);//迭代次数越大越明显
//    imshow("bi",jieguob);//闭操作

    morphologyEx(erzhi,jieguok,MORPH_OPEN,kernel,Point(-1,-1),1);
    imshow("kai",jieguok);//开操作

}
void QuickDemo::kaicaozuotiquhengxian(cv::Mat &change){

    Mat jieguok,jieguok2,erzhi;//size越大效果越明显
    cvtColor(change,erzhi,COLOR_BGR2GRAY);
    threshold(erzhi,erzhi,0,255,THRESH_BINARY|THRESH_OTSU);

    //开操作提取横线水平方向
   Mat kernel=getStructuringElement(MORPH_RECT,Size(11,1));
    morphologyEx(erzhi,jieguok,MORPH_OPEN,kernel,Point(-1,-1),1);
    imshow("kai",jieguok);
    //开操作提取垂直方向横线
    Mat kernel2=getStructuringElement(MORPH_RECT,Size(1,11));
    morphologyEx(erzhi,jieguok2,MORPH_OPEN,kernel2,Point(-1,-1),1);
    imshow("kai2",jieguok2);
}

void QuickDemo::xingtaixuetidu(cv::Mat &change){
    Mat jibentidu,gray;
    cvtColor(change,gray,COLOR_BGR2GRAY);
    Mat kernel=getStructuringElement(MORPH_RECT,Size(3,3));
    //基本梯度  //黑白二值彩色都支持
    morphologyEx(gray,jibentidu,MORPH_GRADIENT,kernel,Point(-1,-1),1);
    imshow("jibentidu",jibentidu);

    //内梯度,原图-腐蚀//z这里所说得原图是指erode得时候原图(输入图像)是哪张图就用那张图
    Mat neitidu,src;
    erode(gray,src,kernel);
    subtract(gray,src,neitidu);
    imshow("neitidu",neitidu);

    //外梯度,膨胀-原图
    Mat waitidu,src2;
    dilate(gray,src2,kernel);
    subtract(src2,gray,waitidu);
    imshow("waitidu",waitidu);

}
void QuickDemo::dmhm(cv::Mat &change){
    //顶帽 = 原图 - 开运算 //得到开运算去除得多余点
    Mat gray,erzhi,dingmao;
    cvtColor(change,gray,COLOR_BGR2GRAY);
    threshold(gray,erzhi,0,255,THRESH_BINARY|THRESH_OTSU);
    imshow("erzhi",erzhi);
    Mat kernel=getStructuringElement(MORPH_RECT,Size(7,7));//MORPH_类型根据实际图像要求得图形来
//    morphologyEx(erzhi,dingmao,MORPH_TOPHAT,kernel,Point(-1,-1),1);
//    imshow("dingmao",dingmao);
    //黑帽 = 原图 - 闭运算//得到闭运算填充得点
    Mat heimao;
    morphologyEx(erzhi,heimao,MORPH_BLACKHAT,kernel,Point(-1,-1),1);
    imshow("heimao",heimao);


}
void QuickDemo::jizhongyubuzhong(cv::Mat &change){
//击中与不中  根据核形状不同选择原图中有没有匹配的,有的留没有的去
    Mat gray,erzhi,jizhong;
    cvtColor(change,gray,COLOR_BGR2GRAY);
    threshold(gray,erzhi,0,255,THRESH_BINARY_INV|THRESH_OTSU);
    imshow("erzhi",erzhi);
    Mat kernel=getStructuringElement(MORPH_CROSS,Size(13,13));//MORPH_类型根据实际图像要求得图形来
    morphologyEx(erzhi,jizhong,MORPH_HITMISS,kernel,Point(-1,-1),1);
    imshow("dingmao",jizhong);

}
void QuickDemo::anli1(cv::Mat &change){
    Mat gray,erzhi,lunkuo,bi;
    cvtColor(change,gray,COLOR_BGR2GRAY);
    threshold(gray,erzhi,0,255,THRESH_BINARY|THRESH_OTSU);
    imshow("erzhi",erzhi);
    Mat kernel=getStructuringElement(MORPH_RECT,Size(9,9));
    morphologyEx(erzhi,bi,MORPH_CLOSE,kernel,Point(-1,-1),1);
    imshow("bi",bi);
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(bi,contours,hierarchy,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);
    double maxmianji{};
    int xuhao{};
    for (vector<vector<Point>>::size_type i{};i<contours.size();++i) {
       double mianji=contourArea(contours[i]);
       if (mianji>maxmianji) {
           maxmianji=mianji;
           xuhao=i;
       }

    }
    drawContours(change,contours,xuhao,Scalar(0,0,255),2,LINE_AA);
    vector<Point> guaidian(contours[xuhao].size());
    approxPolyDP(contours[xuhao],guaidian,4,true);
    for (vector<Point>::size_type i{};i<guaidian.size();++i) {
        circle(change,guaidian[i],1,Scalar(255,0,0),2,LINE_AA);
    }
    imshow("jieguo",change);

}
void QuickDemo::zhifangtufanxiangtouying(cv::Mat &change){
//   calcBackProject(
//            const Mat *        images,
//            int                nimages,
//            const int *        channels,
//            InputArray         hist,
//            OutputArray        backProject,
//            const float **     ranges,
//            double             scale = 1,
//            bool               uniform = true
//        images:输入图像,图像深度必须位CV_8U, CV_16U或CV_32F中的一种,尺寸相同,每一幅图像都可以有任意的通道数
//        nimages : 输入图像的数量
//        channels : 用于计算反向投影的通道列表,通道数必须与直方图维度相匹配,第一个数组的通道是从0到image[0].channels() - 1, 第二个数组通道从图像image[0].channels()到image[0].channels() + image[1].channels() - 1计数
//        hist : 输入的直方图,直方图的bin可以是密集(dense)或稀疏(sparse)
//        backProject : 目标反向投影输出图像,是一个单通道图像,与原图像有相同的尺寸和深度
//        ranges : 直方图中每个维度bin的取值范围
//        scale = 1 : 可选输出反向投影的比例因子
//        uniform = true : 直方图是否均匀分布(uniform)的标识符,有默认值true
    Mat shouroi=imread("E:\\BaiduNetdiskDownload\\7roi.png",IMREAD_ANYCOLOR),roihsv,roizhifangtu,hsv;
    imshow("roi",shouroi);
    cvtColor(shouroi,roihsv,COLOR_BGR2HSV);
    cvtColor(change,hsv,COLOR_BGR2HSV);
    int h_bin=32,s_bin=32;//越大越细腻
    int hitsize[]{h_bin,s_bin};
    int channels[]{0,1};

    float h_range[]{0,180};
    float s_range[]{0,255};
    const float* range[]{h_range,s_range};
    calcHist(&roihsv,1,channels,Mat(),roizhifangtu,2,hitsize,range);
    normalize(roizhifangtu,roizhifangtu,0,255,NORM_MINMAX);
    Mat zhifangtuyoushe;
    calcBackProject(&hsv,1,channels,roizhifangtu,zhifangtuyoushe,range,1);
    imshow("jieguo1",zhifangtuyoushe);
    threshold(zhifangtuyoushe,zhifangtuyoushe,0,255,THRESH_BINARY|THRESH_OTSU);
    imshow("jieguo2",zhifangtuyoushe);
     Mat kernel=getStructuringElement(MORPH_RECT,Size(13,13));
     morphologyEx(zhifangtuyoushe,zhifangtuyoushe,MORPH_CLOSE,kernel,Point(-1,-1),2);//迭代次数越大越明显
     imshow("bi",zhifangtuyoushe);//闭操作


}
void QuickDemo::harris_jiaodianjiance(cv::Mat &change){
//    cornerHarris(InputArray src,        //输入图像  输入(黑白)单通道8位或浮点图像。
//                 OutputArray dst,     //输出图像   类型为CV_32FC1,大小与src相同。里面每个像素存得是一个值,这个值越高,是角点可能性越高R
//                 int blockSize,        //领域大小 ,计算λ1λ2时的矩阵大小 //可以取2
//                 int ksize,               //Sobel 求导中使用的窗口大小//单数
//                 double k,              //Harris 角点检测方程中的自由参数,取值参数为[0,04,0.06] .
//                 int borderType=BORDER_DEFAULT)
    Mat gray,shuchu;
     cvtColor(change,gray,COLOR_BGR2GRAY);
     cornerHarris(gray,shuchu,2,3,0.04);
     normalize(shuchu,shuchu,0,255,NORM_MINMAX);
     convertScaleAbs(shuchu,shuchu);
     for (int hang{};hang<change.rows;++hang) {
         for (int lie{};lie<change.cols;++lie) {
             if (shuchu.at<uchar>(hang,lie)>100) {
                 circle(change,Point(lie,hang),2,Scalar(0,0,255),-1,LINE_AA);
             }
         }
     }
     imshow("jieguo",change);


}
void QuickDemo::shi_tomasi_jiaodianjiance(cv::Mat &change){
//    goodFeaturesToTrack()
//    image:输入图像,一般是灰度图像
//    检测到的角点。vector<Point2f>
//    maxCorners: 角点得最大数量
//    qualtyLevel: 角点的质量水平,0-1之间。它代表了角点的最低质量,实际用于过滤角点的最小特征值是qualtyLevel与图像中最大特征值的乘积,
//    所以qualtyLevel的值不应超过1(常用的值为0.1或0.01)。低于这个值的所有角点都会被忽略。
//    minDistance: 两个角点之间的最短距离。
//    mask:
//    blockSize:计算导数的自相关矩阵时指定点的领域,默认为3,采用小窗口计算的结果比单点(也就是blockSize为1)计算的效果要好。
//    useHarrisDetector: 默认值为0,若非0,则函数使用Harris的角点定义
//    K: 当 useHarrisDetector非0,则K为用于设置hessian自相关矩阵即对hessian行列式的相对权值的权值系数。
    Mat gray;
    cvtColor(change,gray,COLOR_BGR2GRAY);
    vector<Point2f> jiaodianjihe;
    goodFeaturesToTrack(gray,jiaodianjihe,10,0.1,50);
    for (vector<Point2f>::size_type i{};i<jiaodianjihe.size();++i) {
        circle(change,jiaodianjihe[i],2,Scalar(0,0,255),-1);
    }
    imshow("jieguo",change);
}
void QuickDemo::jiyuyansededuixianggenzhong(cv::Mat &change){
    VideoCapture video("E:\\BaiduNetdiskDownload\\small.mp4");
    if (video.isOpened())
    {
        Mat tupian;
        while (true) {
       video.read(tupian);//读没了返回false并且传入空图像
            if (tupian.empty()) {
                break;
            }
            imshow("shiping",tupian);
            Mat hsv,mask;
            cvtColor(tupian,hsv,COLOR_BGR2HSV);
            inRange(hsv,Scalar(100,43,46),Scalar(124,255,255),mask);
            Mat kernel=getStructuringElement(MORPH_RECT,Size(9,9));
            morphologyEx(mask,mask,MORPH_CLOSE,kernel,Point(-1,-1),1);
            imshow("yanma",mask);
            vector<vector<Point>> contours;
            vector<Vec4i> hierarchy;
            findContours(mask,contours,hierarchy,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);
            RotatedRect rrt=minAreaRect(contours[0]);
            circle(tupian,rrt.center,2,Scalar(0,0,255),1,LINE_AA);
            ellipse(tupian,rrt,Scalar(255,0,255),2,LINE_AA);
            imshow("jieguo",tupian);
            if(waitKey(16)==27){
                break;
            }
        }
    }
    video.release();
}
void QuickDemo::beijingjianmo(cv::Mat &change){
//    BackgroundSubtractorMOG2创建及初始化
//    history:用于训练背景的帧数,默认为500帧,如果不手动设置learningRate,history就被用于计算当前的learningRate,此时history越大,learningRate越小,背景更新越慢;
//    varThreshold:方差阈值,用于判断当前像素是前景还是背景。一般默认16,如果光照变化明显,如阳光下的水面,建议设为25,36,具体去试一下也不是很麻烦,值越大,灵敏度越低;
//    detectShadows:是否检测影子,设为true为检测,false为不检测,检测影子会增加程序时间复杂度,如无特殊要求,建议设为false;

//    mog->apply(src_YCrCb, foreGround, 0.005);

//        image 源图
//        fmask 前景(二值图像)
//        learningRate 学习速率,值为0-1,为0时背景不更新,为1时逐帧更新,默认为-1,即算法自动更新;
    VideoCapture video("E:\\BaiduNetdiskDownload\\vtest.avi");
    Ptr<BackgroundSubtractorMOG2> mog = createBackgroundSubtractorMOG2();
    Mat kernel=getStructuringElement(MORPH_RECT,Size(5,5));
    if (video.isOpened())
    {
        Mat tupian;
        while (true) {
       video.read(tupian);//读没了返回false并且传入空图像
            if (tupian.empty()) {
                break;
            }
            imshow("shiping",tupian);
            Mat mask(tupian.size(),CV_8UC1);
            mog->apply(tupian,mask);
            morphologyEx(mask,mask,MORPH_OPEN,kernel,Point(-1,-1),1);
            morphologyEx(mask,mask,MORPH_CLOSE,kernel,Point(-1,-1),1);
            vector<vector<Point>> lunkuodian;
            vector<Vec4i> kuopujiegou;
            findContours(mask,lunkuodian,kuopujiegou,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);
            for (vector<vector<Point>>::size_type i{};i<lunkuodian.size();++i) {
                if (contourArea(lunkuodian[i])>80) {
                    Rect juxing=boundingRect(lunkuodian[i]);
                    rectangle(tupian,juxing,Scalar(0,0,255),2,LINE_AA);
                }
            }
            imshow("jieguo",mask);
            imshow("jieguo2",tupian);
            if(waitKey(16)==27){
                break;
            }
        }

    }
    video.release();
}

void QuickDemo::jingzita(cv::Mat &change){
//    pyrUp(Mat src,Mat dst,Size(src.cols*2,src.rows*2))生成的图像是原图在宽与高各放大二倍
//    pyrDown(Mat src,Mat dst,Size(src.cols/2,src.rowa/2))生成的图像是原图在宽和高都缩小1/2
    //拉普拉斯金字塔就是使用原始图像减去图像向下取样然后向上取样的这样一个过程。
//    Li = Gi - PyrUp(PyrDown(Gi))
    Mat dst;
//    pyrUp(change,dst);
//    imshow("dst",dst);
//    pyrUp(change,dst,Size(change.cols*2,change.rows*2));
//    imshow("dst1",dst);
        pyrDown(change,dst);
        imshow("dst",dst);
        pyrDown(change,dst,Size(change.cols/2,change.rows/2));
        imshow("dst1",dst);
}
void QuickDemo::julibianhuan(cv::Mat &change){
    //Opencv中distanceTransform方法用于计算图像中每一个非零点距离离自己最近的零点的距离
//    void distanceTransform(InputArray src,
//                                OutputArray dst,
//                                int distanceType,
//                                int maskSize,
//                                int dstType=CV_32F
//                               )
//    dstType:输出图像(矩阵)的数据类型,可以是CV_8U 或 CV_32F。当选择CV_8U时,distanceType的类型只能为DIST_L1。
//    src:源矩阵
//    dst:目标矩阵
//    distanceType:距离类型,可选的类型如下:
//    DIST_USER
//    User defined distance.
//    DIST_L1
//    distance = |x1-x2| + |y1-y2|
//    DIST_L2
//    the simple euclidean distance
//    DIST_C
//    distance = max(|x1-x2|,|y1-y2|)
//    DIST_L12
//    L1-L2 metric: distance = 2(sqrt(1+x*x/2) - 1))
//    DIST_FAIR
//    distance = c^2(|x|/c-log(1+|x|/c)), c = 1.3998
//    DIST_WELSCH
//    distance = c^2/2(1-exp(-(x/c)^2)), c = 2.9846
//    DIST_HUBER
//    distance = |x|<c ? x^2/2 : c(|x|-c/2), c=1.345
//    resize(change,change,Size(0,0),0.3,0.3);
    Mat gray,erzhi,src;
    cvtColor(change,gray,COLOR_BGR2GRAY);
    threshold(gray,erzhi,0,255,THRESH_BINARY|THRESH_OTSU);
//    Mat kennel=getStructuringElement(MORPH_RECT,Size(23,23));
//    morphologyEx(erzhi,erzhi,MORPH_OPEN,kennel,Point(-1,-1),3);
    imshow("erzhi",erzhi);
    distanceTransform(erzhi,src,DIST_L2,5);
    normalize(src,src,0,1,NORM_MINMAX);
//    src.convertTo(src,CV_8UC1);
    imshow("src",src);

}
void QuickDemo::fenge(cv::Mat &change){
    Mat src=change(Range(0,280), Range(0,100));//提取几行,提取几列
    imshow("jieg",src);
}
void QuickDemo::toushibianhuan(Mat &change){
//透视变换矫正倾斜为正视图像,主要是获取轮廓四个顶点,顶点获取不到可以用霍夫直线算出边,通过边算交点,得到交点后可进行透视变换
    Mat jieguo=change.clone();
    Mat kai,bi;
    auto contours=geshizhengli(change);
    vector<vector<Point>> approxCurve(contours.size());
    for (vector<vector<Point>>::size_type i=0;i<contours.size();++i)
    {
        approxPolyDP(contours[i],approxCurve[i],10,true);
    }
    vector <Point> dianji{approxCurve[0][0],approxCurve[0][1],approxCurve[0][2],approxCurve[0][3]};
    int w=change.cols/2;
    int h=change.rows/2;
    for (vector <Point>::size_type i{};i<approxCurve[0].size();++i)
    {   Point zhongjie;
        if (approxCurve[0][i].x<w&&approxCurve[0][i].y<h) {
             zhongjie=approxCurve[0][0];
            approxCurve[0][0]=approxCurve[0][i];
            approxCurve[0][i]=zhongjie;
        }
        if (approxCurve[0][i].x>w&&approxCurve[0][i].y<h) {
             zhongjie=approxCurve[0][1];
            approxCurve[0][1]=approxCurve[0][i];
            approxCurve[0][i]=zhongjie;
        }
        if (approxCurve[0][i].x>w&&approxCurve[0][i].y>h) {
             zhongjie=approxCurve[0][2];
            approxCurve[0][2]=approxCurve[0][i];
            approxCurve[0][i]=zhongjie;
        }
        if (approxCurve[0][i].x<w&&approxCurve[0][i].y>h) {
             zhongjie=approxCurve[0][3];
            approxCurve[0][3]=approxCurve[0][i];
            approxCurve[0][i]=zhongjie;
        }
    }
    double widthmax=pow(pow((approxCurve[0][1].x-approxCurve[0][0].x),2)+pow((approxCurve[0][1].y-approxCurve[0][0].y),2),(double)1.0/2);
    double heightmax=pow(pow((approxCurve[0][3].x-approxCurve[0][0].x),2)+pow((approxCurve[0][3].y-approxCurve[0][0].y),2),(double)1.0/2);
    Mat src;
    vector<Point2f>SrcAffinePts{ Point2f(approxCurve[0][0]),Point2f(approxCurve[0][1]) ,Point2f(approxCurve[0][2]) ,Point2f(approxCurve[0][3])};
    vector<Point2f> DstAffinePts{ Point2f(0,0),Point2f(widthmax,0),Point2f(widthmax,heightmax),Point2f(0,heightmax)};
    Mat M = getPerspectiveTransform(SrcAffinePts, DstAffinePts);//用于普通透视
//    Mat M = findHomography(SrcAffinePts,DstAffinePts);
    Mat DstImg;
    warpPerspective(jieguo, DstImg, M,Size(widthmax,heightmax));
    imshow("jg",DstImg);
//    TextRecognitionModel
   }

//没什么用的这个
void QuickDemo::geshizehsi(cv::Mat &change){
    Mat gary,dingmao,sx,sy,reslult,gaosi,erzhi,bi;
    GaussianBlur(change,gaosi,Size(5,5),0);
    cvtColor(gaosi,gary,COLOR_BGR2GRAY);
        Mat kennel=getStructuringElement(MORPH_RECT,Size(5,5));
        morphologyEx(gary,dingmao,MORPH_TOPHAT,kennel,Point(-1,-1),3);
        imshow("dingmao",dingmao);
        threshold(dingmao,erzhi,0,255,THRESH_OTSU);
//    Sobel(dingmao,sx,CV_32F,1,0);
//    Sobel(dingmao,sy,CV_32F,0,1);
//    convertScaleAbs(sx,sx);
//    convertScaleAbs(sy,sy);
//    add(sx,sy,reslult);
        Mat kennel2=getStructuringElement(MORPH_RECT,Size(19,1));
        morphologyEx(erzhi,bi,MORPH_CLOSE,kennel2,Point(-1,-1),3);
    imshow("bi",bi);
}


void QuickDemo::orbguanjiandian(cv::Mat &change){
//    ORB对象创建,
//    Orb = cv::ORB::create(500)
//    virtual void cv::Feature2D::detect(
//    InputArray image, // 输入图像
//    std::vector< KeyPoint > & keypoints, // 关键点
//    InputArray mask = noArray() // 支持mask
//    )
//    KeyPoint数据结构-四个最重要属性:
//    -pt 坐标
//    -angle
//    -response
//    -size
    auto orb=ORB::create(500);//500个关键点
    vector<KeyPoint> keyp;
    orb->detect(change,keyp);
    drawKeypoints(change,keyp,change,Scalar::all(-1),DrawMatchesFlags::DEFAULT);//小点;
//    drawKeypoints(change,keyp,change,Scalar::all(-1),DrawMatchesFlags::DRAW_RICH_KEYPOINTS);//大圈体现出keypoint得angle,pt,size
    imshow("jieguo",change);


}
void QuickDemo::orbmiaoshuzi(cv::Mat &change){

    auto orb=ORB::create(500);//500个关键点
    vector<KeyPoint> keyp;
    orb->detect(change,keyp);
    Mat miaoshuzi;
    orb->compute(change,keyp,miaoshuzi);
    cout <<miaoshuzi.rows<<"x"<<miaoshuzi.cols<<endl;
}
void QuickDemo::siftcs(cv::Mat &img){
    auto sift=SIFT::create(500);
    vector<KeyPoint> ky;
    sift->detect(img,ky);
    drawKeypoints(img,ky,img,Scalar::all(-1),DrawMatchesFlags::DEFAULT);
    imshow("dd",img);
    Mat miaoshuzi;
    sift->compute(img,ky,miaoshuzi);
    cout <<miaoshuzi.rows<<"x"<<miaoshuzi.cols<<endl;

}
void QuickDemo::orbqipei(cv::Mat &img){
    auto orb=ORB::create(500);
    vector<KeyPoint> ky;
    vector<KeyPoint> ky_dest;
    Mat miaoshuzi;
    Mat miaoshuzi_dest;
    Mat imgdest=cv::imread("E:/Desktop/study/class_tezheng/src/book_on_desk.jpg");
    orb->detectAndCompute(img,Mat(),ky,miaoshuzi);
    orb->detectAndCompute(imgdest,Mat(),ky_dest,miaoshuzi_dest);
    //暴力匹配
    auto baoli=BFMatcher::create(NORM_HAMMING);
    vector<DMatch> qipeijieguo;
    Mat result;
//    baoli->knnMatch(miaoshuzi,miaoshuzi_dest,qipeijieguo,2);
    baoli->match(miaoshuzi,miaoshuzi_dest,qipeijieguo);
    drawMatches(img,ky,imgdest,ky_dest,qipeijieguo,result);
    imshow("baoli",result);

    //flann匹配
    auto fannlmatch=FlannBasedMatcher(new flann::LshIndexParams(6,12,2));
    vector<DMatch> flannqipeijieguo;
    Mat result_flann;
    fannlmatch.match(miaoshuzi,miaoshuzi_dest,flannqipeijieguo);
    drawMatches(img,ky,imgdest,ky_dest,flannqipeijieguo,result_flann);
    imshow("flann",result_flann);
}
void QuickDemo::siftqipei(cv::Mat &img){
    auto sift=SIFT::create(500);
    vector<KeyPoint> ky;
    vector<KeyPoint> ky_dest;
    Mat miaoshuzi;
    Mat miaoshuzi_dest;
    Mat imgdest=cv::imread("E:/Desktop/study/class_tezheng/src/book_on_desk.jpg");
    sift->detectAndCompute(img,Mat(),ky,miaoshuzi);
    sift->detectAndCompute(imgdest,Mat(),ky_dest,miaoshuzi_dest);
    //暴力
    auto baoli=BFMatcher::create(NORM_L1,false);
    vector<DMatch> qipeijieguo;
    Mat result;
    baoli->match(miaoshuzi,miaoshuzi_dest,qipeijieguo);
    drawMatches(img,ky,imgdest,ky_dest,qipeijieguo,result);
    imshow("jieguo",result);

    //flann匹配
    auto fannlmatch=FlannBasedMatcher(new flann::KDTreeIndexParams());
//    auto fannlmatch=FlannBasedMatcher();
    vector<DMatch> flannqipeijieguo;
    Mat result_flann;
    fannlmatch.match(miaoshuzi,miaoshuzi_dest,flannqipeijieguo);
    sort(flannqipeijieguo.begin(),flannqipeijieguo.end(),[](const DMatch qian,const DMatch hou){return qian.distance<hou.distance;});
    drawMatches(img,ky,imgdest,ky_dest,flannqipeijieguo,result_flann);
    imshow("flann",result_flann);
}
void QuickDemo::toushizhaoyuandian(cv::Mat &img){
    auto orb=ORB::create(500);
    vector<KeyPoint> ky;
    vector<KeyPoint> ky_dest;
    Mat miaoshuzi;
    Mat miaoshuzi_dest;
    Mat imgdest=cv::imread("E:/Desktop/study/class_tezheng/src/book_on_desk.jpg");
    orb->detectAndCompute(img,Mat(),ky,miaoshuzi);
    orb->detectAndCompute(imgdest,Mat(),ky_dest,miaoshuzi_dest);
    //暴力匹配
    auto baoli=BFMatcher::create(NORM_HAMMING,false);
    vector<DMatch> qipeijieguo;
    Mat result;
    baoli->match(miaoshuzi,miaoshuzi_dest,qipeijieguo);
    sort(qipeijieguo.begin(),qipeijieguo.end(),[](const DMatch &a,const DMatch &b){return a.distance<b.distance;});
     float good_rate = 0.15f;
    int num_good_matches = qipeijieguo.size() * good_rate;
    qipeijieguo.erase(qipeijieguo.begin()+num_good_matches,qipeijieguo.end());
    drawMatches(img,ky,imgdest,ky_dest,qipeijieguo,result);
    vector<Point2f> qAffinePts;
    vector<Point2f> hAffinePts;
    for (size_t t = 0; t < qipeijieguo.size(); t++) {
        qAffinePts.push_back(ky[qipeijieguo[t].queryIdx].pt);
        hAffinePts.push_back(ky_dest[qipeijieguo[t].trainIdx].pt);

           }


    Mat M = findHomography(qAffinePts,hAffinePts,RANSAC);//还可以用rho
    vector<Point2f> q{Point2f(0,0),Point2f(img.cols,0),Point2f(img.cols,img.rows),Point2f(0,img.rows)};
    vector<Point2f> h(4);
   perspectiveTransform(q, h, M);//这个透视变换适用于特征提取然后获取在图像中的点
   for(int i{};i<4;++i){
       line(imgdest,h[i],h[(i+1)%4],Scalar(0,0,255),2,LINE_AA);
   }

    imshow("jg",imgdest);


}
void QuickDemo::wendangduiqi(cv::Mat &img){
    auto orb=ORB::create(500);
    vector<KeyPoint> ky;
    vector<KeyPoint> ky_dest;
    Mat miaoshuzi;
    Mat miaoshuzi_dest;
    Mat imgdest=cv::imread("E:/Desktop/study/class_tezheng/src/form.png");
    orb->detectAndCompute(img,Mat(),ky,miaoshuzi);
    orb->detectAndCompute(imgdest,Mat(),ky_dest,miaoshuzi_dest);
    //暴力匹配
    auto baoli=BFMatcher::create(NORM_HAMMING,false);
    vector<DMatch> qipeijieguo;
    Mat result;
    baoli->match(miaoshuzi,miaoshuzi_dest,qipeijieguo);
    sort(qipeijieguo.begin(),qipeijieguo.end(),[](const DMatch &a,const DMatch &b){return a.distance<b.distance;});
     float good_rate = 0.15f;
    int num_good_matches = qipeijieguo.size() * good_rate;
    qipeijieguo.erase(qipeijieguo.begin()+num_good_matches,qipeijieguo.end());
    drawMatches(img,ky,imgdest,ky_dest,qipeijieguo,result);
    imshow("img",result);
    vector<Point2f> qAffinePts;
    vector<Point2f> hAffinePts;
    for (size_t t = 0; t < qipeijieguo.size(); t++) {
        qAffinePts.push_back(ky[qipeijieguo[t].queryIdx].pt);
        hAffinePts.push_back(ky_dest[qipeijieguo[t].trainIdx].pt);
           }
    Mat M = findHomography(qAffinePts,hAffinePts,RANSAC);//还可以用rho
    Mat DstImg;
    warpPerspective(img, DstImg, M,Size(imgdest.cols,imgdest.rows));
    imshow("jieguo",DstImg);

}

void QuickDemo::tiaomabiaoqiandingwei(Mat &img){
    auto orb=ORB::create(500);
    vector<KeyPoint> ky;
    vector<KeyPoint> ky_dest;
    Mat miaoshuzi;
    Mat miaoshuzi_dest;
    Mat imgdest=cv::imread("E:/Desktop/study/class_tezheng/src/orb_barcode/00059-NG.jpg",WINDOW_FREERATIO);
    orb->detectAndCompute(img,Mat(),ky,miaoshuzi);
    orb->detectAndCompute(imgdest,Mat(),ky_dest,miaoshuzi_dest);
    //暴力匹配
    auto baoli=BFMatcher::create(NORM_HAMMING,false);
    vector<DMatch> qipeijieguo;
    Mat result;
    baoli->match(miaoshuzi,miaoshuzi_dest,qipeijieguo);
    sort(qipeijieguo.begin(),qipeijieguo.end(),[](const DMatch &a,const DMatch &b){return a.distance<b.distance;});
     float good_rate = 0.2f;
    int num_good_matches = qipeijieguo.size() * good_rate;
    qipeijieguo.erase(qipeijieguo.begin()+num_good_matches,qipeijieguo.end());
    drawMatches(img,ky,imgdest,ky_dest,qipeijieguo,result);
    vector<Point2f> qAffinePts;
    vector<Point2f> hAffinePts;
    for (size_t t = 0; t < qipeijieguo.size(); t++) {
        qAffinePts.push_back(ky[qipeijieguo[t].queryIdx].pt);
        hAffinePts.push_back(ky_dest[qipeijieguo[t].trainIdx].pt);

           }


    Mat M = findHomography(qAffinePts,hAffinePts,RANSAC);//还可以用rho
    vector<Point2f> q{Point2f(0,0),Point2f(img.cols,0),Point2f(img.cols,img.rows),Point2f(0,img.rows)};
    vector<Point2f> h(4);
   perspectiveTransform(q, h, M);//这个透视变换适用于特征提取然后获取在图像中的点
   sort(h.begin(),h.end(),[](const Point2f &a,const Point2f &b){return  a.y<b.y;});
   Point2f temp;
   if(h[0].x>h[1].x){
       temp=h[0];
       h[0]=h[1];
       h[1]=temp;
   }
   if(h[2].x>h[3].x){
       temp=h[2];
       h[2]=h[3];
       h[3]=temp;
   }
   Rect roi(h[0],h[3]);
   Mat src(imgdest(roi));
   transpose(src,src);     //转置
   flip(src,src,0) ;
   namedWindow("roi",1);
   imshow("roi",src);

//   flip(Mat src,Mat &dst,int nFlag);//镜像
}
string objNames[] = { "background",
"aeroplane", "bicycle", "bird", "boat",
"bottle", "bus", "car", "cat", "chair",
"cow", "diningtable", "dog", "horse",
"motorbike", "person", "pottedplant",
"sheep", "sofa", "train", "tvmonitor" };
void QuickDemo::wutishibie(cv::Mat &img){
    string modeltextf="E:/BaiduNetdiskDownload/model/MobileNetSSD_deploy.prototxt";
    string modelfile="E:/BaiduNetdiskDownload/model/MobileNetSSD_deploy.caffemodel";
    Net net=readNetFromCaffe(modeltextf,modelfile);
    Mat blob=blobFromImage(img,0.007843,Size(300,300),Scalar(127.5, 127.5, 127.5),false,false);
    net.setInput(blob);
    Mat src=net.forward();
    Mat result(src.size[2],src.size[3],CV_32F,src.ptr<float>());
    for (int i{};i<result.rows;++i) {
        float *pt=result.ptr<float>(i);
        pt+=1;
        size_t fenlei=(size_t)(*pt++);
        float yuzhi=*pt++;
        if (yuzhi>0.5) {
            float x1=(*pt++)*img.cols;
            float y1=(*pt++)*img.rows;
            float x2=(*pt++)*img.cols;
            float y2=(*pt++)*img.rows;
            Rect biankuang(Point(x1,y1),Point(x2,y2));
            rectangle(img,biankuang,Scalar(0,0,255),2,LINE_AA);
            putText(img,string(objNames[fenlei]),Point(x1,y1-5),FONT_HERSHEY_COMPLEX,1.0,Scalar(45,18,77),1,LINE_AA);
        }

    }
    imshow("img",img);
}

void QuickDemo::wutishibiercnn(cv::Mat &img){
    string label_map = "E:/Desktop/study/class_tezheng/faster_rcnn_inception_v2_coco_2018_01_28/faster_rcnn_inception_v2_coco_2018_01_28/laber.pbtxt";
    string model = "E:/Desktop/study/class_tezheng/faster_rcnn_inception_v2_coco_2018_01_28/faster_rcnn_inception_v2_coco_2018_01_28/frozen_inference_graph.pb";
    string config = "E:/Desktop/study/class_tezheng/faster_rcnn_inception_v2_coco_2018_01_28/faster_rcnn_inception_v2_coco_2018_01_28/frozen_inference_graph.pbtxt";

    Net net=readNetFromTensorflow(model,config);
    net.setPreferableBackend(DNN_BACKEND_OPENCV);//使用什么推理,可选openvino,opencv,或者CUDA
    net.setPreferableTarget(DNN_TARGET_CPU);//用什么硬件计算
    Mat blob=blobFromImage(img,1.0,Size(800,600),Scalar(),true,false);
    net.setInput(blob);
    Mat src=net.forward();
    Mat result(src.size[2],src.size[3],CV_32F,src.ptr<float>());
    for (int i{};i<result.rows;++i) {
        float *pt=result.ptr<float>(i);
        pt+=1;
        size_t fenlei=(size_t)(*pt++);
        float yuzhi=*pt++;
        if (yuzhi>0.85) {
            float x1=(*pt++)*img.cols;
            float y1=(*pt++)*img.rows;
            float x2=(*pt++)*img.cols;
            float y2=(*pt++)*img.rows;
            Rect biankuang(Point(x1,y1),Point(x2,y2));
            rectangle(img,biankuang,Scalar(0,0,255),2,LINE_AA);
            putText(img,string(objNames2[fenlei]),Point(x1,y1-5),FONT_HERSHEY_COMPLEX,1.0,Scalar(45,18,77),1,LINE_AA);
            cout <<fenlei<<endl;
        }

    }
    imshow("img",img);

}
void QuickDemo::daopian(cv::Mat &img){
   Mat erzhi,gary;
   cvtColor(img,gary,COLOR_BGR2GRAY);
   threshold(gary,erzhi,0,255,THRESH_BINARY_INV|THRESH_OTSU);
       Mat kennel=getStructuringElement(MORPH_RECT,Size(3,3));
       morphologyEx(erzhi,erzhi,MORPH_OPEN,kennel,Point(-1,-1));
       imshow("erzhi",erzhi);
       vector<vector<Point>> contours;
       vector<Vec4i> hierarchy;
   //    Mat cc=Mat::zeros(change.size(),change.type());
       findContours(erzhi,contours,hierarchy,RETR_LIST,CHAIN_APPROX_SIMPLE);
       vector <Rect> rejihe;
       for (vector<vector<Point>>::size_type i{0};i<contours.size();++i) {
           Rect rect=boundingRect(contours[i]);
           double area=rect.area();
           if (rect.height>(erzhi.rows/2)) {
               continue;
           }
           if (area < 550) {
                       continue;
                   }
           rejihe.push_back(rect);

       }
       for (vector <Rect>::size_type i{0};i<rejihe.size();++i) {

           rectangle(img,rejihe[i],Scalar(255,0,0),1,LINE_AA);

       }
       sort(rejihe.begin(),rejihe.end(),[](Rect a,Rect b){return a.y<b.y;});
       Rect temp=rejihe[1];
       vector <Rect> resmat;

       for (vector <Rect>::size_type i{0};i<rejihe.size();++i) {
           if (i==1) {
               continue;
           }
           Mat maskres;
           vector<Point> feidian;
           Mat qian=erzhi(rejihe[i]);
           resize(qian,qian,erzhi(temp).size());
           absdiff(qian,erzhi(temp),maskres);
           Mat se = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
                   morphologyEx(maskres, maskres, MORPH_OPEN, se);
           findNonZero(maskres,feidian);
           int nw=maskres.cols+2;
           int nh=maskres.rows+2;
           Mat m1=Mat::zeros(Size(nw,nh),maskres.type());
           Rect roir(1,1,maskres.cols,maskres.rows);
           maskres.copyTo(m1(roir));
           vector<vector<Point>> contoursmask;
           vector<Vec4i> hierarchymask;
            threshold(m1,m1,0,255,THRESH_BINARY|THRESH_OTSU);
           findContours(m1,contoursmask,hierarchymask,RETR_LIST,CHAIN_APPROX_SIMPLE);
           bool yn=false;
           for (size_t t = 0; t < contoursmask.size(); t++) {
                       Rect rect = boundingRect(contoursmask[t]);
                       float ratio = (float)rect.width / ((float)rect.height);
                       if (ratio > 4.0 && (rect.y < 5 || (m1.rows - (rect.height + rect.y)) < 10)) {
                           continue;
                       }
                       double area = contourArea(contoursmask[t]);
                       if (area > 10) {
                           yn = true;
                           break;
                       }
                       }
           if (feidian.size()>40&&yn)
           {
               resmat.push_back(rejihe[i]);
           }


       }
       for (vector <Rect>::size_type i{0};i<rejihe.size();++i) {
           rectangle(img,resmat[i],Scalar(255,0,0),1,LINE_AA);
         putText(img,"bad",Point(resmat[i].x,resmat[i].y-10),FONT_HERSHEY_COMPLEX,1.0,Scalar(45,18,77),1,LINE_AA);
       }


           imshow("img",img);

}
void QuickDemo::renlianjianche(cv::Mat &img){
//    getTickFrequency()

}

//交叉匹配
//针对暴力匹配,可以使用交叉匹配的方法来过滤错误的匹配。交叉过滤的思想很简单,再进行一次匹配,反过来使用被匹配到的点进行匹配,如果匹配到的仍然是第一次匹配的点的话,就认为这是一个正确的匹配。举例来说就是,假如第一次特征点A使用暴力匹配的方法,匹配到的特征点是特征点B;反过来,使用特征点B进行匹配,如果匹配到的仍然是特征点A,则就认为这是一个正确的匹配,否则就是一个错误的匹配。OpenCV中BFMatcher已经封装了该方法,创建BFMatcher的实例时,第二个参数传入true即可,BFMatcher bfMatcher(NORM_HAMMING,true)。

//KNN匹配
//K近邻匹配,在匹配的时候选择K个和特征点最相似的点,如果这K个点之间的区别足够大,则选择最相似的那个点作为匹配点,通常选择K = 2,也就是最近邻匹配。对每个匹配返回两个最近邻的匹配,如果第一匹配和第二匹配距离比率足够大(向量距离足够远),则认为这是一个正确的匹配。
//OpenCV中的匹配器中封装了该方法,上面的代码可以调用bfMatcher->knnMatch(descriptors1, descriptors2, knnMatches, 2);

//float fps = getTickFrequency() / (getTickCount() - start); 帧数
//float time = (getTickCount() - start) / getTickFrequency(); 运行时间
void linspace(Mat& image, float begin, float finish, int number, Mat &mask);
void generate_mask(Mat &img, Mat &mask);
void QuickDemo::tuxiangpingjie(cv::Mat &left){
        Mat right=imread("E:/Desktop/study/class_tezheng/src/22.jpg");
        // 提取特征点与描述子
        vector<KeyPoint> keypoints_right, keypoints_left;
        Mat descriptors_right, descriptors_left;
        auto detector = AKAZE::create();
        detector->detectAndCompute(left, Mat(), keypoints_left, descriptors_left);
        detector->detectAndCompute(right, Mat(), keypoints_right, descriptors_right);

        // 暴力匹配
        vector<DMatch> matches;
        auto matcher = DescriptorMatcher::create(DescriptorMatcher::BRUTEFORCE);

        // 发现匹配
        std::vector< std::vector<DMatch> > knn_matches;
        matcher->knnMatch(descriptors_left, descriptors_right, knn_matches, 2);
        const float ratio_thresh = 0.7f;
        std::vector<DMatch> good_matches;
        for (size_t i = 0; i < knn_matches.size(); i++)
        {
            if (knn_matches[i][0].distance < ratio_thresh * knn_matches[i][1].distance)
            {
                good_matches.push_back(knn_matches[i][0]);
            }
        }

        Mat dst;
        drawMatches(left, keypoints_left, right, keypoints_right, good_matches, dst);
        namedWindow("output",WINDOW_FREERATIO);
        imshow("output", dst);
        imwrite("D:/good_matches.png", dst);

        //-- Localize the object
        std::vector<Point2f> left_pts;
        std::vector<Point2f> right_pts;
        for (size_t i = 0; i < good_matches.size(); i++)
        {
            // 收集所有好的匹配点
            left_pts.push_back(keypoints_left[good_matches[i].queryIdx].pt);
            right_pts.push_back(keypoints_right[good_matches[i].trainIdx].pt);
        }

        // 配准与对齐,对齐到第一张
        Mat H = findHomography(right_pts, left_pts, RANSAC);

        // 获取全景图大小
        int h = max(left.rows, right.rows);
        int w = left.cols + right.cols;
        Mat panorama_01 = Mat::zeros(Size(w, h), CV_8UC3);
        Rect roi;
        roi.x = 0;
        roi.y = 0;
        roi.width = left.cols;
        roi.height = left.rows;

        // 获取左侧与右侧对齐图像
        left.copyTo(panorama_01(roi));
        imwrite("D:/panorama_01.png", panorama_01);
        Mat panorama_02;
        warpPerspective(right, panorama_02, H, Size(w, h));
        imwrite("D:/panorama_02.png", panorama_02);


        // 计算融合重叠区域mask
        Mat mask = Mat::zeros(Size(w, h), CV_8UC1);
        generate_mask(panorama_02, mask);

        // 创建遮罩层并根据mask完成权重初始化
        Mat mask1 = Mat::ones(Size(w, h), CV_32FC1);
        Mat mask2 = Mat::ones(Size(w, h), CV_32FC1);

        // left mask
        linspace(mask1, 1, 0, left.cols, mask);

        // right mask
        linspace(mask2, 0, 1, left.cols, mask);
        imshow("mask1", mask1);
        imshow("mask2", mask2);

        // 左侧融合
        Mat m1;
        vector<Mat> mv;
        mv.push_back(mask1);
        mv.push_back(mask1);
        mv.push_back(mask1);
        merge(mv, m1);
        panorama_01.convertTo(panorama_01, CV_32F);
        multiply(panorama_01, m1, panorama_01);

        // 右侧融合
        mv.clear();
        mv.push_back(mask2);
        mv.push_back(mask2);
        mv.push_back(mask2);
        Mat m2;
        merge(mv, m2);
        panorama_02.convertTo(panorama_02, CV_32F);
        multiply(panorama_02, m2, panorama_02);

        // 合并全景图
        Mat panorama;
        add(panorama_01, panorama_02, panorama);
        panorama.convertTo(panorama, CV_8U);

        imshow("jjjjj",panorama);
    }

    void generate_mask(Mat &img, Mat &mask) {
        int w = img.cols;
        int h = img.rows;
        for (int row = 0; row < h; row++) {
            for (int col = 0; col < w; col++) {
                Vec3b p = img.at<Vec3b>(row, col);
                int b = p[0];
                int g = p[1];
                int r = p[2];
                if (b == g && g == r && r == 0) {
                    mask.at<uchar>(row, col) = 255;
                }
            }
        }
        imwrite("D:/mask.png", mask);
    }

    void linspace(Mat& image, float begin, float finish, int w1, Mat &mask) {
        int offsetx = 0;
        float interval = 0;
        float delta = 0;
        for (int i = 0; i < image.rows; i++) {
            offsetx = 0;
            interval = 0;
            delta = 0;
            for (int j = 0; j < image.cols; j++) {
                int pv = mask.at<uchar>(i, j);
                if (pv == 0 && offsetx == 0) {
                    offsetx = j;
                    delta = w1 - offsetx;
                    interval = (finish - begin) / (delta - 1);
                    image.at<float>(i, j) = begin + (j - offsetx)*interval;
                }
                else if (pv == 0 && offsetx > 0 && (j - offsetx) < delta) {
                    image.at<float>(i, j) = begin + (j - offsetx)*interval;
                }
            }
        }
    }



//#include <iostream>
//#include <chrono>
//int main()
//{
//    auto start = std::chrono::steady_clock::now();
//    // 代码段
//    auto end = std::chrono::steady_clock::now();
//    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
//    std::cout << "代码运行时间:" << duration << "毫秒" << std::endl;
//    return 0;
//}
void QuickDemo::tupiancharu(cv::Mat &img){
    Mat roixiao=imread("E:/BaiduNetdiskDownload/83.png");
    int inNum=roixiao.rows;
            copyMakeBorder(img, img, inNum, 0, 0, 0, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0, 0));
            Rect r(0, 0, img.cols, inNum);
            roixiao.copyTo(img(r));
            imshow("charu",img);
}
void QuickDemo::caitu(){
    string path="D:/data/T02023413003/4.13/*.*";
    vector<string> chucun;
    glob(path,chucun);

    for (int i=0;i<chucun.size();++i) {
       Mat tupain=imread(chucun[i]);
       double bili=600.f/tupain.cols;
       int gao=tupain.rows*bili;
       Mat s1;
       cv::resize(tupain,s1,Size(600,gao),0,0,INTER_AREA);
       string imges1name=chucun[i].substr(chucun[i].rfind("\\I")+1);
       imwrite("E:/Desktop/420-2/"+imges1name,s1);
    }
}
void QuickDemo::jiamingzi(){
    string path="E:\\Desktop\\bianren\\match_imgs\\*.bmp";
    vector<string> chucun;
    glob(path,chucun);

    for (int i=0;i<chucun.size();++i) {
       Mat tupain=imread(chucun[i]);
       string imges1name=chucun[i].substr(chucun[i].rfind("\\img")+1);
       int pos=imges1name.find("b.");
       if(pos!=-1){
           imges1name.replace(pos,1,"a_std");
       }
       imwrite("E:/Desktop/bianren/"+imges1name,tupain);
    }
}
void QuickDemo::caituzidongban(){
    auto start = std::chrono::steady_clock::now();
    string cucunlujing="E:/Desktop/";
    string path="D:/data/T02023523002/zv5.23/*.*";
    vector<string> chucun;
    glob(path,chucun);
    size_t pos=path.rfind("/",path.rfind("/")-1);
    string wenjianjiaming=path.substr(pos+1,path.rfind("/")-pos-1);
    cucunlujing=cucunlujing+wenjianjiaming+"_resize/";
    if (_access(cucunlujing.c_str(), 0)==-1)//返回值为-1,表示不存在
    {
//        printf("不存在,创建\n");
        _mkdir(cucunlujing.c_str());
    }
        for (int i=0;i<chucun.size();++i) {
           Mat tupain=imread(chucun[i]);
           double bili=600.f/tupain.cols;
           int gao=tupain.rows*bili;
           Mat s1;
           cv::resize(tupain,s1,Size(600,gao),0,0,INTER_AREA);
           string imges1name=chucun[i].substr(chucun[i].rfind("\\")+1);
           imwrite(cucunlujing+imges1name,s1);
       }
       auto end = std::chrono::steady_clock::now();
       auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
       std::cout << "代码运行时间:" << duration << "毫秒" << std::endl;
}

#include "quickdemo.h"
#include "shuzu.h"
#include <vector>
#include <cmath>
#include <opencv2/features2d.hpp>
#include <direct.h>
#include<io.h>
using namespace cv;
using namespace std;
using namespace dnn;
QuickDemo::QuickDemo()
{

}
//copyto mask用来限制原图给别人多少,接受图的(roi)用来表示接受的区域是哪里
void QuickDemo::colorSpace_Demo(Mat &change){
    Mat one,two;
    cvtColor(change,one,COLOR_BGR2HSV);//转成hsv
    cvtColor(change,two,COLOR_BGR2GRAY);//转成hsv

    imshow("hsv",one);
    imshow("黑白",two);
    imwrite("E:\\BaiduNetdiskDownload\\hsv.jpg",one);
    imwrite("E:\\BaiduNetdiskDownload\\heibai.jpg",two);



}
void QuickDemo::mat_creation_demo(cv::Mat &change){//di3节//创建的时候宽高,高row=行
    Mat m1,m2;//克隆和拷贝都是值拷贝,赋值=是指向同一份数据,类似于指针
    m1.copyTo(change);
    m2=change.clone();//克隆
    //创建空白图像
    Mat m3=Mat::zeros(Size(5,4),CV_8UC3);//8位得无符号int单通道,最后数字表示几通道,值全是0
//    Mat m3=Mat::ones(Size(3,8),CV_8UC1);//使用ones创建空白图像得时候单通道全是1,多通道只有第一个通道是1
    //给像素点通道赋值
//    m3=255;//给单通道赋值
    m3=Scalar(88,66,77);//给多通道赋值
    cout <<"宽度"<<m3.cols<<"高度"<<m3.rows<<"通道数"<<m3.channels()<<endl;

    imshow("dd",m3);
    Mat m4=m3;//共用同一份图像数据
    Mat m5=(Mat_<char>(3,3)<<1,5,6,8,7,9,9,8,7);//默认通道数1

}

void QuickDemo::xiangshuduxie_demo(cv::Mat &change){//像素读写操作//高行
    int tongdao=change.channels();
    int hang=change.rows;
    int lie=change.cols;
    for (int row=0;row<hang;++row)
    {
        for (int col=0;col<lie;++col)
        {
            if(tongdao==1)
            {
                int xiangsudian=change.at<uchar>(row,col);
                change.at<uchar>(row,col)=255-xiangsudian;
            }
            if (tongdao==3) {
                Vec3b xiangsudian=change.at<Vec3b>(row,col);  //Vec3b相当于一个存放3个uchar类型得数组,int类型Vec3i,浮点类型Vec3f
                change.at<Vec3b>(row,col)[0]=255-xiangsudian[0];
                change.at<Vec3b>(row,col)[1]=255-xiangsudian[1];
                change.at<Vec3b>(row,col)[2]=255-xiangsudian[2];
            }
        }
    }
//    for (int row=0;row<hang;++row)//指针版本
//    {   uchar *xiangsudian=change.ptr<uchar>(row);//将第一行赋给指针,数组赋给指针,指针指向数组首地址,也就是该行第一个像素点
//        for (int col=0;col<lie;++col)
//        {
//            if(tongdao==1)
//            {
//                *xiangsudian=255-*xiangsudian;
//                ++xiangsudian;
//            }
//            if (tongdao==3) {
//                for (int i=0;i<3;i++,++xiangsudian) {
//                    *xiangsudian=255-*xiangsudian;//一行有col个像素点,每个像素点有3个值
//                }

//            }
//        }
//    }
    namedWindow("测试窗口",WINDOW_AUTOSIZE);
    imshow("测试窗口",change);
}
void QuickDemo::tupianjisuan_demo(cv::Mat &change){//图片加减
//    saturate_cast<uchar>,当使用它时会保证BGR图像的像素值在[0,255] saturate_cast<uchar>(p1[0]+p2[0]),可以是别的符号,可以保证数值在有效范围内
//    Mat ceshi=change+Scalar(50,50,50);
//    add(change,Scalar(50,50,50),ceshi);  //多种用法,最好用函数,加标准定义,如下
    Mat scr=Mat::zeros(change.size(),change.type());
    Mat zhongjian=Mat::zeros(change.size(),change.type());
    zhongjian=Scalar(50,50,50);
//    add(change,zhongjian,scr);//加法
//  subtract(change,zhongjian,scr);//减法
//    multiply(change,zhongjian,scr);//乘法
    divide(change,zhongjian,scr);//除法
    namedWindow("结果",WINDOW_FREERATIO);
    imshow("结果",scr);
}
//void on_treak(int chushzhi,void *p){/*传统版本亮度*/
//    Mat src=*((Mat*)p);
//    Mat zhongjian=Mat::zeros(src.size(),src.type());
//    Mat jieguo=Mat::zeros(src.size(),src.type());
//    zhongjian=Scalar(chushzhi,chushzhi,chushzhi);
//    add(src,zhongjian,jieguo);
//    imshow("亮度和对比度窗口",jieguo);
//}
void on_treak(int chushzhi,void *p){/*ddWeighted版本亮度*/
    Mat src=*((Mat*)p);
    Mat zhongjian=Mat::zeros(src.size(),src.type());
    Mat jieguo=Mat::zeros(src.size(),src.type());
    double fanwei=chushzhi-50;

    addWeighted(src,1,zhongjian,0,fanwei,jieguo);
    imshow("亮度和对比度窗口",jieguo);
}
void duibidu(int chushzhi,void *p){
//    addWeighted
//        共7个参数
//            第1个参数 第一个输入 src1
//            第2个参数 第一个输入的权重 alpha
//            第3个参数 第二个输入 src2
//            第4个参数 第二个输入的权重 beta
//            第5个参数 每个数要增加的标量 gamma
//            第6个参数 输出 dst图片

//            第7个参数 输出的可选深度(一般用不到)

//        公式
//            dst = src1 * alpha + src2 * beta + gamma
//            范围截断在[0,255]
    Mat src=*((Mat*)p);
    Mat zhongjian=Mat::zeros(src.size(),src.type());
    Mat jieguo=Mat::zeros(src.size(),src.type());
    double quanzhong=chushzhi/100.0;
    addWeighted(src,quanzhong,zhongjian,0,0,jieguo);
    imshow("亮度和对比度窗口",jieguo);
}
void QuickDemo::gundongtiaoliangdu_demo(cv::Mat &change){
    Mat src=change;
    namedWindow("亮度和对比度窗口",WINDOW_FREERATIO);
    int maxliangdu=100;
    int l_chushzhi=50;
    int maxduibidu=200;
    int d_chushzhi=50;
    createTrackbar("亮度:","亮度和对比度窗口",&l_chushzhi,maxliangdu,on_treak,(void*)&change);
    createTrackbar("对比度:","亮度和对比度窗口",&d_chushzhi,maxduibidu,duibidu,(void*)&change);

}
void QuickDemo::jianpanshijian(cv::Mat &change){
   Mat src=Mat::zeros(change.size(),change.type());
   namedWindow("结果窗口",WINDOW_FREERATIO);
   while (true)
   {
       int c=waitKey(1000);
       if (c==27) {
           break;
       }else if (c==49)
       {
           cvtColor(change,src,COLOR_BGR2GRAY);
           imshow("结果窗口",src);
        }else if (c==50)
       {
       cvtColor(change,src,COLOR_BGR2HSV);
       imshow("结果窗口",src);
        }else if (c==51)
       {
    src=change+Scalar(50,50,50);
    imshow("结果窗口",src);
      }
   }

}
void QuickDemo::zidaiyansebiao(cv::Mat &change){
    int colormap[] = {
         COLORMAP_AUTUMN,
         COLORMAP_BONE,
         COLORMAP_JET,
         COLORMAP_WINTER,
         COLORMAP_RAINBOW,
         COLORMAP_OCEAN,
         COLORMAP_SUMMER,
         COLORMAP_SPRING,
         COLORMAP_COOL,
         COLORMAP_HSV,//10
         COLORMAP_PINK,
         COLORMAP_HOT,
         COLORMAP_PARULA,
         COLORMAP_MAGMA,
         COLORMAP_INFERNO,
         COLORMAP_PLASMA,
         COLORMAP_VIRIDIS,
         COLORMAP_CIVIDIS,
         COLORMAP_TWILIGHT,
         COLORMAP_TWILIGHT_SHIFTED,//20
         COLORMAP_TURBO,
         COLORMAP_DEEPGREEN
        };
    Mat src=Mat::zeros(change.size(),change.type());
    int index{};
    namedWindow("结果窗口",WINDOW_FREERATIO);
    while (true) {
        int c=waitKey(1000);
        if (c==27) {
            break;
        }
        applyColorMap(change,src,colormap[index%22]);
        ++index;
        imshow("结果窗口",src);
    }

}
void QuickDemo::luojiweiyunsun(cv::Mat &change){
//    rectangele
//        绘制矩形
//            共7个参数
//                第1个参数 输入
//                第2个参数 矩形左上坐标
//                第3个参数 矩形右下坐标
//                第4个参数 矩形颜色
//                第5个参数 线宽
//                                如果参数 >=0,则表示绘制矩形(如为1,表示绘制的矩形边为1个像素)
//                                如果参数 < 0,则表示填充矩形(如-1,表示填充整个矩形)
//                第6个参数 lineType
//                                关于图像锯齿,有几种方式处理
//                                    不管不顾,就用LINE_4 或者 LINE_8
//                                    消除锯齿,就用LINE_AA (AA就是反锯齿)
//                第7个参数  缩小图像,同时缩短矩形左上顶点与(0,0)位置的距离
//                          0表示不变
//                          1表示图像*1/2,同时距离(0,0)的x方向和y方向距离*1/2
//                          2表示图像*(1/2)^2,同时距离(0,0)的x方向和y方向距离*(1/2)^2

    Mat m1=Mat::zeros(Size(250,250),CV_8UC3);
    Mat m2=Mat::zeros(Size(250,250),CV_8UC3);
    rectangle(m1,Point(120,200),Point(150,250),Scalar(3,88,99),-1,LINE_8,0);
    rectangle(m2,Rect(88,120,130,140),Scalar(35,88,250),-1,LINE_8,0);//用左上角下标,后面两个是矩形大小Rect
    imshow("m1",m1);
    imshow("m2",m2);
    Mat m4;
//    bitwise_and(m1,m2,m4);//交及
//        bitwise_or(m1,m2,m4);//并及
//    bitwise_not(change,m4);//取反相当于255减每个像素点
      bitwise_xor(m1,m2,m4);//异或
    imshow("m4",m4);


}
void QuickDemo::tongdaofenoli(cv::Mat &change){
    vector<Mat> tuxiang;
    split(change,tuxiang);//通道分离
//    imshow("蓝色",tuxiang[0]);
//    imshow("绿色",tuxiang[1]);
//    imshow("红色",tuxiang[2]);
    Mat jieguo=Mat::zeros(change.size(),change.type());//使用mixChannels得时候得初始化
    //    tuxiang[0]=0;
    //    tuxiang[2]=0;//可以得到单一一种彩色得图像,合并起来后
    merge(tuxiang,jieguo);//合并
//    imshow("结果",jieguo);
//    mixChannels
//        混合通道
//            共6个参数
//                第1个参数 输入
//                第2个参数 输入的矩阵数
//                第3个参数 输出
//                第4个参数 输出的矩阵数
//                第5个参数 从哪个通道 变成 哪个通道
//                第6个参数 要变的对数
//    这个混合的意思是,彩色图像本来是bgr的顺序,经过通道混合就变成了rgb。
    int jiaohuan[]={0,2,1,2,2,0};
    mixChannels(&change,1,&jieguo,1,jiaohuan,3);//混合,也可以把个4通道分为一个三通道和一个一同道,输出改成一个数组,数量改成2,对数改一下就可以了
    imshow("结果",jieguo);

}
void QuickDemo::genhuanbeijing(cv::Mat &change){
//    在opencv中,我们提取指定色彩范围的区域,采用inRange实现,这样的一块区域,学名叫做ROI(region of interest),感兴趣区域。
//    关于inRange的提取原理
//        图像中,对于在指定色彩范围内的颜色,将置为255(白色),不在的则置为0(黑色)
//        对于多通道的输入,输出结果是各个通道的结果相与,当各通道结果都在上下限之内时,输出为255,否则为0。
//        因此也有人将输出理解为mask掩码模板,作为mask使用。inRange
//    提取指定色彩范围内的区域
//		共4个参数
//			第1个参数 输入
//			第2个参数 色彩下界
//			第3个参数 色彩上界
//			第4个参数 输出
    Mat src;
    cvtColor(change,src,COLOR_BGR2HSV);
    inRange(src,Scalar(35,43,46),Scalar(77,255,255),src);
    Mat beijing=Mat::zeros(change.size(),change.type());
    beijing=Scalar(20,20,200);
    bitwise_not(src,src);
    imshow("inRange",src);
    change.copyTo(beijing,src);//将src中不为0得像素点扣过来beijing里,再把change中赋值到src扣过来得部分,其余beijing不变
    imshow("jieguo",beijing);

}
void QuickDemo::daxiaopingjun(cv::Mat &change){
//    minMaxLoc
//        求取单通道图像像素的最小值,最大值
//            共6个参数
//                第1个参数 输入单通道图像
//                第2个参数 输出最小值
//                第3个参数 输出最大值
//                第4个参数 输出最小值点的坐标
//                第5个参数 输出最大值点的坐标

//                第6个参数 输入图像的子数组(有时候我们会求取ROI区域的最小/最大值,就会传入mask图像)
//                        (这里的子数组,是一种图像掩模,可以实现加东西/扣东西)
//    meanStdDev
//        求取平均值,标准差
//            共4个参数
//                第1个参数 输入
//                第2个参数 输出图像像素的平均值,每个通道都会输出一个
//                第3个参数 输出图像像素的标准差,每个通道都会输出一个

//                第4个参数 输入图像的子数组(有时候我们会求取ROI区域的平均值/标准差,就会传入mask图像)
//                        (这里的子数组,是一种图像掩模,可以实现加东西/扣东西)
    Point minp,maxp;
    double min,max;
    vector <Mat>fenli;
    split(change,fenli);
    int i{1};
    for(auto dantongdao:fenli){
        minMaxLoc(dantongdao,&min,&max,&minp,&maxp);
        cout <<"第"<<i++<<"个通道最大值为:"<<max<<"最小值为:"<<min<<endl;
    }
    Mat pjz,fangcha;
//    cvtColor(change,change,COLOR_BGR2GRAY);
    meanStdDev(change,pjz,fangcha);
    cout <<"平均值:"<<pjz<<endl;
    cout <<"方差:"<<fangcha<<endl;


}
void QuickDemo::huizhituxing(cv::Mat &change){
//    circle
//        绘制圆形
//            共7个参数
//                第1个参数 输入
//                第2个参数 圆心点
//                第3个参数 圆形半径
//                第4个参数 圆形颜色
//                第5个参数 线宽
//                第6个参数 lineType
//                第7个参数  缩小图像,同时缩短圆心与(0,0)位置的距离
//                          0表示不变
//                          1表示图像*1/2,同时距离(0,0)的x方向和y方向距离*1/2
//                          2表示图像*(1/2)^2,同时距离(0,0)的x方向和y方向距离*(1/2)^2
//    line
//        绘制线段
//            共7个参数
//                第1个参数 输入
//                第2个参数 起点
//                第3个参数 终点
//                第4个参数 线段颜色
//                第5个参数 线宽(注意,这个时候线宽只能>=0)
//                第6个参数 lineType
//                第7个参数  缩短线段左上顶点与(0,0)位置的距离
//                          0表示不变
//                          1表示图像*1/2,同时距离(0,0)的x方向和y方向距离*1/2
//                          2表示图像*(1/2)^2,同时距离(0,0)的x方向和y方向距离*(1/2)^2
//    ellipse
//        绘制椭圆
//            共5个参数
//                第1个参数 输入
//                第2个参数 RotatedRect
//                第3个参数 椭圆颜色
//                第4个参数 线宽
//                第5个参数 lineType
    Mat tuya=Mat::zeros(change.size(),change.type());
    Rect rec;
    rec.x=555;
    rec.y=555;
    rec.width=100;
    rec.height=100;
    rectangle(tuya,rec,Scalar(0,0,200),2,LINE_8,0);
    //等价于rectangle(change,rec(55,55,100,100),Scalar(0,0,2000),2,LINE_8,0);
    circle(tuya,Point(55,55),200,Scalar(255,200,200),2,LINE_AA,0);//圆形
    line(tuya,Point(55,100),Point(105,100),Scalar(222,255,200),2,LINE_AA,0);
    RotatedRect yuan;
    yuan.center=Point(80,100);
    yuan.size=Size(50,20);
    yuan.angle=0;//旋转角度
    ellipse(tuya,yuan,Scalar(22,25,20),2,LINE_AA);
    addWeighted(change,0.7,tuya,0.5,0,tuya);
    imshow("矩形",tuya);

}
void QuickDemo::suijishu(cv::Mat &change){
    RNG rng(12345);
    int w=change.cols;
    int h=change.rows;
    while (true) {
        int c=waitKey(255);
        if (c==27) {
            break;
        }
        int x1=rng.uniform(0,w);
        int x2=rng.uniform(0,w);
        int y1=rng.uniform(0,h);
        int y2=rng.uniform(0,h);
        int b=rng.uniform(0,255);
        int g=rng.uniform(0,255);
        int r=rng.uniform(0,255);
        line(change,Point(x1,y1),Point(x2,y2),Scalar(b,g,r),2,LINE_AA,0);
        namedWindow("随机数",WINDOW_FREERATIO);
        imshow("随机数",change);
    }
}
void QuickDemo::duobianxinghuizhi(cv::Mat &change){
//    polylines
//        绘制多条多边形曲线
//            共7个参数
//                第1个参数 输入
//                第2个参数 输入多边形点集(绘制边的顺序,与点集数组中点的顺序有关)
//                第3个参数 isclosed(bool类型)
//                第4个参数 多边形颜色
//                第5个参数 线宽
//                第6个参数 lineType
//                第7个参数 shift(左顶点缩小)
//    fillPoly
//        填充绘制的多边形
//            共6个参数
//                第1个参数 输入
//                第2个参数 点集数组
//                第3个参数 填充颜色
//                第4个参数 lineType
//                第5个参数 shift
//                第6个参数 轮廓所有点的可选偏移
//    drawContours
//        绘制轮廓轮廓/填充轮廓
//            共9个参数,这里我们只介绍前5个(剩下的有缺省值)
//                第1个参数 输入
//                第2个参数 多边形轮廓的数组
//                第3个参数 选择的多边形轮廓(在数组中的编号,-1为全选)
//                第4个参数 多边形颜色
//                第5个参数 填充/绘制(-1为填充)

    Mat beijing=Mat::zeros(Size(500,500),CV_8UC3);
    vector<Point>duobianxing;
    RNG rng(12345);
    for (int i{};i<5;i++) {
        int x=rng.uniform(0,500);
        int y=rng.uniform(0,500);
        duobianxing.push_back(Point(x,y));
    }
//    int b=rng.uniform(0,255);
//    int g=rng.uniform(0,255);
//    int r=rng.uniform(0,255);
    /*polylines(beijing,duobianxing,true,Scalar(b,g,r),1,LINE_AA,0);
    fillPoly(beijing,duobianxing,Scalar(rng.uniform(0,255),rng.uniform(0,255),rng.uniform(0,255)),LINE_AA,0);*///如果先填充再画线,就有边框
    vector<vector<Point>> jihe;
    jihe.push_back(duobianxing);
    drawContours(beijing,jihe,-1,Scalar(rng.uniform(0,255),rng.uniform(0,255),rng.uniform(0,255)),-1);//jihe是一个多边形得集合,这个函数可以同时画多个多边形
    imshow("多边形",beijing);
}
Point s(0,0),e(0,0);Mat temp;
void shubiaohuidiao(int shijian,int x, int y,int jian,void* tupain){//jian这个参数用到得类型是按着不放那种,6种类型看官方文档,shijian类型是点击
//    MouseCallback类型的函数(鼠标事件回调函数)
//        共5个参数,这5个参数全是针对鼠标的捕捉
//            第1个参数 捕捉的鼠标事件(查阅文档可知,都有什么类型)
//            第2个参数 捕捉的鼠标事件的x坐标
//            第3个参数 捕捉的鼠标事件的y坐标
//            第4个参数 标志捕捉的事件是哪一个键(有5种,查阅文档可知)
//            第5个参数 void*类型的可选传入数据
    Mat src=*((Mat*)tupain);
    if (shijian==EVENT_LBUTTONDOWN) {//按下左键
        s.x=x;
        s.y=y;
        cout <<"起点"<<s<<endl;
    }
    if (jian==EVENT_FLAG_LBUTTON&&shijian==EVENT_MOUSEMOVE) {//按住并且拖动//画之前用原图替换现图,可以去掉之前画的,就永远是最新得那个框了
        e.x=x;
        e.y=y;
        temp.copyTo(src);
        rectangle(src,s,e,Scalar(0,0,255),2,LINE_AA,0);
        imshow("鼠标绘制矩形",src);
    }
    if (shijian==EVENT_LBUTTONUP) {//松开
        e.x=x;
        e.y=y;
        cout <<"终点"<<e<<endl;
        rectangle(src,s,e,Scalar(0,0,255),2,LINE_AA,0);
        imshow("鼠标绘制矩形",src);
        Rect rec(e,s);
        imshow("ROI",src(rec));


    }

}
//画圆形截取
//void shubiaohuidiao(int shijian,int x, int y,int jian,void* tupain){//jian这个参数用到得类型是按着不放那种,6种类型看官方文档,shijian类型是点击

//    Mat src=*((Mat*)tupain);
//    if (shijian==EVENT_LBUTTONDOWN) {//按下左键
//        s.x=x;
//        s.y=y;
//        cout <<"起点"<<s<<endl;
//    }
//    if (jian==EVENT_FLAG_LBUTTON&&shijian==EVENT_MOUSEMOVE) {//按住并且拖动//画之前用原图替换现图,可以去掉之前画的,就永远是最新得那个框了
//        e.x=x;
//        e.y=y;
//        double banjing=(pow((e.x-s.x)*(e.x-s.x)+(e.y-s.y)*(e.y-s.y),1.0f/2))/2;
//        Point yuanxin((e.x+s.x)/2,(e.y+s.y)/2);
//        temp.copyTo(src);
//        circle(src,yuanxin,banjing,Scalar(0,0,255),10,LINE_AA,0);
//        imshow("鼠标绘制矩形",src);
//    }
//    if (shijian==EVENT_LBUTTONUP) {//松开
//        e.x=x;
//        e.y=y;
//        cout <<"终点"<<e<<endl;
//        double banjing=(pow((e.x-s.x)*(e.x-s.x)+(e.y-s.y)*(e.y-s.y),1.0f/2))/2;
//        Point yuanxin((e.x+s.x)/2,(e.y+s.y)/2);
//        circle(src,yuanxin,banjing,Scalar(0,0,255),10,LINE_AA,0);
//        imshow("鼠标绘制矩形",src);
//        Mat lingshi=Mat::zeros(src.size(),CV_8UC1);
//        circle(lingshi,yuanxin,banjing,Scalar(255,255,255),-1,LINE_AA,0);
//        Mat roi=Mat::zeros(src.size(),src.type());
//        temp.copyTo(roi,lingshi);
//        imshow("roi",roi);


//    }

//}
void QuickDemo::shubiaoshijian(cv::Mat &change){
//    setMouseCallback
//        设置指定窗口的鼠标处理程序
//            共3个参数
//                第1个参数 窗口名称
//                第2个参数 处理鼠标事件的回调函数
//                第3个参数 传递给回调函数的可选参数
    namedWindow("鼠标绘制矩形",WINDOW_FREERATIO);
    setMouseCallback("鼠标绘制矩形",shubiaohuidiao,(void*)&change);
    imshow("鼠标绘制矩形",change);
    temp=change.clone();
}

void QuickDemo::tuxiangleixingzhuanhuanyuguiyi(cv::Mat &change){
//    convertTO
//        数据类型转换
//            本文采用了第一种传参方式
//            共2个参数
//                第1个参数 输入
//                第2个参数 将要转换的数据类型
//    normalize
//        归一化,归一指归为同一范围
//            共7个参数
//                第1个参数 输入
//                第2个参数 输出

//                第3个参数 alpha 规范值/归一化范围下限
//                第4个参数 beta 归一化范围上限
//                        默认规范值为1,即数值规范为1,此时beta = 0
//                        (所以这两处有两种传参方式
//                            第一种 1.0 0,默认的这种,取值规范为[0,1]
//                            第二种 0,b 下限上限的这种。取值规范为指定范围
//                        两种方式都可以使用)

//                第5个参数 归一化类型(查阅文档可知)
//                第6个参数 默认类型与src一直
//                            负数,则类型与src一直
//                            否则,通道数和src一致,depth=指定的图像深度(图像深度= 图像列数*通道数)
//                第7个参数 可选mask
//    关于归一化类型,常用的有4种:

//        NORM_L1——求和归一
//        NORM_L2——三维向量转单位向量归一
//        NORM_INF——根据最大值归一
//        NORM_MINMAX——根绝最大最小值差值归一(最为常用)

    cout <<change.type()<<endl;
    change.convertTo(change,CV_32FC3);
    cout <<change.type()<<endl;
    Mat dit;
    normalize(change,dit,1.0,0,NORM_MINMAX);
    imshow("jgg",dit);
}
void QuickDemo::fangsuo(cv::Mat &change){
//    resize
//        重设图像宽长
//            共6个参数
//                第1个参数 输入
//                第2个参数 输出

//                第3个参数 输出图像的size
//                第4个参数 fx 沿水平的比例因子
//                第5个参数 fy 沿垂直的比例因子

//                        可以使用size做放缩插值,也可以使用fx,fy卷积做放缩插值

//                第6个参数 插值方法(其中前两种比较快,后两种比较慢)
//    INTER_NEAREST = 0 ——最近邻插值
//    INTER_LINEAR = 1 ——线性插值//fangda
//    INTER_CUBIC = 2 ——立方插值
//    INTER_LANCZOS4 = 4 ——Lanczos插值
    //    INTER_AREA xiao
    Mat a1,a2;
    int w=change.cols;
    int h=change.rows;
//    resize(change,a1,Size(w*2,h*2),0,0,INTER_NEAREST);//放大
//    resize(change,a1,Size(0,0),0.5,0.5,INTER_NEAREST);//缩小0.5
   resize(change,a1,Size(w/2,h/2),0,0,INTER_NEAREST);//缩小0.5
    imshow("缩小",a1);

}
void QuickDemo::fanzhaun(cv::Mat &change){
    Mat src;
//    flip(change,src,0);//上下翻转
    flip(change,src,-1);//上下左右翻转//180du
//    flip(change,src,1);//左右翻转
    namedWindow("fanzhuan",WINDOW_FREERATIO);
    imshow("fanzhuan",src);
}
void QuickDemo::tupainxuanzuan(cv::Mat &change){
//    warpAffine
//        仿射变换
//            共7个参数
//                第1个参数 输入
//                第2个参数 输出
//                第3个参数 输入的变换矩阵(2行3列)
//                第4个参数 输出图像的size
//                第5个参数 插值方法
//                第6个参数 边界模式
//                第7个参数 边界颜色
//    getRotationMatrix2D
//        计算二维旋转的仿射矩阵(变换矩阵)
//            共3个参数
//                第1个参数 图像的旋转中心
//                第2个参数 旋转角度
//                            (规定坐标原点左上角,正值表示逆时针旋转)
//                第3个参数 各向同性比例因子(对图像本身大小的放缩
//      旋转后:
//    新宽度nw = wcosΘ + hsinΘ
//    新宽度nh = wsinΘ + hcosΘ
//    新旋转中心x +=( nw/2 - w/2)
//    新旋转中心y +=( nh/2 - h/2))
    Mat src,get;
    int w=change.cols;
    int h=change.rows;
    get=getRotationMatrix2D(Point2f(w/2,h/2),45,1.0);//xy是浮点类型
    double cos=abs(get.at<double>(0,0));
    double sin=abs(get.at<double>(0,1));
    int nw=w*cos+h*sin;
    int nh=w*sin+h*cos;
    get.at<double>(0,2)+=(nw/2-w/2);//这里一定要这样字,不能直接赋值nw/2
    get.at<double>(1,2)+=(nh/2-h/2);

    warpAffine(change,src,get,Size(nw,nh),INTER_NEAREST,0,Scalar(255,255,255));

    imshow("旋转",src);
}
void QuickDemo::shipingcaozuo(cv::Mat &change){
    VideoCapture video("E:\\BaiduNetdiskDownload\\opencv\\sources\\samples\\data\\Megamind.avi");
    if (video.isOpened())
    {
        Mat tupian;
        while (true) {
       video.read(tupian);//读没了返回false并且传入空图像
            if (tupian.empty()) {
                break;
            }
            imshow("shiping",tupian);
            if(waitKey(16)==27){
                break;
            }
        }
    }
    video.release();
}
void QuickDemo::shipingshuxing(cv::Mat &change){
//    VideoWriter (const String &filename, int fourcc, double fps, Size frameSize, bool isColor=true)
//        视频写入对象
//            共5个参数
//                第1个参数 视频文件路径
//                第2个参数 视频编码方式(我们可以通过VideoCapture::get(CAP_PROP_FOURCC)获得)
//                第3个参数 fps
//                第4个参数 size
//                第5个参数 是否为彩色
     VideoCapture video("E:\\BaiduNetdiskDownload\\opencv\\sources\\samples\\data\\Megamind.avi");
     int zhenkuandu=video.get(CAP_PROP_FRAME_WIDTH);
     int zhengaodu=video.get(CAP_PROP_FRAME_HEIGHT);
     int zongzhenshu=video.get(CAP_PROP_FRAME_COUNT);
     int fps=video.get(CAP_PROP_FPS);
     cout <<zhenkuandu<<endl;
     cout <<zhengaodu<<endl;
     cout <<zongzhenshu<<endl;
     cout <<fps<<endl;
     VideoWriter wri("E:\\BaiduNetdiskDownload\\488.avi",video.get(CAP_PROP_FOCUS),fps,Size(zhenkuandu,zhengaodu),true);
     Mat tupian;
     if (video.isOpened())
     {
         while (true) {
             video.read(tupian);
             if (tupian.empty()) {
                 break;
             }
             wri.write(tupian);
             imshow("tup",tupian);
             int c=waitKey(17);
             if (c==27) {
                 break;
             }


         }
     }
     video.release();
     wri.release();
}
void QuickDemo::tupianzhifangtu(cv::Mat &change){
//    calcHist
//        计算一维数组的直方图(输入图像可以有多通道)
//            共10个参数
//                第1个参数 图像数组
//                第2个参数 输入图像数量
//                第3个参数 通道数组(单通道0,多通道可指定通道BGR)
//                第4个参数 可选mask

//                第5个参数 输出直方图数据(值与对应频次)的n维数组
//                第6个参数 直方图维数

//                        当通道为1个时,我们选择维度为1维,此时直方图数据就为一维数组
//                        当维度为2个时,我们选择维度为2维,此时直方图数据就为二维数组
//                        ………………
//                        也就是说,n张图像 每张图像m个通道 也可以计算出相应的直方图数据

//                        但对于绘制来说,一般都只绘制到2维,3维及以上就很复杂了

//                第7个参数 histSize( bins数组,x轴长度,是直方图图像得X轴长度,在最终显示图像中实际是有多少个区间多少个点,最终图像宽度除以区间数量等于区间大小)
//                第8个参数 ranges(取值范围数组)//根据维度,有时候有多个范围,所以这里多个范围时候多个范围数组,再把这些范围数组放到一个储存指针得数组里

//                //以下参数暂时用不到
//                第9个参数 指示直方图bin间隔是否一致
//                            默认为true,即等间隔取值
//                            如果为false,则range不能写{0,255}这种,就要写{1,1,……,1}这种

//                第10个参数 累计标志(默认为false)
//                            当多张图像的时候,
//                                如果为true,则绘制直每张方图的时候,不会从头清空
//                                会在前者直方图的基础上继续
//    cvRound
//        将浮点数四舍五入到最近的整数
//            共1个参数
//                第1个参数 要处理的浮点数
    vector<Mat> fenli;
    split(change,fenli);
    const int channels[]={0};
    int changdu[]={256};
    const float fanwei[]={0,255};
    Mat b_zft,g_zft,r_zft;
    const float* fanweizongjie[]={fanwei};
    calcHist(&fenli[0],1,channels,Mat(),b_zft,1,changdu,fanweizongjie);
    calcHist(&fenli[1],1,channels,Mat(),g_zft,1,changdu,fanweizongjie);
    calcHist(&fenli[2],1,channels,Mat(),r_zft,1,changdu,fanweizongjie);
    int zftw=512;
    int zfth=400;
    int bin_w=cvRound((double)zftw/changdu[0]);//区间,两个点之间得距离横坐标
    Mat xianshitu=Mat::zeros(Size(zftw,zfth),CV_8UC3);
    normalize(b_zft,b_zft,0,xianshitu.rows,NORM_MINMAX,-1,Mat());
    normalize(g_zft,g_zft,0,xianshitu.rows,NORM_MINMAX,-1,Mat());
    normalize(r_zft,r_zft,0,xianshitu.rows,NORM_MINMAX,-1,Mat());
    for (int i{};i<256;i++) {
        Point p11(bin_w*i,zfth-cvRound(b_zft.at<float>(i)));
        Point p12(bin_w*i+bin_w,zfth-cvRound(b_zft.at<float>(i+1)));
        Point p21(bin_w*i,zfth-cvRound(g_zft.at<float>(i)));
        Point p22(bin_w*i+bin_w,zfth-cvRound(g_zft.at<float>(i+1)));
        Point p31(bin_w*i,zfth-cvRound(r_zft.at<float>(i)));
        Point p32(bin_w*i+bin_w,zfth-cvRound(r_zft.at<float>(i+1)));
        line(xianshitu,p11,p12,Scalar(255,0,0),1,LINE_AA,0);
        line(xianshitu,p21,p22,Scalar(0,255,0),1,LINE_AA,0);
        line(xianshitu,p31,p32,Scalar(0,0,255),1,LINE_AA,0);
    }
    imshow("直方图",xianshitu);


}
void QuickDemo::tupianzhifangtuerwei(cv::Mat &change){
    Mat zhifangtu,hsv,jieguo;
    cvtColor(change,hsv,COLOR_BGR2HSV);
    int h_bin=30;
    int s_bin=32;
    int arr_bin[]={h_bin,s_bin};
    float h_fanwei[]={0,180};//范围是float
    float s_fanwei[]={0,255};
    const float* range[]={h_fanwei,s_fanwei};//必须是const
    int channels[]={0,1};
    calcHist(&hsv,1,channels,Mat(),zhifangtu,2,arr_bin,range);
    double max{};
    minMaxLoc(zhifangtu,0,&max,0,0);
    int diejialiang{10};
    jieguo=Mat::zeros(Size(s_bin*diejialiang,h_bin*diejialiang),CV_8UC3);
    for (int i=0;i<zhifangtu.rows;i++)
    {
        for (int j=0;j<zhifangtu.cols;j++) {
            int pingci=zhifangtu.at<float>(i,j);
            int guiyizhi=cvRound(pingci/max*255);
            Point p1(i*diejialiang,j*diejialiang);
            Point p2((i+1)*diejialiang,(j+1)*diejialiang);
            rectangle(jieguo,p1,p2,Scalar::all(guiyizhi),-1,LINE_AA);

        }
    }
    applyColorMap(jieguo,jieguo,COLORMAP_DEEPGREEN);
    imshow("zhifangtu",jieguo);
}
void QuickDemo::zhifangtujunheng(cv::Mat &change){
//    equalizeHist
//        均衡灰度图像的直方图
//            共2个参数
//                第1个参数 输入
//                第2个参数 输出

//    Mat huidu;
//    cvtColor(change,huidu,COLOR_BGR2GRAY);
//    Mat jieguo;
//    equalizeHist(huidu,jieguo);
//    imshow("huidu",huidu);
//    imshow("jieguo",jieguo);//灰度图像均衡
    //彩色图像均衡
        Mat hsv,jieguo;
        cvtColor(change,hsv,COLOR_BGR2HSV);
        vector<Mat> fenli;
        split(hsv,fenli);
        equalizeHist(fenli[2],fenli[2]);
        merge(fenli,jieguo);
        cvtColor(jieguo,jieguo,COLOR_HSV2BGR);
        imshow("zftjh",jieguo);

}
void QuickDemo::tuxiangjuanji(cv::Mat &change){
//    blur
//        使用归一化框滤镜模糊图像
//            共5个参数
//                第1个参数 输入
//                第2个参数 输出
//                第3个参数 卷积核size(卷积核越大,图像模糊程度越高)
//                第4个参数 锚点
//                        (默认值Point(-1,-1)表示锚点位于卷积后的内核中心,卷积后的值在这里更新)
//                第5个参数 图像边缘处理方式
//                        (默认参数 BORDER_DEFAULT 边缘有很多处理方式,查阅官方文档可知)
    Mat jieguo;
    blur(change,jieguo,Size(13,13));
//    blur(change,jieguo,Siz e(1,13));//列卷积
//    blur(change,jieguo,Size(13,1));//行卷积
    imshow("jieguo",jieguo);

}
void QuickDemo::gaosimohu(cv::Mat &change){
//    GaussianBlur
//        使用高斯滤镜模糊图像
//            共6个参数
//                第1个参数 输入
//                第2个参数 输出
//                第3个参数 高斯卷积核size(必须是整数和奇数,可以是0,然后根据sigma计算他们)

//                第4个参数 x方向的高斯核标准差
//                第5个参数 y方向的高斯核标准差(如果sigmaY = 0,则设置其 = sigmaX)
//                        (如果两个标准差都为0,则根据高斯卷积核size计算)
//                        (官方文档建议:三个参数最好都指定)

//                         虽然size和sigma都可以实现模糊,但sigma的影响更大

//                第6个参数 图像边缘处理方式
    Mat jieguo;
    GaussianBlur(change,jieguo,Size(5,5),5,5);
    imshow("jieguo",jieguo);

}
void QuickDemo::gaosimohushuangbian(cv::Mat &change){
//    bilateralFilter  通常情况下sigmaColor取100 ~ 150,sigmaSpace取10 ~ 25
//        将双边过滤器应用于图像
//            共6个参数
//                第1个参数 输入
//                第2个参数 输出
//                第3个参数 过滤期间使用的每个像素邻域的直径(如为非正数,则根据sigmaSpac计算)一般为0

//                第4个参数 sigmaColor(在颜色空间中的过滤标准差)

//                            sigmaColor一般取值大一点,
//                            大一点的话根据二维高斯函数计算所得的值越小,越趋近于0,影响越低

//                第5个参数 sigmaSpace(在坐标空间中的过滤标准差)

//                第6个参数  图像边缘处理方式
    Mat jieguo;
    bilateralFilter(change,jieguo,0,100,10);
//    namedWindow("jieguo",WINDOW_FREERATIO);
    imshow("jieguo",jieguo);

}
void QuickDemo::renlianshibie(cv::Mat &change){
    string path="E:\\BaiduNetdiskDownload\\opencv\\sources\\samples\\dnn\\face_detector\\";
    Net net=readNetFromTensorflow(path+"opencv_face_detector_uint8.pb",path+"opencv_face_detector.pbtxt");//读取模型进来
    VideoCapture video("E:\\BaiduNetdiskDownload\\opencv\\sources\\samples\\data\\Megamind.avi");
    Mat tupian;
    if (video.isOpened())
    {
        while (true) {
            video.read(tupian);
            if (tupian.empty()) {
                break;
            }
            Mat bandian=blobFromImage(tupian,1.0,Size(300, 300),Scalar(104, 177, 123),false,false);
            net.setInput(bandian);
            Mat huoqu=net.forward();
            Mat dectectionMat(huoqu.size[2],huoqu.size[3],CV_32F,huoqu.ptr<float>());
            for (int i{0};i<dectectionMat.rows;++i) {
                double zhi=dectectionMat.at<float>(i,2);
                if (zhi>0.5) {
                    int x1=static_cast<int>(dectectionMat.at<float>(i,3)*tupian.cols);
                    int y1=static_cast<int>(dectectionMat.at<float>(i,4)*tupian.rows);
                    int x2=static_cast<int>(dectectionMat.at<float>(i,5)*tupian.cols);
                    int y2=static_cast<int>(dectectionMat.at<float>(i,6)*tupian.rows);
                    rectangle(tupian,Point(x1,y1),Point(x2,y2),Scalar(0,0,255),1,LINE_AA,0);
                }
            }
            imshow("tup",tupian);
            int c=waitKey(17);
            if (c==27) {
                break;
            }


        }
    }
    video.release();
}
void QuickDemo::wenzihuizhi(cv::Mat &change){
//    putText
//        共9个参数
//            第1个参数 输入输出
//            第2个参数 字符串
//            第3个参数 字符串左下角的点
//            第4个参数 字体类型
//            第5个参数 字体大小
//            第6个参数 字体颜色
//            第7个参数 线宽
//            第8个参数 lineType

//            第9个参数 默认false,图像数据原点位于左上角
//                      如果true,图像数据原点位于左下角
        putText(change,"kkpp",Point(200,200),FONT_HERSHEY_COMPLEX,2.0,Scalar(45,18,77),2,LINE_AA);
        namedWindow("jieguo",WINDOW_FREERATIO);
        imshow("jieguo",change);
}
void QuickDemo::hezimohu(cv::Mat &change){
//    boxfilter
//        使用方框滤镜模糊图像
//            共7个参数
//                第1个参数 输入
//                第2个参数 输出
//                第3个参数 输出图像深度(-1表示使用输入图像的深度)
//                第4个参数 ksize(模糊内核大小)
//                第5个参数 锚点(默认Point(-1,-1)表示锚点位于内核中心)
//                第6个参数 是否归一化(默认为true)
//               第7个参数 borderType
    Mat jieguo;
    boxFilter(change,jieguo,-1,Size(30,30));//Size越大越模糊
    namedWindow("jieguo",WINDOW_FREERATIO);
    imshow("jieguo",jieguo);
}
void QuickDemo::zidingyilvbo(cv::Mat &change){
//    filter2D
//        将图像与内核卷积
//            共7个参数
//                第1个参数 输入
//                第2个参数 输出
//                第3个参数 输出图像的深度(-1表示和输入图像一直)
//                第4个参数 卷积核
//                第5个参数 锚点
//                        (指示内核中过滤点的相对位置;
//                          锚点应位于内核中;
//                          默认值Point(-1,-1),表示锚点位于内核中心)
//                第6个参数 delta变量(可以调整亮度)
//                第7个参数 borderType

    //均值滤波
//    int k=15;
//    Mat shuchu=Mat::ones(k,k,CV_32F)/(float)(k*k);//类似于归一化,都成小数,后面imshow得时候会把小数*255
//    Mat src;
//    filter2D(change,src,-1,shuchu);


    //自定滤波
    Mat juanji=(Mat_<int>(2,2)<<0,1,8,5);
    Mat src;
    filter2D(change,src,CV_32F,juanji,Point(-1,-1));
//    convertScaleAbs(src,src);
    namedWindow("src",WINDOW_FREERATIO);
    imshow("src",src);

}
void QuickDemo::tuxiangtidu(cv::Mat &change){
    //梯度,卷积得一种,图片得轮廓,色差值大得地方
    //robot算子x:1,0,0,-1 y:0,1,-1,0(轮廓不明显)
    //CV_32F
    Mat jx=(Mat_<int>(2,2)<<1,0,0,-1);
    Mat jy=(Mat_<int>(2,2)<<0,1,-1,0);
    Mat sx,sy;
    filter2D(change,sx,CV_32F,jx,Point(-1,-1),0);
    filter2D(change,sy,CV_32F,jy,Point(-1,-1),0);
    convertScaleAbs(sx,sx);
    convertScaleAbs(sy,sy);
    Mat reslult;
    add(sx,sy,reslult);
    imshow("jieguo",reslult);

    //Sobel算子:1:输入,2.输出3.深度,4.x方向 5.y方向6.ksize越大越明显
    Sobel(change,sx,CV_32F,1,0);
    Sobel(change,sy,CV_32F,0,1);
    convertScaleAbs(sx,sx);
    convertScaleAbs(sy,sy);
    add(sx,sy,reslult);
    imshow("Sobel",reslult);

    //Scharr算子:1:输入,2.输出3.深度,4.x方向 5.y方向
    Scharr(change,sx,CV_32F,1,0);
    Scharr(change,sy,CV_32F,0,1);
    convertScaleAbs(sx,sx);
    convertScaleAbs(sy,sy);
    add(sx,sy,reslult);
    imshow("Scharr",reslult);

}
void QuickDemo::tuxiangbianyuanfaxian(cv::Mat &change){
//    拉普拉斯算子,二阶导数算子,提取图像轮廓,噪声对拉普拉斯影响较大
//    不用convertScaleAbs 深度用原图-1

    Mat src;
    Laplacian(change,src,-1,1);

    imshow("lapulasi",src);
    //图片锐化 算子:0,-1,0
    //            -1,5,-1
    //             0,-1,0
    Mat ruihua=(Mat_<int>(3,3)<<0,-1,0,-1,5,-1,0,-1,0);
    filter2D(change,src,CV_32F,ruihua,Point(-1,-1));
    convertScaleAbs(src,src);
    imshow("ruihua",src);


}
void QuickDemo::ruihua(cv::Mat &change){
//    gaosi模糊-lapulasi
//    lapu 得权重越大越锐化
    Mat gaosi,lapu;
    GaussianBlur(change,gaosi,Size(3,3),0);
    GaussianBlur(change,change,Size(3,3),0);//高斯降噪锐化效果好
    Laplacian(change,lapu,-1,1);
    imshow("Laplacian",lapu);
    Mat result;
    addWeighted(gaosi,1.0,lapu,-2.0,0,result);
//    namedWindow("ruihua",WINDOW_FREERATIO);
    imshow("ruihua",result);
}
void QuickDemo::tuxiangzaosheng(cv::Mat &change){
    //椒盐噪声
    RNG rng(12345);
    Mat yangcong=change.clone();
    int num=100000;
    for (int i=0;i<num;++i) {
        int x=rng.uniform(0,yangcong.rows);
        int y=rng.uniform(0,yangcong.cols);
        if (i%2==0) {
            yangcong.at<Vec3b>(x,y)=Vec3b(255,255,255);
        }
        else {
            yangcong.at<Vec3b>(x,y)=Vec3b(0,0,0);
        }
    }
    imshow("yangcong",yangcong);
    //高斯噪声
    Mat gaosi=Mat::zeros(change.size(),change.type());
    //参数,均值(亮度)和方差(噪声程度)
    randn(gaosi,Scalar(10,10,10),Scalar(100,100,100));
    add(change,gaosi,gaosi);
    imshow("gaosi",gaosi);
    
}
void QuickDemo::quzaosheng(cv::Mat &change){
    RNG rng(12345);
    Mat yangcong=change.clone();
    int num=100000;
    for (int i=0;i<num;++i) {
        int x=rng.uniform(0,yangcong.rows);
        int y=rng.uniform(0,yangcong.cols);
        if (i%2==0) {
            yangcong.at<Vec3b>(x,y)=Vec3b(255,255,255);
        }
        else {
            yangcong.at<Vec3b>(x,y)=Vec3b(0,0,0);
        }
    }
    imshow("yangcong",yangcong);
    //中值滤波对椒盐噪声比较有效,原理抽取一块像素区,将里面像素值排列,取中间值
//    中值滤波函数-medianBlur
//    -参数InputArray表示输入图像Mat对象
//    -参数OutputArray表示模糊之后输出Mat对象
//    -参数ksize表示卷积核大小,必须是正数而且必须是大于1,如:3、5、7等。
    medianBlur(yangcong,yangcong,3);
    imshow("去噪声",yangcong);
}
void QuickDemo::quzaoshenggs(cv::Mat &change){
    //高斯噪声
    Mat gaosi=Mat::zeros(change.size(),change.type());
    //参数,均值(亮度)和方差(噪声程度)
    randn(gaosi,Scalar(10,10,10),Scalar(50,50,50));
    add(change,gaosi,gaosi);
    imshow("噪声图像",gaosi);
    //高斯双边去噪声、
    Mat reault;
    bilateralFilter(gaosi,reault,0,100,10);
    imshow("高斯双边去噪声",reault);

    //非局部均值滤波(对小噪声比较有效)
    //7是卷积核大小,21是局部范围大小 h越大越模糊,最后一个参数一般是倒数第二得3到5倍
    Mat fjbjzlb;
    fastNlMeansDenoisingColored(gaosi,fjbjzlb,3,3,7,21);
    imshow("非局部均值滤波",fjbjzlb);
    cvtColor(gaosi,gaosi,COLOR_BGR2GRAY);
    fastNlMeansDenoising(gaosi,fjbjzlb);
    imshow("黑白非局部均值滤波",fjbjzlb);

}
void on_candy(int t1,void* tupian){
    Mat src=*((Mat*)tupian);
    //低于低阈值得像素点去除,高于高阈值得像素点保存
    //1.输入 2.输出 3.t1低阈值 4.t2高阈值(为低阈值得2——3倍) 5.用默认值表示 Sobel 算子的孔径大小 6.默认用L1,快一点 为false
    Mat result;
    Canny(src,result,t1,t1*3,3,false);
//    bitwise_and(src,src,dit,result);
    namedWindow("结果",WINDOW_FREERATIO);
    imshow("结果",result);

}
void QuickDemo::bianyuantiqu(cv::Mat &change){
    int chushizhi=50;
    int max=100;
    createTrackbar("边缘程度:","原始",&chushizhi,max,on_candy,(void*)&change);
}

void QuickDemo::erzhituxiang(cv::Mat &change){
    //二值图像就是像素点为0或者255得图像
    //二值分割有5种方法(手动阈值)
//    1、THRESH_BINARY:将大于阈值的设为255,小于阈值的设为0
//    2、THRESH_BINARY_INV:将大于阈值的设为0,小于阈值的设为255
//    3、THRESH_TOZERO:将小于阈值的设为0,大于阈值的保留
//    4、THRESH_TOZERO_INV:将大于阈值的设为0,小于阈值的保留
//    5、THRESH_TRUNC:将大于阈值的值设置为阈值得值,小于阈值的保留
    Mat gray,src;
    cvtColor(change,gray,COLOR_BGR2GRAY);
    //输入 输出 阈值 最大值 分割方法(要用灰度图像)
    threshold(gray,src,171,255,THRESH_BINARY);
    imshow("THRESH_BINARY",src);
    threshold(gray,src,171,255,THRESH_BINARY_INV);
    imshow("THRESH_BINARY_INV",src);
    threshold(gray,src,171,255,THRESH_TOZERO);
    imshow("THRESH_TOZERO",src);
    threshold(gray,src,171,255,THRESH_TOZERO_INV);
    imshow("THRESH_TOZERO_INV",src);
    threshold(gray,src,171,255,THRESH_TRUNC);
    imshow("THRESH_TRUNC",src);

}
void QuickDemo::erzhituxiangquanjuyuzhi(cv::Mat &change){
//    全局分割方法:1.均值法
//                2.直方图法(最优法)THRESH_OTSU
//                3.三角法(对单峰直方图效果好)THRESH_TRIANGLE(2,3自动阈值)
    Mat gray,src;
    cvtColor(change,gray,COLOR_BGR2GRAY);
    Scalar m=mean(gray);//平均值
    threshold(gray,src,m[0],255,THRESH_BINARY);
    imshow("均值法",src);
    threshold(gray,src,0,255,THRESH_OTSU);
    imshow("THRESH_OTSU",src);
    threshold(gray,src,0,255,THRESH_TRIANGLE);
    imshow("THRESH_TRIANGLE",src);
//     cout <<m[0]<<"\t"<<zft<<"\t"<<sjx<<endl;
}
void QuickDemo::zishiyingyuzhi(cv::Mat &change){
//    用到的阈值是自适应的,而不是整张图共用一个固定值。当同一幅图像上的不同部分的具有不同亮度时。这种情况下我们需要采用自适应阈值。
//    此时的阈值是根据图像上的每一个小区域计算与其对应的阈值。因此在同一幅图像上的不同区域采用的是不同的阈值,从而使我们能在亮度不同的情况下得到更好的结果。
//    adaptiveThreshold
//    第一个参数(img):源图像
//    th2:输出图像,与源图像大小一致
//    第三个参数(255):超过阈值的部分取值是多少(对于cv.THRESH_BINARY而言)
//    第四个参数(cv.ADAPTIVE_THRESH_MEAN_C):
//    (1)在一个邻域内计算阈值所采用的算法,有两个取值,分别为 ADAPTIVE_THRESH_MEAN_C 和 ADAPTIVE_THRESH_GAUSSIAN_C
//    (2)ADAPTIVE_THRESH_MEAN_C的计算方法是计算出领域的平均值再减去第七个参数2的值。
//    (3)ADAPTIVE_THRESH_GAUSSIAN_C的计算方法是计算出领域的高斯均值再减去第七个参数2的值
//    第五个参数(cv.THRESH_BINARY):这是阈值类型,只有两个取值,分别为 THRESH_BINARY 和THRESH_BINARY_INV
//    第六个参数(11):adaptiveThreshold的计算单位是像素的邻域块大小选择,这是局部邻域大小,3、5、7等(越大白纹理越多)
//    第七个参数(2):这个参数实际上是一个偏移值调整量,用均值和高斯计算阈值后,再减或加这个值就是最终阈值。
     Mat gray,src;
     cvtColor(change,gray,COLOR_BGR2GRAY);
     adaptiveThreshold(gray,src,255,ADAPTIVE_THRESH_MEAN_C,THRESH_BINARY,3,-5);
     imshow("ADAPTIVE_THRESH_MEAN_C",src);
     adaptiveThreshold(gray,src,255,ADAPTIVE_THRESH_GAUSSIAN_C,THRESH_BINARY,3,-5);
     imshow("ADAPTIVE_THRESH_GAUSSIAN_C",src);
}
void QuickDemo::tiquqianjing(cv::Mat &change){
//    函数的返回值是标签总数(包括背景),输出的是标记图像,所谓标记图像就是图像中每一个连通域(前景)都拥有一个标签(背景的标签是0),该标签即代表了此连通域的灰度值。
//    例如,一幅图像中存在3个连通域(背景除外),那么他们的标签分别是1、2、3,他们的灰度值分别是1、2、3。
//    connectedComponents(
//            InputArray    image,             // 输入二值图像
//            OutputArray   labels,            // 输出的标记图像,背景index=0
//            int           connectivity = 8,  // 连通域,默认是8连通
//            int           ltype = CV_32S     // 输出的labels类型,默认是CV_32S
    GaussianBlur(change,change,Size(3,3),0);//高斯降噪
    //转为二值图像
    Mat gray,src,qianjingtu;
    cvtColor(change,gray,COLOR_RGB2GRAY);
    threshold(gray,src,0,255,THRESH_BINARY|THRESH_OTSU);
    imshow("二值",src);
    int qianjing=connectedComponents(src,qianjingtu,8,CV_32S,CCL_DEFAULT);
    vector<Vec3b> qianjingheji(qianjing);
    qianjingheji[0]=Vec3b(0,0,0);
    RNG rng(12345);
    for (int i=1;i<qianjing;++i) {
        qianjingheji[i]=Vec3b(rng.uniform(0,256),rng.uniform(0,256),rng.uniform(0,256));
    }
    Mat result=Mat::zeros(change.size(),change.type());
    for (int i=0;i<result.rows;++i) {
        for (int j=0;j<result.cols;++j) {
            int huiduzhi=qianjingtu.at<int>(i,j);
            result.at<Vec3b>(i,j)=qianjingheji[huiduzhi];
        }
    }

    putText(result,format("number:%d",qianjing),Point(50,50),FONT_HERSHEY_COMPLEX,1.0,Scalar(255,0,255),2,LINE_AA);
    imshow("result",result);


}
void QuickDemo::tiquqianjingdaitongjixinxi(cv::Mat &change){
//    connectedComponentsWithStats
//            InputArray   image,        // 输入二值图像
//            OutputArray  labels,       // 输出的标记图像,背景index=0
//            OutputArray  stats,        // 统计信息,包括每个组件的位置、宽、高与面积//像素值为int
//            OutputArray  centroids,    // 每个组件的中心位置坐标cx, cy//像素值为double
//            int          connectivity, // 连通域,默认是8连通
//            int          ltype,        // 输出的labels类型,默认是CV_32S
//            int          ccltype       // 连通组件算法
//    其中stats包括以下枚举类型数据信息:
//       CC_STAT_LEFT   组件的左上角点像素点坐标的X位置
//       CC_STAT_TOP    组件的左上角点像素点坐标的Y位置
//       CC_STAT_WIDTH  组件外接矩形的宽度
//       CC_STAT_HEIGHT 组件外接矩形的高度
//       CC_STAT_AREA   当前连通组件的面积(像素单位)

    //stats centroids得每一行相当于行数是连通域得值
    GaussianBlur(change,change,Size(3,3),0);//高斯降噪
    //转为二值图像
    Mat gray,src,qianjingtu,stats,centroids;
    cvtColor(change,gray,COLOR_RGB2GRAY);
    threshold(gray,src,0,255,THRESH_BINARY|THRESH_OTSU);
    imshow("二值",src);
    int qianjing=connectedComponentsWithStats(src,qianjingtu,stats,centroids,8,CV_32S,CCL_DEFAULT);
    vector<Vec3b> qianjingheji(qianjing);
    qianjingheji[0]=Vec3b(0,0,0);
    RNG rng(12345);
    for (int i=1;i<qianjing;++i) {
        qianjingheji[i]=Vec3b(rng.uniform(0,256),rng.uniform(0,256),rng.uniform(0,256));
    }
    Mat result=Mat::zeros(change.size(),change.type());
    for (int i=0;i<result.rows;++i) {
        for (int j=0;j<result.cols;++j) {
            int huiduzhi=qianjingtu.at<int>(i,j);
            result.at<Vec3b>(i,j)=qianjingheji[huiduzhi];
        }
    }
    for (int i=1;i<qianjing;++i) {
        //第一行表示连通域值为1得连通域得中心,第一列为x,第二列为y
        int px=centroids.at<double>(i,0);
        int py=centroids.at<double>(i,1);
        int zsjx=stats.at<int>(i,CC_STAT_LEFT);
        int zsjy=stats.at<int>(i,CC_STAT_TOP);
        int w=stats.at<int>(i,CC_STAT_WIDTH);
        int h=stats.at<int>(i,CC_STAT_HEIGHT);
        int area=stats.at<int>(i,CC_STAT_AREA);
        circle(result,Point(px,py),2,Scalar(0,0,255),1,LINE_AA);
        rectangle(result,Rect(zsjx,zsjy,w,h),Scalar(0,0,255),1,LINE_AA);
        putText(result,format("%d",area),Point(zsjx,zsjy),FONT_HERSHEY_COMPLEX,0.5,Scalar(255,0,255),1,LINE_AA);

    }
     imshow("result",result);
}
void QuickDemo::lunkuofaxian(cv::Mat &change){
//   findContours(
//    InputArray          image,
//    OutputArrayOfArrays contours,
//    OutputArray         hierarchy,
//    int                 mode,
//    int                 method,
//    Point               offset = Point()
//                      )
//参数一: image,输入图像、八位单通道的,背景为黑色的二值图像。(一般是经过Canny、拉普拉斯等边缘检测算子处理过的二值图像)
//参数二:contours,输出轮廓图像。是一个向量,向量的每个元素都是一个轮廓。因此,这个向量的每个元素仍是一个向量。即:
//           vector<vector<Point> > contours;
//参数三:hierarchy,输出各个轮廓的继承关系。hierarchy也是一个向量,长度和contours相等,每个元素和contours的元素对应。
//      hierarchy的每个元素是一个包含四个整型数的向量。即:分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,则该值为负数。
//           vector<Vec4i> hierarchy;
//参数四:mode,检测轮廓的方法。有四种方法:
//    RETR_EXTERNAL:只检测外轮廓。忽略轮廓内部的洞。
//    RETR_LIST:检测所有轮廓,但不建立继承(包含)关系。
//    RETR_TREE:检测所有轮廓,并且建立所有的继承(包含)关系。
//    RETR_CCOMP:检测所有轮廓,但是仅仅建立两层包含关系。
//参数五:method,每个轮廓的编码信息。也有四种(常用前两种)
//    CHAIN_APPROX_NONE:把轮廓上所有的点存储。
//    CHAIN_APPROX_SIMPLE:只存储轮廓上的拐点。
//    CHAIN_APPROX_TC89_L1,CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法

//    drawContours(
//          InputOutputArray  binImg, // 输出图像
//          OutputArrayOfArrays  contours,//  全部发现的轮廓对象
//          Int contourIdx// 轮廓索引号,-1表示绘制所有轮廓
//          const Scalar & color,// 绘制时候颜色
//          int  thickness,// 绘制线宽,-1表示填充轮廓内部
//          int  lineType,// 线的类型LINE_8
//          InputArray hierarchy,// 拓扑结构图
//          int maxlevel,// 最大层数,0只绘制当前的,1表示绘制绘制当前和他的子轮廓 2再画一个子轮廓
//          Point offset = Point()// 轮廓位移,可选
//                )
    Mat gray,src;
    GaussianBlur(change,change,Size(3,3),0);
    cvtColor(change,gray,COLOR_BGR2GRAY);
    threshold(gray,src,0,255,THRESH_BINARY_INV|THRESH_OTSU);
    imshow("THRESH_OTSU",src);
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
//    Mat cc=Mat::zeros(change.size(),change.type());
    findContours(src,contours,hierarchy,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);
    drawContours(change,contours,-1,Scalar(0,0,255),3,LINE_AA/*,hierarchy,1*/);
    imshow("jieguo",change);

}
void QuickDemo::lunkuojisuan(cv::Mat &change){
    Mat gray,src;
    GaussianBlur(change,change,Size(3,3),0);
    cvtColor(change,gray,COLOR_BGR2GRAY);
    threshold(gray,src,0,255,THRESH_BINARY|THRESH_OTSU);
    imshow("THRESH_OTSU",src);
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
     findContours(src,contours,hierarchy,RETR_TREE,CHAIN_APPROX_SIMPLE);
     for (vector<vector<Point>>::size_type i{};i<contours.size();++i) {
         //轮廓面积
         double mianji=contourArea(contours[i]);
//         轮廓周长
         double zhouchang=arcLength(contours[i],true);//第二个参数是否是闭合区域
         cout <<"面积:"<<setw(8)<<setiosflags(ios::left)<<mianji<<"\t"<<"周长:"<<zhouchang<<endl;
//         最大矩形轮廓
//         Rect maxbox=boundingRect(contours[i]);
//         rectangle(change,maxbox,Scalar(0,0,255),2,LINE_AA);

//         RotatedRect 旋转矩形
//                 矩形中心点(质心)
//                 边长(长和宽)
//                 旋转角度
//         //最小矩形轮廓
         RotatedRect minbox=minAreaRect(contours[i]);
         //用椭圆画出来最小矩形轮廓
//         ellipse(change,minbox,Scalar(0,0,255),2,LINE_AA);

         //用矩形画出来最小矩形轮廓
         Point2f arr[4];//创建一个储存point得数组
         minbox.points(arr);//将矩形轮廓得四个点输入到数组
         for (size_t t{};t<4;++t) {
             line(change,arr[t],arr[(t+1)%4],Scalar(0,0,255),2,LINE_AA);
         }//%4保证不超过4

         imshow("maxbox",change);
     }

}
vector<vector<Point>> geshizhengli(cv::Mat &change){
    Mat gray,src;
    GaussianBlur(change,change,Size(3,3),0);
    cvtColor(change,gray,COLOR_BGR2GRAY);
    threshold(gray,change,0,255,THRESH_BINARY|THRESH_OTSU);
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(change,contours,hierarchy,RETR_EXTERNAL,CHAIN_APPROX_NONE,Point());
    return contours;
}
void QuickDemo::lunkuopipei(cv::Mat &change){
   Mat change2=imread("E:\\BaiduNetdiskDownload\\1.png",IMREAD_ANYCOLOR);
   auto contours=geshizhengli(change);
   auto contours2=geshizhengli(change2);
   imshow("1",change);
   imshow("2",change2);
   //几何矩计算//几何矩可以用来计算hu矩,也可以求轮廓中心点
   Moments jh2=moments(contours2[0]);
   //hu矩计算
   Mat hu2;
   HuMoments(jh2,hu2);
   for (vector<vector<Point>>::size_type i{};i<contours.size();++i) {
       Moments jh=moments(contours[i]);
       Mat hu;
       HuMoments(jh,hu);
       //计算两个轮廓hu矩的差值,越小匹配度越高,可以直接两个轮廓比,但效果没有两个hu矩好
       double chazhi=matchShapes(hu2,hu,CONTOURS_MATCH_I1,0);
       if (chazhi<1.0) {
           cvtColor(change,change,COLOR_GRAY2BGR);
           drawContours(change,contours,i,Scalar(0,0,255),-1,LINE_AA);

       }
       //轮廓中心点计算
       double yx=jh.m10/jh.m00;//x坐标位置
       double yy=jh.m01/jh.m00;//y坐标位置
       circle(change,Point(yx,yy),2,Scalar(55,88,99),1,LINE_AA);
   }

   imshow("jieguo",change);
}
void QuickDemo::lunkuobijinyunihe(cv::Mat &change){
//    approxPolyDP(InputArray curve,OutputArray approxCurve,double epsilon,bool closed)//算轮廓顶点
//    Curve:表示轮廓曲线,通常可以是findContours()中参数Contours里的元素,即一个由点集组成的轮廓。
//    approxCurve: 可以设为vector<Point>保存轮廓逼近输出的折点。
//    epsilon: 轮廓逼近的顶点距离真实轮廓曲线的最大距离,该值越小表示越逼近真实轮廓。
//    close: 表示是否为闭合区域。
    Mat jieguo=change.clone();
    auto contours=geshizhengli(change);
    imshow("geshi",change);
    vector<vector<Point>> approxCurve(contours.size());
    for (vector<vector<Point>>::size_type i=0;i<contours.size();++i)
    {
        approxPolyDP(contours[i],approxCurve[i],4,true);
        for (int j=0;j<approxCurve[i].size();++j) {
            circle(jieguo,approxCurve[i][j],2,Scalar(0,0,255),1,LINE_AA);
        }
        //轮廓中心点计算
        Moments jh=moments(contours[i]);
        double yx=jh.m10/jh.m00;//x坐标位置
        double yy=jh.m01/jh.m00;//y坐标位置
        circle(change,Point(yx,yy),2,Scalar(55,88,99),1,LINE_AA);
//        if (approxCurve[i].size()==3) {
//            putText(jieguo,"3",Point(yx,yy),FONT_HERSHEY_COMPLEX,2,Scalar(88,88,88),1);
//        }
//        if (approxCurve[i].size()==4) {
//            putText(jieguo,"4",Point(yx,yy),FONT_HERSHEY_COMPLEX,2,Scalar(88,88,88),1);
//        }
//        if (approxCurve[i].size()>10) {
//            putText(jieguo,"yuan",Point(yx,yy),FONT_HERSHEY_COMPLEX,2,Scalar(88,88,88),1);
//        }

    }
    imshow("jieguo",jieguo);
}
void QuickDemo::nihe(cv::Mat &change){
    //最小外接圆拟合
//    void minEnclosingCircle( InputArray points,Point2f& center,
//                     float& radius );
//        1points:由点集组成的轮廓。
//        2center:输出圆心坐标。
//        3radius:输出圆的半径。
    Mat waijie=change.clone();
    Mat neijie=change.clone();
    auto contours=geshizhengli(change);
    imshow("geshi",change);

    for (vector<vector<Point>>::size_type i=0;i<contours.size();++i)
    {  Point2f yuanxin;
        float banjing{};
       minEnclosingCircle(contours[i],yuanxin,banjing);
       circle(waijie,yuanxin,banjing,Scalar(0,0,245),2,LINE_AA);
    }

    imshow("waijie",waijie);

//    //轮廓最大内接圆
//    double pointPolygonTest( InputArray contour, Point2f pt,
//                            bool measureDist );
//  该函数可以准确计算出一个点距离某个轮廓的距离,如果该点在轮廓上,返回的距离就是0;如果是在外部或者内部,则分别返回负数和正数表示距离。它的参数解释如下:
//    contour:点集组成的轮廓。
//    pt:要判断的点。
//    measureDist:设为True时返回实际点到轮廓的距离,设为False返回0(表示点在轮廓上)或者1(表示点在轮廓外)或者-1(表示点在轮廓内)三者中的一个。

    for (vector<vector<Point>>::size_type x=0;x<contours.size();++x)
    {
        Mat julichucun(change.size(),CV_32F);//用图像灰度值储存各个像素点到轮廓得最短距离,再算出距离最长得像素值就是圆的半径,该点就是圆心
        for (int i=0;i<change.rows;++i)
        {
            for (int j=0;j<change.cols;++j)
            {
             julichucun.at<float>(i,j)=(float)pointPolygonTest(contours[x],Point2f((float)j,(float)i),true);//点得x对应图像得列也就是宽度
            }
        }
        double max{};
        double min{};
        Point maxp,minp;
        minMaxLoc(julichucun,&min,&max,&minp,&maxp);
        circle(neijie,maxp,max,Scalar(255,255,255),2,LINE_AA);
    }
    imshow("neijie",neijie);

}
void QuickDemo::tuoyuannihe(cv::Mat &change){
    //椭圆拟合
    Mat caise=change.clone();
    auto contours=geshizhengli(change);
    imshow("2z",change);
    for (vector<vector<Point>>::size_type x=0;x<contours.size();++x) {
        if (contours[x].size()>5) {
            drawContours(caise,contours,x,Scalar(0,0,255),2,LINE_AA);
            RotatedRect xzjx=fitEllipse(contours[x]);//fitEllipse函数要求轮廓至少要有5个点
           ellipse(caise,xzjx,Scalar(99,99,99),2,LINE_AA);

        }

    }
    imshow("caise",caise);
}
void QuickDemo::huofuzhixian(cv::Mat &change){
//    void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0 )
//    参数:
//    image:边缘检测的输出图像. 它应该是个灰度图 (但事实上是个二值化图)
//    lines:储存着检测到的直线的参数对 的容器  // vector<Vec3f>类型,分别是距离角度累加值
//    rho:参数极径 以像素值为单位的分辨率. 我们使用 1 像素.
//    theta:参数极角 以弧度为单位的分辨率. 我们使用 1度 (即CV_PI/180)
//    theta:要”检测” 一条直线所需最少的的曲线交点,直线交点得阈值,也就是vec3f得最后一个值,累加值,越大直线越少
//    srn and stn: 参数默认为0.
    Mat xian=change.clone(),gray;
    cvtColor(change,gray,COLOR_BGR2GRAY);
    threshold(gray,change,0,255,THRESH_BINARY|THRESH_OTSU);
    imshow("erzhi",change);
    vector<Vec3f> lines;
    HoughLines(change,lines,1,CV_PI/180.0,150);
    Point p1,p2;//画直线得两个顶点
    for (vector<Vec3f>::size_type i{};i<lines.size();++i) {
        float r=lines[i][0];
        float hudu=lines[i][1];
        float leijiazhi=lines[i][2];
        cout <<"1:"<<r<<",2:"<<hudu<<",3:"<<leijiazhi<<endl;
        double c=cos(hudu);
        double s=sin(hudu);
        double x0=r*c,y0=r*s;
        p1.x=cvRound(x0+1000*(-s));
        p1.y=cvRound(y0+1000*c);
        p2.x=cvRound(x0-1000*(-s));
        p2.y=cvRound(y0-1000*c);
        int jiaodu=cvRound((hudu/CV_PI)*180);
        cout <<jiaodu<<endl;
        if (r>0) {
            line(xian,p1,p2,Scalar(0,0,255),2,LINE_AA);
            if (jiaodu==90) {//水平线
            line(xian,p1,p2,Scalar(25,18,25),2,LINE_AA);

            }
            if(jiaodu<=1){//垂直线
                line(xian,p1,p2,Scalar(0,255,0),2,LINE_AA);

            }
        }else {//长度为负得数是往左边倾斜得
            line(xian,p1,p2,Scalar(255,0,0),2,LINE_AA);
        }

    }
    imshow("结果",xian);

}
void QuickDemo::huofuzhixian2(cv::Mat &change){
//    HoughLinesP(
//    InputArray src, // 输入图像,必须8-bit的灰度图像
//    OutputArray lines, // 输出的极坐标来表示直线//vector<Vec4i>
//    double rho, // 生成极坐标时候的像素扫描步长,一般取1
//    double theta, //生成极坐标时候的角度步长,一般取值CV_PI/180
//    int threshold, // 阈值,要”检测” 一条直线所需最少的的曲线交点,直线交点得阈值
//    double minLineLength=0;// 最小直线长度
//    double maxLineGap=0;// 两交点间允许的最大间隔,(经canny的梯度可能不连续,间隔调大)
    //霍夫直线前可以传统方法转换为二值图像,也可以用canny转化为二值图像
    Mat jieguo=change.clone(),bianyuan;
    Canny(change,bianyuan,80,160,3,false);
    imshow("bianyuan",bianyuan);
    vector<Vec4i> lines;
    HoughLinesP(bianyuan,lines,1,CV_PI/180.0,100,100,20);
    for (vector<Vec4i>::size_type i{};i<lines.size();++i) {
        line(jieguo,Point(lines[i][0],lines[i][1]),Point(lines[i][2],lines[i][3]),Scalar(0,0,255),1,LINE_AA);
    }
    imshow("结果",jieguo);

}
void QuickDemo::huofuyuan(cv::Mat &change){
//    HoughCircles(
//        InputArray image, // 输入图像 ,必须是8位的单通道灰度图像,先进行降噪
//        OutputArray circles, // 输出结果,发现的圆信息//圆心x,圆心y,半径vector<Vec3f>
//        Int method, // 方法 - HOUGH_GRADIENT
//        Double dp, // dp = 1; 越大检测越多
//        Double mindist, // 10 最短距离-可以分辨是两个圆的,否则认为是同心圆- src_gray.rows/8//圆心之间得最小距离
//        Double param1, // canny高阈值低阈值默认为它的一半
//        Double param2, // 中心点累加器阈值 ? 候选圆心,累加交点阈值
//        Int minradius, // 最小半径
//        Int maxradius//最大半径
//    )
    //传入灰度图像并降噪//霍夫直线和圆都对噪声敏感,降噪效果更好
    Mat gaosi,gray;
    GaussianBlur(change,gaosi,Size(3,3),5);
    cvtColor(gaosi,gray,COLOR_BGR2GRAY);
    imshow("zhuanhuan",gray);
    vector<Vec3f> circles;
    HoughCircles(gray,circles,HOUGH_GRADIENT,2,60,130,130,20,70);
    for (vector<Vec3f>::size_type i{};i<circles.size();++i) {
        circle(change,Point(circles[i][0],circles[i][1]),circles[i][2],Scalar(0,0,255),3,LINE_AA);
        circle(change,Point(circles[i][0],circles[i][1]),2,Scalar(0,0,255),-1,LINE_AA);
    }
    imshow("jieguo",change);

}
void QuickDemo::fushiyupengzhang(cv::Mat &change){
//    结构元形状构造函数getStructuringElement( int shape, Size ksize, Point anchor )
//   其参数解释如下:
//       shape:  1)MORPH_RECT 表示产生矩形的结构元
//                2)MORPH_ELLIPSEM 表示产生椭圆形的结构元
//                3)MORPH_CROSS 表示产生十字交叉形的结构元
//       ksize:表示结构元的尺寸,即(宽,高),必须是奇数
//       anchor:表示结构元的锚点,即参考点。默认值Point(-1, -1)代表中心像素为锚点

//    dilate()//膨胀
//                其参数解释如下:
//    src 输入图像
//    dst 输出与src相同大小和类型的图像。
//    kernle 用于膨胀的核结构元素 内核可以使用getStructuringElement创建,上面函数得返回值
//    anchor 元素中锚的锚定位置; 默认值(-1,-1)表示锚位于元素中心。
//    iterations 迭代次数
//    borderType 像素外推方法
//    borderValue 当边界为常数时的边界值

//    erode()//腐蚀
//    src 输入图像
//    dst 输出与src相同大小和类型的图像。
//    kernle 用于腐蚀的核结构元素 内核可以使用getStructuringElement创建
//    anchor 元素中锚的锚定位置; 默认值(-1,-1)表示锚位于元素中心。
//    iterations 迭代次数
//    borderType 像素外推方法
//    borderValue 当边界为常数时的边界值
    Mat erzhi,fushi,pengzhang;
     cvtColor(change,erzhi,COLOR_BGR2GRAY);
     threshold(erzhi,erzhi,0,255,THRESH_BINARY|THRESH_OTSU);
     imshow("erzhi",erzhi);
     Mat kernle=getStructuringElement(MORPH_RECT,Size(5,5));
     dilate(erzhi,pengzhang,kernle);
     imshow("pengzhang",pengzhang);
     erode(erzhi,fushi,kernle);
     imshow("fushi",fushi);

//彩色图像也可以用
}
void QuickDemo::kaibicaozuo(cv::Mat &change){
    //开操作 去掉不必要的点  先腐蚀后膨胀
    //闭操作 填充必和区域  先膨胀后腐蚀

//    morphologyEx(形态学操作)
//    src:源图像
//    dst:目标图像
//    op:操作代号
//代号	含义	作用
//0	MORPH_ERODE	腐蚀运算(Erode operation)
//1	MORPH_DILATE	膨胀运算(Dilate operation)
//2	MORPH_OPEN	开运算(Opening operation)
//3	MORPH_CLOSE	闭运算(Closing operation)
//4	MORPH_GRADIENT	形态学梯度(Morphological gradient)
//5	MORPH_TOPHAT	“顶帽”(Top hat)
//6	MORPH_BLACKHAT	“黑帽”(Black hat)
//7	MORPH_HITMISS
//    kernel:核(用于膨胀操作的结构元素),可使用getStructuringElement()方法创建
//    anchor:锚点坐标,为负代表核的中心坐标
//    iterations:迭代次数
//    borderType:像素外推方法,具有默认值 BORDER_CONSTANT
//    borderValue:边界为常数时的边界值,有默认值morphologyDefaultBorderValue()
    Mat jieguob,jieguok,kernel=getStructuringElement(MORPH_RECT,Size(5,5)),erzhi;//size越大效果越明显
    cvtColor(change,erzhi,COLOR_BGR2GRAY);
    threshold(erzhi,erzhi,0,255,THRESH_BINARY_INV|THRESH_OTSU);
    imshow("erzhi",erzhi);

//    morphologyEx(erzhi,jieguob,MORPH_CLOSE,kernel,Point(-1,-1),1);//迭代次数越大越明显
//    imshow("bi",jieguob);//闭操作

    morphologyEx(erzhi,jieguok,MORPH_OPEN,kernel,Point(-1,-1),1);
    imshow("kai",jieguok);//开操作

}
void QuickDemo::kaicaozuotiquhengxian(cv::Mat &change){

    Mat jieguok,jieguok2,erzhi;//size越大效果越明显
    cvtColor(change,erzhi,COLOR_BGR2GRAY);
    threshold(erzhi,erzhi,0,255,THRESH_BINARY|THRESH_OTSU);

    //开操作提取横线水平方向
   Mat kernel=getStructuringElement(MORPH_RECT,Size(11,1));
    morphologyEx(erzhi,jieguok,MORPH_OPEN,kernel,Point(-1,-1),1);
    imshow("kai",jieguok);
    //开操作提取垂直方向横线
    Mat kernel2=getStructuringElement(MORPH_RECT,Size(1,11));
    morphologyEx(erzhi,jieguok2,MORPH_OPEN,kernel2,Point(-1,-1),1);
    imshow("kai2",jieguok2);
}

void QuickDemo::xingtaixuetidu(cv::Mat &change){
    Mat jibentidu,gray;
    cvtColor(change,gray,COLOR_BGR2GRAY);
    Mat kernel=getStructuringElement(MORPH_RECT,Size(3,3));
    //基本梯度  //黑白二值彩色都支持
    morphologyEx(gray,jibentidu,MORPH_GRADIENT,kernel,Point(-1,-1),1);
    imshow("jibentidu",jibentidu);

    //内梯度,原图-腐蚀//z这里所说得原图是指erode得时候原图(输入图像)是哪张图就用那张图
    Mat neitidu,src;
    erode(gray,src,kernel);
    subtract(gray,src,neitidu);
    imshow("neitidu",neitidu);

    //外梯度,膨胀-原图
    Mat waitidu,src2;
    dilate(gray,src2,kernel);
    subtract(src2,gray,waitidu);
    imshow("waitidu",waitidu);

}
void QuickDemo::dmhm(cv::Mat &change){
    //顶帽 = 原图 - 开运算 //得到开运算去除得多余点
    Mat gray,erzhi,dingmao;
    cvtColor(change,gray,COLOR_BGR2GRAY);
    threshold(gray,erzhi,0,255,THRESH_BINARY|THRESH_OTSU);
    imshow("erzhi",erzhi);
    Mat kernel=getStructuringElement(MORPH_RECT,Size(7,7));//MORPH_类型根据实际图像要求得图形来
//    morphologyEx(erzhi,dingmao,MORPH_TOPHAT,kernel,Point(-1,-1),1);
//    imshow("dingmao",dingmao);
    //黑帽 = 原图 - 闭运算//得到闭运算填充得点
    Mat heimao;
    morphologyEx(erzhi,heimao,MORPH_BLACKHAT,kernel,Point(-1,-1),1);
    imshow("heimao",heimao);


}
void QuickDemo::jizhongyubuzhong(cv::Mat &change){
//击中与不中  根据核形状不同选择原图中有没有匹配的,有的留没有的去
    Mat gray,erzhi,jizhong;
    cvtColor(change,gray,COLOR_BGR2GRAY);
    threshold(gray,erzhi,0,255,THRESH_BINARY_INV|THRESH_OTSU);
    imshow("erzhi",erzhi);
    Mat kernel=getStructuringElement(MORPH_CROSS,Size(13,13));//MORPH_类型根据实际图像要求得图形来
    morphologyEx(erzhi,jizhong,MORPH_HITMISS,kernel,Point(-1,-1),1);
    imshow("dingmao",jizhong);

}
void QuickDemo::anli1(cv::Mat &change){
    Mat gray,erzhi,lunkuo,bi;
    cvtColor(change,gray,COLOR_BGR2GRAY);
    threshold(gray,erzhi,0,255,THRESH_BINARY|THRESH_OTSU);
    imshow("erzhi",erzhi);
    Mat kernel=getStructuringElement(MORPH_RECT,Size(9,9));
    morphologyEx(erzhi,bi,MORPH_CLOSE,kernel,Point(-1,-1),1);
    imshow("bi",bi);
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(bi,contours,hierarchy,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);
    double maxmianji{};
    int xuhao{};
    for (vector<vector<Point>>::size_type i{};i<contours.size();++i) {
       double mianji=contourArea(contours[i]);
       if (mianji>maxmianji) {
           maxmianji=mianji;
           xuhao=i;
       }

    }
    drawContours(change,contours,xuhao,Scalar(0,0,255),2,LINE_AA);
    vector<Point> guaidian(contours[xuhao].size());
    approxPolyDP(contours[xuhao],guaidian,4,true);
    for (vector<Point>::size_type i{};i<guaidian.size();++i) {
        circle(change,guaidian[i],1,Scalar(255,0,0),2,LINE_AA);
    }
    imshow("jieguo",change);

}
void QuickDemo::zhifangtufanxiangtouying(cv::Mat &change){
//   calcBackProject(
//            const Mat *        images,
//            int                nimages,
//            const int *        channels,
//            InputArray         hist,
//            OutputArray        backProject,
//            const float **     ranges,
//            double             scale = 1,
//            bool               uniform = true
//        images:输入图像,图像深度必须位CV_8U, CV_16U或CV_32F中的一种,尺寸相同,每一幅图像都可以有任意的通道数
//        nimages : 输入图像的数量
//        channels : 用于计算反向投影的通道列表,通道数必须与直方图维度相匹配,第一个数组的通道是从0到image[0].channels() - 1, 第二个数组通道从图像image[0].channels()到image[0].channels() + image[1].channels() - 1计数
//        hist : 输入的直方图,直方图的bin可以是密集(dense)或稀疏(sparse)
//        backProject : 目标反向投影输出图像,是一个单通道图像,与原图像有相同的尺寸和深度
//        ranges : 直方图中每个维度bin的取值范围
//        scale = 1 : 可选输出反向投影的比例因子
//        uniform = true : 直方图是否均匀分布(uniform)的标识符,有默认值true
    Mat shouroi=imread("E:\\BaiduNetdiskDownload\\7roi.png",IMREAD_ANYCOLOR),roihsv,roizhifangtu,hsv;
    imshow("roi",shouroi);
    cvtColor(shouroi,roihsv,COLOR_BGR2HSV);
    cvtColor(change,hsv,COLOR_BGR2HSV);
    int h_bin=32,s_bin=32;//越大越细腻
    int hitsize[]{h_bin,s_bin};
    int channels[]{0,1};

    float h_range[]{0,180};
    float s_range[]{0,255};
    const float* range[]{h_range,s_range};
    calcHist(&roihsv,1,channels,Mat(),roizhifangtu,2,hitsize,range);
    normalize(roizhifangtu,roizhifangtu,0,255,NORM_MINMAX);
    Mat zhifangtuyoushe;
    calcBackProject(&hsv,1,channels,roizhifangtu,zhifangtuyoushe,range,1);
    imshow("jieguo1",zhifangtuyoushe);
    threshold(zhifangtuyoushe,zhifangtuyoushe,0,255,THRESH_BINARY|THRESH_OTSU);
    imshow("jieguo2",zhifangtuyoushe);
     Mat kernel=getStructuringElement(MORPH_RECT,Size(13,13));
     morphologyEx(zhifangtuyoushe,zhifangtuyoushe,MORPH_CLOSE,kernel,Point(-1,-1),2);//迭代次数越大越明显
     imshow("bi",zhifangtuyoushe);//闭操作


}
void QuickDemo::harris_jiaodianjiance(cv::Mat &change){
//    cornerHarris(InputArray src,        //输入图像  输入(黑白)单通道8位或浮点图像。
//                 OutputArray dst,     //输出图像   类型为CV_32FC1,大小与src相同。里面每个像素存得是一个值,这个值越高,是角点可能性越高R
//                 int blockSize,        //领域大小 ,计算λ1λ2时的矩阵大小 //可以取2
//                 int ksize,               //Sobel 求导中使用的窗口大小//单数
//                 double k,              //Harris 角点检测方程中的自由参数,取值参数为[0,04,0.06] .
//                 int borderType=BORDER_DEFAULT)
    Mat gray,shuchu;
     cvtColor(change,gray,COLOR_BGR2GRAY);
     cornerHarris(gray,shuchu,2,3,0.04);
     normalize(shuchu,shuchu,0,255,NORM_MINMAX);
     convertScaleAbs(shuchu,shuchu);
     for (int hang{};hang<change.rows;++hang) {
         for (int lie{};lie<change.cols;++lie) {
             if (shuchu.at<uchar>(hang,lie)>100) {
                 circle(change,Point(lie,hang),2,Scalar(0,0,255),-1,LINE_AA);
             }
         }
     }
     imshow("jieguo",change);


}
void QuickDemo::shi_tomasi_jiaodianjiance(cv::Mat &change){
//    goodFeaturesToTrack()
//    image:输入图像,一般是灰度图像
//    检测到的角点。vector<Point2f>
//    maxCorners: 角点得最大数量
//    qualtyLevel: 角点的质量水平,0-1之间。它代表了角点的最低质量,实际用于过滤角点的最小特征值是qualtyLevel与图像中最大特征值的乘积,
//    所以qualtyLevel的值不应超过1(常用的值为0.1或0.01)。低于这个值的所有角点都会被忽略。
//    minDistance: 两个角点之间的最短距离。
//    mask:
//    blockSize:计算导数的自相关矩阵时指定点的领域,默认为3,采用小窗口计算的结果比单点(也就是blockSize为1)计算的效果要好。
//    useHarrisDetector: 默认值为0,若非0,则函数使用Harris的角点定义
//    K: 当 useHarrisDetector非0,则K为用于设置hessian自相关矩阵即对hessian行列式的相对权值的权值系数。
    Mat gray;
    cvtColor(change,gray,COLOR_BGR2GRAY);
    vector<Point2f> jiaodianjihe;
    goodFeaturesToTrack(gray,jiaodianjihe,10,0.1,50);
    for (vector<Point2f>::size_type i{};i<jiaodianjihe.size();++i) {
        circle(change,jiaodianjihe[i],2,Scalar(0,0,255),-1);
    }
    imshow("jieguo",change);
}
void QuickDemo::jiyuyansededuixianggenzhong(cv::Mat &change){
    VideoCapture video("E:\\BaiduNetdiskDownload\\small.mp4");
    if (video.isOpened())
    {
        Mat tupian;
        while (true) {
       video.read(tupian);//读没了返回false并且传入空图像
            if (tupian.empty()) {
                break;
            }
            imshow("shiping",tupian);
            Mat hsv,mask;
            cvtColor(tupian,hsv,COLOR_BGR2HSV);
            inRange(hsv,Scalar(100,43,46),Scalar(124,255,255),mask);
            Mat kernel=getStructuringElement(MORPH_RECT,Size(9,9));
            morphologyEx(mask,mask,MORPH_CLOSE,kernel,Point(-1,-1),1);
            imshow("yanma",mask);
            vector<vector<Point>> contours;
            vector<Vec4i> hierarchy;
            findContours(mask,contours,hierarchy,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);
            RotatedRect rrt=minAreaRect(contours[0]);
            circle(tupian,rrt.center,2,Scalar(0,0,255),1,LINE_AA);
            ellipse(tupian,rrt,Scalar(255,0,255),2,LINE_AA);
            imshow("jieguo",tupian);
            if(waitKey(16)==27){
                break;
            }
        }
    }
    video.release();
}
void QuickDemo::beijingjianmo(cv::Mat &change){
//    BackgroundSubtractorMOG2创建及初始化
//    history:用于训练背景的帧数,默认为500帧,如果不手动设置learningRate,history就被用于计算当前的learningRate,此时history越大,learningRate越小,背景更新越慢;
//    varThreshold:方差阈值,用于判断当前像素是前景还是背景。一般默认16,如果光照变化明显,如阳光下的水面,建议设为25,36,具体去试一下也不是很麻烦,值越大,灵敏度越低;
//    detectShadows:是否检测影子,设为true为检测,false为不检测,检测影子会增加程序时间复杂度,如无特殊要求,建议设为false;

//    mog->apply(src_YCrCb, foreGround, 0.005);

//        image 源图
//        fmask 前景(二值图像)
//        learningRate 学习速率,值为0-1,为0时背景不更新,为1时逐帧更新,默认为-1,即算法自动更新;
    VideoCapture video("E:\\BaiduNetdiskDownload\\vtest.avi");
    Ptr<BackgroundSubtractorMOG2> mog = createBackgroundSubtractorMOG2();
    Mat kernel=getStructuringElement(MORPH_RECT,Size(5,5));
    if (video.isOpened())
    {
        Mat tupian;
        while (true) {
       video.read(tupian);//读没了返回false并且传入空图像
            if (tupian.empty()) {
                break;
            }
            imshow("shiping",tupian);
            Mat mask(tupian.size(),CV_8UC1);
            mog->apply(tupian,mask);
            morphologyEx(mask,mask,MORPH_OPEN,kernel,Point(-1,-1),1);
            morphologyEx(mask,mask,MORPH_CLOSE,kernel,Point(-1,-1),1);
            vector<vector<Point>> lunkuodian;
            vector<Vec4i> kuopujiegou;
            findContours(mask,lunkuodian,kuopujiegou,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);
            for (vector<vector<Point>>::size_type i{};i<lunkuodian.size();++i) {
                if (contourArea(lunkuodian[i])>80) {
                    Rect juxing=boundingRect(lunkuodian[i]);
                    rectangle(tupian,juxing,Scalar(0,0,255),2,LINE_AA);
                }
            }
            imshow("jieguo",mask);
            imshow("jieguo2",tupian);
            if(waitKey(16)==27){
                break;
            }
        }

    }
    video.release();
}

void QuickDemo::jingzita(cv::Mat &change){
//    pyrUp(Mat src,Mat dst,Size(src.cols*2,src.rows*2))生成的图像是原图在宽与高各放大二倍
//    pyrDown(Mat src,Mat dst,Size(src.cols/2,src.rowa/2))生成的图像是原图在宽和高都缩小1/2
    //拉普拉斯金字塔就是使用原始图像减去图像向下取样然后向上取样的这样一个过程。
//    Li = Gi - PyrUp(PyrDown(Gi))
    Mat dst;
//    pyrUp(change,dst);
//    imshow("dst",dst);
//    pyrUp(change,dst,Size(change.cols*2,change.rows*2));
//    imshow("dst1",dst);
        pyrDown(change,dst);
        imshow("dst",dst);
        pyrDown(change,dst,Size(change.cols/2,change.rows/2));
        imshow("dst1",dst);
}
void QuickDemo::julibianhuan(cv::Mat &change){
    //Opencv中distanceTransform方法用于计算图像中每一个非零点距离离自己最近的零点的距离
//    void distanceTransform(InputArray src,
//                                OutputArray dst,
//                                int distanceType,
//                                int maskSize,
//                                int dstType=CV_32F
//                               )
//    dstType:输出图像(矩阵)的数据类型,可以是CV_8U 或 CV_32F。当选择CV_8U时,distanceType的类型只能为DIST_L1。
//    src:源矩阵
//    dst:目标矩阵
//    distanceType:距离类型,可选的类型如下:
//    DIST_USER
//    User defined distance.
//    DIST_L1
//    distance = |x1-x2| + |y1-y2|
//    DIST_L2
//    the simple euclidean distance
//    DIST_C
//    distance = max(|x1-x2|,|y1-y2|)
//    DIST_L12
//    L1-L2 metric: distance = 2(sqrt(1+x*x/2) - 1))
//    DIST_FAIR
//    distance = c^2(|x|/c-log(1+|x|/c)), c = 1.3998
//    DIST_WELSCH
//    distance = c^2/2(1-exp(-(x/c)^2)), c = 2.9846
//    DIST_HUBER
//    distance = |x|<c ? x^2/2 : c(|x|-c/2), c=1.345
//    resize(change,change,Size(0,0),0.3,0.3);
    Mat gray,erzhi,src;
    cvtColor(change,gray,COLOR_BGR2GRAY);
    threshold(gray,erzhi,0,255,THRESH_BINARY|THRESH_OTSU);
//    Mat kennel=getStructuringElement(MORPH_RECT,Size(23,23));
//    morphologyEx(erzhi,erzhi,MORPH_OPEN,kennel,Point(-1,-1),3);
    imshow("erzhi",erzhi);
    distanceTransform(erzhi,src,DIST_L2,5);
    normalize(src,src,0,1,NORM_MINMAX);
//    src.convertTo(src,CV_8UC1);
    imshow("src",src);

}
void QuickDemo::fenge(cv::Mat &change){
    Mat src=change(Range(0,280), Range(0,100));//提取几行,提取几列
    imshow("jieg",src);
}
void QuickDemo::toushibianhuan(Mat &change){
//透视变换矫正倾斜为正视图像,主要是获取轮廓四个顶点,顶点获取不到可以用霍夫直线算出边,通过边算交点,得到交点后可进行透视变换
    Mat jieguo=change.clone();
    Mat kai,bi;
    auto contours=geshizhengli(change);
    vector<vector<Point>> approxCurve(contours.size());
    for (vector<vector<Point>>::size_type i=0;i<contours.size();++i)
    {
        approxPolyDP(contours[i],approxCurve[i],10,true);
    }
    vector <Point> dianji{approxCurve[0][0],approxCurve[0][1],approxCurve[0][2],approxCurve[0][3]};
    int w=change.cols/2;
    int h=change.rows/2;
    for (vector <Point>::size_type i{};i<approxCurve[0].size();++i)
    {   Point zhongjie;
        if (approxCurve[0][i].x<w&&approxCurve[0][i].y<h) {
             zhongjie=approxCurve[0][0];
            approxCurve[0][0]=approxCurve[0][i];
            approxCurve[0][i]=zhongjie;
        }
        if (approxCurve[0][i].x>w&&approxCurve[0][i].y<h) {
             zhongjie=approxCurve[0][1];
            approxCurve[0][1]=approxCurve[0][i];
            approxCurve[0][i]=zhongjie;
        }
        if (approxCurve[0][i].x>w&&approxCurve[0][i].y>h) {
             zhongjie=approxCurve[0][2];
            approxCurve[0][2]=approxCurve[0][i];
            approxCurve[0][i]=zhongjie;
        }
        if (approxCurve[0][i].x<w&&approxCurve[0][i].y>h) {
             zhongjie=approxCurve[0][3];
            approxCurve[0][3]=approxCurve[0][i];
            approxCurve[0][i]=zhongjie;
        }
    }
    double widthmax=pow(pow((approxCurve[0][1].x-approxCurve[0][0].x),2)+pow((approxCurve[0][1].y-approxCurve[0][0].y),2),(double)1.0/2);
    double heightmax=pow(pow((approxCurve[0][3].x-approxCurve[0][0].x),2)+pow((approxCurve[0][3].y-approxCurve[0][0].y),2),(double)1.0/2);
    Mat src;
    vector<Point2f>SrcAffinePts{ Point2f(approxCurve[0][0]),Point2f(approxCurve[0][1]) ,Point2f(approxCurve[0][2]) ,Point2f(approxCurve[0][3])};
    vector<Point2f> DstAffinePts{ Point2f(0,0),Point2f(widthmax,0),Point2f(widthmax,heightmax),Point2f(0,heightmax)};
    Mat M = getPerspectiveTransform(SrcAffinePts, DstAffinePts);//用于普通透视
//    Mat M = findHomography(SrcAffinePts,DstAffinePts);
    Mat DstImg;
    warpPerspective(jieguo, DstImg, M,Size(widthmax,heightmax));
    imshow("jg",DstImg);
//    TextRecognitionModel
   }

//没什么用的这个
void QuickDemo::geshizehsi(cv::Mat &change){
    Mat gary,dingmao,sx,sy,reslult,gaosi,erzhi,bi;
    GaussianBlur(change,gaosi,Size(5,5),0);
    cvtColor(gaosi,gary,COLOR_BGR2GRAY);
        Mat kennel=getStructuringElement(MORPH_RECT,Size(5,5));
        morphologyEx(gary,dingmao,MORPH_TOPHAT,kennel,Point(-1,-1),3);
        imshow("dingmao",dingmao);
        threshold(dingmao,erzhi,0,255,THRESH_OTSU);
//    Sobel(dingmao,sx,CV_32F,1,0);
//    Sobel(dingmao,sy,CV_32F,0,1);
//    convertScaleAbs(sx,sx);
//    convertScaleAbs(sy,sy);
//    add(sx,sy,reslult);
        Mat kennel2=getStructuringElement(MORPH_RECT,Size(19,1));
        morphologyEx(erzhi,bi,MORPH_CLOSE,kennel2,Point(-1,-1),3);
    imshow("bi",bi);
}


void QuickDemo::orbguanjiandian(cv::Mat &change){
//    ORB对象创建,
//    Orb = cv::ORB::create(500)
//    virtual void cv::Feature2D::detect(
//    InputArray image, // 输入图像
//    std::vector< KeyPoint > & keypoints, // 关键点
//    InputArray mask = noArray() // 支持mask
//    )
//    KeyPoint数据结构-四个最重要属性:
//    -pt 坐标
//    -angle
//    -response
//    -size
    auto orb=ORB::create(500);//500个关键点
    vector<KeyPoint> keyp;
    orb->detect(change,keyp);
    drawKeypoints(change,keyp,change,Scalar::all(-1),DrawMatchesFlags::DEFAULT);//小点;
//    drawKeypoints(change,keyp,change,Scalar::all(-1),DrawMatchesFlags::DRAW_RICH_KEYPOINTS);//大圈体现出keypoint得angle,pt,size
    imshow("jieguo",change);


}
void QuickDemo::orbmiaoshuzi(cv::Mat &change){

    auto orb=ORB::create(500);//500个关键点
    vector<KeyPoint> keyp;
    orb->detect(change,keyp);
    Mat miaoshuzi;
    orb->compute(change,keyp,miaoshuzi);
    cout <<miaoshuzi.rows<<"x"<<miaoshuzi.cols<<endl;
}
void QuickDemo::siftcs(cv::Mat &img){
    auto sift=SIFT::create(500);
    vector<KeyPoint> ky;
    sift->detect(img,ky);
    drawKeypoints(img,ky,img,Scalar::all(-1),DrawMatchesFlags::DEFAULT);
    imshow("dd",img);
    Mat miaoshuzi;
    sift->compute(img,ky,miaoshuzi);
    cout <<miaoshuzi.rows<<"x"<<miaoshuzi.cols<<endl;

}
void QuickDemo::orbqipei(cv::Mat &img){
    auto orb=ORB::create(500);
    vector<KeyPoint> ky;
    vector<KeyPoint> ky_dest;
    Mat miaoshuzi;
    Mat miaoshuzi_dest;
    Mat imgdest=cv::imread("E:/Desktop/study/class_tezheng/src/book_on_desk.jpg");
    orb->detectAndCompute(img,Mat(),ky,miaoshuzi);
    orb->detectAndCompute(imgdest,Mat(),ky_dest,miaoshuzi_dest);
    //暴力匹配
    auto baoli=BFMatcher::create(NORM_HAMMING);
    vector<DMatch> qipeijieguo;
    Mat result;
//    baoli->knnMatch(miaoshuzi,miaoshuzi_dest,qipeijieguo,2);
    baoli->match(miaoshuzi,miaoshuzi_dest,qipeijieguo);
    drawMatches(img,ky,imgdest,ky_dest,qipeijieguo,result);
    imshow("baoli",result);

    //flann匹配
    auto fannlmatch=FlannBasedMatcher(new flann::LshIndexParams(6,12,2));
    vector<DMatch> flannqipeijieguo;
    Mat result_flann;
    fannlmatch.match(miaoshuzi,miaoshuzi_dest,flannqipeijieguo);
    drawMatches(img,ky,imgdest,ky_dest,flannqipeijieguo,result_flann);
    imshow("flann",result_flann);
}
void QuickDemo::siftqipei(cv::Mat &img){
    auto sift=SIFT::create(500);
    vector<KeyPoint> ky;
    vector<KeyPoint> ky_dest;
    Mat miaoshuzi;
    Mat miaoshuzi_dest;
    Mat imgdest=cv::imread("E:/Desktop/study/class_tezheng/src/book_on_desk.jpg");
    sift->detectAndCompute(img,Mat(),ky,miaoshuzi);
    sift->detectAndCompute(imgdest,Mat(),ky_dest,miaoshuzi_dest);
    //暴力
    auto baoli=BFMatcher::create(NORM_L1,false);
    vector<DMatch> qipeijieguo;
    Mat result;
    baoli->match(miaoshuzi,miaoshuzi_dest,qipeijieguo);
    drawMatches(img,ky,imgdest,ky_dest,qipeijieguo,result);
    imshow("jieguo",result);

    //flann匹配
    auto fannlmatch=FlannBasedMatcher(new flann::KDTreeIndexParams());
//    auto fannlmatch=FlannBasedMatcher();
    vector<DMatch> flannqipeijieguo;
    Mat result_flann;
    fannlmatch.match(miaoshuzi,miaoshuzi_dest,flannqipeijieguo);
    sort(flannqipeijieguo.begin(),flannqipeijieguo.begin(),[](const DMatch qian,const DMatch hou){return qian.distance<hou.distance;});
    drawMatches(img,ky,imgdest,ky_dest,flannqipeijieguo,result_flann);
    imshow("flann",result_flann);
}
void QuickDemo::toushizhaoyuandian(cv::Mat &img){
    auto orb=ORB::create(500);
    vector<KeyPoint> ky;
    vector<KeyPoint> ky_dest;
    Mat miaoshuzi;
    Mat miaoshuzi_dest;
    Mat imgdest=cv::imread("E:/Desktop/study/class_tezheng/src/book_on_desk.jpg");
    orb->detectAndCompute(img,Mat(),ky,miaoshuzi);
    orb->detectAndCompute(imgdest,Mat(),ky_dest,miaoshuzi_dest);
    //暴力匹配
    auto baoli=BFMatcher::create(NORM_HAMMING,false);
    vector<DMatch> qipeijieguo;
    Mat result;
    baoli->match(miaoshuzi,miaoshuzi_dest,qipeijieguo);
    sort(qipeijieguo.begin(),qipeijieguo.end(),[](const DMatch &a,const DMatch &b){return a.distance<b.distance;});
     float good_rate = 0.15f;
    int num_good_matches = qipeijieguo.size() * good_rate;
    qipeijieguo.erase(qipeijieguo.begin()+num_good_matches,qipeijieguo.end());
    drawMatches(img,ky,imgdest,ky_dest,qipeijieguo,result);
    vector<Point2f> qAffinePts;
    vector<Point2f> hAffinePts;
    for (size_t t = 0; t < qipeijieguo.size(); t++) {
        qAffinePts.push_back(ky[qipeijieguo[t].queryIdx].pt);
        hAffinePts.push_back(ky_dest[qipeijieguo[t].trainIdx].pt);

           }


    Mat M = findHomography(qAffinePts,hAffinePts,RANSAC);//还可以用rho
    vector<Point2f> q{Point2f(0,0),Point2f(img.cols,0),Point2f(img.cols,img.rows),Point2f(0,img.rows)};
    vector<Point2f> h(4);
   perspectiveTransform(q, h, M);//这个透视变换适用于特征提取然后获取在图像中的点
   for(int i{};i<4;++i){
       line(imgdest,h[i],h[(i+1)%4],Scalar(0,0,255),2,LINE_AA);
   }

    imshow("jg",imgdest);


}
void QuickDemo::wendangduiqi(cv::Mat &img){
    auto orb=ORB::create(500);
    vector<KeyPoint> ky;
    vector<KeyPoint> ky_dest;
    Mat miaoshuzi;
    Mat miaoshuzi_dest;
    Mat imgdest=cv::imread("E:/Desktop/study/class_tezheng/src/form.png");
    orb->detectAndCompute(img,Mat(),ky,miaoshuzi);
    orb->detectAndCompute(imgdest,Mat(),ky_dest,miaoshuzi_dest);
    //暴力匹配
    auto baoli=BFMatcher::create(NORM_HAMMING,false);
    vector<DMatch> qipeijieguo;
    Mat result;
    baoli->match(miaoshuzi,miaoshuzi_dest,qipeijieguo);
    sort(qipeijieguo.begin(),qipeijieguo.end(),[](const DMatch &a,const DMatch &b){return a.distance<b.distance;});
     float good_rate = 0.15f;
    int num_good_matches = qipeijieguo.size() * good_rate;
    qipeijieguo.erase(qipeijieguo.begin()+num_good_matches,qipeijieguo.end());
    drawMatches(img,ky,imgdest,ky_dest,qipeijieguo,result);
    imshow("img",result);
    vector<Point2f> qAffinePts;
    vector<Point2f> hAffinePts;
    for (size_t t = 0; t < qipeijieguo.size(); t++) {
        qAffinePts.push_back(ky[qipeijieguo[t].queryIdx].pt);
        hAffinePts.push_back(ky_dest[qipeijieguo[t].trainIdx].pt);
           }
    Mat M = findHomography(qAffinePts,hAffinePts,RANSAC);//还可以用rho
    Mat DstImg;
    warpPerspective(img, DstImg, M,Size(imgdest.cols,imgdest.rows));
    imshow("jieguo",DstImg);

}

void QuickDemo::tiaomabiaoqiandingwei(Mat &img){
    auto orb=ORB::create(500);
    vector<KeyPoint> ky;
    vector<KeyPoint> ky_dest;
    Mat miaoshuzi;
    Mat miaoshuzi_dest;
    Mat imgdest=cv::imread("E:/Desktop/study/class_tezheng/src/orb_barcode/00059-NG.jpg",WINDOW_FREERATIO);
    orb->detectAndCompute(img,Mat(),ky,miaoshuzi);
    orb->detectAndCompute(imgdest,Mat(),ky_dest,miaoshuzi_dest);
    //暴力匹配
    auto baoli=BFMatcher::create(NORM_HAMMING,false);
    vector<DMatch> qipeijieguo;
    Mat result;
    baoli->match(miaoshuzi,miaoshuzi_dest,qipeijieguo);
    sort(qipeijieguo.begin(),qipeijieguo.end(),[](const DMatch &a,const DMatch &b){return a.distance<b.distance;});
     float good_rate = 0.2f;
    int num_good_matches = qipeijieguo.size() * good_rate;
    qipeijieguo.erase(qipeijieguo.begin()+num_good_matches,qipeijieguo.end());
    drawMatches(img,ky,imgdest,ky_dest,qipeijieguo,result);
    vector<Point2f> qAffinePts;
    vector<Point2f> hAffinePts;
    for (size_t t = 0; t < qipeijieguo.size(); t++) {
        qAffinePts.push_back(ky[qipeijieguo[t].queryIdx].pt);
        hAffinePts.push_back(ky_dest[qipeijieguo[t].trainIdx].pt);

           }


    Mat M = findHomography(qAffinePts,hAffinePts,RANSAC);//还可以用rho
    vector<Point2f> q{Point2f(0,0),Point2f(img.cols,0),Point2f(img.cols,img.rows),Point2f(0,img.rows)};
    vector<Point2f> h(4);
   perspectiveTransform(q, h, M);//这个透视变换适用于特征提取然后获取在图像中的点
   sort(h.begin(),h.end(),[](const Point2f &a,const Point2f &b){return  a.y<b.y;});
   Point2f temp;
   if(h[0].x>h[1].x){
       temp=h[0];
       h[0]=h[1];
       h[1]=temp;
   }
   if(h[2].x>h[3].x){
       temp=h[2];
       h[2]=h[3];
       h[3]=temp;
   }
   Rect roi(h[0],h[3]);
   Mat src(imgdest(roi));
   transpose(src,src);     //转置
   flip(src,src,0) ;
   namedWindow("roi",1);
   imshow("roi",src);

//   flip(Mat src,Mat &dst,int nFlag);//镜像
}
string objNames[] = { "background",
"aeroplane", "bicycle", "bird", "boat",
"bottle", "bus", "car", "cat", "chair",
"cow", "diningtable", "dog", "horse",
"motorbike", "person", "pottedplant",
"sheep", "sofa", "train", "tvmonitor" };
void QuickDemo::wutishibie(cv::Mat &img){
    string modeltextf="E:/BaiduNetdiskDownload/model/MobileNetSSD_deploy.prototxt";
    string modelfile="E:/BaiduNetdiskDownload/model/MobileNetSSD_deploy.caffemodel";
    Net net=readNetFromCaffe(modeltextf,modelfile);
    Mat blob=blobFromImage(img,0.007843,Size(300,300),Scalar(127.5, 127.5, 127.5),false,false);
    net.setInput(blob);
    Mat src=net.forward();
    Mat result(src.size[2],src.size[3],CV_32F,src.ptr<float>());
    for (int i{};i<result.rows;++i) {
        float *pt=result.ptr<float>(i);
        pt+=1;
        size_t fenlei=(size_t)(*pt++);
        float yuzhi=*pt++;
        if (yuzhi>0.5) {
            float x1=(*pt++)*img.cols;
            float y1=(*pt++)*img.rows;
            float x2=(*pt++)*img.cols;
            float y2=(*pt++)*img.rows;
            Rect biankuang(Point(x1,y1),Point(x2,y2));
            rectangle(img,biankuang,Scalar(0,0,255),2,LINE_AA);
            putText(img,string(objNames[fenlei]),Point(x1,y1-5),FONT_HERSHEY_COMPLEX,1.0,Scalar(45,18,77),1,LINE_AA);
        }

    }
    imshow("img",img);
}

void QuickDemo::wutishibiercnn(cv::Mat &img){
    string label_map = "E:/Desktop/study/class_tezheng/faster_rcnn_inception_v2_coco_2018_01_28/faster_rcnn_inception_v2_coco_2018_01_28/laber.pbtxt";
    string model = "E:/Desktop/study/class_tezheng/faster_rcnn_inception_v2_coco_2018_01_28/faster_rcnn_inception_v2_coco_2018_01_28/frozen_inference_graph.pb";
    string config = "E:/Desktop/study/class_tezheng/faster_rcnn_inception_v2_coco_2018_01_28/faster_rcnn_inception_v2_coco_2018_01_28/frozen_inference_graph.pbtxt";

    Net net=readNetFromTensorflow(model,config);
    net.setPreferableBackend(DNN_BACKEND_OPENCV);//使用什么推理,可选openvino,opencv,或者CUDA
    net.setPreferableTarget(DNN_TARGET_CPU);//用什么硬件计算
    Mat blob=blobFromImage(img,1.0,Size(800,600),Scalar(),true,false);
    net.setInput(blob);
    Mat src=net.forward();
    Mat result(src.size[2],src.size[3],CV_32F,src.ptr<float>());
    for (int i{};i<result.rows;++i) {
        float *pt=result.ptr<float>(i);
        pt+=1;
        size_t fenlei=(size_t)(*pt++);
        float yuzhi=*pt++;
        if (yuzhi>0.85) {
            float x1=(*pt++)*img.cols;
            float y1=(*pt++)*img.rows;
            float x2=(*pt++)*img.cols;
            float y2=(*pt++)*img.rows;
            Rect biankuang(Point(x1,y1),Point(x2,y2));
            rectangle(img,biankuang,Scalar(0,0,255),2,LINE_AA);
            putText(img,string(objNames2[fenlei]),Point(x1,y1-5),FONT_HERSHEY_COMPLEX,1.0,Scalar(45,18,77),1,LINE_AA);
            cout <<fenlei<<endl;
        }

    }
    imshow("img",img);

}
void QuickDemo::daopian(cv::Mat &img){
   Mat erzhi,gary;
   cvtColor(img,gary,COLOR_BGR2GRAY);
   threshold(gary,erzhi,0,255,THRESH_BINARY_INV|THRESH_OTSU);
       Mat kennel=getStructuringElement(MORPH_RECT,Size(3,3));
       morphologyEx(erzhi,erzhi,MORPH_OPEN,kennel,Point(-1,-1));
       imshow("erzhi",erzhi);
       vector<vector<Point>> contours;
       vector<Vec4i> hierarchy;
   //    Mat cc=Mat::zeros(change.size(),change.type());
       findContours(erzhi,contours,hierarchy,RETR_LIST,CHAIN_APPROX_SIMPLE);
       vector <Rect> rejihe;
       for (vector<vector<Point>>::size_type i{0};i<contours.size();++i) {
           Rect rect=boundingRect(contours[i]);
           double area=rect.area();
           if (rect.height>(erzhi.rows/2)) {
               continue;
           }
           if (area < 550) {
                       continue;
                   }
           rejihe.push_back(rect);

       }
       for (vector <Rect>::size_type i{0};i<rejihe.size();++i) {

           rectangle(img,rejihe[i],Scalar(255,0,0),1,LINE_AA);

       }
       sort(rejihe.begin(),rejihe.end(),[](Rect a,Rect b){return a.y<b.y;});
       Rect temp=rejihe[1];
       vector <Rect> resmat;

       for (vector <Rect>::size_type i{0};i<rejihe.size();++i) {
           if (i==1) {
               continue;
           }
           Mat maskres;
           vector<Point> feidian;
           Mat qian=erzhi(rejihe[i]);
           resize(qian,qian,erzhi(temp).size());
           absdiff(qian,erzhi(temp),maskres);
           Mat se = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
                   morphologyEx(maskres, maskres, MORPH_OPEN, se);
           findNonZero(maskres,feidian);
           int nw=maskres.cols+2;
           int nh=maskres.rows+2;
           Mat m1=Mat::zeros(Size(nw,nh),maskres.type());
           Rect roir(1,1,maskres.cols,maskres.rows);
           maskres.copyTo(m1(roir));
           vector<vector<Point>> contoursmask;
           vector<Vec4i> hierarchymask;
            threshold(m1,m1,0,255,THRESH_BINARY|THRESH_OTSU);
           findContours(m1,contoursmask,hierarchymask,RETR_LIST,CHAIN_APPROX_SIMPLE);
           bool yn=false;
           for (size_t t = 0; t < contoursmask.size(); t++) {
                       Rect rect = boundingRect(contoursmask[t]);
                       float ratio = (float)rect.width / ((float)rect.height);
                       if (ratio > 4.0 && (rect.y < 5 || (m1.rows - (rect.height + rect.y)) < 10)) {
                           continue;
                       }
                       double area = contourArea(contoursmask[t]);
                       if (area > 10) {
                           yn = true;
                           break;
                       }
                       }
           if (feidian.size()>40&&yn)
           {
               resmat.push_back(rejihe[i]);
           }


       }
       for (vector <Rect>::size_type i{0};i<rejihe.size();++i) {
           rectangle(img,resmat[i],Scalar(255,0,0),1,LINE_AA);
         putText(img,"bad",Point(resmat[i].x,resmat[i].y-10),FONT_HERSHEY_COMPLEX,1.0,Scalar(45,18,77),1,LINE_AA);
       }


           imshow("img",img);

}
void QuickDemo::renlianjianche(cv::Mat &img){
//    getTickFrequency()

}

//交叉匹配
//针对暴力匹配,可以使用交叉匹配的方法来过滤错误的匹配。交叉过滤的思想很简单,再进行一次匹配,反过来使用被匹配到的点进行匹配,如果匹配到的仍然是第一次匹配的点的话,就认为这是一个正确的匹配。举例来说就是,假如第一次特征点A使用暴力匹配的方法,匹配到的特征点是特征点B;反过来,使用特征点B进行匹配,如果匹配到的仍然是特征点A,则就认为这是一个正确的匹配,否则就是一个错误的匹配。OpenCV中BFMatcher已经封装了该方法,创建BFMatcher的实例时,第二个参数传入true即可,BFMatcher bfMatcher(NORM_HAMMING,true)。

//KNN匹配
//K近邻匹配,在匹配的时候选择K个和特征点最相似的点,如果这K个点之间的区别足够大,则选择最相似的那个点作为匹配点,通常选择K = 2,也就是最近邻匹配。对每个匹配返回两个最近邻的匹配,如果第一匹配和第二匹配距离比率足够大(向量距离足够远),则认为这是一个正确的匹配。
//OpenCV中的匹配器中封装了该方法,上面的代码可以调用bfMatcher->knnMatch(descriptors1, descriptors2, knnMatches, 2);

//float fps = getTickFrequency() / (getTickCount() - start); 帧数
//float time = (getTickCount() - start) / getTickFrequency(); 运行时间
void linspace(Mat& image, float begin, float finish, int number, Mat &mask);
void generate_mask(Mat &img, Mat &mask);
void QuickDemo::tuxiangpingjie(cv::Mat &left){
        Mat right=imread("E:/Desktop/study/class_tezheng/src/22.jpg");
        // 提取特征点与描述子
        vector<KeyPoint> keypoints_right, keypoints_left;
        Mat descriptors_right, descriptors_left;
        auto detector = AKAZE::create();
        detector->detectAndCompute(left, Mat(), keypoints_left, descriptors_left);
        detector->detectAndCompute(right, Mat(), keypoints_right, descriptors_right);

        // 暴力匹配
        vector<DMatch> matches;
        auto matcher = DescriptorMatcher::create(DescriptorMatcher::BRUTEFORCE);

        // 发现匹配
        std::vector< std::vector<DMatch> > knn_matches;
        matcher->knnMatch(descriptors_left, descriptors_right, knn_matches, 2);
        const float ratio_thresh = 0.7f;
        std::vector<DMatch> good_matches;
        for (size_t i = 0; i < knn_matches.size(); i++)
        {
            if (knn_matches[i][0].distance < ratio_thresh * knn_matches[i][1].distance)
            {
                good_matches.push_back(knn_matches[i][0]);
            }
        }

        Mat dst;
        drawMatches(left, keypoints_left, right, keypoints_right, good_matches, dst);
        namedWindow("output",WINDOW_FREERATIO);
        imshow("output", dst);
        imwrite("D:/good_matches.png", dst);

        //-- Localize the object
        std::vector<Point2f> left_pts;
        std::vector<Point2f> right_pts;
        for (size_t i = 0; i < good_matches.size(); i++)
        {
            // 收集所有好的匹配点
            left_pts.push_back(keypoints_left[good_matches[i].queryIdx].pt);
            right_pts.push_back(keypoints_right[good_matches[i].trainIdx].pt);
        }

        // 配准与对齐,对齐到第一张
        Mat H = findHomography(right_pts, left_pts, RANSAC);

        // 获取全景图大小
        int h = max(left.rows, right.rows);
        int w = left.cols + right.cols;
        Mat panorama_01 = Mat::zeros(Size(w, h), CV_8UC3);
        Rect roi;
        roi.x = 0;
        roi.y = 0;
        roi.width = left.cols;
        roi.height = left.rows;

        // 获取左侧与右侧对齐图像
        left.copyTo(panorama_01(roi));
        imwrite("D:/panorama_01.png", panorama_01);
        Mat panorama_02;
        warpPerspective(right, panorama_02, H, Size(w, h));
        imwrite("D:/panorama_02.png", panorama_02);


        // 计算融合重叠区域mask
        Mat mask = Mat::zeros(Size(w, h), CV_8UC1);
        generate_mask(panorama_02, mask);

        // 创建遮罩层并根据mask完成权重初始化
        Mat mask1 = Mat::ones(Size(w, h), CV_32FC1);
        Mat mask2 = Mat::ones(Size(w, h), CV_32FC1);

        // left mask
        linspace(mask1, 1, 0, left.cols, mask);

        // right mask
        linspace(mask2, 0, 1, left.cols, mask);
        imshow("mask1", mask1);
        imshow("mask2", mask2);

        // 左侧融合
        Mat m1;
        vector<Mat> mv;
        mv.push_back(mask1);
        mv.push_back(mask1);
        mv.push_back(mask1);
        merge(mv, m1);
        panorama_01.convertTo(panorama_01, CV_32F);
        multiply(panorama_01, m1, panorama_01);

        // 右侧融合
        mv.clear();
        mv.push_back(mask2);
        mv.push_back(mask2);
        mv.push_back(mask2);
        Mat m2;
        merge(mv, m2);
        panorama_02.convertTo(panorama_02, CV_32F);
        multiply(panorama_02, m2, panorama_02);

        // 合并全景图
        Mat panorama;
        add(panorama_01, panorama_02, panorama);
        panorama.convertTo(panorama, CV_8U);

        imshow("jjjjj",panorama);
    }

    void generate_mask(Mat &img, Mat &mask) {
        int w = img.cols;
        int h = img.rows;
        for (int row = 0; row < h; row++) {
            for (int col = 0; col < w; col++) {
                Vec3b p = img.at<Vec3b>(row, col);
                int b = p[0];
                int g = p[1];
                int r = p[2];
                if (b == g && g == r && r == 0) {
                    mask.at<uchar>(row, col) = 255;
                }
            }
        }
        imwrite("D:/mask.png", mask);
    }

    void linspace(Mat& image, float begin, float finish, int w1, Mat &mask) {
        int offsetx = 0;
        float interval = 0;
        float delta = 0;
        for (int i = 0; i < image.rows; i++) {
            offsetx = 0;
            interval = 0;
            delta = 0;
            for (int j = 0; j < image.cols; j++) {
                int pv = mask.at<uchar>(i, j);
                if (pv == 0 && offsetx == 0) {
                    offsetx = j;
                    delta = w1 - offsetx;
                    interval = (finish - begin) / (delta - 1);
                    image.at<float>(i, j) = begin + (j - offsetx)*interval;
                }
                else if (pv == 0 && offsetx > 0 && (j - offsetx) < delta) {
                    image.at<float>(i, j) = begin + (j - offsetx)*interval;
                }
            }
        }
    }



//#include <iostream>
//#include <chrono>
//int main()
//{
//    auto start = std::chrono::steady_clock::now();
//    // 代码段
//    auto end = std::chrono::steady_clock::now();
//    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
//    std::cout << "代码运行时间:" << duration << "毫秒" << std::endl;
//    return 0;
//}
void QuickDemo::tupiancharu(cv::Mat &img){
    Mat roixiao=imread("E:/BaiduNetdiskDownload/83.png");
    int inNum=roixiao.rows;
            copyMakeBorder(img, img, inNum, 0, 0, 0, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0, 0));
            Rect r(0, 0, img.cols, inNum);
            roixiao.copyTo(img(r));
            imshow("charu",img);
}
void QuickDemo::caitu(){
    string path="D:/data/T02023413003/4.13/*.*";
    vector<string> chucun;
    glob(path,chucun);

    for (int i=0;i<chucun.size();++i) {
       Mat tupain=imread(chucun[i]);
       double bili=600.f/tupain.cols;
       int gao=tupain.rows*bili;
       Mat s1;
       cv::resize(tupain,s1,Size(600,gao),0,0,INTER_AREA);
       string imges1name=chucun[i].substr(chucun[i].rfind("\\I")+1);
       imwrite("E:/Desktop/420-2/"+imges1name,s1);
    }
}
void QuickDemo::jiamingzi(){
    string path="E:\\Desktop\\bianren\\match_imgs\\*.bmp";
    vector<string> chucun;
    glob(path,chucun);

    for (int i=0;i<chucun.size();++i) {
       Mat tupain=imread(chucun[i]);
       string imges1name=chucun[i].substr(chucun[i].rfind("\\img")+1);
       int pos=imges1name.find("b.");
       if(pos!=-1){
           imges1name.replace(pos,1,"a_std");
       }
       imwrite("E:/Desktop/bianren/"+imges1name,tupain);
    }
}
void QuickDemo::caituzidongban(){
    auto start = std::chrono::steady_clock::now();
    string cucunlujing="E:/Desktop/";
    string path="D:/data/T02023427001/ct/*.*";
    vector<string> chucun;
    glob(path,chucun);
    size_t pos=path.rfind("/",path.rfind("/")-1);
    string wenjianjiaming=path.substr(pos+1,path.rfind("/")-pos-1);
    cucunlujing=cucunlujing+wenjianjiaming+"_resize/";
    if (_access(cucunlujing.c_str(), 0)==-1)//返回值为-1,表示不存在
    {
//        printf("不存在,创建\n");
        _mkdir(cucunlujing.c_str());
    }
        for (int i=0;i<chucun.size();++i) {
           Mat tupain=imread(chucun[i]);
           double bili=600.f/tupain.cols;
           int gao=tupain.rows*bili;
           Mat s1;
           cv::resize(tupain,s1,Size(600,gao),0,0,INTER_AREA);
           string imges1name=chucun[i].substr(chucun[i].rfind("\\I")+1);
           imwrite(cucunlujing+imges1name,s1);
       }
       auto end = std::chrono::steady_clock::now();
       auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
       std::cout << "代码运行时间:" << duration << "毫秒" << std::endl;
}

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

opencv学习笔记 的相关文章

随机推荐

  • lazarus开发应用提供http接口

    lazarus开发应用提供http接口 简单试用了一下fphttpapp 发现非常易用 直接支持中文内容 引用3个核心单元 fphttpapp httpdefs httproute 启用端口定义路由 procedure route1 aRe
  • mysql8.0查看用户_MySQL 8.0用户和角色管理

    MySQL8 0新加了很多功能 其中在用户管理中增加了角色的管理 默认的密码加密方式也做了调整 由之前的sha1改为了sha2 同时加上5 7的禁用用户和用户过期的设置 这样方面用户的管理和权限的管理 也增加了用户的安全性 MySQL8 0
  • pytorch的序列化

    PyTorch是一个基于Python的开源机器学习框架 序列化是指将模型 张量或其他Python对象转换为一种可存储的格式 以便于在后续的时间点进行加载 重用或共享 通过序列化 可以将模型保存到磁盘上 方便后续再次加载和使用 具体来说 Py
  • 【Reacte】 React 嵌入JS表达式 、条件渲染 、数组列表渲染 、样式处理

    嵌入JS表达式 语法 js表达式 let content 插入的内容 let h1 h1 我是通过JSX创建的元素 content h1 描述 1 只要是合法的js表达式都可以进行嵌入 2 JSX自身就是JS表达式 注意 语法是单花括号 不
  • 操作系统-c语言实现空闲块表的存储空间的分配和回收 ...

    include
  • SQL查询表中最后一条数据

    SQL查询表中最后一条数据 文章目录 SQL查询表中最后一条数据 准备数据表 查询最后一条数据 准备数据表 建立student表 并插入几条数据 查询最后一条数据 本文共分为三种方式 max id 函数 select from studen
  • How to be a under-graduate student

    1 想法去搜集这种记载的文字 所以研究生要学会去搜集资料或信息 2所以我们要看很多的资料 看资料是我们研究生阶段特别重要的 不要轻视了 3 现在培养研究生 就是培养你们要勇于拓宽科技新领域 到第一线去干4论文记载了一个研究课题的过程和结论
  • 电容的频率特性曲线

    电容的频率特性曲线 电容 电容 Capacitance 亦称作 电容量 是指在给定电位差下的电荷储藏量 记为C 国际单位是法拉 F 一般来说 电荷在电场中会受力而移动 当导体之间有了介质 则阻碍了电荷移动而使得电荷累积在导体上 造成电荷的累
  • python-opencv计算重叠矩形面积IOU

    import cv2 import numpy as np def calc riou r1 r2 r1 np array r1 r2 np array r2 rect1 r1 0 r1 1 r1 2 r1 3 r1 4 rect2 r2
  • Redis第二十四讲 Redis集群如何确保数据能被插入到同一个哈希槽与集群的哈希槽为什么是16384

    为什么哈希槽是16384 Redis 集群并没有使用一致性hash 而是引入了哈希槽的概念 Redis 集群有16384个哈希槽 每个key通过CRC16校验后对16384取模来决定放置哪个槽 集群的每个节点负责一部分hash槽 但为什么哈
  • 25_Vue3路由-VueRouter的基本使用及动态路由和路由嵌套

    Vue3路由之Vue router的基本使用及路由嵌套和动态路由 认识前端路由 路由其实是网络工程中的一个术语 在架构一个网络时 非常重要的两个设备就是路由器和交换机 当然 目前在我们生活中路由器也是越来越被大家所熟知 因为我们生活中都会用
  • 代理模式 与装饰模式的区别 干货

    装饰器模式关注于在一个对象上动态的添加方法 然而代理模式关注于控制对对象的访问 换句话 说 用代理模式 代理类 proxy class 可以对它的客户隐藏一个对象的具体信息 因此 当使用代理模式的时候 我们常常在一个代理类中创建一个对象的实
  • 关于机器学习中查准率与查全率(召回率)矛盾关系的探讨

    昨天和舍友讨论查准率和查全率 很多资料中指出之所以需要F1值的原因在于准确率与召回率的关系是矛盾的 即查准率高的查全率低 查全率高的查准率低 因此需要F1值综合度量 但关于为什么两者的关系是矛盾的 一直不太理解 现在看过帖子后总结一些想法
  • ERROR: The install method you used for conda--probably either `pip install conda` or `e

    TOCERROR The install method you used for conda probably either pip install conda or easy install conda is not compatible
  • MySQL安装(全网最全最详细教程)

    目录 1 MySQL的卸载 1 1 停止MySQL服务 1 2 软件的卸载 2 MySQL的下载 安装 配置 2 1 MySQL的4大版本 2 2 软件的下载 2 3 软件的安装 2 4 软件的配置 3 多版本mysql同时安装 4 安装过
  • 西门子PLC全系列模块接线

    https www siemensplc com biancheng 10649 html
  • 最燃黑客情报官薛锋:端起AK伏特加,代表人民把坏人抓

    文 史中 一 情报专家从未凋零真相并不总是像女神一样遥不可及 在二战最为焦灼的时候 盟军迫切地想知道一件事情 德国人究竟有多少坦克 于是他们派出间谍 破译电报 对德国俘虏刑讯逼供 得出的结论是 德国每个月可以生产1000多辆坦克 可是好像哪
  • 用echo输出多个空行

    大家用shell编程 用echo输出空行的时候 通常想到就是用N个echo 如下输出三个空行 echo echo echo 虽然内心里还是觉得如上写法有些丑陋 其实 echo是还有更多用法的 这在手册里面是有说明的 通过查看手册 我们可以知
  • 深度学习系列资料总结

    作者简介 CSDN 阿里云人工智能领域博客专家 新星计划计算机视觉导师 百度飞桨PPDE 专注大数据与AI知识分享 公众号 GoAI的学习小屋 免费分享书籍 简历 导图等 更有交流群分享宝藏资料 关注公众号回复 加群 或 链接 加群 专栏推
  • opencv学习笔记

    include quickdemo h include shuzu h include