日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

OpenCV探索之路(二十五):制作简易的图像标注小工具

發布時間:2025/3/8 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 OpenCV探索之路(二十五):制作简易的图像标注小工具 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

搞圖像深度學習的童鞋一定碰過圖像數據標注的東西,當我們訓練網絡時需要訓練集數據,但在網上又沒有找到自己想要的數據集,這時候就考慮自己制作自己的數據集了,這時就需要對圖像進行標注。圖像標注是件很枯燥又很費人力物力的一件事情,但是又不能回避,畢竟搞深度學習如果沒有數據集那一切都是瞎搞。最近我在參加一個有關圖像深度學習的比賽,因為命題方沒有給出訓練集,所以需要隊伍自己去標注訓練集,所以我花點時間開發了一些圖像標注小工具給我的團隊使用,以減輕標注的難度,加快標注的速度。

這篇文章我將分享三個標注小工具,分別用于圖像分類、目標檢測以及語義分割的圖像標注任務。

圖像分類標注小工具

實現圖像分類的小工具太好開發了,因為它功能很簡單,無非是對一個文件夾內的所有圖片進行分類,生成每張圖片所對應的類別標簽,用txt文件存儲起來,當然也可以把每一類圖片放在對應的該類的文件夾下。

我實現的這個圖像分類小工具的功能就是,循環彈出一個文件夾內所有的圖片,標注人員對這張圖片進行分類,屬于1類就按1,屬于2類就按2,如此類推,按完相應號碼后圖片自動跳到下一張,直至文件夾內的圖片都被標注完畢。

我們以下面的圖庫為例,將其分為3類。

首先我們需要創建相應的文件夾來存儲每個類的圖片

圖像分類標注小工具代碼:

#include "opencv2/imgproc.hpp" #include "opencv2/highgui.hpp" #include <iostream>#define DATA_DIR ".\\dataset\\" #define IMG_MAX_NUM 20using namespace cv; using namespace std;int main() {FILE* fp;FILE* fp_result;fp = fopen("start.txt", "r"); //讀取開始的圖片名字,方便從某一圖片開始標注int start_i = 0;fscanf(fp, "%d", &start_i);fclose(fp);fp_result = fopen("classify_record.txt", "a+"); //用于記錄每張圖每個框的標注信息printf("start_i: %d\n", start_i);/*循環讀取圖片來標注*/for (int i = start_i; i < IMG_MAX_NUM; i++){stringstream ss1,ss2,ss3;ss1 << DATA_DIR <<"data\\"<< i << ".jpg";ss3 << i << ".jpg";Mat src = imread(ss1.str());if (src.empty()){continue;}printf("正在操作的圖像: %s\n", string(ss1.str()).c_str());imshow("標注", src);char c = 0;c = waitKey(0);while ( c != '1' && c != '2' && c != '3') {c = waitKey(0);printf("invaid input!\n");}ss2 << DATA_DIR << c << "\\" << i << ".jpg";char type = c - '0';printf("分類為: %d\n", c - '0'); imwrite(ss2.str(), src); //copy一份到對應類別的文件夾fprintf(fp_result, "%s %d\n", string(ss3.str()).c_str(), type);}fclose(fp_result);return 0; }

利用工具進行標注

每一類圖片被分到相應的文件夾內

同時也生成標簽文件,每行以圖片路徑+對應的類別的方式呈現。

目標檢測圖像標注小工具

在目標檢測相關的網絡訓練中,我們需要有帶有以下標簽的數據集:

我們做標注時不僅僅要把我們想要識別的物體用矩形框將其框出來,還需要記錄這個框的相關信息,比如這個框的左頂點坐標、寬度高度等(x,y,w,h)。為了能實現這個標注任務,這個標注小工具必須具備框圖和自動記錄(x,y,w,h)信息的功能。

利用opencv我們可以快速實現用矩形框框出對應物體的功能,再加上將每個矩形框的信息有序記錄在txt文件的功能,一個用于檢測圖像標注小工具就算開發好了。

目標檢測圖像標注小工具代碼:

