Ubuntu系統VScode實現opencv(c++)鼠標操作與響應

在之前的創作中心-CSDN滾動條調整圖片亮度-CSDN博客創作中心-CSDN中,我們已經了解了滾動條實現亮度以及對比度調節,為了實現對圖像中感興趣區域(ROI, Region of Interest)的交互式選取,本文利用 OpenCV 提供的鼠標事件回調機制,設計并實現了一套基于鼠標操作的圖像區域標注功能。用戶通過點擊鼠標左鍵確定矩形區域的起點,并在拖動過程中實時顯示選框,釋放鼠標左鍵后自動繪制最終的矩形框并截取所選區域。

首先,我們先了解所有的鼠標事件:

事件常量描述(鼠標事件)
EVENT_MOUSEMOVE鼠標移動
EVENT_LBUTTONDOWN鼠標左鍵按下
EVENT_LBUTTONUP鼠標左鍵釋放
EVENT_LBUTTONDBLCLK鼠標左鍵雙擊
EVENT_RBUTTONDOWN鼠標右鍵按下
EVENT_RBUTTONUP鼠標右鍵釋放
EVENT_RBUTTONDBLCLK鼠標右鍵雙擊
EVENT_MBUTTONDOWN鼠標中鍵按下
EVENT_MBUTTONUP鼠標中鍵釋放
EVENT_MBUTTONDBLCLK鼠標中鍵雙擊
EVENT_MOUSEWHEEL鼠標滾輪垂直滾動(上滾為正,下滾為負)
EVENT_MOUSEHWHEEL鼠標滾輪水平滾動(右滾為正,左滾為負)

簡單了解之后,需要深度理解鼠標響應函數:cv::setMouseCallback()?

void cv::setMouseCallback(const String& winname, MouseCallback onMouse, void* userdata = 0);
參數類型說明
winnameconst String&要監聽鼠標事件的窗口名稱,必須是通過 cv::namedWindow() 創建的窗口
onMouseMouseCallback用戶定義的鼠標事件回調函數指針
userdatavoid*(可選)可選的用戶數據指針,可傳遞額外參數,如圖像指針或上下文結構體

?有了之前的基礎后,對于回調函數的定義就較為容易了:

void onMouse(int event, int x, int y, int flags, void* userdata);
參數說明
event當前觸發的鼠標事件(如 EVENT_LBUTTONDOWN
x, y鼠標當前在圖像坐標系中的位置
flags當前的鼠標鍵/修飾鍵狀態(如 EVENT_FLAG_CTRLKEY
userdata通過 setMouseCallback() 傳遞的用戶指針

在了解相關的函數后,就可以開始實現開頭的功能了,首先,我們依舊是在hpp文件定義這個方法函數:

class Demo{
public:void colorspace_Demo(Mat &image);void Mat_creat(Mat &image);void pixel_RW_Demo(Mat &image);void operator_Demo(Mat &image);void Tracking_Demo(Mat &image);void Color_Demo(Mat &image);void bitwise_Demo(Mat &image);void channel_Demo(Mat &image);void inrange_Demo(Mat &image);void pixel_statistics_Demo(Mat &image);void Shapes_Demo(Mat &image);void polygon_drawing_Demo();void random_Demo();void mouse_Demo(Mat &image);
};

?緊接著回到Demo.cpp定義該函數:

void Demo::mouse_Demo(Mat &image)
{namedWindow("mouse_draw",WINDOW_AUTOSIZE);setMouseCallback("mouse_draw",draw,(void*)(&image));imshow("mouse_draw",image);
}

?首先生成一個窗口,用于鼠標操作顯示,draw是回調換函數;接下來定義draw回調函數:接下來,我們一步一步來實現鼠標框選區域的功能:

1.鼠標左鍵按下;

2.左鍵按下的同時鼠標移動;

3.左鍵松去,畫出所框區域;

那么針對上述三步,一共有三個鼠標事件:左鍵按下,鼠標移動,左鍵松掉,所框區域可以是繪制矩形;思路清晰了;

繪制矩形需要,起始點坐標,結束坐標,長寬,這些就需要程序來獲得,我們上述的回調函數中,會獲取當前鼠標事件發生時的坐標xy,那起始點以及結束點坐標就有了,對于長寬,也只需要結束點x-起始點x;

首先我們定義兩個點,起始點以及結束點:

Point  sp(-1,-1);
Point  ep(-1,-1); 

回調函數定義:

static void draw(int event,int x,int y,int flags, void *userdata) 
{Mat image = *((Mat*)userdata);
}

?Mat image = *((Mat*)userdata);這一步就不過多解釋;

?1.鼠標左鍵按下;我們要做的就是記錄下起始點的坐標:

    if (event == EVENT_LBUTTONDOWN){sp.x = x;sp.y = y;cout<<sp<<endl;}

我們先編寫鼠標松掉,看能否繪制矩形:

else if (event == EVENT_LBUTTONUP){ep.x = x;ep.y = y;int dx = ep.x - sp.x;int dy = ep.y - sp.y;Rect box(sp.x,sp.y,dx,dy);rectangle(image,box,Scalar(255,0,0),4,LINE_8);imshow("ROI",image(box));imshow("mouse_draw",image);sp.x=-1;sp.y=-1;ep.x=-1;ep.y=-1;}

同樣先做的是記錄下結束的坐標,這樣我們就得到了兩個先決條件 :那么長寬就計算出來了:

int dx = ep.x - sp.x;
int dy = ep.y - sp.y;

問題來了,這里的dx,dy是存在負數的情況的,看下面的圖片;?

左上角的點為(0,0);如果從左上角拉到右下角就是正數,而從右下角拉到左上角就是負數;?

?我們只需要分情況就行了:

第一種正數:

        if (dx>0&&dy>0){Rect box(sp.x,sp.y,dx,dy);rectangle(image,box,Scalar(255,0,0),4,LINE_8);imshow("ROI",image(box));}

第二種負數:?

        {dx=-dx;dy=-dy;Rect box(ep.x,ep.y,dx,dy);rectangle(image,box,Scalar(255,0,0),4,LINE_8);imshow("ROI",image(box));}

負數我們只需要將dxdy取反得正,那么應該將結束點作為起始點繪制矩形就可以了;這里的imshow("ROI",image(box));用于顯示所框選的區域;到這一步我們的程序應該是這樣:

Point  sp(-1,-1);
Point  ep(-1,-1); static void draw(int event,int x,int y,int flags, void *userdata) 
{Mat image = *((Mat*)userdata);if (event == EVENT_LBUTTONDOWN){sp.x = x;sp.y = y;cout<<sp<<endl;}else if (event == EVENT_LBUTTONUP){ep.x = x;ep.y = y;int dx = ep.x - sp.x;int dy = ep.y - sp.y;if (dx>0&&dy>0){Rect box(sp.x,sp.y,dx,dy);rectangle(image,box,Scalar(255,0,0),4,LINE_8);imshow("ROI",image(box));}if (dx<0&&dy<0){dx=-dx;dy=-dy;Rect box(ep.x,ep.y,dx,dy);rectangle(image,box,Scalar(255,0,0),4,LINE_8);imshow("ROI",image(box));}imshow("mouse_draw",image);sp.x=-1;sp.y=-1;ep.x=-1;ep.y=-1;}
}

?運行發現,整體大致沒什么問題,但是我們在拉動的時候,希望這個矩形能跟著鼠標移動:并且畫面也沒有更新,繪制的框在繪制下一個依舊顯示在上面;

現在,我們還缺少鼠標移動事件的情況,編寫之后,再接著糾錯,實際上很簡單,鼠標移動時,矩形跟著放大或者變小,實際上也是鼠標移動,繪制矩形;只需要復制粘貼,

   else if (event == EVENT_MOUSEMOVE){if(sp.x > 0 && sp.y > 0){ep.x = x;ep.y = y;int dx = ep.x - sp.x;int dy = ep.y - sp.y;if (dx>0&&dy>0){Rect box(sp.x,sp.y,dx,dy);rectangle(image,box,Scalar(255,0,0),4,LINE_8);}if (dx<0&&dy<0){dx=-dx;dy=-dy;Rect box(ep.x,ep.y,dx,dy);rectangle(image,box,Scalar(255,0,0),4,LINE_8);}imshow("mouse_draw", image);  }}

?運行發現好像有1.問題:

?所幸我打開GPT,得知問題所在:鼠標移動繪制,但是我們應該給一個原圖刷新不讓其繪制很多矩形,解決方案就是在定義一個全局變量temp;在方法函數中拷貝原圖;在繪制完矩形后,立刻將這個temp拷貝到繪制矩形的圖片上,就可以只繪制第一個矩形,其他都被原圖覆蓋了;

    else if (event == EVENT_MOUSEMOVE){if(sp.x > 0 && sp.y > 0){ep.x = x;ep.y = y;int dx = ep.x - sp.x;int dy = ep.y - sp.y;if (dx>0&&dy>0){Rect box(sp.x,sp.y,dx,dy);temp.copyTo(image);rectangle(image,box,Scalar(255,0,0),4,LINE_8);}if (dx<0&&dy<0){dx=-dx;dy=-dy;Rect box(ep.x,ep.y,dx,dy);temp.copyTo(image);rectangle(image,box,Scalar(255,0,0),4,LINE_8);}imshow("mouse_draw", image);  // 🟢 實時刷新窗口}}

運行發現成功;

那整體的回調函數定義就是:

static void draw(int event,int x,int y,int flags, void *userdata) 
{Mat image = *((Mat*)userdata);if (event == EVENT_LBUTTONDOWN){sp.x = x;sp.y = y;cout<<sp<<endl;}else if (event == EVENT_MOUSEMOVE){if(sp.x > 0 && sp.y > 0){ep.x = x;ep.y = y;int dx = ep.x - sp.x;int dy = ep.y - sp.y;if (dx>0&&dy>0){Rect box(sp.x,sp.y,dx,dy);temp.copyTo(image);rectangle(image,box,Scalar(255,0,0),4,LINE_8);}if (dx<0&&dy<0){dx=-dx;dy=-dy;Rect box(ep.x,ep.y,dx,dy);temp.copyTo(image);rectangle(image,box,Scalar(255,0,0),4,LINE_8);}imshow("mouse_draw", image);  // 🟢 實時刷新窗口}}else if (event == EVENT_LBUTTONUP){ep.x = x;ep.y = y;int dx = ep.x - sp.x;int dy = ep.y - sp.y;if (dx>0&&dy>0){Rect box(sp.x,sp.y,dx,dy);rectangle(image,box,Scalar(255,0,0),4,LINE_8);imshow("ROI",image(box));}if (dx<0&&dy<0){dx=-dx;dy=-dy;Rect box(ep.x,ep.y,dx,dy);rectangle(image,box,Scalar(255,0,0),4,LINE_8);imshow("ROI",image(box));}imshow("mouse_draw",image);sp.x=-1;sp.y=-1;ep.x=-1;ep.y=-1;}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/91816.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/91816.shtml
英文地址,請注明出處:http://en.pswp.cn/web/91816.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

True or False? 基于 BERT 學生數學問題誤解檢測

True or False? 基于 BERT 學生數學問題誤解檢測 代碼詳見&#xff1a;https://github.com/xiaozhou-alt/Student_Math_Misconception 文章目錄True or False? 基于 BERT 學生數學問題誤解檢測一、項目介紹二、文件夾結構三、數據集介紹四、BERT 模型介紹五、項目實現1. 數據…

小程序基于vue+nodejs的私人定做訂制訂單發布與對應商品出售平臺

文章目錄項目介紹主要技術與實現手段具體實現截圖關于我本系統開發思路研究思路、方法和步驟java類核心代碼部分展示系統測試本系統技術可行性分析源碼獲取詳細視頻演示或者查看其他版本&#xff1a;文章底部獲取博主聯系方式&#xff01;項目介紹主要技術與實現手段 uni-app框…

為什么要有動態內存分配?

文章目錄1.為什么要有動態內存分配2.malloc和free2.1 malloc2.2 free3.calloc和realloc3.1 calloc3.2 realloc4.常見的動態內存的錯誤4.1 對NULL指針的解引用操作4.2 對動態開辟空間的越界訪問4.3 對?動態開辟內存使?free釋放4.4 使?free釋放?塊動態開辟內存的?部分4.5 對…

docker hub 拉取鏡像失敗報Get “https://registry-1.docker.io/v2/“: net/http: request canceled while waiting

自己記錄一把&#xff0c;給兄弟們避坑 1.上問題報錯代碼 [rootlocalhost ~]# docker pull hello-world Using default tag: latestError response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connectio…

Hive數倉部署/分層/ETL腳本基礎指南

部署Hive數倉及分層基礎指南 部署和構建一個Hive數倉涉及多個步驟&#xff0c;包括設置Hadoop集群、配置Hive、設計數據倉庫架構以及實現ETL&#xff08;Extract, Transform, Load&#xff09;過程。下面是一個詳細的指南&#xff0c;幫助你完成這些步驟。 1. 設置Hadoop集群 首…

深入 Go 底層原理(六):垃圾回收(GC)

1. 引言Go 語言自帶垃圾回收&#xff08;Garbage Collection, GC&#xff09;&#xff0c;讓開發者從手動管理內存的繁重任務中解脫出來。Go 的 GC 以其低延遲和并發性而聞名&#xff0c;其目標是在不長時間暫停&#xff08;Stop The World, STW&#xff09;整個程序的情況下完…

專網內網IP攻擊防御:從應急響應到架構加固

內網IP攻擊防御&#xff1a;從應急響應到架構加固內網IP攻擊的隱蔽性遠超外網威脅&#xff0c;其本質是信任邊界內的權限濫用。應對需遵循"識別-隔離-溯源-加固"四步法則&#xff0c;兼顧應急止損與長效防御。應急處置&#xff1a;30分鐘響應窗口1. 流量阻斷&#xf…

Git、Gitee、GitHub、GitLab完整講解:從基礎到進階

第一部分&#xff1a;Git是什么&#xff1f; &#x1f4da;比喻&#xff1a;Git就像是一本"時光日記本" ? 每一段代碼的改動&#xff0c;Git都會幫你記錄下來&#xff0c;像是在寫日記。 ? 如果出現問題或者想查看之前的版本&#xff0c;Git可以帶你"穿越回…

WinForm之CheckBox 控件

CheckBox&#xff08;復選框&#xff09;是 WinForm 中用于實現 “多項選擇” 的控件&#xff0c;允許用戶從一組選項中選擇任意數量的項&#xff08;包括零項、一項或多項&#xff09;&#xff0c;適用于需要同時選擇多個選項的場景&#xff08;如愛好、權限設置、功能開關等&…

鯨魚優化算法(Whale Optimization Algorithm, WOA)是一種受座頭鯨捕食行為啟發的群體智能優化算法,由Seyedali Mirjalili于2016年提出

鯨魚優化算法(Whale Optimization Algorithm, WOA)是一種受座頭鯨捕食行為啟發的群體智能優化算法,由Seyedali Mirjalili于2016年提出。 它通過模擬鯨魚的狩獵策略(特別是“氣泡網捕食”行為)來解決優化問題,廣泛應用于函數優化、工程設計、機器學習參數優化等領域。以下…

信息量,驚奇度,熵、KL散度(相對熵),交叉熵、最大似然估計MLE與最小化交叉熵的等價證明、

一&#xff1a; 一些基本概念 1.1 信息量:特定事件所攜帶的信息多少信息量衡量的是特定事件所攜帶的信息多少&#xff0c;其數學定義為&#xff1a;其中p(x)是事件x發生的概率。核心思想&#xff1a;越罕見的事件&#xff0c;其攜帶的信息量越大&#xff1b;越常見的事件&#…

VBA 64位API聲明語句第012講

跟我學VBA&#xff0c;我這里專注VBA, 授人以漁。我98年開始&#xff0c;從源碼接觸VBA已經20余年了&#xff0c;隨著年齡的增長&#xff0c;越來越覺得有必要把這項技能傳遞給需要這項技術的職場人員。希望職場和數據打交道的朋友&#xff0c;都來學習VBA,利用VBA,起碼可以提高…

深入理解Java中String.intern()方法:從原理到并發控制實踐

深入理解 Java 中 String.intern () 方法&#xff1a;從原理到并發控制實踐 在 Java 開發中&#xff0c;String.intern()方法是一個看似簡單卻蘊含深意的 API。它在字符串常量池管理、內存優化以及并發控制等場景中有著關鍵作用。本文將從底層原理出發&#xff0c;結合實際案例…

在Linux中創建LVGL應用

在Linux中創建LVGL應用 簡介 上一篇文章介紹了在imx6上開發UI的流程 . 這篇接上文&#xff0c; 介紹具體的開發步驟。 1. 創建項目主目錄 mkdir my_lvgl_project cd my_lvgl_project2. 初始化 Git 倉庫 (可選但推薦) git init echo "# My Project with Dependencies&…

大模型對比評測:Qwen2.5 VS Gemini 2.0誰更能打?

一、背景與選型關鍵 在 AI 應用落地的時代&#xff0c;“AI大模型選型對比”成為關鍵環節。選擇合適的模型要綜合考量性能、上下文長度、推理能力、中文/編程支持、成本等多維度指標。 本文重點比較 Gemini2.0Flash-Lite &#xff08;Preview&#xff09;、Gemini2.0Flash &a…

轉置卷積解釋與示例計算

文章目錄轉置卷積的三種等價實現方法&#xff1a;原理、公式與等價性分析數學定義與核心公式方法一&#xff1a;零填充翻轉核卷積&#xff08;數學定義方法&#xff09;原理與公式等價性說明方法二&#xff1a;直接位置映射&#xff08;pytorch框架高效實現&#xff09;原理與公…

關于車位引導及汽車乘梯解決方案的專業性、系統性、可落地性強的綜合設計方案與技術實現說明,旨在為現代智慧停車樓提供高效、安全、智能的停車體驗。

一、系統概述隨著城市土地資源日益緊張&#xff0c;立體停車、自動化停車成為發展趨勢。本方案圍繞“車位引導系統 汽車乘梯系統”構建智慧停車核心體系&#xff0c;結合地磁/視頻/超聲波檢測、AI識別、語音交互、電梯自動調度等先進技術&#xff0c;實現車輛入場、引導、停泊…

【相機】曝光時間長-->拖影

曝光時間長 → 運動目標在快門開啟期間持續移動 → 同一像素記錄多個位置的能量 → 圖像出現“拖影”&#xff08;運動模糊&#xff09;。&#x1f50d; 具體原因卷簾快門&#xff08;Rolling Shutter&#xff09;效應 RealSense 的 RGB 傳感器&#xff08;如 IMX 系列&#xf…

day36 力扣1049.最后一塊石頭的重量II 力扣494.目標和 力扣474.一和零

最后一塊石頭的重量II有一堆石頭&#xff0c;用整數數組 stones 表示。其中 stones[i] 表示第 i 塊石頭的重量。每一回合&#xff0c;從中選出任意兩塊石頭&#xff0c;然后將它們一起粉碎。假設石頭的重量分別為 x 和 y&#xff0c;且 x < y。那么粉碎的可能結果如下&#…

Java內存模型(Java Memory Model,JMM)

?? JMM?? 是Java虛擬機&#xff08;JVM&#xff09;規范中定義的一組規則和規范&#xff0c;用于描述多線程環境下&#xff0c;Java程序中變量的訪問和修改行為&#xff0c;尤其是在并發編程中如何保證內存可見性、原子性和有序性。JMM 是 Java 并發編程的基石&…