【opencv】顯微鏡/投影儀 圓陣列標定板標定
由於在顯微鏡以及投影儀下,棋盤格角點提取會非常不準確,因此必須採用圓形陣列標定板進行標定,opencv裡本身提供了圓形陣列標定版的介面,然而在顯微鏡以及投影儀下卻提取不到,因為標定板很小(顯微鏡下才6mm*6mm)經過放大,圓變形很嚴重,因此這裡通過另外的方法求取。
在顯微鏡視場中,前景與背景區別很大,直接固定閾值128得到二值圖即可,在二值圖中檢測所有輪廓,對得到的輪廓做一個篩選,首先是輪廓周長(點的個數),當然也可以通過形態學,去除掉小的噪點。對剩下的輪廓求取最小外接圓,進行圓形狀的一個比對,如果輪廓是圓心,則為目標
圓形狀比對的一個依據參考博文:http://www.cnblogs.com/jsxyhelu/p/4503900.html
//根據輪廓點和圓心計算方差
float ComputeVariance(std::vector<cv::Point> theContour, Point2f theCenter)
{
int a[65535],n;
float aver,s;
float sum=0,e=0;
n = theContour.size();
for(int i=0;i<n;i++)
{
a[i] = sqrt((theContour[i].x - theCenter.x) * (theContour[i].x - theCenter.x) + (theContour[i].y - theCenter.y) * (theContour[i].y - theCenter.y));
sum+=a[i];
}
aver=sum/n;
for(int i=0;i<n;i++)
e+=(a[i]-aver)*(a[i]-aver);
e/=n-1;
s=sqrt(e);
return e;
}
經過以上步驟,可以得到視場中的16個圓心,但是其順序是亂的,需要按照一定的順序進行排序,由於連通域檢測時,16個圓輪廓在vector中是連續儲存的並且是以Y座標從大到小排的(如果不是這樣,也可以通過演算法後面的結果),先拿到2個邊上的圓心座標,計算兩點線段的斜率,尋找下一個點的原則是,下一個點與上一個點線段的斜率(角度)與上一個點與上上一個點的線段斜率(角度)夾角要最小,距離要最短。但兩者不能同時兼顧到,因此根據標定板圓的排列分為了2種情況,下一個點不在同一行和在同一行的兩種情況,在同一行時,則主要兼顧距離最短,不在同一行時,則主要兼顧夾角最小。
bool findMicroCircle(Mat image, Size board_size, vector<Point2f> &corners)
{
Mat imageGray;
cvtColor(image, imageGray , CV_RGB2GRAY);
Mat imageThreshold;
threshold(imageGray, imageThreshold, 128, 255, CV_THRESH_BINARY);
//提取輪廓
vector<vector<Point>>contours;
vector<Point2f> corners_temp;
int count = 0;
findContours(imageThreshold, contours, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
for (int i = 0; i < contours.size(); i++)
{
if (contours[i].size() > 200)
{
Point2f center;
float radius;
//drawContours(image, contours, i, Scalar(0,0,255), 2);
minEnclosingCircle(contours[i], center, radius);
float e = ComputeVariance(contours[i], center);//方差
if (e < 50)
{
count++;
corners_temp.push_back(center);
}
}
}
//drawContours(image, contours, -1, Scalar(0,0,255), 2);
if (count == board_size.height * board_size.width)
{
int index;//下標記錄
float k1,k2;//斜率
float angle1,angle2,angle,anglemin,dis,dismin;//前後斜率夾角
corners.push_back(corners_temp[0]);//0,1號點壓入,供計算初始斜率
corners.push_back(corners_temp[1]);
//k1 = (corners[0].y - corners[1].y) / (corners[0].x - corners[1].x);//初始斜率
for (int j = 2; j < count; j++)
{
bool samelineFlag = true;
anglemin = 90;//最小角度差
dismin = 9999999;//兩點最小距離
k1 = (corners[j-1].y - corners[j-2].y) / (corners[j-1].x - corners[j-2].x);//初始斜率
angle1 = atan(k1) * 180 / CV_PI;
for (int i = 2; i < corners_temp.size(); i++)
{
if (corners_temp[i].x - corners[j-1].x == 0)//斜率為0
{
k2 = 0;
}
else
{
k2 = (corners_temp[i].y - corners[j-1].y) / (corners_temp[i].x - corners[j-1].x);//後續斜率
}
angle2 = atan(k2) * 180 / CV_PI;
angle = abs(angle1 - angle2);
dis = sqrt((corners_temp[i].y - corners[j-1].y) * (corners_temp[i].y - corners[j-1].y) + (corners_temp[i].x - corners[j-1].x) * (corners_temp[i].x - corners[j-1].x));
int sameline = (atan(1.0 / (board_size.height-1)) * 180 / CV_PI) + 5;
if (angle < sameline)//如果計算的角度差小於最小角度值與同一條直線判斷閾值的和,則更新下標
{
if (angle < sameline/4 && dis < dismin && j % board_size.width != 0 && (j-1) % board_size.width != 0)//同一條直線上,角度差不超過5度認為是同一條直線上,此時應該比較距離
{
dismin = dis;
index = i;
}
if (angle < anglemin && j % board_size.width == 0)//換行,只找角度差最小的
{
anglemin = angle;
index = i;
}
if (dis < dismin && (j-1) % board_size.width == 0)//換行後第一次,只找角度差最小的
{
dismin = dis;
index = i;
}
}
}
corners.push_back(corners_temp[index]);
circle(image, corners_temp[index], 15, Scalar(0,255,0), 3, 8, 0);
circle(image, corners_temp[0], 15, Scalar(0,0,255), -1, 8, 0);
circle(image, corners_temp[1], 15, Scalar(0,0,255), -1, 8, 0);
std::vector<Point2f>::iterator it = corners_temp.begin() + index;
corners_temp.erase(it);
}
return true;
}
else
{
return false;
}
}
其實從哪個點提取無所謂,但是要保證一個原則就是一行一行按順序排列點即可。
標定與校正結果如下:
第1幅影象的平均誤差:0.80577畫素
第2幅影象的平均誤差:0.701184畫素
第3幅影象的平均誤差:0.70272畫素
第4幅影象的平均誤差:0.547063畫素
第5幅影象的平均誤差:1.09536畫素
第6幅影象的平均誤差:0.76506畫素
第7幅影象的平均誤差:0.667793畫素
第8幅影象的平均誤差:0.822369畫素
第9幅影象的平均誤差:0.877443畫素
第10幅影象的平均誤差:0.856679畫素
第11幅影象的平均誤差:0.681006畫素
總體平均誤差:0.774767畫素
相機內引數矩陣:
[39251.0769115962, 0, 932.5315829671392;
0, 41185.50490132452, 694.5148137172613;
0, 0, 1]
畸變係數:
[-24.56078184561823, -13.9232076080276, 0.004546530516996267, 0.005352287915247098, -0.007488930052855393]
第1幅影象的旋轉向量:
[-2.400648046930939;
2.39789006446686;
-0.5030773963661545]
第1幅影象的旋轉矩陣:
[0.0007116874091214198, -0.9999975463682025, 0.002097798514260035;
-0.9165202388330997, -0.001491368632978607, -0.3999855342745764;
0.3999876814482535, -0.001638010126739448, -0.9165190514181771]
第1幅影象的平移向量:
[1.740430650160699;
1.980388421080897;
213.9421545468698]
第2幅影象的旋轉向量:
[2.026069655685576;
-1.95425564704494;
0.4404653877035846]
第2幅影象的旋轉矩陣:
[0.03229009767692703, -0.9993252200120062, 0.01750583445509979;
-0.91021254996335, -0.03663704040549276, -0.4125176858748505;
0.4128806891599921, -0.002613793848253693, -0.9107813703631013]
第2幅影象的平移向量:
[1.434303442340692;
1.933775134314402;
215.7635414434346]
第3幅影象的旋轉向量:
[-2.506973189718734;
2.266959042094225;
-0.5296331774750954]
第3幅影象的旋轉矩陣:
[0.09190965608196688, -0.9949790263151063, 0.03961505158317322;
-0.9095342846029065, -0.1000781939290523, -0.4034002233908377;
0.4053393642995623, 0.00104512819403188, -0.9141656892797304]
第3幅影象的平移向量:
[2.115457782230888;
2.63554510808202;
214.3826147104438]
第4幅影象的旋轉向量:
[2.091249202153444;
-1.904469058116255;
0.4192461330508303]
第4幅影象的旋轉矩陣:
[0.08818120869555068, -0.9957969531149588, 0.02474878986851795;
-0.9141364173315228, -0.09076976925361158, -0.3951144891014927;
0.3957002463245116, 0.01221790311839707, -0.9182984470759549]
第4幅影象的平移向量:
[1.229216205018381;
2.958858389701409;
215.2865289017586]
第5幅影象的旋轉向量:
[2.052248883184534;
-1.782921024377806;
0.516852253248877]
第5幅影象的旋轉矩陣:
[0.1311653007292452, -0.9908488716301933, 0.03184615320222786;
-0.8542562667193232, -0.129267016908198, -0.5035238515804592;
0.5030326974018403, 0.03884008147299184, -0.8633942051089989]
第5幅影象的平移向量:
[1.211696914955099;
1.858255483709739;
215.9341707929622]
第6幅影象的旋轉向量:
[2.096455574273039;
-1.930547560805298;
0.301475989544044]
第6幅影象的旋轉矩陣:
[0.08784392817508158, -0.9956067885038483, -0.03241245081484015;
-0.9383224556449042, -0.07177843894483371, -0.3382289534252786;
0.334416526976894, 0.06012469033347367, -0.9405055066808574]
第6幅影象的平移向量:
[2.702045961192787;
1.77720171122464;
220.6739208255806]
第7幅影象的旋轉向量:
[-2.249647067123821;
1.790500361198483;
0.4486109104045156]
第7幅影象的旋轉矩陣:
[0.2060376646047496, -0.9740240671687818, -0.09394465040759759;
-0.90325267668592, -0.2262354126433894, 0.3646260277678985;
-0.3764081333126875, 0.00972906172558119, -0.926402861898644]
第7幅影象的平移向量:
[1.36362867973697;
2.669282558564297;
225.9698060155932]
第8幅影象的旋轉向量:
[-2.15307960749997;
2.011120796187208;
0.09171998559648421]
第8幅影象的旋轉矩陣:
[0.07581061381791343, -0.9933676827270825, 0.08644881575809901;
-0.9813750892821667, -0.05898515769153534, 0.1828214574619851;
-0.1765097305220621, -0.09869852119222058, -0.9793379993370511]
第8幅影象的平移向量:
[2.760921681914984;
1.602440113356596;
222.0324924843436]
第9幅影象的旋轉向量:
[-2.441623710430115;
1.442733098603825;
0.01861088167794312]
第9幅影象的旋轉矩陣:
[0.4943342731749869, -0.8575998295617726, 0.1419723871045915;
-0.8536522578609274, -0.4481113765116447, 0.2654694274107283;
-0.1640470938892193, -0.2524256852550024, -0.9536088424558804]
第9幅影象的平移向量:
[1.56167374380016;
2.69886532502509;
222.8328423002089]
第10幅影象的旋轉向量:
[-0.3020672487178615;
-0.3177006009417958;
-1.748049962240801]
第10幅影象的旋轉矩陣:
[-0.1947892568904155, 0.9804375716976252, 0.02827213122040473;
-0.9077898948900995, -0.1911219165559888, 0.373349594545632;
0.3714493937745785, 0.04705933505299129, 0.9272598162591047]
第10幅影象的平移向量:
[-0.3428851599804441;
2.695720889270264;
218.1581049286656]
第11幅影象的旋轉向量:
[-2.501831188933102;
1.332345638343028;
0.3106030443469637]
第11幅影象的旋轉矩陣:
[0.5492422450311522, -0.8339504535735243, -0.05347520226845928;
-0.771624664883495, -0.5306805918268061, 0.3506757562218761;
-0.3208244579463564, -0.1513431545953232, -0.934968938917525]
第11幅影象的平移向量:
[0.2722978014067166;
2.75998728572356;
218.6845829362973]
以下程式碼從標定圖資料夾下讀取標定圖,進行定標,校正待標定圖資料夾下的所有圖片,並把校正結果儲存到校正圖資料夾下
//opencv2.4.9 vs2012
#include <opencv2\opencv.hpp>
#include <fstream>
#include <direct.h>
using namespace std;
using namespace cv;
//根據輪廓點和圓心計算方差
float ComputeVariance(std::vector<cv::Point> theContour, Point2f theCenter)
{
int a[65535],n;
float aver,s;
float sum=0,e=0;
n = theContour.size();
for(int i=0;i<n;i++)
{
a[i] = sqrt((theContour[i].x - theCenter.x) * (theContour[i].x - theCenter.x) + (theContour[i].y - theCenter.y) * (theContour[i].y - theCenter.y));
sum+=a[i];
}
aver=sum/n;
for(int i=0;i<n;i++)
e+=(a[i]-aver)*(a[i]-aver);
e/=n-1;
s=sqrt(e);
return e;
}
bool findMicroCircle(Mat image, Size board_size, vector<Point2f> &corners)
{
Mat imageGray;
cvtColor(image, imageGray , CV_RGB2GRAY);
Mat imageThreshold;
threshold(imageGray, imageThreshold, 128, 255, CV_THRESH_BINARY);
//提取輪廓
vector<vector<Point>>contours;
vector<Point2f> corners_temp;
int count = 0;
findContours(imageThreshold, contours, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
for (int i = 0; i < contours.size(); i++)
{
if (contours[i].size() > 200)
{
Point2f center;
float radius;
//drawContours(image, contours, i, Scalar(0,0,255), 2);
minEnclosingCircle(contours[i], center, radius);
float e = ComputeVariance(contours[i], center);//方差
if (e < 50)
{
count++;
corners_temp.push_back(center);
}
}
}
//drawContours(image, contours, -1, Scalar(0,0,255), 2);
if (count == board_size.height * board_size.width)
{
int index;//下標記錄
float k1,k2;//斜率
float angle1,angle2,angle,anglemin,dis,dismin;//前後斜率夾角
corners.push_back(corners_temp[0]);//0,1號點壓入,供計算初始斜率
corners.push_back(corners_temp[1]);
//k1 = (corners[0].y - corners[1].y) / (corners[0].x - corners[1].x);//初始斜率
for (int j = 2; j < count; j++)
{
bool samelineFlag = true;
anglemin = 90;//最小角度差
dismin = 9999999;//兩點最小距離
k1 = (corners[j-1].y - corners[j-2].y) / (corners[j-1].x - corners[j-2].x);//初始斜率
angle1 = atan(k1) * 180 / CV_PI;
for (int i = 2; i < corners_temp.size(); i++)
{
if (corners_temp[i].x - corners[j-1].x == 0)//斜率為0
{
k2 = 0;
}
else
{
k2 = (corners_temp[i].y - corners[j-1].y) / (corners_temp[i].x - corners[j-1].x);//後續斜率
}
angle2 = atan(k2) * 180 / CV_PI;
angle = abs(angle1 - angle2);
dis = sqrt((corners_temp[i].y - corners[j-1].y) * (corners_temp[i].y - corners[j-1].y) + (corners_temp[i].x - corners[j-1].x) * (corners_temp[i].x - corners[j-1].x));
int sameline = (atan(1.0 / (board_size.height-1)) * 180 / CV_PI) + 5;
if (angle < sameline)//如果計算的角度差小於最小角度值與同一條直線判斷閾值的和,則更新下標
{
if (angle < sameline/4 && dis < dismin && j % board_size.width != 0 && (j-1) % board_size.width != 0)//同一條直線上,角度差不超過5度認為是同一條直線上,此時應該比較距離
{
dismin = dis;
index = i;
}
if (angle < anglemin && j % board_size.width == 0)//換行,只找角度差最小的
{
anglemin = angle;
index = i;
}
if (dis < dismin && (j-1) % board_size.width == 0)//換行後第一次,只找角度差最小的
{
dismin = dis;
index = i;
}
}
}
corners.push_back(corners_temp[index]);
//circle(image, corners_temp[index], 15, Scalar(0,255,0), 3, 8, 0);
//circle(image, corners_temp[0], 15, Scalar(0,0,255), -1, 8, 0);
//circle(image, corners_temp[1], 15, Scalar(0,0,255), -1, 8, 0);
std::vector<Point2f>::iterator it = corners_temp.begin() + index;
corners_temp.erase(it);
}
return true;
}
else
{
return false;
}
}
int main()
{
_mkdir("校正圖");
double time0 = static_cast<double>(getTickCount());
ofstream fout("caliberation_result.txt"); /** 儲存定標結果的檔案 **/
/************************************************************************
讀取每一幅影象,從中提取出角點,然後對角點進行亞畫素精確化
*************************************************************************/
cout<<"開始提取角點………………"<<endl;
int image_count= 11; /**** 影象數量 ****/
Size image_size; /**** 影象的尺寸 ****/
Size board_size = Size(4,4); /**** 定標板上每行、列的角點數 ****/
vector<Point2f> corners; /**** 快取每幅影象上檢測到的角點 ****/
vector<vector<Point2f>> corners_Seq; /**** 儲存檢測到的所有角點 ****/
vector<Mat> image_Seq;
int count = 0;
for( int i = 0; i < image_count ; i++)
{
cout<<"標定圖【"<< i+1 <<"】..."<<endl;
string imageFileName;
std::stringstream StrStm;
StrStm<<i+1;
StrStm>>imageFileName;
imageFileName += ".bmp";
Mat image = imread("標定圖/" + imageFileName);//引號中可加統一檔名字首
image_size = image.size();
//image_size = Size(image.cols , image.rows);
/* 提取角點 */
Mat imageGray;
cvtColor(image, imageGray , CV_RGB2GRAY);
//棋盤格方式
//bool patternfound = findChessboardCorners(image, board_size, corners,CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE+
// CALIB_CB_FAST_CHECK );
//圓陣列方式
//bool patternfound = findCirclesGrid(image, board_size, corners);
//圓陣列非對稱方式
//bool patternfound = findCirclesGrid(image, board_size, corners, CALIB_CB_ASYMMETRIC_GRID );
//自定義顯微鏡下圓陣列方式
bool patternfound = findMicroCircle(image, board_size, corners);
if (!patternfound)
{
cout<<"can not find chessboard corners!\n";
continue;
exit(1);
}
else
{
/* 亞畫素精確化 如果是自定義顯微鏡下圓陣列方式則需要註釋*/
//cornerSubPix(imageGray, corners, Size(11, 11), Size(-1, -1), TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1));
/* 繪製檢測到的角點並儲存 */
Mat imageTemp = image.clone();
for (int j = 0; j < corners.size(); j++)
{
if (j + 1 < corners.size())
{
line(imageTemp, corners[j], corners[j + 1], Scalar(0,255,0), 2, 8, 0);
}
circle( imageTemp, corners[j], 10, Scalar(0,0,255), -1, 8, 0);
}
string imageFileName;
std::stringstream StrStm;
StrStm<<i+1;
StrStm>>imageFileName;
imageFileName += "_corner.jpg";
imwrite(imageFileName, imageTemp);
cout<<"標定圖【"<<i+1<<"】...end"<<endl;
count = count + corners.size();
corners_Seq.push_back(corners);
}
corners.clear();
image_Seq.push_back(image);
}
cout<<"角點提取完成!\n";
/************************************************************************
攝像機定標
*************************************************************************/
cout<<"開始定標………………"<<endl;
Size2d square_size = Size2d(1.2,1.2); /**** 實際測量得到的定標板上每個棋盤格的大小 ****/
vector<vector<Point3f>> object_Points; /**** 儲存定標板上角點的三維座標 ****/
Mat image_points = Mat(1, count , CV_32FC2, Scalar::all(0)); /***** 儲存提取的所有角點 *****/
vector<int> point_counts; /***** 每幅影象中角點的數量 ****/
Mat intrinsic_matrix = Mat(3,3, CV_32FC1, Scalar::all(0)); /***** 攝像機內引數矩陣 ****/
Mat distortion_coeffs = Mat(1,4, CV_32FC1, Scalar::all(0)); /* 攝像機的4個畸變係數:k1,k2,p1,p2 */
vector<cv::Mat> rotation_vectors; /* 每幅影象的旋轉向量 */
vector<cv::Mat> translation_vectors; /* 每幅影象的平移向量 */
/* 初始化定標板上角點的三維座標 */
for (int t=0;t<image_count;t++)
{
vector<Point3f> tempPointSet;
for (int i=0;i<board_size.height;i++)
{
for (int j=0;j<board_size.width;j++)
{
/* 假設定標板放在世界座標系中z=0的平面上 */
Point3f tempPoint;
tempPoint.x = i*square_size.width;
tempPoint.y = j*square_size.height;
tempPoint.z = 0;
tempPointSet.push_back(tempPoint);
}
}
object_Points.push_back(tempPointSet);
}
/* 初始化每幅影象中的角點數量,這裡我們假設每幅影象中都可以看到完整的定標板 */
for (int i=0; i< image_count; i++)
{
point_counts.push_back(board_size.width*board_size.height);
}
/* 開始定標 */
calibrateCamera(object_Points, corners_Seq, image_size, intrinsic_matrix ,distortion_coeffs, rotation_vectors, translation_vectors, 0);
cout<<"定標完成!\n";
/************************************************************************
對定標結果進行評價
*************************************************************************/
cout<<"開始評價定標結果………………"<<endl;
double total_err = 0.0; /* 所有影象的平均誤差的總和 */
double err = 0.0; /* 每幅影象的平均誤差 */
vector<Point2f> image_points2; /**** 儲存重新計算得到的投影點 ****/
cout<<"每幅影象的定標誤差:"<<endl;
cout<<"每幅影象的定標誤差:"<<endl<<endl;
for (int i=0; i<image_count; i++)
{
vector<Point3f> tempPointSet = object_Points[i];
/**** 通過得到的攝像機內外引數,對空間的三維點進行重新投影計算,得到新的投影點 ****/
projectPoints(tempPointSet, rotation_vectors[i], translation_vectors[i], intrinsic_matrix, distortion_coeffs, image_points2);
/* 計算新的投影點和舊的投影點之間的誤差*/
vector<Point2f> tempImagePoint = corners_Seq[i];
Mat tempImagePointMat = Mat(1,tempImagePoint.size(),CV_32FC2);
Mat image_points2Mat = Mat(1,image_points2.size(), CV_32FC2);
for (size_t i = 0 ; i != tempImagePoint.size(); i++)
{
image_points2Mat.at<Vec2f>(0,i) = Vec2f(image_points2[i].x, image_points2[i].y);
tempImagePointMat.at<Vec2f>(0,i) = Vec2f(tempImagePoint[i].x, tempImagePoint[i].y);
}
err = norm(image_points2Mat, tempImagePointMat, NORM_L2);
total_err += err/= point_counts[i];
cout<<"第"<<i+1<<"幅影象的平均誤差:"<<err<<"畫素"<<endl;
fout<<"第"<<i+1<<"幅影象的平均誤差:"<<err<<"畫素"<<endl;
}
cout<<"總體平均誤差:"<<total_err/image_count<<"畫素"<<endl;
fout<<"總體平均誤差:"<<total_err/image_count<<"畫素"<<endl<<endl;
cout<<"評價完成!"<<endl;
/************************************************************************
儲存定標結果
*************************************************************************/
cout<<"開始儲存定標結果………………"<<endl;
Mat rotation_matrix = Mat(3,3,CV_32FC1, Scalar::all(0)); /* 儲存每幅影象的旋轉矩陣 */
fout<<"相機內引數矩陣:"<<endl;
fout<<intrinsic_matrix<<endl;
fout<<"畸變係數:\n";
fout<<distortion_coeffs<<endl;
for (int i=0; i<image_count; i++)
{
fout<<"第"<<i+1<<"幅影象的旋轉向量:"<<endl;
fout<<rotation_vectors[i]<<endl;
/* 將旋轉向量轉換為相對應的旋轉矩陣 */
Rodrigues(rotation_vectors[i],rotation_matrix);
fout<<"第"<<i+1<<"幅影象的旋轉矩陣:"<<endl;
fout<<rotation_matrix<<endl;
fout<<"第"<<i+1<<"幅影象的平移向量:"<<endl;
fout<<translation_vectors[i]<<endl;
}
cout<<"完成儲存"<<endl;
fout<<endl;
/************************************************************************
顯示定標結果
*************************************************************************/
Mat mapx = Mat(image_size,CV_32FC1);
Mat mapy = Mat(image_size,CV_32FC1);
Mat R = Mat::eye(3,3,CV_32F);
cout<<"儲存矯正影象"<<endl;
for (int i = 0 ; i != image_count ; i++)
{
cout<<"標定圖【"<<i+1<<"】..."<<endl;
Mat newCameraMatrix = Mat(3,3,CV_32FC1,Scalar::all(0));
initUndistortRectifyMap(intrinsic_matrix,distortion_coeffs,R, getOptimalNewCameraMatrix(intrinsic_matrix, distortion_coeffs, image_size, 1, image_size, 0),image_size,CV_32FC1,mapx,mapy);
Mat t = image_Seq[i].clone();
cv::remap(image_Seq[i],t,mapx, mapy, INTER_LINEAR);
string imageFileName;
std::stringstream StrStm;
StrStm<<i+1;
StrStm>>imageFileName;
imageFileName += "_d.jpg";
imwrite(imageFileName,t);
}
cout<<"儲存結束"<<endl;
time0 = ((double)getTickCount()-time0)/getTickFrequency();
cout<<"標定用時:"<<time0<<"秒"<<endl;
/************************************************************************
批量校正圖片
*************************************************************************/
if (1)
{
double time1 = static_cast<double>(getTickCount());
int image_count = 47; /**** 待校正影象數量 ****/
for( int i = 0; i < image_count ; i++)
{
cout<<"待校正圖【"<< i+1 <<"】..."<<endl;
string imageFileName;
std::stringstream StrStm;
StrStm<<i+1;
StrStm>>imageFileName;
imageFileName += ".bmp";
Mat image = imread("待校正圖/" + imageFileName);//引號中可加統一檔名字首
image_size = image.size();
cout<<"待校正圖【"<< i+1 <<"】校正完畢"<<endl;
initUndistortRectifyMap(intrinsic_matrix,distortion_coeffs,R, getOptimalNewCameraMatrix(intrinsic_matrix, distortion_coeffs, image_size, 1, image_size, 0),image_size,CV_32FC1,mapx,mapy);
Mat t = image.clone();
cv::remap(image, t, mapx, mapy, INTER_LINEAR);
imwrite("校正圖/" + imageFileName, t);
}
cout<<"儲存結束"<<endl;
time1 = ((double)getTickCount()-time1)/getTickFrequency();
cout<<"校正用時:"<<time1<<"秒"<<endl;
}
getchar();
return 0;
}
相關文章
- 使用Halcon軟體和圓形標定板進行相機標定的步驟和教程
- 張正友標定Opencv實現、標定流程以及影像座標轉為世界座標OpenCV
- OpenCV攝像頭標定(待修改)OpenCV
- 陣列指標,指標陣列陣列指標
- 陣列指標 指標陣列陣列指標
- 指標陣列與陣列指標指標陣列
- 陣列指標和指標陣列陣列指標
- win10如何連線投影儀 win10系統投影儀設定Win10
- 指標陣列和陣列指標與二維陣列指標陣列
- OPENCV3.0 單目攝像頭標定(使用官方自帶的標定圖片)OpenCV
- win10投影不能全屏顯示如何解決_win10怎麼設定投影儀全屏顯示Win10
- 標準燈座插口 Beam智慧無線投影儀
- Go 陣列指標(指向陣列的指標)Go陣列指標
- QT設定標籤顯示位置QT
- 陣列指標陣列指標
- 單目標定:從理論到OpenCV實踐OpenCV
- 設定SQL標識列的值SQL
- 陣列指標:a pointer to an array,即指向陣列的指標陣列指標
- 【leetcode】34. Search for a Range 給定陣列的給定值的下標範圍LeetCode陣列
- 陣列和指標陣列指標
- 指標和陣列指標陣列
- Golang 學習——陣列指標和指標陣列的區別Golang陣列指標
- 筆記本Win10電腦外接顯示器(投影儀)的設定步驟筆記Win10
- C++ OpenCv二值化找圓心座標C++OpenCV
- C++ opencv的圓轉矩形,極座標轉笛卡爾座標系C++OpenCV
- OpenCV開發筆記(七十七):相機標定(二):透過棋盤標定計算相機內參矩陣矯正畸變攝像頭影像OpenCV筆記矩陣
- 路標設定
- win10 工作列角標怎麼設定_win10系統設定角標方法Win10
- 指向陣列的指標陣列指標
- C陣列和指標陣列指標
- win10系統筆記本怎麼設定投影儀 win10筆記本連線投影儀Win10筆記
- 給定一個整數陣列 nums 和一個目標值 target,請你在該陣列中找出和為目標值的那 兩個 整數,並返回他們的陣列下標。陣列
- javascript陣列定義JavaScript陣列
- C語言程式設計基礎:指標陣列與陣列指標C語言程式設計指標陣列
- C語言學習歷程(十二)陣列指標與指標陣列C語言陣列指標
- 關於基於OPENCV攝像機標定的一點感受OpenCV
- OPENCV版本的單目標定示例程式碼(張正友)OpenCV
- 徹底搞清C/C++中一維陣列,二維陣列,指標,陣列指標和指標陣列以及指向指標的指標,行地址和列地址之間的關係C++陣列指標