基于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 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提供了可用的方法我们就使用,后面如果出现其它问题再进行研究。

来自为知笔记(Wiz)

附件列表

原文地址:https://www.cnblogs.com/jsxyhelu/p/10588559.html

时间: 2024-09-28 12:42:03

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

基于 OpenCV 的人脸识别

基于 OpenCV 的人脸识别 一点背景知识 OpenCV 是一个开源的计算机视觉和机器学习库.它包含成千上万优化过的算法,为各种计算机视觉应用提供了一个通用工具包.根据这个项目的关于页面,OpenCV 已被广泛运用在各种项目上,从谷歌街景的图片拼接,到交互艺术展览的技术实现中,都有 OpenCV 的身影. OpenCV 起始于 1999 年 Intel 的一个内部研究项目.从那时起,它的开发就一直很活跃.进化到现在,它已支持如 OpenCL 和 OpenGL 的多种现代技术,也支持如 iOS

基于OpenCV的图片卡通化处理

学习OpenCV已有一段时间,除了研究各种算法的内容,在空闲之余,根据书本及资料的引导,尝试结合图像处理算法和日常生活联系起来,首先在台式机上(带摄像头)完成一系列视频流处理功能,开发平台为Qt5.3.2+OpenCV2.4.9. 本次试验实现的功能主要有: 调用摄像头捕获视频流: 将帧图像转换为素描效果图片: 将帧图像卡通化处理: 简单地生成"怪物"形象: 人脸肤色变换. 本节所有的算法均由类cartoon中的函数cartoonTransform()来实现: // Frame:输入每

每日一练之自适应中值滤波器(基于OpenCV实现)

本文主要介绍了自适应的中值滤波器,并基于OpenCV实现了该滤波器,并且将自适应的中值滤波器和常规的中值滤波器对不同概率的椒盐噪声的过滤效果进行了对比.最后,对中值滤波器的优缺点了进行了总结. 空间滤波器 一个空间滤波器包括两个部分: 一个邻域,滤波器进行操作的像素集合,通常是一个矩形区域 对邻域中像素进行的操作 一个滤波器就是在选定的邻域像素上执行预先定义好的操作产生新的像素,并用新的像素替换掉原来像素形成新的图像. 通常,也可以将滤波器称之为核(kernel),模板(template)或者窗

基于opencv和mfc的摄像头采集代码(GOMFCTemplate2)

基于opencv和mfc的摄像头采集框架(GOMFCTemplate2) 编写带界面的图像处理程序,选择opencv+mfc是一种很好的选择:在读取摄像头数据方面,网上的方法很多,其中shiqiyu的camerads的方法是较好的. 基于现有资料,通过在实际项目中的积累,我总结出来一套结合opencv和mfc的摄像头采集框架.具有以下特点: 1.基于directshow,兼容性好,速度快.到目前为止,无论是工业相机还是普通相机,没发现不兼容的: 2.摄像头部分通过线程读取,保证界面的运行流畅:

Java基于opencv实现图像数字识别(二)—基本流程

Java基于opencv实现图像数字识别(二)-基本流程 做一个项目之前呢,我们应该有一个总体把握,或者是进度条:来一步步的督促着我们来完成这个项目,在我们正式开始前呢,我们先讨论下流程. 我做的主要是表格中数字的识别,但这个不是重点.重点是通过这个我们可以举一反三,来实现我们自己的业务. 图像的识别主要分为两步:图片预处理和图像识别:这两步都很重要 图像预处理: 1. 图像灰度化:二值化 2. 图像降噪,去除干扰线 3. 图像腐蚀.膨胀处理 4. 字符分割 5. 字符归一化 图像识别: 1.

基于OpenCV的Android软件开发

最近在做Android软件开发,手头有一些C.OpenCV版本的代码想移植到手机中,于是调查了OpenCV在Android中的使用方法,总结如下. 我使用的Android软件开发环境为Android ADT(Android Developer Tools),它包含了Android软件开发必备的开发插件,下载下来解压就能用.对于编译C/C++ Android Native代码开发,需要NDK,也是下载下来解压,在eclipse里配置一下路径即可,如下图(Window->Preferences).

基于opencv和QT的瞳孔精确检测程序

本文为作者为毕业设计所写的瞳孔精确检测程序,谢绝任何形式的转载. 本篇博客是在作者的前两篇博客 <基于QT和opencv的摄像头(本地图片)读取并输出程序>和< 基于opencv和QT的人脸(人眼)检测程序>的基础上进行开发的.主要原理是:针对已经检测到的人眼区域图像,利用边缘检测和Hough变换实现瞳孔的精确检测. 首先建立一个图像处理类,对每一帧图像进行处理. class ImgProcess { private: Mat inimg;//输入图像 Mat outimg;//输

基于OpenCV的dnn模块的SSD demo运行

最近项目有个任务,要在windows环境下用VS+OpenCV实现caffe模型的调用,于是在网上找了几个相关的博客跑了几个demo练练手.这些博客写得都很详细,但是有些细节由于版本更新的问题,配置的过程中有些变化,所以自己再发篇博客记录下. 前期的准备工作可以参考这篇博客:基于opencv dnn模块 的caffe模型的调用,关于配置环境,我用的VS2015,CMake是官网的最新版本,OpenCV选的是博客中用的OpenCV3.2.0.其中需要注意的是,运行环境及前期准备过程中, OpenC

Java基于opencv实现图像数字识别(一)

Java基于opencv实现图像数字识别(一) 最近分到了一个任务,要做数字识别,我分配到的任务是把数字一个个的分开:当时一脸懵逼,直接百度java如何分割图片中的数字,然后就百度到了用BufferedImage这个类进行操作:尝试着做了一下,做到灰度化,和二值化就做不下去了:然后几乎就没有啥java的资料了,最多的好像都是c++,惹不起.惹不起...... 我也想尝试着用c++做一下,百度到了c++基于opencv来做图像识别的:但是要下vs啊,十几个g呢,我内存这么小,配置这么麻烦,而且vs