【OpenCV】 视频处理(读取视频、写入视频、目标检测、特征匹配、相机校正)

https://www.hahack.com/wiki/opencv-video.html#

参考资料:

  • 《OpenCV 2 Computer Vision Application Programming Cookbook》
  • 《The OpenCV Reference Manual》

读取视频

 使用 CV::VideoCapture 来读取视频序列。

#include 
#include 
#include 
#include 
#include 
int main()
{
    // Open the video file
    cv::VideoCapture capture("../stomp.avi");
    // check if video successfully opened
    if (!capture.isOpened())
        return 1;
    // Get the frame rate
    double rate = capture.get(CV_CAP_PROP_FPS);
    bool stop(false);
    cv::Mat frame;  // current video frame
    cv::namedWindow("Extracted Frame");
    // Delay between each frame in ms
    // corresponds to video frame rate
    int delay = 1000 / rate;
    // for all frames in video
    while (!stop){
        // read next frame if any
        if (!capture.read(frame))
            break;
        cv::imshow("Extracted Frame", frame);
        // introduce a delay
        // or press key to stop
        if (cv::waitKey(delay) >= 0)
            stop = true;
    }
    // Close the video file
    // Not required since called by destructor
    capture.release();
}

 也可以通过类似的方法读入摄像头捕捉的视频,要改动的地方仅仅是将上面的视频文件名改为摄像头的 ID,默认的摄像头 ID 为 0。

写入视频

使用 CV::VideoWriter 来写入视频。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace cv;
using namespace std;
int main()
{
    VideoCapture cap;
    cap.open(0);
    namedWindow("MyVideo", 1);
    double dWidth = cap.get(CV_CAP_PROP_FRAME_WIDTH);
    double dHeight = cap.get(CV_CAP_PROP_FRAME_HEIGHT);
    Size frameSize(static_cast(dWidth), static_cast(dHeight));
    
    VideoWriter oVideoWriter("MyVideo.avi", CV_FOURCC('P','I','M','1'), 20, frameSize, true);	// initialize the VideoWriter objetct
    while (1) {
        Mat frame;
        bool bSuccess = cap.read(frame);
        if (!bSuccess){ // if not success, break loop
            cout << "ERROR: cannot read a frame from video file" << endl;
            break;
        }
        
        oVideoWriter.write(frame);
        imshow("MyVideo", frame); // show the frame in "MyVideo" window
        if(waitKey(10) == 27) {// wait for ESC key
            cout << "ESC key is pressed by user" << endl;
            break;
        }
    }
    
    return 0;
}

 目标检测


mean-shift 和 camshift


均值漂移(mean-shift)

mean-shift 算法是一种在一组数据的密度分布中寻找局部极值的稳定 [1] 的方法。若分布是连续的,处理过程就比较容易,这种情况下本质上只需要对数据的密度直方图应用爬山算法即可。然而,对于离散的数据集,这个问题在某种程度上是比较麻烦的。

mean-shift 算法的步骤如下:

  1. 选择搜索窗口。
  2. 计算窗口(可能是带权重的)的重心。
  3. 将窗口的中心设置在计算出的重心处。
  4. 返回第 2 步,直到窗口的位置不再变化(通常会)。

OpenCV 提供 cv::meanshift() 函数来进行 mean-shift 算法跟踪。

int cv::meanShift(InputArray probImage, Rect& window, TermCriteria criteria)

其中,

  • probImage - 图像直方图反投影后的结果;
  • window - 初始的查找窗口,即要跟踪的区域;
  • criteria - 迭代搜索算法的终止条件,主要由 mean-shift 移动的最大迭代次数和可视为窗口位置收敛的最小移动距离组成。
  • 返回的是收敛时算法的迭代次数。

对于第一个参数 probImage ,可以直接使用 cv::calcBackProject() 得到的结果。但《OpenCV 2 Computer Vision Application Programming Cookbook》建议先把图像转换到 HSV 颜色空间,然后使用 Hue 单通道的直方图的反投影变换结果作为 probImage ;

获取彩色图像的 Hue 通道的直方图算法实现如下:

// Computes the 1D Hue histogram with a mask.
// BGR source image is converted to HSV
cv::MatND getHueHistogram(const cv::Mat &image,
                             int minSaturation = 0) {
    cv::MatND hist;
    // Convert to HSV color space
    cv::Mat hsv;
    cv::cvtColor(image, hsv, CV_BGR2HSV);
       // Mask to be used (or not)
       cv::Mat mask;
       if (minSaturation > 0) {
           // Spliting the 3 channels into 3 images
           std::vector v;
           cv::split(hsv, v);
           // Mask out the low saturated pixels
           cv::threshold(v[1], mask, minSaturation,
                         255, cv::THRESH_BINARY);
       }
    // Prepare arguments for a 1D hue histogram
    hranges[0]= 0.0;
    hranges[1]= 180.0;
    channels[0]= 0; // the hue channel
       // Compute histogram
    cv::calcHist(&hsv, 
        1,			// histogram of 1 image only
        channels,	// the channel used
        mask,	// no mask is used
        hist,		// the resulting histogram
        1,			// it is a 1D histogram
        histSize,	// number of bins
        ranges		// pixel value range
    );
    return hist;
}

使用 cv::meanshift() 函数在两幅图像间跟踪某一物体的步骤如下:

  1. 读入第一张图像,定义好目标跟踪窗口,即感兴趣区域 ROI 。
  2. 计算这个 ROI 的 Hue 通道的直方图;
  3. 读入第二张图像,转换到 HSV 颜色空间,并对其 Hue 分量进行阈值处理 cv::threshold(),去掉低饱和度的像素,以保证较高的饱和度;
  4. 应用 ROI 的 直方图信息对第 3 步得到的图像进行反投影变换;
  5. 对反投影变换的结果在进行一次按位与操作 cv::bitwise_and() ,去除结果中低饱和度的像素;
  6. 最后,对第 5 步得到的结果进行 meanshift 操作 cv::meanShift(),得到新的跟踪窗口。

实现如下:

int main()
{
    // Read reference image
    cv::Mat image= cv::imread("../baboon1.jpg");
    if (!image.data)
        return 0; 
    // Define ROI
    cv::Mat imageROI= image(cv::Rect(110,260,35,40));
    cv::rectangle(image, cv::Rect(110,260,35,40), cv::Scalar(0,0,255));
    // Display image
    cv::namedWindow("Image 1");
    cv::imshow("Image 1",image);
    // Get the Hue histogram
    int minSat=65;
    ColorHistogram hc;
    cv::MatND colorhist= hc.getHueHistogram(imageROI, minSat);
    ObjectFinder finder;
    finder.setHistogram(colorhist);
    finder.setThreshold(0.2f);
    // Second image
    image= cv::imread("../baboon3.jpg");
 	// Display image
    cv::namedWindow("Image 2");
    cv::imshow("Image 2",image);
    // Convert to HSV space
    cv::Mat hsv;
    cv::cvtColor(image, hsv, CV_BGR2HSV);
    // Split the image
    vector v;
    cv::split(hsv,v);
    // Eliminate pixels with low saturation
    cv::threshold(v[1],v[1],minSat,255,cv::THRESH_BINARY);
    cv::namedWindow("Saturation");
    cv::imshow("Saturation",v[1]);
    // Get back-projection of hue histogram
    int ch[1]={0};
    cv::Mat result= finder.find(hsv,0.0f,180.0f,ch,1);
    cv::namedWindow("Result Hue");
    cv::imshow("Result Hue",result);
    // Eliminate low stauration pixels
    cv::bitwise_and(result,v[1],result);
    cv::namedWindow("Result Hue and raw");
    cv::imshow("Result Hue and raw",result);
    cv::Rect rect(110,260,35,40);
    cv::rectangle(image, rect, cv::Scalar(0,0,255));
    cv::TermCriteria criteria(cv::TermCriteria::MAX_ITER,10,0.01);
    cout << "meanshift= " << cv::meanShift(result,rect,criteria) << endl;
    cv::rectangle(image, rect, cv::Scalar(0,255,0));
    // Display image
    cv::namedWindow("Image 2 result");
    cv::imshow("Image 2 result",image);
    cv::waitKey();
    return 0;
}

