OpenCV基础应用20例

Posted 啥都想干好&&啥都干不好

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV基础应用20例相关的知识,希望对你有一定的参考价值。

        本人呢,是一位大二的学生,前几天参加全国大学生智能制造挑战赛——企业命题方向,有幸取得全国初赛第一名,本来在8月15号就要进行比赛了。但是为了响应国家疫情防控,赛方也是决定延期比赛。于是现在我就在家休息了;闲暇之余呢就看了一下Opencv的教程,之前呢我其实没有涉及人工智能这一块,主要一直在学习linux系统开发,和驱动开发这一块,但是由于比赛需要,并且最近人工智能实在太火。我就也来学习了。本人菜鸡一名,还望CSDN上的大佬们多发文章,让我们菜鸟们也能学习学习。

        本人仅仅是把CSDN当作自己的笔记,同时也鼓励一下自己的学习,同时也能帮助需要帮助的人。

/*********************************************************
*               Mat的基本用法
**********************************************************/
void my_test01(void) {

    //一切图像皆Mat
    Mat src = imread("D:/图片/519期/my_prc.jpg");
    
    Mat mat1 = src.clone();                             //深拷贝

    Mat mat2;
    src.copyTo(mat2);                                   //深拷贝

    Mat mat3 = src;                                     //赋值只是让mat3指向src,并没有拷贝一份,应该是引用;

    Mat mat4 = Mat::zeros(src.size(),src.type());
    Mat mat5 = Mat::zeros(Size(512,512), CV_8UC3);      //CV_8UC3无符号的八位三通道的图片,每一个像素点由三个8位无符号的数表示
    Mat mat6 = Mat::ones(Size(512, 512), CV_8UC3);      //初始化每个像素点为一

    Mat kernel = (Mat_<char>(3,3) << 0, -1, 0,
        -1, 5, -1,
        0, -1, 0);                                      //以矩阵的方式创建图片

    std::cout << "width:"<<mat5.cols<<std::endl;        //获取图片的宽度
    std::cout << "height:" << mat5.rows << std::endl;   //获取图片的高度
    std::cout << "channels:" << mat5.channels() << std::endl;   //获取图片的通道数

    mat5 = Scalar(100,100,100);                         //分别给每个通道赋值100;
}

/*********************************************************
*               Mat对象的遍历方法
**********************************************************/
void my_test02(void) {

    Mat mat = Mat::zeros(Size(512, 512), CV_8UC1);
    mat = Scalar(100);                         //分别给每个通道赋值100;
    imshow("test02", mat);


    int w = mat.cols;
    int h = mat.rows;
    int ch = mat.channels();

#if 0
    //使用数组的方法进行操作,效率更高
    for (int row = 0; row < h; row++){
        for (int col = 0; col < w; col++) {
            if (ch == 1) {                          //灰度图像处理
                uchar pv = mat.at<uchar>(row, col);
                mat.at<uchar>(row, col) = 255 - pv; //图像颜色取反
            }
            else if (ch == 3) {                     //彩色图像处理
                Vec3b bgr = mat.at<Vec3b>(row, col);        //Vec3b专门保存三个通道的数据类型
                mat.at<Vec3b>(row, col)[0] = 255 - bgr[0];  //图像颜色取反
                mat.at<Vec3b>(row, col)[1] = 255 - bgr[1];  //图像颜色取反
                mat.at<Vec3b>(row, col)[2] = 255 - bgr[2];  //图像颜色取反
            }
        }
    }
#endif

#if 1
    //使用指针的方法进行操作,效率更高
    for (int row = 0; row < h; row++) {
        uchar* current_row = mat.ptr<uchar>(row);
        for (int col = 0; col < w; col++) {
            if (ch == 1) {                              //灰度图像处理
                *current_row++ = 255 - *current_row;    //图像颜色取反
            }
            else if (ch == 3) {                         //彩色图像处理
                *current_row++ = 255 - *current_row;    //图像颜色取反
                *current_row++ = 255 - *current_row;    //图像颜色取反
                *current_row++ = 255 - *current_row;    //图像颜色取反
            }
        }
    }
#endif
    imshow("test03", mat);
    waitKey(0);                             //有按键事件触发就继续执行
}

