C++?OpenCV實(shí)現(xiàn)物體尺寸測(cè)量示例詳解
前言
本文將使用OpenCV C++ 進(jìn)行物體尺寸測(cè)量。具體來(lái)說就是先定位到待測(cè)物體的位置,然后測(cè)量物體的寬高。
一、圖像透視矯正

原圖如圖所示。本案例的需求是測(cè)量圖片中兩張卡片的尺寸。首先,我們得定位到兩張卡片的位置。第一步,我們首先得將白色A4紙切割出來(lái),這樣方便定位到兩張卡片所在位置。這里用到的算法是圖像透視矯正,具體可以參考OpenCV C++案例實(shí)戰(zhàn)四《圖像透視矯正》
//圖像矯正
void getWarp(Mat src, Mat &Warp)
{
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
Mat thresh;
threshold(gray, thresh, 0, 255, THRESH_BINARY | THRESH_OTSU);
Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));
Mat open;
morphologyEx(thresh, open, MORPH_OPEN, kernel);
vector<vector<Point>>contours;
findContours(open, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
vector<vector<Point>>conPoly(contours.size());
vector<Point>srcPts;
//找到最大輪廓
int MaxIndex = 0;
double Area = 0;
for (int i = 0; i < contours.size(); i++)
{
double area = contourArea(contours[i]);
if (area > Area)
{
Area = area;
MaxIndex = i;
}
}
//獲取矩形四個(gè)角點(diǎn)
double peri = arcLength(contours[MaxIndex], true);
approxPolyDP(contours[MaxIndex], conPoly[MaxIndex], 0.02*peri, true);
srcPts = { conPoly[MaxIndex][0],conPoly[MaxIndex][1],conPoly[MaxIndex][2],conPoly[MaxIndex][3] };
int T_L, B_L, B_R, T_R;
int width = src.cols / 2;
int height = src.rows / 2;
for (int i = 0; i < srcPts.size(); i++)
{
if (srcPts[i].x < width && srcPts[i].y < height)
{
T_L = i;
}
if (srcPts[i].x < width && srcPts[i].y > height)
{
B_L = i;
}
if (srcPts[i].x > width && srcPts[i].y > height)
{
B_R = i;
}
if (srcPts[i].x > width && srcPts[i].y < height)
{
T_R = i;
}
}
double UpWidth = EuDis(srcPts[T_L], srcPts[T_R]);
double DownWidth = EuDis(srcPts[B_L], srcPts[B_R]);
double MaxWidth = max(UpWidth, DownWidth);
double UpHeight = EuDis(srcPts[T_L], srcPts[B_L]);
double DownHeight = EuDis(srcPts[T_R], srcPts[B_R]);
double MaxHeight = max(UpHeight, DownHeight);
//透視變換進(jìn)行圖像矯正
Point2f SrcAffinePts[4] = { Point2f(srcPts[T_L]),Point2f(srcPts[T_R]) ,Point2f(srcPts[B_R]) ,Point2f(srcPts[B_L]) };
Point2f DstAffinePts[4] = { Point2f(0,0),Point2f(MaxWidth,0),Point2f(MaxWidth,MaxHeight),Point2f(0,MaxHeight) };
Mat M = getPerspectiveTransform(SrcAffinePts, DstAffinePts);
warpPerspective(src, Warp, M, Point(MaxWidth, MaxHeight));
}

