這一篇博客我對我寫的《使用opencv實作單目尺寸估計(一)》里面透視變換的代碼做了一個優化,那篇文章種我直接采用了角點檢測,這一篇文章我將帶你實作得到直線方程算交點以及交點排序的演算法,最終根據盤子實際尺寸還原影像,
攝像頭拍到的影像:

實作步驟:
1.轉灰度圖片以及二值化

2.canny邊緣檢測

3.直線擬合,求方成,算交點,算四個交點與原點距離得到排序方案

4.根據盤子實際尺寸比例45*35透視變換,

/**
* Filename:main.cpp
* Author:visual_eagle
* Date:2022.01.05
* version:1.0
*/
#include<opencv2/opencv.hpp>
#include<iostream>
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;
//交點
double x[4];
double y[4];
int len = sizeof(x) / sizeof(x[0]);
// 獲取交點
void getCross()
{
int s=0;
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)
{
std::cout<<"i:"<<i<<" j:"<<j<<" is "<<" true"<<std::endl;
}
else if(int(abs(line_k[i]))!=0&&int(abs(line_k[j]))!=0)
{
std::cout<<"i:"<<i<<" j:"<<j<<" is "<<" true"<<std::endl;
}
else
{
std::cout<<"s:"<<s<<std::endl;
std::cout<<"i:"<<i<<" j:"<<j<<" is "<<" false"<<std::endl;
/*
* L1:A1*x+B1*y+C1=0
* L2:A2*x+B2*y+C2=0
*
* y?
* L1*A2-L2*A1
* A1A2*x+B1A2*y+A2C1=0
* A1A2*x+B2A1*y+A1C2=0
* B1A2*y+A2C1=B2A1*y+A1C2
* y=(A1C2-A2C1)/(B1A2-B2A1)
*
* x?
* L1*B2-L2*B1
* A1B2*x+B1B2*y+B2C1=0
* A2B1*x+B1B2*y+B1C2=0
* A1B2*x+B2C1=A2B1*x+B1C2
* x=(B1C2-B2C1)/(A1B2-A2B1)
*
* L1:y=k1x+b1
* L2:y=k2x+b2
* x=(b2-b1)/(k1-k2)
* y=(k1b2-k2b1)/(k1-k2)
*/
x[s]=(line_b[i]-line_b[j])/(line_k[j]-line_k[i]);
y[s]=(line_k[j]*line_b[i]-line_k[i]*line_b[j])/(line_k[j]-line_k[i]);
std::cout<<s<<".(x,y):"<<x[s]<<","<<y[s]<<std::endl;
s=s+1;
}
}
}
}
void drawLine(cv::Mat &img, //要標記直線的影像
std::vector<cv::Vec2f> lines, //檢測的直線資料
double rows, //原影像的行數(高)
double cols, //原影像的列數(寬)
cv::Scalar scalar, //繪制直線的顏色
int n //繪制直線的線寬
)
{
int image_channels=img.channels();
cv::Point pt1, pt2;
if(lines.size()>4)
{
for(int i=0;i<lines.size()-4;i++)
{
lines.pop_back();
}
}
for (size_t i = 0; i < lines.size(); i++)
{
float rho = lines[i][0]; //直線距離坐標原點的距離
float theta = lines[i][1]; //直線過坐標原點垂線與x軸夾角
double a = cos(theta); //夾角的余弦值
double b = sin(theta); //夾角的正弦值
double x0 = a*rho, y0 = b*rho; //直線與過坐標原點的垂線的交點
double length = std::max(rows, cols); //影像高寬的最大值
//計算直線上的一點
pt1.x = cvRound(x0 + length * (-b));
pt1.y = cvRound(y0 + length * (a));
//計算直線上另一點
pt2.x = cvRound(x0 - length * (-b));
pt2.y = cvRound(y0 - length * (a));
//兩點繪制一條直線
if(i==0&&image_channels!=1)
{
scalar=cv::Scalar(255,0,0);//blue
}
else if(i==1&&image_channels!=1)
{
scalar=cv::Scalar(255,255,0);//yellow
}
else if(i==2&&image_channels!=1)
{
scalar=cv::Scalar(0,0,255);//red
}
else if(i==3&&image_channels!=1)
{
scalar=cv::Scalar(0,255,0);//green
}
else;
if(image_channels==1)
{
scalar=cv::Scalar(255,255,255);
}
line(img, pt1, pt2, scalar, 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];
std::cout<<i+1<<":"<<"y="<<line_k[i]<<"*x+"<<line_b[i]<<std::endl;
}
std::cout<<"lines_number:"<<lines.size()<<std::endl;
line_number=lines.size();
if(line_number==4)
{
getCross();
//2 3 change
double cap_x=0;
double cap_y=0;
cap_x=x[2];
cap_y=y[2];
x[2]=x[3];
y[2]=y[3];
x[3]=cap_x;
y[3]=cap_y;
//和(0,0)兩點距離最短確定起始點,左上角點
double point_cup_x=cols;
double point_cup_y=rows;
double p0=cols;
int p0_flag=8;
for (int i = 0; i <line_number; i++)
{
double p0_m=sqrt(x[i]*x[i]+y[i]*y[i]);
if(p0>p0_m)
{
p0_flag=i;
p0=p0_m;
}
std::cout<<"p"<<i<<":"<<p0_m<<std::endl;
if(i==3)
{
std::cout<<"p0:"<<p0<<" p0_flag:"<<p0_flag<<std::endl;
}
}
if(p0_flag==0)
{
std::cout<<"Point ok"<<std::endl;
}
else
{
double temp_x;
double temp_y;
for (int o = 0; o < p0_flag; o++)
{
temp_x = x[0];
temp_y = y[0];
for (int p = 0; p <len - 1; p++)
{
x[p] = x[p + 1];
y[p] = y[p + 1];
}
x[len - 1] = temp_x;
y[len - 1] = temp_y;
}
for(int i=0;i<4;i++)
{
std::cout<<"i:"<<i<<"(x,y):"<<x[i]<<","<<y[i]<<std::endl;
}
//1 3 change
double cap_x2=0;
double cap_y2=0;
cap_x2=x[1];
cap_y2=y[1];
x[1]=x[3];
y[1]=y[3];
x[3]=cap_x2;
y[3]=cap_y2;
std::cout<<"Point deal ok"<<std::endl;
}
for(int i=0;i<4;i++)
{
if(i==0)
circle(img, cv::Point(x[i],y[i]), 10, cv::Scalar(0, 0, 0),-1);//black
else if(i==1)
circle(img, cv::Point(x[i],y[i]), 10, cv::Scalar(255, 0, 0),-1);//blue
else if(i==2)
circle(img, cv::Point(x[i],y[i]), 10, cv::Scalar(0, 255, 0),-1);//green
else
circle(img, cv::Point(x[i],y[i]), 10, cv::Scalar(0, 0, 255),-1);//red
}
}
}
int main(int argc, char *argv[])
{
/*
* Mat cv::imread(const String & filename,int flags=IMREAD_COLOR)
* flags=0: src.channels() is 1
* flags=1: src.channels() is 3
* flags=-1:if src have alpha ,src.channels() is 4
*/
cv::Mat src=cv::imread("D:/program/mycompany/4food2321/food/image/fruit (6).jpg",1);
std::cout<<"src.channels():"<<src.channels()<<std::endl;
std::cout<<"src.size():"<<src.size()<<std::endl;
int r_width=src.cols*0.5;
int r_height=src.rows*0.5;
cv::Mat src_resize;
cv::resize(src,src_resize,cv::Size(r_width,r_height));
std::cout<<"src_resize.size():"<<src_resize.size()<<std::endl;
cv::imshow("src_resize",src_resize);
/*
* void cv::cvtColor(inputArray src,OutputArray dst,int code,int dstCn=0)
* code :https://docs.opencv.org/4.x/d8/d01/group__imgproc__color__conversions.html#ga4e0972be5de079fed4e3a10e24ef5ef0
* dstCn:number of channels in the destination image; if the parameter is 0, the number of the channels is derived automatically from src and code.
*/
cv::Mat src_gray;
cv::cvtColor(src_resize,src_gray,cv::COLOR_BGR2GRAY,0);
//frame_gray(x,y)>90 frame_threshold(x,y)=255 else 0
cv::threshold(src_gray,src_gray,90,255,cv::THRESH_BINARY);
cv::imshow("src_gray",src_gray);
/*
* Canny has two ways to use it, I use the first.
* 1.void cv::Canny(InputArray image,OutputArray edges,double threshold1,double threshold2,int apertureSize = ,3, bool L2gradient = false)
* InputArray image is 8bit
* OutputArray edges is 8bit ,edges.size()=image.size()
* threshold1 and threshold2:低于閾值1的會被認為不是邊緣,高于閾值2的像素點會被認為是強邊緣,在閾值1和2之間的是弱邊緣
* apertureSize is Sober operator size
* L2gradient is 是否采用更精確的方式計算影像梯度
*/
cv::Mat gray_canny;
cv::Canny(src_gray, gray_canny, 200, 250, 3, false);
cv::imshow("gray_canny",gray_canny);
/*
* void HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength=0, double maxLineGap=0 )
* OutputArray lines,每條直線有四個元素矢量(x_1,y_1,x_2,y_2)
* double rho為直線搜索的進步尺寸的單位半徑
* double theta為直線搜索的進步尺寸的單位角度
* threshold為累加平面的閾值引數,大于這個閾值才是認為是直線
* minLineLength最低線段長度
* maxLineGap同一方向上兩線段判斷為一條線的最大允許間隔,超過閾值則將兩線認為是一條直線
*/
//way1:
// std::vector<cv::Vec4i> lines;
// int threshold=r_height*0.15;
// double minLineLength=r_height*0.2;
// double maxLineGap=threshold*0.2;
// std::cout<<"threshold:"<<threshold<<" minLineLength:"<<minLineLength<<" maxLineGap:"<<maxLineGap<<std::endl;
// HoughLinesP(gray_canny, lines, 1, CV_PI / 180, threshold, minLineLength, maxLineGap);
// cv::Mat frame_HoughLines=cv::Mat::zeros(r_height,r_width,CV_8UC3);
// for( size_t i = 0; i < lines.size(); i++ )
// {
// line( frame_HoughLines, cv::Point(lines[i][0], lines[i][1]),cv::Point( lines[i][2], lines[i][3]), cv::Scalar(255,255,255), 1.5, 8 );
// }
// imshow("frame_HoughLines",frame_HoughLines);
//way2:
//void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0, double min_theta = 0, double max_theta = CV_PI )
//累加器進行檢測直線
cv::Mat frame_HoughLines=cv::Mat::zeros(r_height,r_width,CV_8UC1);
std::vector<cv::Vec2f> lines2;
HoughLines(gray_canny, lines2, 0.85, CV_PI / 180, 120,0,0,0,CV_PI);
cv::Mat frame_HoughLines2=src_resize.clone();
drawLine(frame_HoughLines2, lines2, frame_HoughLines2.rows, frame_HoughLines2.cols, cv::Scalar(255,255,255), 1);
imshow("frame_HoughLines2",frame_HoughLines2);
//透視變換
std::vector<cv::Point2f>dstpoint(4);//存放變換后四頂點
//mm
float a4_width=3500/6;
float a4_height=4500/6;
std::vector<cv::Point2f>srcpoint(4);//存放變換前四頂點
cv::Mat result = cv::Mat::zeros(a4_width, a4_height,src_resize.type());
for(int i=0;i<4;i++)
{
srcpoint[i].x=x[i];
srcpoint[i].y=y[i];
}
//定義矯正后四頂點
dstpoint[0] = cv::Point2f(0, 0);
dstpoint[1] = cv::Point2f(result.cols, 0);
dstpoint[2] = cv::Point2f(result.cols, result.rows);
dstpoint[3] = cv::Point2f(0, result.rows);
cv::Mat M = getPerspectiveTransform(srcpoint, dstpoint);
cv::Mat frame_result=src_resize.clone();
warpPerspective(frame_result, result, M, result.size());
imshow("result", result);
cv::waitKey(0);
return 0;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/404229.html
標籤:其他
上一篇:深度學習筆記總結(待更新)
