學(xué)習(xí)OpenCV3:計(jì)算三角形的3個(gè)角度


一飞崖、問題

? ? 已知三角形的3個(gè)點(diǎn)p0烂叔、p1p2」掏幔現(xiàn)希望畫出三角形,并計(jì)算3個(gè)角度的大小逢防。

二、分析

? ? 直線a的直線方程:
\frac{y-y_1}{y_2-y_1} = \frac{x-x_1}{x_2-x_1} \Rightarrow \begin{cases} ax+by+c=0 \\a=-(y_2-y_1) \\ b = x_2-x_1 \\ c = (y_2-y_1)x_1 - (x_2-x_1) y_1\end{cases}

? ? 已知3個(gè)點(diǎn)p0灰署、p1p2辜伟,要構(gòu)造三角形必須確保3個(gè)點(diǎn)不在同一直線上,即p0不在直線a上:

    a * p0.x + b * p0.y + c != 0

? ?三角形各邊邊長(zhǎng)為:

a = \sqrt{(p2.x-p1.x)^2+(p2.y-p1.y)^2}

b = \sqrt{(p2.x-p0.x)^2+(p2.y-p0.y)^2}

c = \sqrt{(p1.x-p0.x)^2+(p1.y-p0.y)^2}

??由余弦定理得:
a^2=b^2+c^2-2bc \cos \theta \Rightarrow \cos \theta = \frac{b^2+c^2-a^2}{2bc} \Rightarrow \theta = \arccos (\frac{b^2+c^2-a^2}{2bc})

三、實(shí)現(xiàn)

#include <opencv2/opencv.hpp>
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
using namespace cv;

Point2d g_p0(200, 500), g_p1(400, 100), g_p2(600, 500);

// 判斷3個(gè)點(diǎn)不在同一直線上
bool same_line(Point2d p0, Point2d p1, Point2d p2)
{
    double a = -(p2.y - p1.y);
    double b = p2.x - p1.x;
    double c = (p2.y - p1.y) * p1.x - (p2.x - p1.x) * p1.y;
    if (a * p0.x + b * p0.y + c != 0) // p0不在直線p1p2上
        return true;
    else
        return false;
}

// 計(jì)算夾角
double calculate_angle(Point2d p0, Point2d p1, Point2d p2)
{
    double a = sqrt(pow(p2.x - p1.x, 2) + pow(p2.y - p1.y, 2));
    double b = sqrt(pow(p2.x - p0.x, 2) + pow(p2.y - p0.y, 2));
    double c = sqrt(pow(p1.x - p0.x, 2) + pow(p1.y - p0.y, 2));
    double angle = acos((b * b + c * c - a * a) / (2 * b * c)) * 180 / CV_PI;
    return angle;
}

// 畫夾角
void draw_angle(Mat img, Point2d p0, Point2d p1, Point2d p2, double radius, const Scalar color, int thickness)
{
    // 計(jì)算直線的角度
    double angle1 = atan2(-(p1.y - p0.y), (p1.x - p0.x)) * 180 / CV_PI;
    double angle2 = atan2(-(p2.y - p0.y), (p2.x - p0.x)) * 180 / CV_PI;
    // 計(jì)算主軸的角度
    double angle = angle1 <= 0 ? -angle1 : 360 - angle1;
    // 計(jì)算圓弧的結(jié)束角度
    double end_angle = (angle2 < angle1) ? (angle1 - angle2) : (360 - (angle2 - angle1));
    if (end_angle > 180)
    {
        angle = angle2 <= 0 ? -angle2 : 360 - angle2;
        end_angle = 360 - end_angle;
    }
    // 畫圓弧
    ellipse(img, p0, Size(radius, radius), angle, 0, end_angle, color, thickness);
}

// 鼠標(biāo)回調(diào)函數(shù)
void mouse_callback(int event, int x, int y, int flags, void *param)
{
    static Point2d p(0, 0), p1(0, 0), p2(0, 0);
    static int n = -1;
    switch (event)
    {
    case cv::EVENT_LBUTTONDOWN: // 鼠標(biāo)左鍵點(diǎn)擊
    {
        p1 = Point2d(x, y);
        int w = 15, h = 15;
        Rect r0(g_p0.x - w, g_p0.y - h, 2 * w, 2 * h);
        Rect r1(g_p1.x - w, g_p1.y - h, 2 * w, 2 * h);
        Rect r2(g_p2.x - w, g_p2.y - h, 2 * w, 2 * h);
        if (r0.contains(p1)) // 鼠標(biāo)落在中心點(diǎn)g_p0
        {
            n = 0;
            p = g_p0;
        }
        else if (r1.contains(p1)) // 鼠標(biāo)落在g_p1
        {
            n = 1;
            p = g_p1;
        }
        else if (r2.contains(p1)) // 鼠標(biāo)落在g_p2
        {
            n = 2;
            p = g_p2;
        }
        break;
    }
    case cv::EVENT_MOUSEMOVE: // 鼠標(biāo)移動(dòng)
        p2 = Point2d(x, y);
        if (n == 0)
        {
            g_p0 = p + p2 - p1;
        }
        else if (n == 1)
        {
            g_p1 = p + p2 - p1;
        }
        else if (n == 2)
        {
            g_p2 = p + p2 - p1;
        }
        break;
    case cv::EVENT_LBUTTONUP: // 鼠標(biāo)左鍵釋放
        p1 = Point2d(0, 0);
        p2 = Point2d(0, 0);
        n = -1;
        break;
    default:
        break;
    }
}