效果如圖所示。接下來(lái),我們需要定位兩張卡片所在位置,尋找特征。
二、物體定位
//獲取物體坐標(biāo)
void FindPts(Mat &Warp, vector<vector<Point>>&TargetPts)
{
Mat gray;
cvtColor(Warp, gray, COLOR_BGR2GRAY);
Mat thresh;
threshold(gray, thresh, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
Mat open;
morphologyEx(thresh, open, MORPH_OPEN, kernel);
vector<vector<Point>>contours;
findContours(open, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
vector<vector<Point>>conPoly(contours.size());
//定位卡片四個(gè)角點(diǎn)
for (int i = 0; i < contours.size(); i++)
{
double area = contourArea(contours[i]);
if (area > 1000)
{
double peri = arcLength(contours[i], true);
approxPolyDP(contours[i], conPoly[i], 0.02*peri, true);
vector<Point>temp;
temp = { conPoly[i][0],conPoly[i][1], conPoly[i][2], conPoly[i][3] };
TargetPts.push_back(temp);
}
}
}

如圖所示。通過上面代碼段,我們已經(jīng)定位出卡片的四個(gè)角點(diǎn)。接下來(lái),只需根據(jù)角點(diǎn)位置就可以計(jì)算卡片的寬高了。
三、尺寸測(cè)量
//計(jì)算距離
void DrawAndCompute(Mat &Warp, vector<vector<Point>>&TargetPts)
{
for (int i = 0; i < TargetPts.size(); i++)
{
for (int j = 0; j < TargetPts[i].size(); j++)
{
//尺寸測(cè)量
Point PtA = Point(TargetPts[i][j]);
Point PtB = Point(TargetPts[i][(j + 1) % TargetPts[i].size()]);
double dis = round(EuDis(PtA, PtB) * 100) / 100;
//效果顯示
circle(Warp, TargetPts[i][j], 5, Scalar(0, 255, 0), -1);
line(Warp, PtA, PtB, Scalar(0, 0, 255), 2);
char text[20];
sprintf_s(text, "%.2f", dis);
Point point = Point((PtA.x + PtB.x) / 2, (PtA.y + PtB.y) / 2);
putText(Warp, text, point, FONT_HERSHEY_SIMPLEX, 1, Scalar(255, 0, 255), 2);
}
}
}
四、效果顯示

五、源碼
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;
//歐式距離
double EuDis(Point pt1, Point pt2)
{
return sqrt((pt2.x - pt1.x)*(pt2.x - pt1.x) + (pt2.y - pt1.y)*(pt2.y - pt1.y));
}
//圖像矯正
void getWarp(Mat src, Mat &Warp)
{
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
Mat thresh;
threshold(gray, thresh, 0, 255, THRESH_BINARY | THRESH_OTSU);
Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));
Mat open;
morphologyEx(thresh, open, MORPH_OPEN, kernel);
vector<vector<Point>>contours;
findContours(open, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
vector<vector<Point>>conPoly(contours.size());
vector<Point>srcPts;
//找到最大輪廓
int MaxIndex = 0;
double Area = 0;
for (int i = 0; i < contours.size(); i++)
{
double area = contourArea(contours[i]);
if (area > Area)
{
Area = area;
MaxIndex = i;
}
}
//獲取矩形四個(gè)角點(diǎn)
double peri = arcLength(contours[MaxIndex], true);
approxPolyDP(contours[MaxIndex], conPoly[MaxIndex], 0.02*peri, true);
srcPts = { conPoly[MaxIndex][0],conPoly[MaxIndex][1],conPoly[MaxIndex][2],conPoly[MaxIndex][3] };
int T_L, B_L, B_R, T_R;
int width = src.cols / 2;
int height = src.rows / 2;
for (int i = 0; i < srcPts.size(); i++)
{
if (srcPts[i].x < width && srcPts[i].y < height)
{
T_L = i;
}
if (srcPts[i].x < width && srcPts[i].y > height)
{
B_L = i;
}
if (srcPts[i].x > width && srcPts[i].y > height)
{
B_R = i;
}
if (srcPts[i].x > width && srcPts[i].y < height)
{
T_R = i;
}
}
double UpWidth = EuDis(srcPts[T_L], srcPts[T_R]);
double DownWidth = EuDis(srcPts[B_L], srcPts[B_R]);
double MaxWidth = max(UpWidth, DownWidth);
double UpHeight = EuDis(srcPts[T_L], srcPts[B_L]);
double DownHeight = EuDis(srcPts[T_R], srcPts[B_R]);
double MaxHeight = max(UpHeight, DownHeight);
//透視變換進(jìn)行圖像矯正
Point2f SrcAffinePts[4] = { Point2f(srcPts[T_L]),Point2f(srcPts[T_R]) ,Point2f(srcPts[B_R]) ,Point2f(srcPts[B_L]) };
Point2f DstAffinePts[4] = { Point2f(0,0),Point2f(MaxWidth,0),Point2f(MaxWidth,MaxHeight),Point2f(0,MaxHeight) };
Mat M = getPerspectiveTransform(SrcAffinePts, DstAffinePts);
warpPerspective(src, Warp, M, Point(MaxWidth, MaxHeight));
}
//獲取物體坐標(biāo)
void FindPts(Mat &Warp, vector<vector<Point>>&TargetPts)
{
Mat gray;
cvtColor(Warp, gray, COLOR_BGR2GRAY);
Mat thresh;
threshold(gray, thresh, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
Mat open;
morphologyEx(thresh, open, MORPH_OPEN, kernel);
vector<vector<Point>>contours;
findContours(open, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
vector<vector<Point>>conPoly(contours.size());
//定位卡片四個(gè)角點(diǎn)
for (int i = 0; i < contours.size(); i++)
{
double area = contourArea(contours[i]);
if (area > 1000)
{
double peri = arcLength(contours[i], true);
approxPolyDP(contours[i], conPoly[i], 0.02*peri, true);
vector<Point>temp;
temp = { conPoly[i][0],conPoly[i][1], conPoly[i][2], conPoly[i][3] };
TargetPts.push_back(temp);
}
}
}
//計(jì)算距離
void DrawAndCompute(Mat &Warp, vector<vector<Point>>&TargetPts)
{
for (int i = 0; i < TargetPts.size(); i++)
{
for (int j = 0; j < TargetPts[i].size(); j++)
{
//尺寸測(cè)量
Point PtA = Point(TargetPts[i][j]);
Point PtB = Point(TargetPts[i][(j + 1) % TargetPts[i].size()]);
double dis = round(EuDis(PtA, PtB) * 100) / 100;
//效果顯示
circle(Warp, TargetPts[i][j], 5, Scalar(0, 255, 0), -1);
line(Warp, PtA, PtB, Scalar(0, 0, 255), 2);
char text[20];
sprintf_s(text, "%.2f", dis);
Point point = Point((PtA.x + PtB.x) / 2, (PtA.y + PtB.y) / 2);
putText(Warp, text, point, FONT_HERSHEY_SIMPLEX, 1, Scalar(255, 0, 255), 2);
}
}
}
int main()
{
Mat src = imread("src.jpg");
if (src.empty())
{
cout << "No Image!" << endl;
system("pause");
return -1;
}
Mat Warp;
getWarp(src, Warp);
vector<vector<Point>>TargetPts;
FindPts(Warp, TargetPts);
DrawAndCompute(Warp, TargetPts);
imshow("Warp", Warp);
waitKey(0);
destroyAllWindows();
system("pause");
return 0;
}
總結(jié)
本文使用OpenCV C++ 進(jìn)行物體尺寸測(cè)量,關(guān)鍵步驟有以下幾點(diǎn)。
1、圖像透視矯正。方便定位物體所在位置。
2、物體定位。定位所需物體位置,獲取特征。
3、根據(jù)已知特征進(jìn)行計(jì)算。
以上就是C++ OpenCV實(shí)現(xiàn)物體尺寸測(cè)量示例詳解的詳細(xì)內(nèi)容,更多關(guān)于C++ OpenCV物體尺寸測(cè)量的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C++與QML進(jìn)行數(shù)據(jù)交互的常見方法總結(jié)
這篇文章主要為大家詳細(xì)介紹了C++與QML進(jìn)行數(shù)據(jù)交互的常見方法,文中 的示例代碼講解詳細(xì),具有一定的參考價(jià)值,有需要的小伙伴可以跟隨小編一起了解一下2023-10-10
C++多態(tài)的實(shí)現(xiàn)及原理詳細(xì)解析
C++的多態(tài)性用一句話概括就是:在基類的函數(shù)前加上virtual關(guān)鍵字,在派生類中重寫該函數(shù),運(yùn)行時(shí)將會(huì)根據(jù)對(duì)象的實(shí)際類型來(lái)調(diào)用相應(yīng)的函數(shù)。如果對(duì)象類型是派生類,就調(diào)用派生類的函數(shù);如果對(duì)象類型是基類,就調(diào)用基類的函數(shù)2013-09-09
基于實(shí)現(xiàn)Qt秒表設(shè)計(jì)
這篇文章主要為大家詳細(xì)介紹了基于實(shí)現(xiàn)Qt秒表設(shè)計(jì),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08
C語(yǔ)言動(dòng)態(tài)內(nèi)存函數(shù)詳解
這篇文章主要介紹了C語(yǔ)言動(dòng)態(tài)內(nèi)存函數(shù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-09-09
C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單推箱子游戲
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單推箱子游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-02-02