/*********************************************************
*               Mat的算数操作
**********************************************************/
void my_test03(void) {

    Mat src = Mat::zeros(Size(512, 512), CV_8UC3);
    src = Scalar(100,150,200);
    Mat src2 = Mat::zeros(Size(512, 512), CV_8UC3);
    src2 = Scalar(2, 2, 2);
    Mat dst;

    //dst = src + Scalar(2,2,2);            //加运算,add()
    //dst = src - Scalar(2, 2, 2);          //减运算,subtract()
    multiply(src, src2, dst);               //乘法运算
    //divide(src, src2, dst);                 //除法运算

    //saturate_cast<uchar>(ch1+ch2);        //saturate_cast<>()可以保证后面括号内数据运算后的范围还在所定义类型范围之内
    
    imshow("test04", src);
    imshow("test05", dst);
    waitKey(0);                             //有按键事件触发就继续执行
}

/*********************************************************
*               滚动条回调函数
**********************************************************/
static void on_track(int b,void *userdata) {
    Mat image = *((Mat*)userdata);
    Mat m = Mat::zeros(image.size(), image.type());
    Mat dst1 = Mat::zeros(image.size(), image.type());
    m = Scalar(b, b, b);
    subtract(image,m,dst1);
    //namedWindow("test_opencv_track2", CV_WINDOW_AUTOSIZE);
    imshow("test_opencv_track1", dst1);
}

/*********************************************************
*               Mat的GUI编程,方便调试通过滚动条调节亮度
**********************************************************/
void my_test04(void) {

    Mat src = imread("D:/图片/opencv_prc/马里奥.jpg");

    namedWindow("test_opencv_track1", CV_WINDOW_AUTOSIZE);           //生成一个窗口,大小与图片一致
    //imshow("test_opencv_track", src);                               //在指定窗口展示图片
    int lightness = 50;
    int max_value = 100;

    //滚动条创建函数,1.滚动条名称。2.滚动条所在窗口3.传递的一个参数4.滚动条最大值5.回调函数6.第二个传递参数
    createTrackbar("Value Bar","test_opencv_track1",&lightness, max_value,on_track,(void*)(&src));
    on_track(50,&src);                      //调用回调函数
    waitKey(0);                             //有按键事件触发就继续执行

}

/*********************************************************
*				按键的使用
**********************************************************/
void my_test05(void) {

    Mat src = imread("D:/图片/opencv_prc/马里奥.jpg");
    namedWindow("test_opencv_key", CV_WINDOW_AUTOSIZE);
    imshow("test_opencv_key", src);

    while (true)
    {
        int c = waitKey(100);       //Opencv使用的键值是ASCII码,必须要在Opencv创建的窗口上按才有效果;
                                    //100 是按键等待时间100ms
        if (c == 27)
        {
            break;
        }
        else if (c == 49)
        {
            std::cout << "您按下了 # 1"<< std::endl;
        }
        std::cout << c << std::endl;
    }

}

/*********************************************************
*				Opencv颜色表的使用
**********************************************************/
void my_test06(void) {

    int color_style[] = {   //Opencv支持的所有颜色风格
    COLORMAP_AUTUMN ,       //!< ![autumn](pics/colormaps/colorscale_autumn.jpg)
    COLORMAP_BONE ,         //!< ![bone](pics/colormaps/colorscale_bone.jpg)
    COLORMAP_JET ,          //!< ![jet](pics/colormaps/colorscale_jet.jpg)
    COLORMAP_WINTER ,       //!< ![winter](pics/colormaps/colorscale_winter.jpg)
    COLORMAP_RAINBOW ,      //!< ![rainbow](pics/colormaps/colorscale_rainbow.jpg)
    COLORMAP_OCEAN ,        //!< ![ocean](pics/colormaps/colorscale_ocean.jpg)
    COLORMAP_SUMMER ,       //!< ![summer](pics/colormaps/colorscale_summer.jpg)
    COLORMAP_SPRING ,       //!< ![spring](pics/colormaps/colorscale_spring.jpg)
    COLORMAP_COOL ,         //!< ![cool](pics/colormaps/colorscale_cool.jpg)
    COLORMAP_HSV ,          //!< ![HSV](pics/colormaps/colorscale_hsv.jpg)
    COLORMAP_PINK ,         //!< ![pink](pics/colormaps/colorscale_pink.jpg)
    COLORMAP_HOT ,          //!< ![hot](pics/colormaps/colorscale_hot.jpg)
    COLORMAP_PARULA         //!< ![parula](pics/colormaps/colorscale_parula.jpg)
    };

    //applyColorMap();
    Mat src = imread("D:/图片/opencv_prc/马里奥.jpg");
    namedWindow("test_opencv_key", CV_WINDOW_AUTOSIZE);
    imshow("test_opencv_key", src);

    Mat dst = Mat::zeros(src.size(), src.type());

    while (true)
    {
        int c = waitKey(100);       //Opencv使用的键值是ASCII码,必须要在Opencv创建的窗口上按才有效果;
                                    //100 是按键等待时间100ms
        if (c == 27)
        {
            break;
        }
        else if (c == 49)
        {
            applyColorMap(src, dst, COLORMAP_JET);
        }
        imshow("test_opencv_key", dst);
    }

}