《The OpenCV Reference Manual》里还建议再对反投影的结果进行降噪处理。例如,可以先使用 cv::findContours() 检索到一些闭合的轮廓边,使用 cv::contourArea() 去掉一些面积较小的轮廓边,再使用 cv::drawContours() 填充剩下的轮廓边。

camshift

camshift 是“continuously adaptive mean-shift”的缩写,顾名思义就是 mean-shift 的一个改进版本,它的实现基于 meanshift ,而与 meanshift 的最大区别在于窗口的大小和朝向是可变的。如果有一个易于分割的分布(例如保持紧密的人脸特征),此算法可以根据人在走近或远离摄像机时脸的尺寸而自动调整窗口的尺寸。

OpenCV 提供 cv::Camshift() 函数来进行 mean-shift 算法跟踪。

RotatedRect CamShift(InputArray probImage, Rect& window, TermCriteria criteria)

该函数将返回一个经过旋转的矩形结构体 RotatedRect ,该结构体包含了目标的位置、大小和朝向信息。可以通过 RotatedRect::boundingRect() 函数获得查找窗口的下一个位置。

?:

OpenCV 自带的示例程序中包含一个应用 camshift 捕捉彩色物体的示例 camshiftdemo.c 。


特征匹配


Harris 角点


经典版本

void cornerHarris(InputArray src,	// input
                OutputArray dst,	// output
                int blockSize,	// neighborhood size
                int ksize,		// aperture size
                double k,			// Harris parameter
                int borderType=BORDER_DEFAULT )

示例:

// Detect Harris Corners
cv::Mat cornerStrength;
cv::cornerHarris(image,cornerStrength,
                3,	// neighborhood size
                3,	// aperture size
                0.01); // Harris parameter
// threshold the corner strengths
cv::Mat harrisCorners;
double threshold= 0.0001;
cv::threshold(cornerStrength,harrisCorners,
            threshold,255,cv::THRESH_BINARY_INV);

原图:

                                                        【OpenCV】 视频处理(读取视频、写入视频、目标检测、特征匹配、相机校正)_第1张图片

结果图:

                                                                 

与原图结合:

                                                                 【OpenCV】 视频处理(读取视频、写入视频、目标检测、特征匹配、相机校正)_第2张图片

改进版本

用上面的方法得到的 Harris 角点在图像中的分布不是很均匀,一个改进的方法是使用 cv::goodFeaturesToTrack() 方法(听名字就很彪悍),用它可以获得更加 strong 的角点:

void goodFeaturesToTrack(InputArray image,	// input 8-bit or floating-point 32-bit, single-channel image
                        OutputArray corners,	// output vector of detected corners.
                        int maxCorners,		// maximum number of corners to return
                        double qualityLevel,	// quality level
                        double minDistance,	// minimum allowed distance between points
                        InputArray mask=noArray(),// optional region of interest
                        int blockSize=3,		// size of an average block
                        bool useHarrisDetector=false, //  whether to use a classical Harris detector 
                        double k=0.04 )		// free parameter of the Harris detector

示例:

// Compute good features to track
std::vector corners;
cv::goodFeaturesToTrack(imaeg, corners,
                        500,	// maximum number of corners to return
                        0.01,	// quality level
                        10);	// minimum allowed distance between points

结果:

                                                             【OpenCV】 视频处理(读取视频、写入视频、目标检测、特征匹配、相机校正)_第3张图片

这种方法得到的 Harris 角点在分布均匀程度上得到了很明显的改进。不过,这种改进的方案计算复杂度也更高,因为它需要对检测到的特征点按照 Harris 得分进行排序。另外,如果把第 8 个参数 useHarrisDetector 的值设为 true,则用的将是经典的 Harris 检测器。

通用接口

为了方便使用这些特征检测算法,OpenCV 2 提供了特征点检测的通用接口,封装在抽象类 cv::FeatureDetector 里:

 

