找回密码
 加入慢享
猜你喜欢
旅行常客论坛

OpenCV中积分图介绍与应用

[复制链接]
发表于 2022-6-28 23:09:17 | 显示全部楼层 |阅读模式

点击上方蓝字关注我们

微信公众号:OpenCV学堂

关注获取更多计算机视觉与深度学习知识

OpenCV中积分图函数与应用

一:图像积分图概念

积分图像是Crow在1984年首次提出,是为了在多尺度透视投影中提高渲染速度。随后这种技术被应用到基于NCC的快速匹配、对象检测和SURF变换中、基于统计学的快速滤波器等方面。积分图像是一种在图像中快速计算矩形区域和的方法,这种算法主要优点是一旦积分图像首先被计算出来我们可以计算图像中任意大小矩形区域的和而且是在常量时间内。这样在图像模糊、边缘提取、对象检测的时候极大降低计算量、提高计算速度。第一个应用积分图像技术的应用是在Viola-Jones的对象检测框架中出现。

上图左侧四个点的矩形区域像素求和,只要根据每个点左上方所有像素和表值,进行两次减法与一次加法即可=》46 – 22 – 20 + 10 = 14

二:OpenCV中积分图函数

OpenCV中通过integral()函数可以很容易的计算图像的积分图,该函数支持和表积分图、平方和表积分图、瓦块和表积分图计算。integral函数与参数解释如下:

  1. void cv::integral(

  2.    InputArray  src, // 输入图像

  3.    OutputArray  sum, // 和表

  4.    OutputArray  sqsum, // 平方和表

  5.    OutputArray  tilted, // 瓦块和表

  6.    int     sdepth = -1, // 和表数据深度常见CV_32S

  7.    int     sqdepth = -1 // 平方和表数据深度 常见 CV_32F

  8. )

三:使用积分图函数