/*********************************************************
*				Opencv的位操作、与或非
**********************************************************/
void my_test07(void) {

    Mat mat1 = Mat::zeros(Size(256, 256), CV_8UC3);
    Mat mat2 = Mat::zeros(Size(256, 256), CV_8UC3);

    rectangle(mat1, Rect(100, 100, 80, 80), Scalar(255, 255, 0), -1, LINE_8, 0);        //绘制矩形Rect()矩形大小和位置,Scalar()矩形颜色,-1实心填充,大于0的表示边框的宽度
    rectangle(mat2, Rect(150, 150, 80, 80), Scalar(0, 255, 255), -1, LINE_8, 0);
    
    Mat dst;
    //namedWindow("mat1", CV_WINDOW_AUTOSIZE);
    //namedWindow("mat2", CV_WINDOW_AUTOSIZE);
    imshow("mat1", mat1);
    imshow("mat2", mat2);
    //bitwise_and(mat1, mat2, dst);           //对图像的三个通道取交集;
    //bitwise_or(mat1, mat2, dst);              //对图像的三个通道取并集;
    bitwise_not(mat1, dst);                     //对图像的三个通道取非;
    //bitwise_xor(mat1, mat2, dst);             //对图像的三个通道取交集;
    imshow("dst", dst);
    waitKey(0);                             //有按键事件触发就继续执行
}

/*********************************************************
*				Opencv通道的分离与合并
*               三个通道其实就是三种颜色的灰度图像
**********************************************************/
void my_test08(void) {

    //通道分离
    std::vector<Mat> mv;

    Mat src = imread("D:/图片/opencv_prc/马里奥.jpg");
    split(src, mv);                         //通道分离API

    imshow("原图", src);
    imshow("蓝色", mv[0]);
    imshow("绿色", mv[1]);
    imshow("红色", mv[2]);

    Mat dst;
    mv[0] = 0;
    //mv[1] = 0;
    merge(mv, dst);                         //通道合并API
    imshow("红色1", dst);

    int from_to[] = {0,2,1,1,2,0};                      //用于指明通道混合的方式,那个通道和那个通道交换;
    mixChannels(&src, 1,&dst, 1, from_to, 3);                           //通道调换API
    imshow("通道混合", dst);

    waitKey(0);                                                         //有按键事件触发就继续执行
}

/*********************************************************
*				Opencv图像空间转化,人物识别
*               HSV色彩空间,色调(H),饱和度(S),明度(V)
**********************************************************/
void my_test09(void) {

    Mat src = imread("D:/图片/opencv_prc/马里奥.jpg");
    Mat hsv;
    cvtColor(src, hsv,COLOR_BGR2HSV);

    Mat mask;
    inRange(hsv,Scalar(20,20,20), Scalar(200,200,150),mask);           //色彩二值化操作,1.输入的HSV格式的图片2.HSV颜色的下限3.HSV颜色的上限4.输出的RGB图片
    imshow("色彩空间", mask);

    Mat dst;
    src.copyTo(dst,mask);                           //把mask中像素值不为一的部分从src中拷贝到dst中,精髓所在
    waitKey(0);                                                         //有按键事件触发就继续执行
}


/*********************************************************
*				Opencv图像像素值统计,均值,方差等
**********************************************************/
void my_test10(void) {

    std::vector<Mat> mv;
    Mat src = imread("D:/图片/opencv_prc/马里奥.jpg");
    double minv, maxv;
    Point minloc, maxloc;
    split(src, mv);

    for (int i = 0; i < mv.size(); i++)
    {
        minMaxLoc(mv[i], &minv, &maxv, &minloc, &maxloc);
        std::cout <<"通道数:" <<i << "  minv: " << minv << "  maxv: " << maxv << std::endl;
    }

    Mat mean, stddev;
    meanStdDev(src, mean, stddev);
    std::cout << "均值: " << mean <<"\\n" << "方差: " << stddev << std::endl;
    std::cout << "均值: " << mean.at<double>(2,0) << std::endl;                   //单独输出每个通道的值
                                //mean.at<double>(2,0), 第一个是通道数,第二个是0固定
}


/*********************************************************
*				Opencv图像几何形状的绘制
**********************************************************/
void my_test11(void) {

    Mat src = imread("D:/图片/opencv_prc/马里奥.jpg");
    Rect rect;

    rect.x = 100;
    rect.y = 100;
    rect.width = 300;
    rect.height = 300;

    //rectangle(src, rect, Scalar(0, 0, 255), 3, 8, 0);                 //绘制矩形

    //circle(src, Point(350,400), 15, Scalar(255 ,0 ,0), 3 , 8, 0);     //绘制圆形

    Mat dst;
    Mat bg = Mat::zeros(src.size(), src.type());

    line(bg , Point(100, 100), Point(400, 400), Scalar(255, 0, 255), 4, 8, 0);
    rectangle(bg, rect, Scalar(0, 255, 0), -1, 8, 0);                   //绘制实心矩形

    addWeighted(src , 0.7, bg, 0.3, 0, dst);                            //对两张照片进行合并,并设置透明度

    imshow("形状绘制", dst);
}



/*********************************************************
*				Opencv像素类型转化于归一化
**********************************************************/
void my_test12(void) {

    Mat src = imread("D:/图片/opencv_prc/马里奥.jpg");
    Mat dst;

    std::cout << "数据类型1: " << src.type()<< std::endl;

    src.convertTo(src,CV_32F);                              //数据类型转化 CV_32F转化成32位的浮点数据,CV_32S 转化成32位的整型数据 

    std::cout << "数据类型2: " << src.type() << std::endl;

    normalize(src, dst, 1.0,0, NORM_MINMAX);                //数据归一化处理,转化成浮点数据后必须进行归一化处理之后才能正常显示

    std::cout << "数据类型3: " << dst.type() << std::endl;

    imshow("归一化处理", dst);
}


/*********************************************************
*				Opencv图像的放缩与插值
**********************************************************/
void my_test13(void) {

    Mat src = imread("D:/图片/opencv_prc/马里奥.jpg");
    Mat zoomin, zoomax;

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

    resize(src, zoomin, Size(w/2,h/2), 0, 0, INTER_LINEAR);         //图像的放大 INTER_LINEAR 计算方式,还有很多方式,这是最简单的一种
    imshow("图像的缩小:", zoomin);

    resize(src, zoomax, Size(w*1.5, h*1.5), 0, 0, INTER_LINEAR);        
    imshow("图像的放大:", zoomax);
}

/*********************************************************
*				Opencv图像的翻转
**********************************************************/
void my_test14(void) {
    
    Mat src = imread("D:/图片/opencv_prc/马里奥.jpg");
    Mat dst;

    flip(src, dst, 0);          //上下镜像
    //flip(src, dst, 1);        //左右镜像
    //flip(src, dst, -1);       //旋转180°
    imshow("图像反转:", dst);
}

 
/*********************************************************
*				Opencv图像的旋转
**********************************************************/
void my_test15(void) {

    Mat src = imread("D:/图片/opencv_prc/马里奥.jpg");
    Mat M, dst;

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

    M = getRotationMatrix2D(Point2f(w/2, h/2), 45,1.0);         //先用一个模板旋转45°

    //计算旋转后新的宽高
    double cos = abs(M.at<double>(0, 0));
    double sin = abs(M.at<double>(0, 1));
    int nw = cos * w + sin * h;
    int nh = sin * w + cos * h;
    M.at<double>(0, 2) += (nw / 2 - w / 2);
    M.at<double>(1, 2) += (nh / 2 - h / 2);

    warpAffine(src, dst, M, Size(nw, nh), INTER_LINEAR, 0, Scalar(255, 200, 100));      //图片旋转API

    imshow("旋转演示", dst);
}


/*********************************************************
*				Opencv视频的读取
**********************************************************/
void my_test16(void) {
    
    VideoCapture capture(0);           //定义一个视频对象从摄像头中读取图像信息 
    //VideoCapture capture("D:/图片/opencv_prc/马里奥.jpg");       //放入一个视频文件的路径也可以
    
    Mat frame;

    while (true)
    {
        capture.read(frame);
        flip(frame, frame, 1);

        if (frame.empty())
        {
            break;
        }

        imshow("frame",frame);

        int c = waitKey(1);
        if (c == 27)
        {
            break;
        }
    }
    capture.release();              //手动释放相机
}


/*********************************************************
*				Opencv视频的属性
**********************************************************/
void my_test17(void) {

    VideoCapture capture("D:/文件/非标擂台格斗/初赛作品/驭梦队作品视频.mp4");           //定义一个视频对象从摄像头中读取图像信息 

    int frame_w = capture.get(CAP_PROP_FRAME_WIDTH);            //宽度
    int frame_h = capture.get(CAP_PROP_FRAME_HEIGHT);           //高度
    int frame_zhen = capture.get(CAP_PROP_FRAME_COUNT);         //总共多少张图片
    double fps = capture.get(CAP_PROP_FPS);                     //帧率,每秒多少张图片

    std::cout << " frame_w: " << frame_w << " frame_h: " << frame_h << " frame_zhen: " << frame_zhen << " fps: " << fps << std::endl;

    VideoWriter writer("D:/图片/opencv_prc/my_Opencv.mp4", capture.get(CAP_PROP_FOURCC), fps, Size(frame_w, frame_h), true);             //保存视频初始化函数,指明路径,要保存的视频,以及大小

    Mat frame;

    while (true)
    {
        capture.read(frame);
        //flip(frame, frame, 1);          //以X轴翻转视频

        if (frame.empty())
        {
            break;
        }

        writer.write(frame);            //一帧一帧的对图片进行保存,就是视频

        imshow("frame", frame);

        int c = waitKey(1);
        if (c == 27)
        {
            break;
        }
    }
    capture.release();              //手动释放相机
    writer.release();               //手动释放保存
}


/*********************************************************
*		Opencv图像直方图,每个通道上,每个灰度值有多少个点
*       一个图像平移,旋转,放大缩小,直方图数据都不会变,但是不能唯一表示图片;
**********************************************************/
void my_test18(void) {

    Mat src = imread("D:/图片/opencv_prc/马里奥.jpg");
    imshow("原图", src);
    //通道分离
    std::vector<Mat> bgr_plane;
    split(src, bgr_plane);

    //设置基本参数
    const int channels[1] = { 0 };
    const int bins[1] = { 256 };        //分成多少份,X轴
    float hranges[2] = { 0, 255 };      //第一个通道的取值范围
    const float* ranges[1] = { hranges };

    //定义存储对象
    Mat b_hist;
    Mat g_hist;
    Mat r_hist;

    //统计像素点个数
    calcHist(&bgr_plane[0], 1, 0, Mat(), b_hist, 1, bins, ranges);      //倒数第三个数,表示通道数
    calcHist(&bgr_plane[1], 1, 0, Mat(), g_hist, 1, bins, ranges);
    calcHist(&bgr_plane[2], 1, 0, Mat(), r_hist, 1, bins, ranges);

    //画布大小定义
    int hist_w = 700;
    int hist_h = 550;
    int bin_W = cvRound((double)hist_w / bins[0]);
    Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);

    //图像值归一化处理
    normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
    normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
    normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());

    //绘制直方图
    for (int i = 1; i < bins[0]; i++)
    {
        line(histImage, Point(bin_W * (i - 1), hist_h - cvRound(b_hist.at<float>(i - 1))),
            Point(bin_W * (i), hist_h - cvRound(b_hist.at<float>(i))), Scalar(255, 0, 0), 2, 8, 0);
        line(histImage, Point(bin_W * (i - 1), hist_h - cvRound(g_hist.at<float>(i - 1))),
            Point(bin_W * (i), hist_h - cvRound(g_hist.at<float>(i))), Scalar(0, 255, 0), 2, 8, 0);
        line(histImage, Point(bin_W * (i - 1), hist_h - cvRound(r_hist.at<float>(i - 1))),
            Point(bin_W * (i), hist_h - cvRound(r_hist.at<float>(i))), Scalar(0, 0, 255), 2, 8, 0);
    }

    //显示直方图
    namedWindow("图像直方图", WINDOW_AUTOSIZE);
    imshow("图像直方图", histImage);
}