// 主函數(shù)
int main()
{
    string window_name = "image";
    namedWindow(window_name, WINDOW_AUTOSIZE);
    int w = 800, h = 600;
    Mat image_original = Mat(h, w, CV_8UC3, Scalar(255, 255, 255));
    cv::setMouseCallback(window_name, mouse_callback); // 調(diào)用鼠標(biāo)回調(diào)函數(shù)
    while (true)
    {
        Mat img = image_original.clone(); // 拷貝空白圖片踩麦,方便重復(fù)畫圖

        line(img, g_p1, g_p2, Scalar(255, 0, 0), 2); // 畫藍(lán)線a
        line(img, g_p2, g_p0, Scalar(0, 255, 0), 2); // 畫綠線b
        line(img, g_p1, g_p0, Scalar(0, 0, 255), 2); // 畫紅線c

        if (same_line(g_p0, g_p1, g_p2)) // 3個(gè)點(diǎn)不在同一條直線上
        {
            draw_angle(img, g_p0, g_p1, g_p2, 15, Scalar(255, 0, 0), 2); // 畫藍(lán)色夾角
            double b = calculate_angle(g_p0, g_p1, g_p2);
            string s2 = "p0: (" + to_string(g_p1.x) + ", " + to_string(g_p1.y) + ") " + to_string(b);
            putText(img, s2, Point2d(10, 25), cv::FONT_HERSHEY_COMPLEX, 0.5, Scalar(255, 0, 0));

            draw_angle(img, g_p1, g_p2, g_p0, 15, Scalar(0, 255, 0), 2); // 畫綠色夾角
            double c = calculate_angle(g_p1, g_p2, g_p0);
            string s3 = "p1: (" + to_string(g_p2.x) + ", " + to_string(g_p2.y) + ") " + to_string(c);
            putText(img, s3, Point2d(10, 50), cv::FONT_HERSHEY_COMPLEX, 0.5, Scalar(0, 255, 0));

            draw_angle(img, g_p2, g_p0, g_p1, 15, Scalar(0, 0, 255), 2); // 畫紅色夾角
            double a = calculate_angle(g_p2, g_p0, g_p1);
            string s1 = "p2: (" + to_string(g_p0.x) + ", " + to_string(g_p0.y) + ") " + to_string(a);
            putText(img, s1, Point2d(10, 75), cv::FONT_HERSHEY_COMPLEX, 0.5, Scalar(0, 0, 255));
        }
        else
        {
            putText(img, "error", Point2d(10, 25), cv::FONT_HERSHEY_COMPLEX, 0.5, Scalar(0, 0, 255), 1);
        }

        imshow(window_name, img);
        if (waitKey(3) > 0)
            break;
    }
    return 0;
}

操作方法:
??鼠標(biāo)左鍵點(diǎn)擊三角形任意一個(gè)頂線并按住移動(dòng)谓谦,由此可修改三角形贫橙。在鍵盤輸入任意的鍵反粥,可退出程序。

運(yùn)行結(jié)果:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市幅垮,隨后出現(xiàn)的幾起案子尾组,更是在濱河造成了極大的恐慌,老刑警劉巖讳侨,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異甘桑,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)跑杭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來爹橱,“玉大人窄做,你說我怎么就攤上這事⊥终担” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵糟红,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我乌叶,道長(zhǎng),這世上最難降的妖魔是什么准浴? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任乐横,我火速辦了婚禮求橄,結(jié)果婚禮上晰奖,老公的妹妹穿的比我還像新娘。我一直安慰自己啃匿,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布溯乒。 她就那樣靜靜地躺著豹爹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪臂聋。 梳的紋絲不亂的頭發(fā)上或南,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天艾君,我揣著相機(jī)與錄音,去河邊找鬼蹬癌。 笑死虹茶,一個(gè)胖子當(dāng)著我的面吹牛逝薪,可吹牛的內(nèi)容都是我干的蝴罪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼感局,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼暂衡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起狂巢,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤书聚,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后雌续,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡受啥,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年鸽心,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片藤肢。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡糯景,死狀恐怖省骂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情钞澳,我是刑警寧澤温学,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站仗岖,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏揽祥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一拄丰、第九天 我趴在偏房一處隱蔽的房頂上張望俐末。 院中可真熱鬧,春花似錦卓箫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逢勾。三九已至藐吮,卻和暖如春溺拱,著一層夾襖步出監(jiān)牢的瞬間炎码,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工攒菠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人辖众。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像凹炸,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子啤它,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353