《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 argc, TCHAR* argv[], TCHAR* envp[])
{
    //读入实现采集好的带棋盘标定板的图片
    Mat image = imread("E:/template/stereo_calib/left01.jpg");
    // 输出图像角点的向量  
    std::vector<cv::Point2f> imageCorners; 
    // 棋盘内部角点的数量 
    cv::Size boardSize(9,6); 
    // 获得棋盘角点 
    bool found = cv::findChessboardCorners( 
        image,         // 包含棋盘图案的图像 
        boardSize,     // 图案的尺寸 
        imageCorners); // 检测到的角点列表 
    // 画出角点 
    cv::drawChessboardCorners(image, boardSize, 
        imageCorners, found); // 找到的角点 
    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 argc, TCHAR* argv[], TCHAR* envp[])
{
    //读入图片序列
    vector<string> fileNames;
    GO::getFiles("E:/template/calibrateImages",fileNames);
    Mat image;
    for (size_t index = 0;index<fileNames.size();index++)
    {
        //读入当前序列图片
        image = imread(fileNames[index]);
        // 输出图像角点的向量  
        std::vector<cv::Point2f> imageCorners; 
        // 棋盘内部角点的数量 
        cv::Size boardSize(8,6); 
        // 获得棋盘角点 
        bool found = cv::findChessboardCorners( 
            image,         // 包含棋盘图案的图像 
            boardSize,     // 图案的尺寸 
            imageCorners); // 检测到的角点列表 
        // 画出角点 
        cv::drawChessboardCorners(image, boardSize, 
            imageCorners, found); // 找到的角点 
        //绘制结果
        cv::imshow("image",image);
        cv::waitKey();
    }
    
    return 0;
}
进一步修改以后,就更完美了,能够找到全部的图片。
那么这里的问题,就是更进一步,这么好的效果是如何实现的?如果要提高速度,如何来办?
绘制图案的函数,相对来说比较简单。(E:\GItHub\opencv\modules\calib3d\src\calibinit.cpp 这个代码2250行,opencv可以的)
//// 画出角点 
//cv::drawChessboardCorners(image, boardSize, 
//                          imageCorners,
//                        found); 
void drawChessboardCorners( InputOutputArray image, Size patternSize,
                           InputArray _corners,
                           bool patternWasFound )
{
    CV_INSTRUMENT_REGION();
    int type = image.type();
    int cn = CV_MAT_CN(type);
    CV_CheckType(type, cn == 1 || cn == 3 || cn == 4,
        "Number of channels must be 1, 3 or 4" );
    int depth = CV_MAT_DEPTH(type);
    CV_CheckType(type, depth == 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 Point2f* corners_data = corners.ptr<Point2f>(0);
    int nelems = corners.checkVector(2, CV_32F, true);
    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 < nelems; i++ )
        {
            cv::Point2i pt(
                cvRound(corners_data[i].x*(1 << shift)),
                cvRound(corners_data[i].y*(1 << shift))
                );
            line(image, Point(pt.x - r, pt.y - r), Point( pt.x + r, pt.y + r), color, 1, line_type, shift);//每个圆配一个X
            line(image, Point(pt.x - r, pt.y + r), Point( pt.x + r, pt.y - r), color, 1, line_type, shift);
            circle(image, pt, r+(1<<shift), color, 1, line_type, shift);
        }
    }
    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.height; y++)
        {
            const int* line_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.width; x++, i++)
            {
                cv::Point2i pt(
                    cvRound(corners_data[i].x*(1 << shift)),
                    cvRound(corners_data[i].y*(1 << shift))
                    );
                if (i != 0)
                    line(image, prev_pt, pt, color, 1, line_type, shift);
                line(image, Point(pt.x - r, pt.y - r), Point( pt.x + r, pt.y + r), color, 1, line_type, shift);
                line(image, Point(pt.x - r, pt.y + r), Point( pt.x + r, pt.y - r), color, 1, line_type, shift);
                circle(image, pt, r+(1<<shift), color, 1, line_type, shift);
                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.width, pattern_size.height, flags);
    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(type, depth == 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::Point2f> out_corners;
    if (img.channels() != 1)
    {
        cvtColor(img, img, COLOR_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_new, pattern_size) <= 0) //fall back to the old method
        {
            if (!checkChessboard(img, pattern_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_dilations; dilations <= max_dilations; dilations++)
    {
        //USE BINARY IMAGE COMPUTED USING icvBinarizationHistogramBased METHOD
        dilate( thresh_img_new, thresh_img_new, Mat(), 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()...
        rectangle( thresh_img_new, Point(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_img, flags);
        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_corners, prev_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(img, img);
        }
        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(img, thresh_img, thresh_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 && !found; k++)
        {
            for (int dilations = min_dilations; dilations <= max_dilations; dilations++)
            {
                // convert the input grayscale image to binary (black-n-white)
                if (useAdaptive)
                {
                    int block_size = cvRound(prev_sqr_size == 0
                        ? std::min(img.cols, img.rows) * (k % 2 == 0 ? 0.2 : 0.1)
                        : prev_sqr_size * 2);
                    block_size = block_size | 1;
                    // convert to binary
                    adaptiveThreshold( img, thresh_img, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, block_size, (k/2)*5 );
                    if (dilations > 0)
                        dilate( thresh_img, thresh_img, Mat(), Point(-1, -1), dilations-1 );
                }
                else
                {
                    dilate( thresh_img, thresh_img, Mat(), 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()...
                rectangle( thresh_img, Point(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_img, flags);
                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_corners, prev_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)
        {
            if( out_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(img, out_corners, Size(2, 2), Size(-1,-1),
            cv::TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 15, 0.1));
    }
    Mat(out_corners).copyTo(corners_);
    return found;
}
这段代码就不好理解了。不过需要清醒认识到的是,棋盘标定这个事情,不是需要重复做的事情,一次标定完了,后面反复使用就可以,所以OpenCV提供了可用的方法我们就使用,后面如果出现其它问题再进行研究。

附件列表

基于OpenCV做“三维重建”(1)--找到并绘制棋盘的更多相关文章

  1. 基于OpenCV做“三维重建”(0)-- OpenCV3.2+VIZ6.3.0在vs2012下的编译和使用

    一.问题提出         ViZ对于显示3维的效果图来说,非常有帮助:我在使用OpenCV进行双目测距的过程中,有一些参数希望能够通过可视化的方法显示出来,所以参考了这方面相关的资料.做了一些实验 ...

  2. 基于OpenCV做“三维重建”(3)--相机参数矩阵

    通过前面的相机标定,我们能够获得一些参数模型.但是这些相机的参数矩阵到底是什么意思?怎样才能够判断是否正确?误差都会来自哪里?这里就必须要通过具体实验来加深认识.采集带相机参数的图片具有一定难度,幸好 ...

  3. 基于OpenCV做“三维重建”(4)--相机姿态还原和实现三维重建

    v当我们构建成功了viz,就可以使用3维效果给我们提供的便利,进一步进行一些3维的操作. 在这个动画中,注意图片后面的那个黑线,对应的是相机的位置. /*----------------------- ...

  4. 基于OpenCV做“三维重建”(2)--封装标定过程

    既然已经能够找到了标定点,那么下边的工作就是使用标定结果了.[这本书在这里的内容组织让人莫名其妙]但是通过阅读代码能够很方便地串起来. /*------------------------------ ...

  5. 算法+OpenCV】基于opencv的直线和曲线拟合与绘制(最小二乘法)

    http://blog.csdn.net/guduruyu/article/details/72866144 最小二乘法多项式曲线拟合,是常见的曲线拟合方法,有着广泛的应用,这里在借鉴最小二乘多项式曲 ...

  6. 基于 OpenCV 的人脸识别

    基于 OpenCV 的人脸识别 一点背景知识 OpenCV 是一个开源的计算机视觉和机器学习库.它包含成千上万优化过的算法,为各种计算机视觉应用提供了一个通用工具包.根据这个项目的关于页面,OpenC ...

  7. 【计算机视觉】基于OpenCV的人脸识别

    一点背景知识 OpenCV 是一个开源的计算机视觉和机器学习库.它包含成千上万优化过的算法,为各种计算机视觉应用提供了一个通用工具包.根据这个项目的关于页面,OpenCV 已被广泛运用在各种项目上,从 ...

  8. 基于OpenCV制作道路车辆计数应用程序

    基于OpenCV制作道路车辆计数应用程序 发展前景 随着科学技术的进步和工业的发展,城市中交通量激增,原始的交通方式已不能满足要求:同时,由于工业发展为城市交通提供的各种交通工具越来越多,从而加速了城 ...

  9. 基于 opencv 的图像处理入门教程

    前言 虽然计算机视觉领域目前基本是以深度学习算法为主,但实际上很多时候对图片的很多处理方法,并不需要采用深度学习的网络模型,采用目前成熟的图像处理库即可实现,比如 OpenCV 和 PIL ,对图片进 ...

随机推荐

  1. java script基本数据类型与数组

    基本数据类型 1.undefined  (var a;) 2.null   (var a=null); 3.String  (var a=" " or ' '); 4.boolea ...

  2. Java笔记--引用类型的使用

    使用引用类型的一般步骤: 1.导包:指定需要使用的目标在什么位置,在publicclass之前一行写代码 import 包名路径 2.创建:通常需要创建之才能使用,格式: 数据类型 变量名称 = ne ...

  3. npm run dev没反应

    npm config set registry https://registry.npm.taobao.org npm install npm run dev

  4. oracle单字段拆分成多行

    已上图为例 先以逗号分隔拆分 拆分函数: CREATE OR REPLACE FUNCTION SPLIT(P_STRING VARCHAR2, P_SEP VARCHAR2 := ',') RETU ...

  5. iOS 字体下载

    iOS可以动态的为系统下载字体,这些字体都下载到了系统的目录下,并且可以被其他应用公用 来看下如何实现动态下载: // 创建下载字体请求描述的准备 NSMutableDictionary *attrs ...

  6. 24个 CSS 高级技巧合集

    上期入口:史上最全实用网络爬虫合集! 1.使用CSS复位 CSS复位可以在不同的浏览器上保持一致的样式风格.您可以使用CSS reset 库Normalize等,也可以使用一个更简化的复位方法: ** ...

  7. Azure架构(一):云计算基础

    云计算的定义 云计算(英语:cloud computing),是一种基于互联网的计算方式,通过这种方式,共享的软硬件资源和信息可以按需求提供给使用各种计算终端(桌面电脑.笔记本电脑.平板电脑.手机等) ...

  8. Windows环境下安装Oracle数据库

    Windows环境 1.解压文件 1)Oracle下载官网地址: http://www.oracle.com/technetwork/cn/database/enterprise-edition/do ...

  9. 解决SQL Server 2008无法连接127.0.0.1的问题

    电脑操作系统是Win10中文版,新装的英文版SQL Server 2008,纯默认安装,没有做任何改动. 装完SQL Server 2008之后,发现只能用默认的机器名来登录: 如果用127.0.0. ...

  10. FB面经 Prepare: LCA of Deepest Nodes in Binary Tree

    给一个 二叉树 , 求最深节点的最小公共父节点 . retrun . 先用 recursive , 很快写出来了, 要求用 iterative . 时间不够了... Recursion: 返回的时候返 ...