class CV_EXPORTS FeatureDetector
{
  public:
    virtual ~FeatureDetector();
    void detect( const Mat& image, vector& keypoints,
            const Mat& mask=Mat() ) const;
    void detect( const vector& images,
            vector >& keypoints,
            const vector& masks=vector() ) const;
    virtual void read(const FileNode&);
    virtual void write(FileStorage&) const;
    static Ptr create( const string& detectorType );
  protected:
...
};

其中:

  • create() 函数用于创建一个特征检测器,其参数 detectorType 可以是以下几种:
    • “FAST” – FastFeatureDetector
    • “STAR” – StarFeatureDetector
    • “SIFT” – SIFT (nonfree module)
    • “SURF” – SURF (nonfree module)
    • “ORB” – ORB
    • “BRISK” – BRISK
    • “MSER” – MSER
    • “GFTT” – GoodFeaturesToTrackDetector
    • “HARRIS” – GoodFeaturesToTrackDetector with Harris detector enabled
    • “Dense” – DenseFeatureDetector
    • “SimpleBlob” – SimpleBlobDetector
  • 两个 detect() 函数用于进行特征点检测;
  • read() 和 write() 函数用于将检测到的特征点进行文件读/写。

为了方便存储不同的特征点的信息,OpenCV 还为这个接口还定义了一个 Keypoint 类,用于存储特征点的所有相关属性。例如,对于 Harris 角点,该类的实例将会用来储存检测得到的特征点的位置。

另外,OpenCV 为每种特征点提供了从 cv::FeatureDetector 继承而来的子类。例如,改进的 Harris 角点对应子类 cv::GoodFeatureToTrackDetector 。使用方法如下:

// vector of keypoints
std::vector keypoints;
// Construction of the Good Feature to Track detector
cv::GoodFeatureToTrackDetector gftt (
    500,	// maximum number of corners to be returned
    0.01,	// quality level
    10);	// minimum allowed distance between points
// point detection using FeatureDetector method
gftt.detect(image, keypoints);

OpenCV 还提供了一个通用方法 cv::drawKeyPoints 用于在图像上绘制得到的特征点:

cv::drawKeyPoints(image,		// original image
                keypoints,	// vector of keypoints
                image,		// the output image
                cv::Scalar(255, 255, 255), // keypoint color
                cv::DrawMatchesFlags::DRAW_OVER_OUTIMG); // drawing flag

这些类的使用方法大同小异。

FAST 特征点

FAST 特征点是为了提高检测速度而设计的。使用示例:

// vector of keypoints
std::vector keypoints;
// Construction of the Fast feature detector object
cv::FastFeatureDetector fast(
    40);	// threshold for detection
// feature point detection
fast.detect(image, keypoints);
// Draw key points
cv::drawKeyPoints(image,		// original image
                keypoints,	// vector of keypoints
                image,		// the output image
                cv::Scalar(255, 255, 255), // keypoint color
                cv::DrawMatchesFlags::DRAW_OVER_OUTIMG); // drawing flag

                                                               【OpenCV】 视频处理(读取视频、写入视频、目标检测、特征匹配、相机校正)_第4张图片

SURF 特征点

SURF(Speeded Up Robust Features) 特征点是一种尺度不变,且运算效率快的特征点。

示例:

// vector of keypoints
std::vector keypoints;
// Construct the SURF feature detector object
cv::SurfFeatureDetector surf(
    2500.);	// threshold
// Detect the SURF features
surf.detect(image, keypoints);
// Draw the keypoints with scale and orientation information
cv::drawKeypoints(image,	// original image
                keypoints,  // vector of keypoints
                featureImage, // the resulting image
                cv::Scalar(255,255,255), // color of the points
                cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS); //flag

                                                               【OpenCV】 视频处理(读取视频、写入视频、目标检测、特征匹配、相机校正)_第5张图片

 对另一幅尺度不同的图使用 SURF 特征检测,得到结果如下:

                                                          【OpenCV】 视频处理(读取视频、写入视频、目标检测、特征匹配、相机校正)_第6张图片

仔细对比两幅图,可以发现当图像的尺度发生变化后,两幅图像存在对应的圆圈也会跟着改变大小。尽管不是全部特征点都有对应,但通常存在对应的特征点的数量已经足够用在两幅图像间的比配。