#include "opencv2/imgproc.hpp" #include "opencv2/highgui.hpp" #include <iostream>#define DATA_DIR ".\\cut256\\" #define IM_ROWS 5106 #define IM_COLS 15106 #define ROI_SIZE 256using namespace cv; using namespace std;Point ptL, ptR; //鼠標畫出矩形框的起點和終點,矩形的左下角和右下角 Mat imageSource, imageSourceCopy; FILE* fp_result;struct UserData {Mat src;vector<Rect> rect; };void OnMouse(int event, int x, int y, int flag, void *dp) {UserData *d = (UserData *)dp;imageSourceCopy = imageSource.clone();if (event == CV_EVENT_LBUTTONDOWN) //按下鼠標右鍵,即拖動開始{ptL = Point(x, y);ptR = Point(x, y);}if (flag == CV_EVENT_FLAG_LBUTTON) //拖拽鼠標右鍵,即拖動進行{ptR = Point(x, y);imageSourceCopy = imageSource.clone();rectangle(imageSourceCopy, ptL, ptR, Scalar(0, 255, 0));imshow("標注", imageSourceCopy);}if (event == CV_EVENT_LBUTTONUP) //拖動結束{if (ptL != ptR){rectangle(imageSourceCopy, ptL, ptR, Scalar(0, 255, 0));imshow("標注", imageSourceCopy);int h = ptR.y - ptL.y;int w = ptR.x - ptL.x;printf("選擇的信息區域是:x:%d y:%d w:%d h:%d\n", ptL.x, ptL.y, w, h);d->rect.push_back(Rect(ptL.x, ptL.y, w, h));//d->src(imageSourceCopy);}}//點擊右鍵刪除一個矩形if (event == CV_EVENT_RBUTTONDOWN){if (d->rect.size() > 0){Rect temp = d->rect.back();printf("刪除的信息區域是:x:%d y:%d w:%d h:%d\n", temp.x, temp.y, temp.width, temp.height);d->rect.pop_back();for (int i = 0; i < d->rect.size(); i++){rectangle(imageSourceCopy, d->rect[i], Scalar(0, 255, 0), 1);}}}}void DrawArea(Mat& src, string img_name, string path_name) {Mat img = src.clone();char c = 'x';UserData d;d.src = img.clone();while (c != 'n'){Mat backup = src.clone();imageSource = img.clone();namedWindow("標注", 1);imshow("標注", imageSource);setMouseCallback("標注", OnMouse, &d);c = waitKey(0);if (c == 'a'){printf("rect size: %d\n", d.rect.size());for (int i = 0; i < d.rect.size(); i++){rectangle(backup, d.rect[i], Scalar(0, 255, 0), 1);}img = backup.clone();}}fprintf(fp_result, "%s\n", img_name.c_str());fprintf(fp_result, "%d\n", d.rect.size());for (int i = 0; i < d.rect.size(); i++){Rect t = d.rect[i];fprintf(fp_result, "%d %d %d %d\n", t.x, t.y, t.width, t.height);}imwrite(path_name, img);} int main() {FILE* fp;fp = fopen("start.txt", "r");int start_i = 0;int start_j = 0;fscanf(fp, "%d %d", &start_i, &start_j);fclose(fp);fp_result = fopen("record.txt", "a+");printf("start_i: %d, start_j: %d\n", start_i, start_j);/*循環讀取圖片來標注*/for (int i = start_i; i< IM_ROWS / ROI_SIZE + 1; i++){for (int j = start_j; j<IM_COLS / ROI_SIZE; j++){stringstream ss1, ss2;ss1 << DATA_DIR << "2017\\" << i << "_" << j << "_" << ROI_SIZE << "_.jpg";ss2 << DATA_DIR << "label_img\\" << i << "_" << j << "_" << ROI_SIZE << "_.jpg";cout << ss1.str() << endl;string str(ss1.str());string str2(ss2.str());cv::Mat src = cv::imread(ss1.str());DrawArea(src, str,str2);}}fclose(fp_result);return 0; }

以標注建筑物為例子吧!

然后在txt文件中可以看到我們標記的矩形信息記錄,第一行是圖片路徑+框的個數,第二行開始是每個矩形的x,y,w,h。

語義分割圖像標注小工具

語義分割的標注相比上面的標注要復雜得多,所以標注工具開發起來也略難一點。

比如有這么一個任務,我們需要把圖像中的建筑物給標注出來,生成一個mask圖。

比如這樣子

然后我們以后就可以根據這些mask圖作為label來進行語義分割網絡的訓練了。

