首页 > 其他分享 >基于OpenCV做“三维重建”(1)--找到并绘制棋盘

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

时间:2022-12-25 12:04:25浏览次数:40  
标签:img -- corners image OpenCV int thresh 三维重建 size

     这里我要做的是第11章,关于3维重建的相关内容。【读书,做例子,多么轻松的学生岁月……】


例子11.2.1 获得图片的角点并且绘制出来。


基于OpenCV做“三维重建”(1)--找到并绘制棋盘_#include


基于OpenCV做“三维重建”(1)--找到并绘制棋盘_#include_02


// GOCVHelper.cpp : 定义控制台应用程序的入口点。



//




说明:以下内容,用于支持《基于OpenCV做“三维重建”》



作者:jsxyhelu


组织: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;



}



这段代码简明扼要,使用官方提供的图片,一次性运行成功;使用自己的图片,分辨率可能大些,这样速度慢些,但是也是一次性运行成功。


基于OpenCV做“三维重建”(1)--找到并绘制棋盘_#include_03


// GOCVHelper.cpp : 定义控制台应用程序的入口点。



//




说明:以下内容,用于支持《基于OpenCV做“三维重建”》



作者:jsxyhelu



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



标签:img,--,corners,image,OpenCV,int,thresh,三维重建,size
From: https://blog.51cto.com/jsxyhelu2017/5968068

相关文章