SIFT 特征点

SURF 的一个发展是另一个著名的特征点 —— SIFT (Scale-Invariant Feature Transform) 。

使用示例:

// vector of keypoints
std::vector keypoints;
// Construct the SURF feature detector object
cv::SiftFeatureDetector sift{
    0.03,	// feature threshold
    10.);	// threshold to reduce
            // sensitivity to lines
    // Detect the SURF features
    sift.detect(image, keypoints);
}

结果和 SURF 提取的结果很相似:

                                                               【OpenCV】 视频处理(读取视频、写入视频、目标检测、特征匹配、相机校正)_第7张图片

SIFT 特征点比 SURF 更精确,不过速度上也慢一些。

特征匹配

特征点的一大用处是用来进行特征匹配,例如寻找同个场景在两个不同角度的照片的对应关系。

在特征匹配中,特征描述子(feature descriptors)通常是一个用于描述一个特征点的 N 维向量,且在理想情况下该向量可以对光照的变化和小幅度的形变具有不变性。另外,描述子的好坏还可以通过一个距离方程(例如欧氏距离)来比较和度量。因此,特征描述子是特征匹配算法中的一个强有力的工具。

同样,OpenCV 2 提供了一个通用的接口类 cv::DescriptorExtractor 用于构造特征描述子:

class CV_EXPORTS DescriptorExtractor
{
  public:
    virtual ~DescriptorExtractor();
    void compute( const Mat& image, vector& keypoints,
                Mat& descriptors ) const;
    void compute( const vector& images, vector >& keypoints,
    vector& descriptors ) const;
    virtual void read( const FileNode& );
    virtual void write( FileStorage& ) const;
    virtual int descriptorSize() const = 0;
    virtual int descriptorType() const = 0;
    static Ptr create( const string& descriptorExtractorType );
  protected:
...
};

其中:

  • create() 函数用于创建一个特征描述子,其参数 detectorType 可以是以下几种:
    • “SIFT” – SIFT
    • “SURF” – SURF
    • “ORB” – ORB
    • “BRISK” – BRISK
    • “BRIEF” – BriefDescriptorExtractor
  • 两个 compute() 函数用于从一组检测得到的特征点计算特征描述子;
  • read() 和 write() 函数用于将检测到的特征点进行文件读/写;

示例:

// Construction of the SURF descriptor extractor
cv::SurfDescriptorExtractor surfDesc;
// Extraction of the SURF descriptors
cv::Mat descriptors1;
surfDesc.compute(image1,keypoints1,descriptors1);

结果是一个行数和特征点数量相同的矩阵,每一行都是一个 N 维 的描述子的 vector (以 SURF 描述子为例,N 默认为 64) 。每个 vector 可以很好的描述一个特征点周围的色值信息。两个特征点越相似,则它们的特征描述子越接近。

特征描述子在图像匹配中尤其有用。OpenCV 提供了一个最基本的匹配算法 cv::BruteForceMatcher() ,用法如下:

// Construction of the matcher
cv::BruteForceMatcher> matcher;
// Match the two image descriptors
std::vector matches;
matcher.match(descriptors1,descriptors2, matches);

同样,OpenCV 也提供了一个通用接口类 cv::DescriptorMatcher ,封装了多种匹配方法。

得到的匹配结果是一个 cv::DMatch 类型的数据结构,这个结构体是一种 pair 类型 ,每一个 pair 分别包含第一个 vector 和第二个 vector 的匹配到的元素的序号。

OpenCV 也提供了函数用于绘制匹配结果 cv::drawMatches ,下面示例将上面的代码得到的结果的前 25 个配对给绘制出来:

std::nth_element(matches.begin(),	// initial position
                matches.begin()+24, // position of the sorted element
                matches.end());	// end position
// remove all elements after the 25th
matches.erase(matches.begin()+25, matches.end());
// Visualize matches
cv::Mat imageMatches;
cv::drawMatches(
    image1,keypoints1,	// 1st image and its keypoints
    image2,keypoints2,	// 2nd image and its keypoints
    matches,				// the matches
    imageMatches,			// the image produced
    cv::Scalar(255,255,255));   // color of the lines

