OpenCV實(shí)現(xiàn)單目尺寸估計(jì)的案例詳解
一個(gè)攝像頭無(wú)法獲取深度信息,兩個(gè)攝像頭雙目標(biāo)定可以實(shí)現(xiàn)雙目測(cè)距。
但是我現(xiàn)在只有一個(gè)攝像頭,如果想實(shí)現(xiàn)測(cè)量尺寸,我的思路:選一張固定尺寸的背景,例如一張A4紙,從圖像中提取A4紙并進(jìn)行透視變換進(jìn)行圖像矯正,A4紙的尺寸我可以確定,那么也可以確定圖像中的物體長(zhǎng)寬信息(高度忽略不計(jì)的情況,例如:卡片)。當(dāng)攝像頭距離目標(biāo)物距離L,此時(shí)像素所占面積為S,那么理論上來(lái)說(shuō),目標(biāo)物圖像變化后的面積為S1,則距離L1=(L/S)*S1,假定目標(biāo)物上面貼有很多個(gè)面積為1平方厘米的正方形貼紙,那么獲取四個(gè)角點(diǎn)和四條邊的信息通過(guò)算法可以獲取出物體在深度方向上的偏移量。有想法就實(shí)踐。
1.在地板上放一張A4紙隨便放一些物體。利用opencv打開(kāi)攝像頭獲取圖像并顯示。

2.轉(zhuǎn)灰度圖像

3.如果直接使用canny的畫(huà)周?chē)匕宓木€(xiàn)條不好去除,所以先二值化分割。

4.觀察圖像中存在噪點(diǎn),使用中值濾波處理

5.使用canny進(jìn)行邊緣檢測(cè)

6.使用累加器方法進(jìn)行直線(xiàn)擬合


7.得到了四條線(xiàn)段,此時(shí)可以求交點(diǎn),但是我這里為了方便直接角點(diǎn)檢測(cè)

8.得到角點(diǎn)排序后進(jìn)行透視變換

