問題來源為網友提供的資料,原文地址為:《Object Orientation, Principal Component Analysis & OpenCV》
問題描述:
對於這樣的圖像(2副,采用了背投光),如何獲得上面工件的主要方向
主要思路:
1、分別獲得每個工件的輪廓;
2、處理每個輪廓,
采用pca(主成分分析)方法,獲得所有輪廓點的集合的中點,主要方向等信息;
3、繪圖並返回結果。
注:pca相關函數請查看
代碼略解:
1、讀入圖片,尋找輪廓;
//讀入圖像,轉換為灰度
Mat img = imread( "e:/sandbox/pca1.jpg");
Mat bw;
cvtColor(img, bw, COLOR_BGR2GRAY);
//閾值處理
threshold(bw, bw, 150, 255, CV_THRESH_BINARY);
//尋找輪廓
vector <vector <Point > > contours;
vector <Vec4i > hierarchy;
findContours(bw, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
Mat img = imread( "e:/sandbox/pca1.jpg");
Mat bw;
cvtColor(img, bw, COLOR_BGR2GRAY);
//閾值處理
threshold(bw, bw, 150, 255, CV_THRESH_BINARY);
//尋找輪廓
vector <vector <Point > > contours;
vector <Vec4i > hierarchy;
findContours(bw, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
2、首先以大小篩選輪廓;
//輪廓分析,找到工件
for (size_t i = 0; i < contours.size(); ++i)
{
//計算輪廓大小
double area = contourArea(contours[i]);
//去除過小或者過大的輪廓區域(科學計數法表示)
if (area < 1e2 || 1e5 < area) continue;
//繪制輪廓
drawContours(img, contours, i, CV_RGB( 255, 0, 0), 2, 8, hierarchy, 0);
//尋找每一個輪廓的方向
getOrientation(contours[i], img);
}
for (size_t i = 0; i < contours.size(); ++i)
{
//計算輪廓大小
double area = contourArea(contours[i]);
//去除過小或者過大的輪廓區域(科學計數法表示)
if (area < 1e2 || 1e5 < area) continue;
//繪制輪廓
drawContours(img, contours, i, CV_RGB( 255, 0, 0), 2, 8, hierarchy, 0);
//尋找每一個輪廓的方向
getOrientation(contours[i], img);
}
3、單獨處理每個輪廓,分析其主要方向,繪制結果
//獲得構建的主要方向
double getOrientation(vector <Point > &pts, Mat &img)
{
//構建pca數據。這里做的是將輪廓點的x和y作為兩個維壓到data_pts中去。
Mat data_pts = Mat(pts.size(), 2, CV_64FC1); //使用mat來保存數據,也是為了后面pca處理需要
for ( int i = 0; i < data_pts.rows; ++i)
{
data_pts.at < double >(i, 0) = pts[i].x;
data_pts.at < double >(i, 1) = pts[i].y;
}
//執行PCA分析
PCA pca_analysis(data_pts, Mat(), CV_PCA_DATA_AS_ROW);
//獲得最主要分量,在本例中,對應的就是輪廓中點,也是圖像中點
Point pos = Point(pca_analysis.mean.at < double >( 0, 0),pca_analysis.mean.at < double >( 0, 1));
//存儲特征向量和特征值
vector <Point2d > eigen_vecs( 2);
vector < double > eigen_val( 2);
for ( int i = 0; i < 2; ++i)
{
eigen_vecs[i] = Point2d(pca_analysis.eigenvectors.at < double >(i, 0),pca_analysis.eigenvectors.at < double >(i, 1));
eigen_val[i] = pca_analysis.eigenvalues.at < double >(i, 0); //注意,這個地方原代碼寫錯了
}
//在輪廓/圖像中點繪制小圓
circle(img, pos, 3, CV_RGB( 255, 0, 255), 2);
//計算出直線,在主要方向上繪制直線
line(img, pos, pos + 0. 02 * Point(eigen_vecs[ 0].x * eigen_val[ 0], eigen_vecs[ 0].y * eigen_val[ 0]) , CV_RGB( 255, 255, 0));
line(img, pos, pos + 0. 02 * Point(eigen_vecs[ 1].x * eigen_val[ 1], eigen_vecs[ 1].y * eigen_val[ 1]) , CV_RGB( 0, 255, 255));
//返回角度結果
return atan2(eigen_vecs[ 0].y, eigen_vecs[ 0].x);
}
double getOrientation(vector <Point > &pts, Mat &img)
{
//構建pca數據。這里做的是將輪廓點的x和y作為兩個維壓到data_pts中去。
Mat data_pts = Mat(pts.size(), 2, CV_64FC1); //使用mat來保存數據,也是為了后面pca處理需要
for ( int i = 0; i < data_pts.rows; ++i)
{
data_pts.at < double >(i, 0) = pts[i].x;
data_pts.at < double >(i, 1) = pts[i].y;
}
//執行PCA分析
PCA pca_analysis(data_pts, Mat(), CV_PCA_DATA_AS_ROW);
//獲得最主要分量,在本例中,對應的就是輪廓中點,也是圖像中點
Point pos = Point(pca_analysis.mean.at < double >( 0, 0),pca_analysis.mean.at < double >( 0, 1));
//存儲特征向量和特征值
vector <Point2d > eigen_vecs( 2);
vector < double > eigen_val( 2);
for ( int i = 0; i < 2; ++i)
{
eigen_vecs[i] = Point2d(pca_analysis.eigenvectors.at < double >(i, 0),pca_analysis.eigenvectors.at < double >(i, 1));
eigen_val[i] = pca_analysis.eigenvalues.at < double >(i, 0); //注意,這個地方原代碼寫錯了
}
//在輪廓/圖像中點繪制小圓
circle(img, pos, 3, CV_RGB( 255, 0, 255), 2);
//計算出直線,在主要方向上繪制直線
line(img, pos, pos + 0. 02 * Point(eigen_vecs[ 0].x * eigen_val[ 0], eigen_vecs[ 0].y * eigen_val[ 0]) , CV_RGB( 255, 255, 0));
line(img, pos, pos + 0. 02 * Point(eigen_vecs[ 1].x * eigen_val[ 1], eigen_vecs[ 1].y * eigen_val[ 1]) , CV_RGB( 0, 255, 255));
//返回角度結果
return atan2(eigen_vecs[ 0].y, eigen_vecs[ 0].x);
}
結果展示:

感謝關注,希望有所幫助。
提供的這個gif錄屏軟件,非常好用。