结果如下:

                                         【OpenCV】 视频处理(读取视频、写入视频、目标检测、特征匹配、相机校正)_第8张图片

相机校正

畸变的由来

计算机视觉领域使用的相机模型称为针孔相机模型(pin-hole camera):

                                                              

 

基本的投影方程如下:

                                                                                       {\color{Green} _{hi}}= f\frac{^{_{ho}}}{do}

其中,图像的大小 (hihi) 与物体的大小 (hoho) 沿着与焦点的距离 (dodo) 成比例关系。

根据前面所提的背景知识,我们可以清楚地认识到在针孔相机模型里的关键参数就是相机的焦距和它的成像大小。另外,因为我们要处理的是数字图像,相机的像素数量也是一个相机的另一个重要参数。最后,为了计算场景中的一点在像素坐标中的位置,我们还需要一个额外的信息:考虑那条从焦点引出的与成像平面垂直的直线,我们需要知道这条线穿入成像平面的哪个像素点,这个点称为 主要点 (principal point)。逻辑上,这个主要点应该设在成像平面的中心,但实际上这个点往往会由于机械原因,难免会偏移一些像素,这就是畸变的由来。

                                                     【OpenCV】 视频处理(读取视频、写入视频、目标检测、特征匹配、相机校正)_第9张图片

校正方法

检测棋盘角点

对相机进行畸变校正的主要思想是给相机展示一些三维空间间位置已知的点,之后寻找这些点在图像上的相应投影位置,最后根据这些对应关系计算相机的相关信息。OpenCV 建议通过从不同角度获取一个棋盘模板图像来校正相机。在这个情况下,可以利用每张棋盘图像上的角点来获取实际位置和投影位置的对应关系,从而计算相机的相关信息。一个示例棋盘模板:

                                                                       【OpenCV】 视频处理(读取视频、写入视频、目标检测、特征匹配、相机校正)_第10张图片

从上面这张图也可以很明显的看出相机存在较严重的径向畸变(radial distortion),这种畸变现象在鱼眼镜头中非常普遍。

OpenCV 提供了一个自动检测棋盘模板中的角点的函数 cv::findChessboardCorners()

bool findChessboardCorners(InputArray image,	// source chessboard view
                        Size patternSize,		// number of inner corners per a chessboard row and column 
                        OutputArray corners,	// output array of detected corners
                        int flags=CALIB_CB_ADAPTIVE_THRESH+CALIB_CB_NORMALIZE_IMAGE
)

调用它通常只需要提供一幅图像以及棋盘的大小信息(水平和竖直的内角点的数量,如上图中分别是 6 和 4),该方法会返回这些棋盘角点在图像中的位置:

// output vectors of image points
std::vector imageCorners;
// number of corners on the chessboard
cv::Size boardSize(6, 4);
// Get the chessboard corners
bool found = cv::findChessboardCorners(image,
                                    boardSize,
                                    imageCorners);

绘制检测得到的结点

OpenCV 也提供了一个函数用于将检测到的结点画到棋盘图像上:

// Draw the corners
cv::drawChessboardCorners(image,
                        boardSize, imageCorners,
                        found);	// corners have been found

                                                                 

这些角点间的线条可以表示这些角点在 vector 中的顺序。

校正相机

当检测足够多不同角度的棋盘图像后(大概 10 ~ 20 张),就可以对相机进行校正了,OpenCV 提供了一个函数 cv::calibrateCamera()进行校正:

double calibrateCamera(InputArrayOfArrays objectPoints,	// a vector of vectors of calibration pattern points 
                        InputArrayOfArrays imagePoints,	// a vector of vectors of the projections of calibration pattern points.
                        Size imageSize,	// size of the image
                        InputOutputArray cameraMatrix,	// output 3x3 floating-point camera matrix 
                        InputOutputArray distCoeffs,		// output vector of distortion coefficients 
                        OutputArrayOfArrays rvecs,		// output vector of rotation vectors 
                        OutputArrayOfArrays tvecs,		// output vector of translation vectors e
                        int flags=0,						// flags
                        TermCriteria criteria=TermCriteria( // termination criteria
                            TermCriteria::COUNT+TermCriteria::EPS,
                            30, DBL_EPSILON) )

