摘要
本篇來用OpenCV實(shí)現(xiàn)Halcon中一個(gè)計(jì)算回形針方向的實(shí)例clip.hdev,并構(gòu)建了計(jì)算角度函數(shù)和畫箭頭函數(shù),得到的角度與halcon例程相差無多。
原圖如下:
Halcon代碼比較簡單,這里也貼出來:
dev_update_window ('off')
read_image (Clip, 'clip')
get_image_size (Clip, Width, Height)
dev_close_window ()
dev_open_window (0, 0, Width / 2, Height / 2, 'black', WindowID)
dev_display (Clip)
set_display_font (WindowID, 14, 'mono', 'true', 'false')
disp_continue_message (WindowID, 'black', 'true')
stop ()
binary_threshold (Clip, Dark, 'max_separability', 'dark', UsedThreshold)
connection (Dark, Single)
select_shape (Single, Selected, 'area', 'and', 5000, 10000)
dev_set_draw ('fill')
dev_set_colored (12)
dev_display (Selected)
disp_continue_message (WindowID, 'black', 'true')
stop ()
dev_display (Clip)
dev_set_color ('green')
dev_display (Selected)
orientation_region (Selected, Phi)
area_center (Selected, Area, Row, Column)
dev_set_line_width (3)
dev_set_draw ('margin')
Length := 80
dev_set_color ('blue')
disp_arrow (WindowID, Row, Column, Row - Length * sin(Phi), Column + Length * cos(Phi), 4)
disp_message (WindowID, deg(Phi)$'3.1f' + ' deg', 'image', Row, Column - 100, 'black', 'false')
dev_update_window ('on')
可以看到halcon的思路是:
① 讀取圖像
② 二值化
③ 根據(jù)面積剔除非回形針的region
④ 計(jì)算每個(gè)region的方向和中心
⑤ 畫箭頭,結(jié)果輸出
但是opencv中沒有計(jì)算角度的函數(shù)和畫箭頭的函數(shù),因此需要我們自己定義。
opecv實(shí)現(xiàn):
(一)構(gòu)建角度計(jì)算函數(shù)
double calLineAngle(Point&ptStrat, Point&ptEnd)
{
const double PI = 3.1415926;
double angle = 0;
if (ptStrat.x==ptEnd.x)
{
angle = 90;
}
else if (ptStrat.y==ptEnd.y)
{
angle = 0;
}
else
{
angle = atan((double)(ptEnd.y - ptStrat.y) / (ptEnd.x - ptStrat.x))*(180 / PI);
if (angle<0)
{
angle = abs(angle);
}
else if (angle>0)
{
angle = 180 - angle;
}
if (ptEnd.y-ptStrat.y>0&&ptEnd.x-ptStrat.x)
{
angle = angle - 180;
}
}
return angle;
}
反正切函數(shù)atan與atan2的區(qū)別:
atan 和 atan2 都是求反正切函數(shù),如:有兩個(gè)點(diǎn) point(x1,y1), 和 point(x2,y2);那么這兩個(gè)點(diǎn)形成的斜率的角度計(jì)算方法分別是:
float angle = atan( (y2-y1)/(x2-x1) );
float angle = atan2( y2-y1, x2-x1 );
atan 和 atan2 區(qū)別:
1:參數(shù)的填寫方式不同;
2:atan2 的優(yōu)點(diǎn)在于 如果 x2-x1等于0 依然可以計(jì)算,但是atan函數(shù)就會(huì)導(dǎo)致程序出錯(cuò);
(二)構(gòu)建畫箭頭函數(shù)
void drawArrow(Mat&img, Point pStart, Point pEnd, int len, int alpha, Scalar color, int thickness, int lineType)
{
const double PI = 3.1415926;
Point arrow;
//計(jì)算theat角
double angel = atan2((double)(pStart.y - pEnd.y), (double)(pStart.x - pEnd.x));
line(img, pStart, pEnd, color, thickness, lineType);
//計(jì)算箭角邊的另一端的端點(diǎn)位置(上面的還是下面的要看箭頭的指向,即pStart和pEnd的位置)
arrow.x = pEnd.x + len * cos(angel + PI * alpha / 180);
arrow.y = pEnd.y + len * sin(angel + PI * alpha / 180);
line(img, pEnd, arrow, color, thickness, lineType);
arrow.x = pEnd.x + len * cos(angel - PI * alpha / 180);
arrow.y = pEnd.y + len * sin(angel - PI * alpha / 180);
line(img, pEnd, arrow, color, thickness, lineType);
}
(三)主函數(shù)
int main(int argc, char** argv)
{
double pointToLineDist(Point&pt, Point&lineStart, Point&lineEnd);
double calLineAngle(Point&ptStrat, Point&ptEnd);
void drawArrow(Mat&img, Point pStart, Point pEnd, int len, int alpha, Scalar color, int thickness, int lineType);
Mat src = imread("D:/opencv練習(xí)圖片/回形針方向.png");
imshow("輸入圖像", src);
Mat gray, binary;
cvtColor(src, gray, COLOR_BGR2GRAY);
//閾值處理
threshold(gray, binary, 150, 255, THRESH_BINARY_INV);
imshow("二值化", binary);
//尋找輪廓
vector<vector<Point> > contours;
findContours(binary, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE,Point());
cout << contours.size() << endl;
//輪廓分析,找到工件
for (size_t i = 0; i < contours.size(); ++i)
{
//計(jì)算輪廓大小
double area = contourArea(contours[i]);
if (area < 12000)continue;
double max_dist = 0;
Moments mm = moments(contours[i],false);
Point ptCenter = Point(mm.m10 / mm.m00, mm.m01 / mm.m00);
Point ptFarthest;
for (int j = 0; j < contours[i].size(); j++)
{
double distance = sqrt(pow(contours[i][j].x - ptCenter.x, 2) + pow(contours[i][j].y - ptCenter.y, 2));
if (distance>max_dist)
{
ptFarthest = Point(contours[i][j].x, contours[i][j].y);
max_dist = distance;
}
}
cout << max_dist << endl;
drawContours(src, contours, i, Scalar(0, 0, 255), 5, 8);
drawArrow(src, ptCenter, ptFarthest, 25, 30, Scalar(255,0,255), 3, 8);
double angle = calLineAngle(ptCenter, ptFarthest);
cout << angle << endl;
char strAngle[20];
sprintf_s(strAngle, "%0.1fdegree", angle);
putText(src, strAngle, ptCenter, FONT_HERSHEY_SIMPLEX, 0.8, Scalar(0, 255, 255), 2, 8);
}
imshow("結(jié)果", src);
waitKey(0);
return 0;
}