OpenCV繪制圖形功能

本文實例為大傢分享瞭OpenCV繪制圖形功能的具體代碼,供大傢參考,具體內容如下

1、繪制直線

繪制直線函數是cv::line,函數完整形式如下

void line(InputOutputArray img, Point pt1, Point pt2, const Scalar& color, int thickness = 1, int lineType = LINE_8, int shift = 0);
/*
@param img 圖像
@param pt1 線段端點1
@param pt2 線段端點2
@param color 線段顏色
@param thickness 線寬
@param lineType 線的類型
@param shift 點坐標的小數位偏移
*/

color可以使用cv::Scalar構造,但是傳入參數的順序是BGR,使用CV_RGB宏更直觀,以RGB順序傳入;

lineType取值有LINE_4、LINE_8和LINE_AA,分別表示4連接線,8連接線,抗鋸齒線,是以不同的算法產生直線(也可以是FILLED=-1,直接填充);

shift是指點坐標的二進制表示的位偏移,每加1坐標值減一半,實驗的結果,不知道理解的對不對,使用默認值0就可以瞭;

在兩個點之間繪制線寬為1的紅色直線定義為一個函數

void DrawLine(const cv::Mat& destImg, const cv::Point& pt1, const cv::Point& pt2)
{
    cv::line(destImg, pt1, pt2, CV_RGB(255, 0, 0), 1);
}

下面的實例在鼠標兩次點擊位置中間畫一根直線,繪制完成可以按Enter鍵保存圖像。

cv::Mat g_originImage;//原始圖像
cv::Mat g_editImage;//編輯的圖像
std::vector<cv::Point> g_editPoints;//正在繪制的圖形的點
std::vector<std::vector<cv::Point>> g_lines;//所有的線段
 
void RedrawAllLines()
{
    g_originImage.copyTo(g_editImage);//恢復背景圖像
    for (int i = 0; i < g_lines.size(); i++)
    {
        if (g_lines[i].size() >= 2)
        {
            DrawLine(g_editImage,g_lines[i][0], g_lines[i][1]);
        }
    }
}
void OnDrawLineMouseEvent(int event, int x, int y, int flags, void* userdata)
{
    if (event == cv::EVENT_LBUTTONDOWN)
    {
        if (g_editPoints.size() > 0)
        {
            //在第二個點按下鼠標之後添加到線段列表中,並重繪圖像
            g_editPoints.push_back(cv::Point(x, y));
            g_lines.push_back(g_editPoints);
            RedrawAllLines();
            g_editPoints.clear();
            imshow("image", g_editImage);
        }
        else
        {
            g_editPoints.push_back(cv::Point(x, y));//第一個點
        }
    }
    else if (event == cv::EVENT_MOUSEMOVE)
    {
        if (g_editPoints.size() > 0)
        {
            //鼠標移動中,繪制到鼠標位置的直線,但鼠標當前點不加入到g_editPoints中
            RedrawAllLines();
            DrawLine(g_editImage,g_editPoints[g_editPoints.size() - 1], cv::Point(x, y));
            imshow("image", g_editImage);
        }
    }
}
 
int main(int argc, char **arv)
{
    g_originImage = cv::imread("walkers.jpg");
    g_originImage.copyTo(g_editImage);
    cv::namedWindow("image");
    imshow("image", g_editImage);
    cv::setMouseCallback("image", OnDrawLineMouseEvent);
    int key = cv::waitKey(0);
    while (key != 27)
    {
        if (key == 13)
        {
            cv::imwrite("testsave.png", g_editImage);
        }
        key = cv::waitKey(0);
    }
    return 0;
}

2、繪制圓

繪制圓的函數cv::circle

void circle(InputOutputArray img, Point center, int radius,const Scalar& color, int thickness = 1,int lineType = LINE_8, int shift = 0);
/*
@param img 圖像
@param center 圓心
@param radius 半徑
@param color 線顏色
@param thickness 線寬,如果小於0則填充圓
@param lineType 線類型
@param shift 圓心和半徑值的位偏移
*/

以兩個點畫一個圓,第一個點為圓心,兩點距離為圓半徑,定義為一個函數DrawCircle

void DrawCircle(const cv::Mat& destImg, const cv::Point& pt1, const cv::Point& pt2)
{
    cv::Point deltaPt = pt2 - pt1;
    float radius = cv::sqrt(deltaPt.x*deltaPt.x + deltaPt.y*deltaPt.y);
    cv::circle(destImg, pt1, radius, CV_RGB(255, 0, 0), 1);
}

以下示例實現鼠標點擊兩次繪制如上的一個圓,main函數與畫直線一樣,隻要將鼠標事件回調改成OnDrawCircleMouseEvent

std::vector<std::vector<cv::Point>> g_circles;//所有的圓
 