调用该函数将可以得到两个重要的输出:相机矩阵 cameraMatrix 和畸变系数 distCoeffs 。

为了方便,可以将上面的所有步骤封装成一个类 CameraCalibrator :

class CameraCalibrator {
    // input points
    std::vector> objectPoints;
    std::vector> imagePoints;
    // output Matrices
    cv::Mat cameraMatrix;
    cv::Mat distCoeffs;
    // flag to specify how calibration is done
    int flag;
    // used in image undistortion 
    cv::Mat map1,map2; 
    bool mustInitUndistort;
  public:
    CameraCalibrator() : flag(0), mustInitUndistort(true) {};
    // Open the chessboard images and extract corner points
    int addChessboardPoints(const std::vector& filelist, cv::Size & boardSize);
    // Add scene points and corresponding image points
    void addPoints(const std::vector& imageCorners, const std::vector& objectCorners);
    // Calibrate the camera
    double calibrate(cv::Size &imageSize);
    // Set the calibration flag
    void setCalibrationFlag(bool radial8CoeffEnabled=false, bool tangentialParamEnabled=false);
    // Remove distortion in an image (after calibration)
    cv::Mat remap(const cv::Mat &image);
    // Getters
    cv::Mat getCameraMatrix() { return cameraMatrix; }
    cv::Mat getDistCoeffs()   { return distCoeffs; }
};

其中:

  • addChessboardPoints() 函数 - 用于读入一系列的棋盘图像并检测角点;
  • calibrate() 函数 - 用于进行相机校正,得到相机的参数矩阵和畸变系数;
  • remap() 函数 - 用于根据相机校正结果修复图像的畸变;
  • addPoints() 函数 - addChessboardPoints() 在检测完角点后会调用这个函数。也可自己手动调用这个函数添加已知的角点位置和对应的空间坐标点。

使用示例:

int main()
{
    cv::namedWindow("Image");
    cv::Mat image;
    std::vector filelist;
    // generate list of chessboard image filename
    for (int i=1; i<=20; i++) {
        std::stringstream str;
        str << "../chessboards/chessboard" << std::setw(2) << std::setfill('0') << i << ".jpg";
        std::cout << str.str() << std::endl;
        filelist.push_back(str.str());
        image= cv::imread(str.str(),0);
        cv::imshow("Image",image);
    
         cv::waitKey(100);
    }
    // Create calibrator object
    CameraCalibrator cameraCalibrator;
    // add the corners from the chessboard
    cv::Size boardSize(6,4);
    cameraCalibrator.addChessboardPoints(
        filelist,	// filenames of chessboard image
        boardSize);	// size of chessboard
        // calibrate the camera
    //	cameraCalibrator.setCalibrationFlag(true,true);
    cameraCalibrator.calibrate(image.size());
    // Image Undistortion
    image = cv::imread(filelist[6]);
    cv::Mat uImage= cameraCalibrator.remap(image);
    // display camera matrix
    cv::Mat cameraMatrix= cameraCalibrator.getCameraMatrix();
    std::cout << " Camera intrinsic: " << cameraMatrix.rows << "x" << cameraMatrix.cols << std::endl;
    std::cout << cameraMatrix.at(0,0) << " " << cameraMatrix.at(0,1) << " " << cameraMatrix.at(0,2) << std::endl;
    std::cout << cameraMatrix.at(1,0) << " " << cameraMatrix.at(1,1) << " " << cameraMatrix.at(1,2) << std::endl;
    std::cout << cameraMatrix.at(2,0) << " " << cameraMatrix.at(2,1) << " " << cameraMatrix.at(2,2) << std::endl;
    imshow("Original Image", image);
    imshow("Undistorted Image", uImage);
    cv::waitKey();
    return 0;
}

结果:

                                                                          【OpenCV】 视频处理(读取视频、写入视频、目标检测、特征匹配、相机校正)_第11张图片

你可能感兴趣的:(【OpenCV】)