通过代码演示计算积分图实现任意窗口大小的盒子模糊与垂直边缘提取,完整的代码实现如下:

  1. #include <opencv2/opencv.hpp>

  2. #include <iostream>

  3. using namespace cv;

  4. using namespace std;

  5. void blur_demo(Mat &image, Mat &sum);

  6. void edge_demo(Mat &image, Mat &sum);

  7. int getblockSum(Mat &sum, int x1, int y1, int x2, int y2, int i);

  8. int main(int argc, char** argv) {

  9.    Mat src = imread("D:/vcprojects/images/yuan_test.png");

  10.    if (src.empty()) {

  11.        printf("could not load image...
    "
    );

  12.        return -1;

  13.    }

  14.    namedWindow("input", CV_WINDOW_AUTOSIZE);

  15.    imshow("input", src);

  16.    namedWindow("output", CV_WINDOW_AUTOSIZE);

  17.    // 计算积分图

  18.    Mat sum, sqrsum;

  19.    integral(src, sum, sqrsum, CV_32S, CV_32F);

  20.    // 积分图应用

  21.    int type = 0;

  22.    while (true) {

  23.        char c = waitKey(100);

  24.        if (c > 0) {

  25.            type = (int)c;

  26.            printf("c : %d
    "
    , type);

  27.        }

  28.        if (c == 27) {

  29.            break; // ESC

  30.        }

  31.        if (type == 49) { // 数字键 1

  32.            blur_demo(src, sum);

  33.        }

  34.        else if (type == 50) { // 数字键 2

  35.            edge_demo(src, sum);

  36.        }

  37.        else {

  38.            blur_demo(src, sum);

  39.        }

  40.    }

  41.    waitKey(0);

  42.    return 0;

  43. }

  44. void blur_demo(Mat &image, Mat &sum) {

  45.    int w = image.cols;

  46.    int h = image.rows;

  47.    Mat result = Mat::zeros(image.size(), image.type());

  48.    int x2 = 0, y2 = 0;

  49.    int x1 = 0, y1 = 0;

  50.    int ksize = 5;

  51.    int radius = ksize / 2;

  52.    int ch = image.channels();

  53.    int cx = 0, cy = 0;

  54.    for (int row = 0; row < h + radius; row++) {

  55.        y2 = (row + 1)>h ? h : (row + 1);

  56.        y1 = (row - ksize) < 0 ? 0 : (row - ksize);

  57.        for (int col = 0; col < w + radius; col++) {

  58.            x2 = (col + 1)>w ? w : (col + 1);

  59.            x1 = (col - ksize) < 0 ? 0 : (col - ksize);

  60.            cx = (col - radius) < 0 ? 0 : col - radius;

  61.            cy = (row - radius) < 0 ? 0 : row - radius;

  62.            int num = (x2 - x1)*(y2 - y1);

  63.            for (int i = 0; i < ch; i++) {

  64.                // 积分图查找和表,计算卷积

  65.                int s = getblockSum(sum, x1, y1, x2, y2, i);

  66.                result.at<Vec3b>(cy, cx)[i] = saturate_cast<uchar>(s / num);

  67.            }

  68.        }

  69.    }

  70.    imshow("output", result);

  71.    imwrite("D:/result.png", result);

  72. }

  73. /**

  74. * 3x3 sobel 垂直边缘检测演示

  75. */

  76. void edge_demo(Mat &image, Mat &sum) {

  77.    int w = image.cols;

  78.    int h = image.rows;

  79.    Mat result = Mat::zeros(image.size(), CV_32SC3);

  80.    int x2 = 0, y2 = 0;

  81.    int x1 = 0, y1 = 0;

  82.    int ksize = 3; // 算子大小,可以修改,越大边缘效应越明显

  83.    int radius = ksize / 2;

  84.    int ch = image.channels();

  85.    int cx = 0, cy = 0;

  86.    for (int row = 0; row < h + radius; row++) {

  87.        y2 = (row + 1)>h ? h : (row + 1);

  88.        y1 = (row - ksize) < 0 ? 0 : (row - ksize);

  89.        for (int col = 0; col < w + radius; col++) {

  90.            x2 = (col + 1)>w ? w : (col + 1);

  91.            x1 = (col - ksize) < 0 ? 0 : (col - ksize);

  92.            cx = (col - radius) < 0 ? 0 : col - radius;

  93.            cy = (row - radius) < 0 ? 0 : row - radius;

  94.            int num = (x2 - x1)*(y2 - y1);

  95.            for (int i = 0; i < ch; i++) {

  96.                // 积分图查找和表,计算卷积

  97.                int s1 = getblockSum(sum, x1, y1, cx, y2, i);

  98.                int s2 = getblockSum(sum, cx, y1, x2, y2, i);

  99.                result.at<Vec3i>(cy, cx)[i] = saturate_cast<int>(s2 - s1);

  100.            }

  101.        }

  102.    }

  103.    Mat dst, gray;

  104.    convertScaleAbs(result, dst);

  105.    normalize(dst, dst, 0, 255, NORM_MINMAX);

  106.    cvtColor(dst, gray, COLOR_BGR2GRAY);

  107.    imshow("output", gray);

  108.    imwrite("D:/edge_result.png", gray);

  109. }

  110. int getblockSum(Mat &sum, int x1, int y1, int x2, int y2, int i) {

  111.    int tl = sum.at<Vec3i>(y1, x1)[i];

  112.    int tr = sum.at<Vec3i>(y2, x1)[i];

  113.    int bl = sum.at<Vec3i>(y1, x2)[i];

  114.    int br = sum.at<Vec3i>(y2, x2)[i];

  115.    int s = (br - bl - tr + tl);

  116.    return s;

  117. }

这里最重要的是要注意到上面的图示,积分图对象的Mat(1,1)对应实际图像Mat(0,0),如果不加处理的话会导致结果有明显的中心迁移。edge_demo实现了积分图查找提取图像边缘、blur_demo函数实现积分图查找图像均值模糊,getblockSum函数实现和表查找功能,运行显示:

原图:

模糊效果

边缘效果


为山者基于一篑之土,以成千丈之峭


扫码查看OpenCV+OpenVIO+Pytorch系统化学习路线图


 推荐阅读 

CV全栈开发者说 - 从传统算法到深度学习怎么修炼

2022入坑深度学习,我选择Pytorch框架!

Pytorch轻松实现经典视觉任务

教程推荐 | Pytorch框架CV开发-从入门到实战

OpenCV4 C++学习 必备基础语法知识三

OpenCV4 C++学习 必备基础语法知识二

OpenCV4.5.4 人脸检测+五点landmark新功能测试

OpenCV4.5.4人脸识别详解与代码演示

OpenCV二值图象分析之Blob分析找圆

OpenCV4.5.x DNN + YOLOv5 C++推理

OpenCV4.5.4 直接支持YOLOv5 6.1版本模型推理

OpenVINO2021.4+YOLOX目标检测模型部署测试

比YOLOv5还厉害的YOLOX来了,官方支持OpenVINO推理


回复

使用道具 举报

快速回复 返回顶部 返回列表