void RedrawAllCircles()
{
    g_originImage.copyTo(g_editImage);//恢復背景圖像
    for (int i = 0; i < g_circles.size(); i++)
    {
        if (g_circles[i].size() >= 2)
        {
            DrawCircle(g_editImage, g_circles[i][0], g_circles[i][1]);
        }
    }
}
void OnDrawCircleMouseEvent(int event, int x, int y, int flags, void* userdata)
{
    if (event == cv::EVENT_LBUTTONDOWN)
    {
        if (g_editPoints.size() > 0)
        {
            g_editPoints.push_back(cv::Point(x, y));
            g_circles.push_back(g_editPoints);
            RedrawAllCircles();
            g_editPoints.clear();
            imshow("image", g_editImage);
        }
        else
        {
            g_editPoints.push_back(cv::Point(x, y));//第一個點
        }
    }
    else if (event == cv::EVENT_MOUSEMOVE)
    {
        if (g_editPoints.size() > 0)
        {
            RedrawAllCircles();
            DrawCircle(g_editImage, g_editPoints[g_editPoints.size() - 1], cv::Point(x, y));
            imshow("image", g_editImage);
        }
    }
}

3、繪制橢圓

繪制橢圓的函數cv::ellipse,有兩種形式,其中一個定義如下

void ellipse(InputOutputArray img, const RotatedRect& box, const Scalar& color,int thickness = 1, int lineType = LINE_8);
/*
@param img 圖像
@param box 可以調整旋轉角度的矩形
@param color 線顏色
@param thickness 線寬,如果小於0則填充橢圓
@param lineType 線類型
*/

以兩個點組成的矩形內畫一個橢圓,定義為函數DrawEllipse,這裡不考慮矩形的旋轉,固定為0

void DrawEllipse(const cv::Mat& destImg, const cv::Point& pt1, const cv::Point& pt2)
{
    cv::Point2f center = cv::Point2f((pt1.x + pt2.x) / 2.0, (pt1.y + pt2.y) / 2.0);
    cv::Point2f size = cv::Point2f(cv::abs(pt2.x - pt1.x), cv::abs(pt2.y - pt1.y));
    cv::ellipse(destImg,cv::RotatedRect(center, size, 0),CV_RGB(255, 0, 0), 1);
}

以下示例實現在鼠標兩次點擊位置中間畫一個橢圓,main函數與畫直線一樣,將鼠標事件回調改成OnDrawEllipseMouseEvent

std::vector<std::vector<cv::Point>> g_ellipses;//所有的橢圓
void RedrawAllEllipses()
{
    g_originImage.copyTo(g_editImage);//恢復背景圖像
    for (int i = 0; i < g_ellipses.size(); i++)
    {
        if (g_ellipses[i].size() >= 2)
        {
            DrawEllipse(g_editImage, g_ellipses[i][0], g_ellipses[i][1]);
        }
    }
}
void OnDrawEllipseMouseEvent(int event, int x, int y, int flags, void* userdata)
{
    if (event == cv::EVENT_LBUTTONDOWN)
    {
        if (g_editPoints.size() > 0)
        {
            g_editPoints.push_back(cv::Point(x, y));
            g_ellipses.push_back(g_editPoints);
            RedrawAllEllipses();
            g_editPoints.clear();
            imshow("image", g_editImage);
        }
        else
        {
            g_editPoints.push_back(cv::Point(x, y));//第一個點
        }
    }
    else if (event == cv::EVENT_MOUSEMOVE)
    {
        if (g_editPoints.size() > 0)
        {
            RedrawAllEllipses();
            DrawEllipse(g_editImage, g_editPoints[g_editPoints.size() - 1], cv::Point(x, y));
            imshow("image", g_editImage);
        }
    }
}

4、繪制矩形

繪制矩形的函數cv::rectangle,有兩種形式,其中一個定義如下

void rectangle(InputOutputArray img, Rect rec,const Scalar& color, int thickness = 1,int lineType = LINE_8, int shift = 0);
/*
@param img 圖像
@param rec 矩形坐標
@param color 線顏色
@param thickness 線寬,如果小於0則填充橢圓
@param lineType 線類型
@param shift  點坐標位偏移
*/

在兩個點間畫一個矩形,定義為函數DrawRectangle

void DrawRectangle(const cv::Mat& destImg, const cv::Point& pt1, const cv::Point& pt2)
{
    cv::rectangle(destImg, cv::Rect(pt1, pt2), CV_RGB(255, 0, 0), 1);
}

以下示例實現在鼠標兩次點擊位置中間畫一個矩形,main函數與畫直線一樣,將鼠標事件回調改成OnDrawRectangleMouseEvent

 std::vector<std::vector<cv::Point>> g_rectangles;//所有的矩形
