zoukankan      html  css  js  c++  java
  • 基于OpenCV做“三维重建”(1)--找到并绘制棋盘

        《OpenCV计算机视觉编程攻略(第3版)》这套书已经出到第3版了,如果你非要我说这本书有多好,我说不出来;只是很多我第一手的例子都是来源于这本书的—相比较OpenCV官方提供的代码,这本书的例子提供了更好的帮助。所以说这里我还将继续这个工作,将来我自己出书的时候这种模式也是可选的。
         这里我要做的是第11章,关于3维重建的相关内容。【读书,做例子,多么轻松的学生岁月……】
    例子11.2.1 获得图片的角点并且绘制出来。
    // GOCVHelper.cpp : 定义控制台应用程序的入口点。
    //
    ////说明:以下内容,用于支持《基于OpenCV做“三维重建”》
    ////作者:jsxyhelu(1755311380@qq.com http://jsxyhelu.cnblogs.com)
    ////组织:GREENOPEN
    ////日期:2019-3-24
    #include "stdafx.h"
    #include <opencv2/core.hpp>
    #include <opencv2/highgui.hpp>
    #include <opencv2/imgproc.hpp>
    #include <opencv2/calib3d.hpp>
    #include <opencv2/imgproc/imgproc_c.h>
    #include "GOCVHelper.h"
    using namespace std;
    using namespace cv;
    using namespace GO;
    int _tmain(int argcTCHARargv[], TCHARenvp[])
    {
        //读入实现采集好的带棋盘标定板的图片
        Mat image = imread("E:/template/stereo_calib/left01.jpg");
        // 输出图像角点的向量  
        std::vector<cv::Point2fimageCorners
        // 棋盘内部角点的数量 
        cv::Size boardSize(9,6); 
        // 获得棋盘角点 
        bool found = cv::findChessboardCorners
            image,         // 包含棋盘图案的图像 
            boardSize,     // 图案的尺寸 
            imageCorners); // 检测到的角点列表 
        // 画出角点 
        cv::drawChessboardCorners(imageboardSize
            imageCornersfound); // 找到的角点 
        return 0;
    }

    这段代码简明扼要,使用官方提供的图片,一次性运行成功;使用自己的图片,分辨率可能大些,这样速度慢些,但是也是一次性运行成功。
    // GOCVHelper.cpp : 定义控制台应用程序的入口点。
    //
    ////说明:以下内容,用于支持《基于OpenCV做“三维重建”》
    ////作者:jsxyhelu(1755311380@qq.com http://jsxyhelu.cnblogs.com)
    ////组织:GREENOPEN
    ////日期:2019-3-24
    #include "stdafx.h"
    #include <opencv2/core.hpp>
    #include <opencv2/highgui.hpp>
    #include <opencv2/imgproc.hpp>
    #include <opencv2/calib3d.hpp>
    #include <opencv2/imgproc/imgproc_c.h>
    #include "GOCVHelper.h"
    using namespace std;
    using namespace cv;
    using namespace GO;
    int _tmain(int argcTCHARargv[], TCHARenvp[])
    {
        //读入图片序列
        vector<stringfileNames;
        GO::getFiles("E:/template/calibrateImages",fileNames);
        Mat image;
        for (size_t index = 0;index<fileNames.size();index++)
        {
            //读入当前序列图片
            image = imread(fileNames[index]);
            // 输出图像角点的向量  
            std::vector<cv::Point2fimageCorners
            // 棋盘内部角点的数量 
            cv::Size boardSize(8,6); 
            // 获得棋盘角点 
            bool found = cv::findChessboardCorners
                image,         // 包含棋盘图案的图像 
                boardSize,     // 图案的尺寸 
                imageCorners); // 检测到的角点列表 
            // 画出角点 
            cv::drawChessboardCorners(imageboardSize
                imageCornersfound); // 找到的角点 
            //绘制结果
            cv::imshow("image",image);
            cv::waitKey();
        }
        
        return 0;
    }
    进一步修改以后,就更完美了,能够找到全部的图片。
    那么这里的问题,就是更进一步,这么好的效果是如何实现的?如果要提高速度,如何来办?
    绘制图案的函数,相对来说比较简单。(E:GItHubopencvmodulescalib3dsrccalibinit.cpp 这个代码2250行,opencv可以的)
    //// 画出角点 
    //cv::drawChessboardCorners(image, boardSize, 
    //                          imageCorners,
    //                        found); 
    void drawChessboardCornersInputOutputArray imageSize patternSize,
                               InputArray _corners,
                               bool patternWasFound )
    {
        CV_INSTRUMENT_REGION();
        int type = image.type();
        int cn = CV_MAT_CN(type);
        CV_CheckType(typecn == 1 || cn == 3 || cn == 4,
            "Number of channels must be 1, 3 or 4" );
        int depth = CV_MAT_DEPTH(type);
        CV_CheckType(typedepth == CV_8U || depth == CV_16U || depth == CV_32F,
            "Only 8-bit, 16-bit or floating-point 32-bit images are supported");
        if (_corners.empty())
            return;
        Mat corners = _corners.getMat();
        const Point2fcorners_data = corners.ptr<Point2f>(0);
        int nelems = corners.checkVector(2, CV_32Ftrue);
        CV_Assert(nelems >= 0);
        const int shift = 0;
        const int radius = 4;
        const int r = radius*(1 << shift);
        double scale = 1;
        switch (depth)
        {
        case CV_8U:
            scale = 1;
            break;
        case CV_16U:
            scale = 256;
            break;
        case CV_32F:
            scale = 1./255;
            break;
        }
        int line_type = (type == CV_8UC1 || type == CV_8UC3) ? LINE_AA : LINE_8;
        if (!patternWasFound//是否找到了“棋盘”
        {
            Scalar color(0,0,255,0);
            if (cn == 1)
                color = Scalar::all(200);
            color *= scale;
            for (int i = 0; i < nelemsi++ )
            {
                cv::Point2i pt(
                    cvRound(corners_data[i].x*(1 << shift)),
                    cvRound(corners_data[i].y*(1 << shift))
                    );
                line(imagePoint(pt.x - rpt.y - r), Pointpt.x + rpt.y + r), color, 1, line_typeshift);//每个圆配一个X
                line(imagePoint(pt.x - rpt.y + r), Pointpt.x + rpt.y - r), color, 1, line_typeshift);
                circle(imageptr+(1<<shift), color, 1, line_typeshift);
            }
        }
        else
        {
            const int line_max = 7;
            static const int line_colors[line_max][4] =
            {
                {0,0,255,0},
                {0,128,255,0},
                {0,200,200,0},
                {0,255,0,0},
                {200,200,0,0},
                {255,0,0,0},
                {255,0,255,0}
            };
            cv::Point2i prev_pt;
            for (int y = 0, i = 0; y < patternSize.heighty++)
            {
                const intline_color = &line_colors[y % line_max][0];
                Scalar color(line_color[0], line_color[1], line_color[2], line_color[3]);
                if (cn == 1)
                    color = Scalar::all(200);
                color *= scale;
                for (int x = 0; x < patternSize.widthx++, i++)
                {
                    cv::Point2i pt(
                        cvRound(corners_data[i].x*(1 << shift)),
                        cvRound(corners_data[i].y*(1 << shift))
                        );
                    if (i != 0)
                        line(imageprev_ptptcolor, 1, line_typeshift);
                    line(imagePoint(pt.x - rpt.y - r), Pointpt.x + rpt.y + r), color, 1, line_typeshift);
                    line(imagePoint(pt.x - rpt.y + r), Pointpt.x + rpt.y - r), color, 1, line_typeshift);
                    circle(imageptr+(1<<shift), color, 1, line_typeshift);
                    prev_pt = pt;
                }
            }
        }
    }

    重头戏是寻找这棋盘的函数
    //bool found = cv::findChessboardCorners( 
    //    image,         // 包含棋盘图案的图像 
    //    boardSize,     // 图案的尺寸 
    //    imageCorners); // 检测到的角点列表 
    bool findChessboardCorners(InputArray image_Size pattern_size,
                               OutputArray corners_int flags)
    {
        CV_INSTRUMENT_REGION();
        DPRINTF("==== findChessboardCorners(img=%dx%d, pattern=%dx%d, flags=%d)",
            image_.cols(), image_.rows(), pattern_size.widthpattern_size.heightflags);
        bool found = false;
        const int min_dilations = 0;
        const int max_dilations = 7;
        int type = image_.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
        Mat img = image_.getMat();
        CV_CheckType(typedepth == CV_8U && (cn == 1 || cn == 3 || cn == 4),
            "Only 8-bit grayscale or color images are supported");
        if (pattern_size.width <= 2 || pattern_size.height <= 2)
            CV_Error(Error::StsOutOfRange"Both width and height of the pattern should have bigger than 2");
        if (!corners_.needed())
            CV_Error(Error::StsNullPtr"Null pointer to corners");
        std::vector<cv::Point2fout_corners;
        if (img.channels() != 1)
        {
            cvtColor(imgimgCOLOR_BGR2GRAY);
        }
        int prev_sqr_size = 0;
        Mat thresh_img_new = img.clone();
        icvBinarizationHistogramBased(thresh_img_new); // process image in-place
        SHOW("New binarization"thresh_img_new);
        if (flags & CALIB_CB_FAST_CHECK)
        {
            //perform new method for checking chessboard using a binary image.
            //image is binarised using a threshold dependent on the image histogram
            if (checkChessboardBinary(thresh_img_newpattern_size) <= 0) //fall back to the old method
            {
                if (!checkChessboard(imgpattern_size))
                {
                    corners_.release();
                    return false;
                }
            }
        }
        ChessBoardDetector detector(pattern_size); //调用了预置的ChessBoard寻找类
        // Try our standard "1" dilation, but if the pattern is not found, iterate the whole procedure with higher dilations.
        // This is necessary because some squares simply do not separate properly with a single dilation.  However,
        // we want to use the minimum number of dilations possible since dilations cause the squares to become smaller,
        // making it difficult to detect smaller squares.
        for (int dilations = min_dilationsdilations <= max_dilationsdilations++)
        {
            //USE BINARY IMAGE COMPUTED USING icvBinarizationHistogramBased METHOD
            dilatethresh_img_newthresh_img_newMat(), Point(-1, -1), 1 );
            // So we can find rectangles that go to the edge, we draw a white line around the image edge.
            // Otherwise FindContours will miss those clipped rectangle contours.
            // The border color will be the image mean, because otherwise we risk screwing up filters like cvSmooth()...
            rectanglethresh_img_newPoint(0,0), Point(thresh_img_new.cols-1, thresh_img_new.rows-1), Scalar(255,255,255), 3, LINE_8);
            detector.reset();
    #ifdef USE_CV_FINDCONTOURS
            Mat binarized_img = thresh_img_new;
    #else
            Mat binarized_img = thresh_img_new.clone(); // make clone because cvFindContours modifies the source image
    #endif
            detector.generateQuads(binarized_imgflags);
            DPRINTF("Quad count: %d/%d"detector.all_quads_count, (pattern_size.width/2+1)*(pattern_size.height/2+1));
            SHOW_QUADS("New quads"thresh_img_new, &detector.all_quads[0], detector.all_quads_count);
            if (detector.processQuads(out_cornersprev_sqr_size))
            {
                found = true;
                break;
            }
        }
        DPRINTF("Chessboard detection result 0: %d", (int)found);
        // revert to old, slower, method if detection failed
        if (!found)
        {
            if (flags & CALIB_CB_NORMALIZE_IMAGE)
            {
                img = img.clone();
                equalizeHist(imgimg);
            }
            Mat thresh_img;
            prev_sqr_size = 0;
            DPRINTF("Fallback to old algorithm");
            const bool useAdaptive = flags & CALIB_CB_ADAPTIVE_THRESH;
            if (!useAdaptive)
            {
                // empiric threshold level
                // thresholding performed here and not inside the cycle to save processing time
                double mean = cv::mean(img).val[0];
                int thresh_level = std::max(cvRound(mean - 10), 10);
                threshold(imgthresh_imgthresh_level, 255, THRESH_BINARY);
            }
            //if flag CALIB_CB_ADAPTIVE_THRESH is not set it doesn't make sense to iterate over k
            int max_k = useAdaptive ? 6 : 1;
            for (int k = 0; k < max_k && !foundk++)
            {
                for (int dilations = min_dilationsdilations <= max_dilationsdilations++)
                {
                    // convert the input grayscale image to binary (black-n-white)
                    if (useAdaptive)
                    {
                        int block_size = cvRound(prev_sqr_size == 0
                            ? std::min(img.colsimg.rows) * (k % 2 == 0 ? 0.2 : 0.1)
                            : prev_sqr_size * 2);
                        block_size = block_size | 1;
                        // convert to binary
                        adaptiveThresholdimgthresh_img, 255, ADAPTIVE_THRESH_MEAN_CTHRESH_BINARYblock_size, (k/2)*5 );
                        if (dilations > 0)
                            dilatethresh_imgthresh_imgMat(), Point(-1, -1), dilations-1 );
                    }
                    else
                    {
                        dilatethresh_imgthresh_imgMat(), Point(-1, -1), 1 );
                    }
                    SHOW("Old binarization"thresh_img);
                    // So we can find rectangles that go to the edge, we draw a white line around the image edge.
                    // Otherwise FindContours will miss those clipped rectangle contours.
                    // The border color will be the image mean, because otherwise we risk screwing up filters like cvSmooth()...
                    rectanglethresh_imgPoint(0,0), Point(thresh_img.cols-1, thresh_img.rows-1), Scalar(255,255,255), 3, LINE_8);
                    detector.reset();
    #ifdef USE_CV_FINDCONTOURS
                    Mat binarized_img = thresh_img;
    #else
                    Mat binarized_img = (useAdaptive) ? thresh_img : thresh_img.clone(); // make clone because cvFindContours modifies the source image
    #endif
                    detector.generateQuads(binarized_imgflags);
                    DPRINTF("Quad count: %d/%d"detector.all_quads_count, (pattern_size.width/2+1)*(pattern_size.height/2+1));
                    SHOW_QUADS("Old quads"thresh_img, &detector.all_quads[0], detector.all_quads_count);
                    if (detector.processQuads(out_cornersprev_sqr_size))
                    {
                        found = 1;
                        break;
                    }
                }
            }
        }
        DPRINTF("Chessboard detection result 1: %d", (int)found);
        if (found)
            found = detector.checkBoardMonotony(out_corners);
        DPRINTF("Chessboard detection result 2: %d", (int)found);
        // check that none of the found corners is too close to the image boundary
        if (found)
        {
            const int BORDER = 8;
            for (int k = 0; k < pattern_size.width*pattern_size.height; ++k)
            {
                ifout_corners[k].x <= BORDER || out_corners[k].x > img.cols - BORDER ||
                    out_corners[k].y <= BORDER || out_corners[k].y > img.rows - BORDER )
                {
                    found = false;
                    break;
                }
            }
        }
        DPRINTF("Chessboard detection result 3: %d", (int)found);
        if (found)
        {
            if ((pattern_size.height & 1) == 0 && (pattern_size.width & 1) == 0 )
            {
                int last_row = (pattern_size.height-1)*pattern_size.width;
                double dy0 = out_corners[last_row].y - out_corners[0].y;
                if (dy0 < 0)
                {
                    int n = pattern_size.width*pattern_size.height;
                    for(int i = 0; i < n/2; i++ )
                    {
                        std::swap(out_corners[i], out_corners[n-i-1]);
                    }
                }
            }
            cv::cornerSubPix(imgout_cornersSize(2, 2), Size(-1,-1),
                cv::TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 15, 0.1));
        }
        Mat(out_corners).copyTo(corners_);
        return found;
    }
    这段代码就不好理解了。不过需要清醒认识到的是,棋盘标定这个事情,不是需要重复做的事情,一次标定完了,后面反复使用就可以,所以OpenCV提供了可用的方法我们就使用,后面如果出现其它问题再进行研究。





    附件列表

    • 相关阅读:
      团队冲刺二(6)
      团队冲刺二(5)
      JAVA面试中问及HIBERNATE与 MYBATIS的对比,在这里做一下总结
      解决ADB server didn't ACK问题,连上手机问题
      ADB server didn't ACK
      android错误
      Andy
      在Eclipse下搭建Android开发环境教程
      VM VirtualBox 安装 Android 4.3虚拟机完整教程
      电脑安装Android4.0虚拟机的做法
    • 原文地址:https://www.cnblogs.com/jsxyhelu/p/10588559.html
    Copyright © 2011-2022 走看看