/*********************************************************
*				Opencv二维直方图,HSV色彩空间
**********************************************************/
void my_test19(void) {

    Mat src = imread("D:/图片/opencv_prc/马里奥.jpg");
    Mat hsv, hs_hist;

    cvtColor(src, hsv, COLOR_BGR2HSV);          //转化色彩空间

    int hbins = 30, sbins = 32;
    int hist_bins[] = { hbins, sbins };
    float h_range[] = { 0, 180 };
    float s_range[] = { 0, 256 };
    const float* hs_ranges[] = { h_range , s_range };
    int hs_channels[] = { 0, 1 };

    calcHist(&hsv, 1, hs_channels, Mat(), hs_hist, 2, hist_bins, hs_ranges, true, true);        //计算直方图数据

    double maxVal = 0;

    minMaxLoc(hs_hist, 0, &maxVal, 0, 0);               //计算出最大值

    int scale = 10;

    Mat hist2d_image = Mat::zeros(sbins * scale, hbins * scale, CV_8UC3);

    for (int h = 0; h < hbins; h++)                     //绘制2D直方图
    {
        for (int s = 0; s < sbins; s++)
        {
            float binVal = hs_hist.at<float>(h, s);
            int intensity = cvRound(binVal * 255 / maxVal);
            rectangle(hist2d_image, Point(h * scale, s * scale), 
                Point((h + 1) * scale - 1, (s + 1) * scale - 1), 
                Scalar::all(intensity), -1);
        }
    }

    applyColorMap(hist2d_image, hist2d_image, COLORMAP_JET);        //把灰度图转化为彩色图
    imshow("2D直方图", hist2d_image);
    imwrite("D:/图片/opencv_prc/2D直方图.png", hist2d_image);        //保存图片
}


/*********************************************************
*				Opencv直方图均衡化
**********************************************************/
void my_test20(void) {

    Mat src = imread("D:/图片/opencv_prc/马里奥.jpg");
    Mat dst;

    cvtColor(src, dst, COLOR_BGR2GRAY);
    imshow("直方图均衡化之前", dst);

    equalizeHist(dst, dst);                         //均衡化API
    imshow("直方图均衡化之后", dst);
}


/*********************************************************
*		Opencv图像的线性卷积操作:图像对比度减小,变模糊
**********************************************************/
void my_test21(void) {
    
    Mat src = imread("D:/图片/opencv_prc/马里奥.jpg");
    imshow("图像模糊之前", src);
    Mat dst;
    blur(src, dst, Size(10, 10), Point(-1, -1));        //卷积API
    imshow("图像模糊之后", dst);
}


/*********************************************************
*				Opencv的高斯模糊,中心卷积
**********************************************************/
void my_test22(void) {

    Mat src = imread("D:/图片/opencv_prc/马里奥.jpg");
    imshow("图像模糊之前", src);
    Mat dst;

    GaussianBlur(src, dst, Size(11, 11), 30);           //Size()里面的值必须是奇数,最后一个数越大越模糊

    imshow("图像模糊之后", dst);
}

以上是关于OpenCV基础应用20例的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV 完整例程78. 频率域图像滤波基础

OpenCV 完整例程20. 图像的按位运算

在 Python 多处理进程中运行较慢的 OpenCV 代码片段

单例片段或保存网页视图状态

OpenCV 完整例程67. 空间域图像增强的综合应用

OpenCV 完整例程86. 频率域滤波应用:指纹图像处理