實現這么一個工具還是不算太復雜,主要功能的實現就在于使用了opencv的多邊形的生成與填充函數。標注人員只需要在要標注的物體邊緣打點,然后工具就會自動填充該區域,進而生成黑白mask圖。

#include <iostream> #include <sstream> #include <vector> #include <opencv2/opencv.hpp> using namespace std;#define DATA_DIR ".\\cut256\\"#define IM_ROWS 5106 #define IM_COLS 15106 #define ROI_SIZE 256 struct UserData {cv::Mat src;vector<cv::Point> pts; };FILE* fpts_set;void on_mouse(int event, int x, int y, int flags, void *dp) {UserData *d = (UserData *)dp;if (event == CV_EVENT_LBUTTONDOWN){d->pts.push_back(cv::Point(x, y));}if (event == CV_EVENT_RBUTTONDOWN){if (d->pts.size()>0)d->pts.pop_back();}cv::Mat temp = d->src.clone();if (d->pts.size()>2){const cv::Point* ppt[1] = { &d->pts[0] };int npt[] = { static_cast<int>(d->pts.size()) };cv::fillPoly(temp, ppt, npt, 1, cv::Scalar(0, 0, 255), 16);}for (int i = 0; i<d->pts.size(); i++){cv::circle(temp, d->pts[i], 1, cv::Scalar(0, 0, 255), 1, 16);}cv::circle(temp, cv::Point(x, y), 1, cv::Scalar(0, 255, 0), 1, 16);cv::imshow("2017", temp);}void WriteTxT(vector<cv::Point>& pst) {for (int i = 0; i < pst.size(); i++){fprintf(fpts_set, "%d %d", pst[i].x, pst[i].y);if (i == pst.size() - 1){fprintf(fpts_set, "\n");}else{fprintf(fpts_set, " ");}} }int label_img(cv::Mat &src, cv::Mat &mask, string& name) {char c = 'x';vector<vector<cv::Point> > poly_point_set;while (c != 'n'){UserData d;d.src = src.clone();cv::namedWindow("2017", 1);cv::setMouseCallback("2017", on_mouse, &d);cv::imshow("2017", src);c = cv::waitKey(0);if (c == 'a'){if (d.pts.size()>0){const cv::Point* ppt[1] = { &d.pts[0] };int npt[] = { static_cast<int>(d.pts.size()) };cv::fillPoly(src, ppt, npt, 1, cv::Scalar(0, 0, 255), 16);cv::fillPoly(mask, ppt, npt, 1, cv::Scalar(255), 16);poly_point_set.push_back(d.pts);}}}fprintf(stdout, "%s %d\n", name.c_str(), poly_point_set.size());fprintf(fpts_set, "%s %d\n", name.c_str(), poly_point_set.size());//將點集寫入文件for (int i = 0; i < poly_point_set.size(); i++){WriteTxT(poly_point_set[i]);}return 0; } int main() {FILE* fp;fp = fopen("start.txt", "r");int start_i = 0;int start_j = 0;fscanf(fp, "%d %d", &start_i, &start_j);fclose(fp);fpts_set = fopen("semantic_label.txt", "a+");printf("start_i: %d, start_j: %d\n", start_i, start_j);for (int i = start_i; i<IM_ROWS / ROI_SIZE + 1; i++){for (int j = start_j; j<IM_COLS / ROI_SIZE; j++){stringstream ss1,ss2,ss3;cv::Mat mask(256, 256, CV_8UC1);mask.setTo(0);ss1 << DATA_DIR << "2017\\" << i << "_" << j << "_" << ROI_SIZE << "_.jpg";ss2 << DATA_DIR << "label\\" << i << "_" << j << "_" << ROI_SIZE << "_.jpg";ss3 << i << "_" << j << "_" << ROI_SIZE << "_.jpg";cout << ss1.str() << endl;cv::Mat src = cv::imread(ss1.str());label_img(src, mask, string(ss3.str()));// label based on tinycv::imwrite(ss2.str(), mask);}}fclose(fpts_set);return 0; }

所以我們可以利用這個標注工具對任意形狀的物體進行標注,原理就是利用多邊形的逼近。看看效果吧

生成的mask圖

當然我們也可以根據需求把每個標注的每個圖形的邊緣點記錄下來

希望這三款小工具能給你帶來一點小幫助和小啟發~

總結

以上是生活随笔為你收集整理的OpenCV探索之路(二十五):制作简易的图像标注小工具的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。