實(shí)現(xiàn)1-8效果代碼:
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
double x_1[4];
double y_1[4];
double x_2[4];
double y_2[4];
double line_k[4];
double line_b[4];
int line_number=0;
// 獲取交點(diǎn)
//void getCross()
//{
// for (int i = 0; i <line_number; i++)
// {
// for(int j=i+1;j<line_number;j++)
// {
// if(int(abs(line_k[i]))==0&&int(abs(line_k[j]))==0)
// {
// cout<<"i:"<<i<<" j:"<<j<<" is "<<" true"<<endl;
// }
// }
// }
//}
void drawLine(Mat &img, //要標(biāo)記直線(xiàn)的圖像
vector<Vec2f> lines, //檢測(cè)的直線(xiàn)數(shù)據(jù)
double rows, //原圖像的行數(shù)(高)
double cols, //原圖像的列數(shù)(寬)
Scalar scalar, //繪制直線(xiàn)的顏色
int n //繪制直線(xiàn)的線(xiàn)寬
)
{
int image_channels=img.channels();
Point pt1, pt2;
for (size_t i = 0; i < lines.size(); i++)
{
float rho = lines[i][0]; //直線(xiàn)距離坐標(biāo)原點(diǎn)的距離
float theta = lines[i][1]; //直線(xiàn)過(guò)坐標(biāo)原點(diǎn)垂線(xiàn)與x軸夾角
double a = cos(theta); //夾角的余弦值
double b = sin(theta); //夾角的正弦值
double x0 = a*rho, y0 = b*rho; //直線(xiàn)與過(guò)坐標(biāo)原點(diǎn)的垂線(xiàn)的交點(diǎn)
double length = max(rows, cols); //圖像高寬的最大值
//計(jì)算直線(xiàn)上的一點(diǎn)
pt1.x = cvRound(x0 + length * (-b));
pt1.y = cvRound(y0 + length * (a));
//計(jì)算直線(xiàn)上另一點(diǎn)
pt2.x = cvRound(x0 - length * (-b));
pt2.y = cvRound(y0 - length * (a));
//兩點(diǎn)繪制一條直線(xiàn)
if(i==0&&image_channels!=1)
{
scalar=Scalar(255,0,0);//blue
}
else if(i==1&&image_channels!=1)
{
scalar=Scalar(255,255,0);//yellow
}
else if(i==2&&image_channels!=1)
{
scalar=Scalar(0,0,255);//red
}
else if(i==3&&image_channels!=1)
{
scalar=Scalar(0,255,0);//green
}
else;
if(image_channels==1)
{
scalar=Scalar(255,255,255);
}
line(img, pt1, pt2, scalar, n);
//計(jì)算直線(xiàn)方程
x_1[i]=pt1.x;
y_1[i]=pt1.y;
x_2[i]=pt2.x;
y_2[i]=pt2.y;
line_k[i]=(y_2[i]-y_1[i])/(x_2[i]-x_1[i]);
line_b[i]=y_1[i]-line_k[i]*x_1[i];
cout<<i+1<<":"<<"y="<<line_k[i]<<"*x+"<<line_b[i]<<endl;
}
cout<<"lines_number:"<<lines.size()<<endl;
line_number=lines.size();
// getCross();
}
int main(int argc, char *argv[])
{
VideoCapture cap;
cap.open(0);
Mat frame;
Mat src;
while(line_number!=4)
{
cap>>frame;
src=frame;
imshow("frame",frame);
Mat frame_gray;
cvtColor(frame,frame_gray,COLOR_BGR2GRAY);
imshow("frame_gray",frame_gray);
Mat frame_threshold;
threshold(frame_gray,frame_threshold,160,255,THRESH_BINARY);//frame_gray(x,y)>160 frame_threshold(x,y)=255 else 0
imshow("frame_threshold",frame_threshold);
Mat frame_medianBlur;
medianBlur(frame_threshold, frame_medianBlur, 3);
imshow("frame_medianBlur",frame_medianBlur);
Mat frame_Canny;
Canny(frame_medianBlur, frame_Canny, 10, 180, 3, false);
imshow("frame_Canny",frame_Canny);
//累加器進(jìn)行檢測(cè)直線(xiàn)
vector<Vec2f> lines;
HoughLines(frame_Canny, lines, 1, CV_PI / 180, 100, 0, 0);
Mat frame_HoughLines=frame;
drawLine(frame_HoughLines, lines, frame_HoughLines.rows, frame_HoughLines.cols, Scalar(0,0,0), 1);
imshow("frame_HoughLines",frame_HoughLines);
Mat frame_zeros = Mat::zeros(frame_HoughLines.rows, frame_HoughLines.cols, CV_8UC1);
drawLine(frame_zeros, lines, frame_HoughLines.rows, frame_HoughLines.cols, Scalar(0,0,0), 1);
imshow("frame_zeros",frame_zeros);
vector<Point2f> conners;//檢測(cè)到的角點(diǎn)
int maxConers = 4;//檢測(cè)角點(diǎn)上限
double qualityLevel = 0.1;//最小特征值
double minDistance = 20;//最小距離
Mat frame_ShiTomasi=frame;
goodFeaturesToTrack(frame_zeros, conners, maxConers, qualityLevel, minDistance);
cout<<"Shi-Tomasi(x,y):"<<conners<<endl;
//角點(diǎn)繪制
for (int i = 0; i < conners.size(); i++)
{
// string text=to_string(i)+"(x,y):"+"("+to_string((int)conners[i].x)+","+to_string((int)conners[i].y)+")";
// cv::putText(frame_ShiTomasi, text, conners[i], FONT_HERSHEY_COMPLEX, 0.6, cv::Scalar(0, 0, 0), 1.4, 8, 0);
circle(frame_ShiTomasi, conners[i], 3, Scalar(0,255,0), 2, 8, 0);
}
//分割四個(gè)坐標(biāo)
int width_flag=frame_HoughLines.cols/2;
int height_flag=frame_HoughLines.rows/2;
vector<Point2f>srcpoint(4);//存放變換前四頂點(diǎn)
for (int i = 0; i < conners.size(); i++)
{
if(conners[i].x<width_flag&&conners[i].y<height_flag)
{
// cv::putText(frame_ShiTomasi, "left1", conners[i], FONT_HERSHEY_COMPLEX, 0.6, cv::Scalar(0, 0, 0), 1.4, 8, 0);
srcpoint[0]=conners[i];
}
else if(conners[i].x>width_flag&&conners[i].y<height_flag)
{
// cv::putText(frame_ShiTomasi, "right1", conners[i], FONT_HERSHEY_COMPLEX, 0.6, cv::Scalar(0, 0, 0), 1.4, 8, 0);
srcpoint[1]=conners[i];
}
else if(conners[i].x<width_flag&&conners[i].y>height_flag)
{
// cv::putText(frame_ShiTomasi, "left2", conners[i], FONT_HERSHEY_COMPLEX, 0.6, cv::Scalar(0, 0, 0), 1.4, 8, 0);
srcpoint[2]=conners[i];
}
else if(conners[i].x>width_flag&&conners[i].y>height_flag)
{
// cv::putText(frame_ShiTomasi, "right2", conners[i], FONT_HERSHEY_COMPLEX, 0.6, cv::Scalar(0, 0, 0), 1.4, 8, 0);
srcpoint[3]=conners[i];
}
else;
}
imshow("frame_ShiTomasi",frame_ShiTomasi);
waitKey(30);
//透視變換
vector<Point2f>dstpoint(4);//存放變換后四頂點(diǎn)
//mm
float a4_width=2100/4;
float a4_height=2970/4;
Mat result = Mat::zeros(a4_width, a4_height,frame.type());
//定義矯正后四頂點(diǎn)
dstpoint[0] = Point2f(0, result.rows);
dstpoint[1] = Point2f(0, 0);
dstpoint[2] = Point2f(result.cols, result.rows);
dstpoint[3] = Point2f(result.cols, 0);
Mat M = getPerspectiveTransform(srcpoint, dstpoint);
Mat frame_result=src;
imshow("1",frame_result);
warpPerspective(frame_result, result, M, result.size());
imshow("result", result);
}
cap.release();
waitKey(0);
return 0;
}
9.進(jìn)行尺寸估計(jì)(將矯正后圖像傳入,最小外接矩形,然后閾值劃分,取出區(qū)域求長(zhǎng)寬,按照比例關(guān)系估計(jì)最后的長(zhǎng)寬比)下面代碼僅僅實(shí)現(xiàn)了找出最小矩形和輸出一些點(diǎn)信息。由于時(shí)間有限,計(jì)算距離算法部分后續(xù)更新。
void get_dist(Mat src)
{
cvtColor(src,src,COLOR_BGR2GRAY);
threshold(src,src,160,255,THRESH_BINARY);//frame_gray(x,y)>160 frame_threshold(x,y)=255 else 0
medianBlur(src, src, 3);
Canny(src, src, 10, 180, 3, false);
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(src, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point());
vector<Rect> boundRect(contours.size());
Mat drawingPicture = Mat::zeros(src.size(), CV_8UC1); //最小外接矩形畫(huà)布
int width_i=2100/8;
int height_i=2970/8;
for (int i = 0; i < contours.size(); i++)
{
//繪制輪廓的最小外結(jié)矩形
RotatedRect rect = minAreaRect(contours[i]);
Point2f P[4];
rect.points(P);
for (int j = 0; j <= 3; j++)
{
line(src, P[j], P[(j + 1) % 4], Scalar(255), 1);
cout<<"P[j],P[(j + 1) % 4]:"<<P[j]<<","<< P[(j + 1) % 4]<<endl;
}
}
imshow("dist",src);
}
以上就是OpenCV實(shí)現(xiàn)單目尺寸估計(jì)的案例詳解的詳細(xì)內(nèi)容,更多關(guān)于OpenCV單目尺寸估計(jì)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C++探索構(gòu)造函數(shù)私有化會(huì)產(chǎn)生什么結(jié)果
C++的構(gòu)造函數(shù)的作?:初始化類(lèi)對(duì)象的數(shù)據(jù)成員。即類(lèi)的對(duì)象被創(chuàng)建的時(shí)候,編譯系統(tǒng)對(duì)該對(duì)象分配內(nèi)存空間,并?動(dòng)調(diào)?構(gòu)造函數(shù),完成類(lèi)成員的初始化。構(gòu)造函數(shù)的特點(diǎn):以類(lèi)名作為函數(shù)名,?返回類(lèi)型2022-05-05
C++?qt實(shí)現(xiàn)打開(kāi)關(guān)閉狀態(tài)按鈕的代碼
這篇文章主要介紹了C++?qt實(shí)現(xiàn)打開(kāi)關(guān)閉狀態(tài)按鈕,用QCheckBox可以實(shí)現(xiàn),只要在選擇與未選擇的狀態(tài)設(shè)置不同的圖片即可完成,代碼簡(jiǎn)單易懂,需要的朋友可以參考下2022-03-03
C語(yǔ)言游戲必備:光標(biāo)定位與顏色設(shè)置的實(shí)現(xiàn)方法
本篇文章是對(duì)c語(yǔ)言中光標(biāo)定位與顏色設(shè)置的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
MFC對(duì)話(huà)框中添加狀態(tài)欄的方法
這篇文章主要介紹了MFC對(duì)話(huà)框中添加狀態(tài)欄的方法,實(shí)例分析了MFC對(duì)話(huà)框添加狀態(tài)欄所涉及的相關(guān)成員變量與事件實(shí)現(xiàn)技巧,需要的朋友可以參考下2015-07-07
C語(yǔ)言實(shí)現(xiàn)輸出平均成績(jī)最高學(xué)生的信息
這篇文章主要介紹利用C語(yǔ)言實(shí)現(xiàn)輸出平均成績(jī)最高學(xué)生的信息,文章舉例說(shuō)明并附有詳細(xì)代碼,需要的朋友可以參考一下2021-10-10
解決在Mac下直接解壓C++靜態(tài)庫(kù)出現(xiàn)的問(wèn)題
最近在研究C++的各種編譯構(gòu)建過(guò)程,學(xué)習(xí)了一下cmake,gyp/ninja這些自動(dòng)化構(gòu)建工具后,想著自己試下用純命令行跑一遍編譯流程。在試圖把C++靜態(tài)庫(kù)編譯為動(dòng)態(tài)庫(kù)的過(guò)程中遇到了棘手的問(wèn)題,找了好久后發(fā)現(xiàn)是跟Mac平臺(tái)相關(guān)的,這里記錄一下,望對(duì)遇到類(lèi)似問(wèn)題的童鞋有幫助。2016-12-12
C語(yǔ)言實(shí)現(xiàn)多項(xiàng)式的相加
這篇文章主要為大家介紹了C語(yǔ)言實(shí)現(xiàn)多項(xiàng)式的相加,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-10-10
Matlab實(shí)現(xiàn)同步子圖視角的方法詳解
這篇文章主要和大家分享三個(gè)可以Matlab中更簡(jiǎn)便實(shí)現(xiàn)同步子圖視角的技巧,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以學(xué)習(xí)一下2022-06-06