void RedrawAllRectangles()
{
    g_originImage.copyTo(g_editImage);//恢復背景圖像
    for (int i = 0; i < g_rectangles.size(); i++)
    {
        if (g_rectangles[i].size() >= 2)
        {
            DrawRectangle(g_editImage, g_rectangles[i][0], g_rectangles[i][1]);
        }
    }
}
void OnDrawRectangleMouseEvent(int event, int x, int y, int flags, void* userdata)
{
    if (event == cv::EVENT_LBUTTONDOWN)
    {
        if (g_editPoints.size() > 0)
        {
            g_editPoints.push_back(cv::Point(x, y));
            g_rectangles.push_back(g_editPoints);
            RedrawAllRectangles();
            g_editPoints.clear();
            imshow("image", g_editImage);
        }
        else
        {
            g_editPoints.push_back(cv::Point(x, y));//第一個點
        }
    }
    else if (event == cv::EVENT_MOUSEMOVE)
    {
        if (g_editPoints.size() > 0)
        {
            RedrawAllRectangles();
            DrawRectangle(g_editImage, g_editPoints[g_editPoints.size() - 1], cv::Point(x, y));
            imshow("image", g_editImage);
        }
    }
}

5、繪制多邊形輪廓

繪制多邊形的函數cv::polylines,有兩種形式,其中一個定義如下

void polylines(InputOutputArray img, InputArrayOfArrays pts,bool isClosed, const Scalar& color,int thickness = 1, int lineType = LINE_8, int shift = 0 );
/*
@param img 圖像
@param pts 多邊形坐標數組
@param isClosed 是否繪制閉合多邊形
@param color 線顏色
@param thickness 線寬
@param lineType 線類型
@param shift  點坐標位偏移
*/

這裡的pts是一個2維數組,表示多個多邊形,以下分別實現繪制多個多邊形和單個多邊形的函數

void DrawMultiPolys(const cv::Mat& destImg, const std::vector<std::vector<cv::Point>>& points, bool bClose)
{
    cv::polylines(destImg, points, bClose, CV_RGB(255, 0, 0), 1);
}
void DrawOnePoly(const cv::Mat& destImg, const std::vector<cv::Point>& points,bool bClose)
{
    if (points.size() >= 2)
    {
        std::vector<std::vector<cv::Point>> polyPoints;
        polyPoints.push_back(points);
        DrawMultiPolys(destImg,polyPoints,bClose);
    }
}

以下示例實現在鼠標多次點擊的位置繪制多邊形,main函數與畫直線一樣,將鼠標事件回調改成OnDrawPolyMouseEvent

std::vector<std::vector<cv::Point>> g_polys;//所有的多邊形
void RedrawAllPolys()
{
    g_originImage.copyTo(g_editImage);//恢復背景圖像
    DrawMultiPolys(g_editImage,g_polys,true);
}
void OnDrawPolyMouseEvent(int event, int x, int y, int flags, void* userdata)
{
    if (event == cv::EVENT_LBUTTONDOWN)
    {
        if (g_editPoints.size() > 0)
        {
            g_editPoints.push_back(cv::Point(x, y));
            RedrawAllPolys();
            DrawOnePoly(g_editImage, g_editPoints, false);//正在繪制的多邊形要單獨畫,而且不能閉合
            imshow("image", g_editImage);
        }
        else
        {
            g_editPoints.push_back(cv::Point(x, y));//第一個點
        }
    }
    else if (event == cv::EVENT_MOUSEMOVE)
    {
        if (g_editPoints.size() > 0)
        {
            RedrawAllPolys();
            DrawOnePoly(g_editImage,g_editPoints,false);//正在繪制的多邊形要單獨畫,而且不能閉合
            DrawLine(g_editImage, g_editPoints[g_editPoints.size() - 1], cv::Point(x, y));//繪制一根到鼠標位置的直線
            imshow("image", g_editImage);
        }
    }
    else if (event == cv::EVENT_RBUTTONDOWN)
    {
        //右鍵按下結束多邊形繪制,加入到g_polys
        g_polys.push_back(g_editPoints);
        RedrawAllPolys();
        g_editPoints.clear();
        cv::imshow("image", g_editImage);
    }
}

6、繪制填充多邊形

繪制填充多邊形函數cv::fillPoly,有兩種形式,其中一個定義如下

void fillPoly(InputOutputArray img, InputArrayOfArrays pts,const Scalar& color, int lineType = LINE_8, int shift = 0,Point offset = Point() );
/*
@param img 圖像
@param pts 多邊形坐標數組
@param color 線顏色
@param lineType 線類型
@param shift  點坐標位偏移
@param offset 所有多邊形點的偏移
*/

繪制填充多邊形與繪制多邊形輪廓差不多,隻要將polylines換成fillpoly

void DrawMultiPolys(const cv::Mat& destImg, const std::vector<std::vector<cv::Point>>& points)
{
    cv::fillPoly(destImg, points,CV_RGB(255, 0, 0));
}
void DrawOnePoly(const cv::Mat& destImg, const std::vector<cv::Point>& points)
{
    if (points.size() >= 2)
    {
        std::vector<std::vector<cv::Point>> polyPoints;
        polyPoints.push_back(points);
        DrawMultiPolys(destImg,polyPoints);
    }
}

如果將上面的所有功能以一定方式組合起來,就可以在圖像上繪制多種形狀圖形並保存瞭。

以上就是本文的全部內容,希望對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。

推薦閱讀: