<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          基于opencv的視覺巡線實現(xiàn)

          共 9213字,需瀏覽 19分鐘

           ·

          2024-07-29 10:05

          點擊上方小白學(xué)視覺”,選擇加"星標(biāo)"或“置頂

          重磅干貨,第一時間送達(dá)

          前言


          這段時間在和學(xué)弟打軟件杯的比賽,有項任務(wù)就是機器人的視覺巡線,這雖然不是什么稀奇的事情,但是對于一開始不了解視覺的我來說可以說是很懵了,所以現(xiàn)在就想著和大家分享一下,來看看是如何基于opencv來實現(xiàn)巡線的。我這里以ubuntu20.04為例了


          正文


          1.查看相機設(shè)備


          首先要完成視覺巡線那必不可少的就是相機了,使用


          ll /dev/video*


          來查看相機。



          這里可以看到我有兩個相機設(shè)備,一個是我電腦自帶的相機video0,另一個是我的usb相機video1。


          2.顯示實時圖像


          新建一個工作空間,然后新建一個cpp文件,然后進(jìn)行相機的初始化,以及調(diào)用窗口實時顯示圖像


          #include <opencv2/opencv.hpp>#include <iostream>#include <chrono>
          using namespace std;
          int camera_width = 640;int camera_height = 480;
          int main(int argc, char const *argv[]){    // 初始化變量和對象    cv::VideoCapture cap(1);    cap.set(CAP_PROP_FRAME_WIDTH, camera_width);    cap.set(CAP_PROP_FRAME_HEIGHT, camera_height);    // 循環(huán)處理每一幀圖像    while (true) {        cv::Mat color_image;        cap.read(color_image);        if (color_image.empty()) {            cerr << "Failed to capture image" << endl;            break;        }     imshow("Color Image", color_image);     char key = waitKey(1);        if (key == 'q') {            break;        }    }
             // 釋放資源    cap.release();    destroyAllWindows();    return 0;}


          這里初始化cv::VideoCapture cap(1)傳入的參數(shù)就是上面查看到的設(shè)備,如果想要調(diào)用系統(tǒng)自帶相機,那就改為cap(0)。


          3.巡線函數(shù)


          我這里函數(shù)聲明如下:


          tuple<cv::Mat, float, bool, bool, bool> followBlindPath(cv::Mat color_image) 


          由于我想要多個返回值所以就采用了tuple模版,后面采用tie函數(shù)進(jìn)行解包,其中輸入?yún)?shù)為要識別的圖片,輸出參數(shù)分別為經(jīng)識別后標(biāo)記的圖片,以及水平方向上偏差(后面會具體解釋是什么偏差),后面三個布爾值表示三個狀態(tài),分別為巡線,轉(zhuǎn)彎和停止。


          在識別開始之前,由于圖片在opencv保存的格式默認(rèn)為BGR格式圖片,我們要轉(zhuǎn)為HSV格式,因為后面的操作都是基于HSV圖片進(jìn)行的。


          cv::cvtColor(color_image, hsvFrame, COLOR_BGR2HSV);


          效果如下:




          然后指定HSV的色域,scalar函數(shù)三個參數(shù)分別為色調(diào)(Hue)、飽和度(Saturation)和亮度(Value),我這里設(shè)置的值為黃色的色域。


          cv::Scalar color_lower =  cv::Scalar(10, 40, 120);cv::Scalar color_upper =  cv::Scalar(40, 255, 255);cv::inRange(hsvFrame, color_lower, color_upper,color_mask);


          inRange函數(shù)用于判斷一個像素或像素矩陣是否在指定的范圍內(nèi),hsvFrame是輸入圖像,返回圖像color_mask是一個二值圖像,即在色域內(nèi)的為白色,色域外為黑色。


          處理效果如下:



          然后進(jìn)行濾波,過濾掉一些噪聲然后進(jìn)行膨脹和腐蝕的操作使得圖片識別效果更好


          //矩形結(jié)構(gòu)cv::Mat dilate_kernel = cv::getStructuringElement(MORPH_RECT, Size(10, 10));cv::Mat erode_kernel = cv::getStructuringElement(MORPH_RECT, Size(5, 5));cv::medianBlur(color_mask, color_mask, 9);  // 中值濾波cv::dilate(color_mask, color_mask, dilate_kernel);  // 膨脹 cv::erode(color_mask, color_mask, erode_kernel);  // 腐蝕


          上面定義了用于膨脹和腐蝕的矩形結(jié)構(gòu),然后進(jìn)行了濾波、膨脹和腐蝕。


          效果如下:



          接著就要劃定ROI區(qū)域了,也就是要識別的區(qū)域:


          cv::Mat mask_roi = cv::color_mask(Rect(0, 0, camera_width, 20));  // 劃定ROI區(qū)域


          我這里ROI區(qū)域為上面高20的區(qū)域,因為我實際的相機在下方,具體的ROI區(qū)域,還是要和相機的位置進(jìn)行相應(yīng)的設(shè)置


          區(qū)域如下:



          然后進(jìn)行識別操作:


          vector<vector<cv::Point>> contours_roi;    //cv::Vec4i是二維向量,    vector<Vec4i> hierarchy;    cv::findContours(mask_roi, contours_roi, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);    if (!contours_roi.empty()) {        //內(nèi)聯(lián)函數(shù),找到面積最大值        vector<Point> c = *max_element(contours_roi.begin(), contours_roi.end(), [](vector<Point> a, vector<Point> b) {            return contourArea(a) < contourArea(b);        });        //cv::boundingRect用于計算輪廓的最小邊界矩形。        //它返回值是一個輪廓所包圍的最小矩形,該矩形的邊界與輪廓的邊界平行,并且完全包含輪廓。        //cv::Rect 表示矩形        cv::Rect bound_rect = boundingRect(c);        //用于在圖像上繪制矩形。color_image輸入輸出圖像,bound_rect是標(biāo)記的矩形        cv::rectangle(color_image, bound_rect, Scalar(255, 255, 255), 2);
                 int center_x = bound_rect.x + bound_rect.width / 2;        int center_y = bound_rect.y + bound_rect.height / 2;        cv::circle(color_image, Point(center_x, center_y), 5, Scalar(0, 0, 255), -1);        cv::imshow("Path3", color_image);        last_center_x = center_x;                return make_tuple(color_image, ((center_x / (float)camera_width) * 2 - 1), true, false, false);    }


          findContours用于在二值圖像中查找輪廓,其中:


          • contours:輸出的輪廓向量,每個輪廓表示為一個 std::vector對象


          • hierarchy:可選的輸出向量,包含了輪廓的層次結(jié)構(gòu)信息。默認(rèn)情況下,不輸出層次結(jié)構(gòu)信息,可以傳入一個空的 cv::OutputArray 對象。


          • 輪廓數(shù)據(jù)存儲在 contours 和 hierarchy 兩個輸出參數(shù)中。


          執(zhí)行findContours過后,判斷contours_roi.empty()是否為空,如果為空說明所劃的區(qū)域未識別到黃線,轉(zhuǎn)為了下一個狀態(tài),如果識別到了就找到面積最大的區(qū)域劃矩形框。


          其中采用rectangle()函數(shù)繪制矩形框,然后找到中心點(center_x,center_y),使用circle繪制點,同時返回值第一個bool值置為true,其他為false。


          最終結(jié)果如下:



          如果上面contours_roi.empty()為空,則說明所劃的區(qū)域未識別到黃線,那么就需要重新劃定剩下ROI區(qū)域


          else {        // TurnState        mask_roi = cv::color_mask(Rect(0, 20, camera_width, 80));  // 劃定ROI區(qū)域        vector<vector<Point>> contours_roi;        vector<Vec4i> hierarchy;        cv::findContours(mask_roi, contours_roi, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);        if (!contours_roi.empty()) {            vector<Point> c = *max_element(contours_roi.begin(), contours_roi.end(), [](vector<Point> a, vector<Point> b) {                return contourArea(a) < contourArea(b);            });            cv::Rect bound_rect = boundingRect(c);            cv::rectangle(color_image, bound_rect, Scalar(255, 255, 255), 2);
                     return make_tuple(color_image, ((last_center_x / (float)camera_width) * 2 - 1), false, true, false);        }        else{            // StopState            return make_tuple(color_image, 0.0, false, false, true);        }    }


          如果這部分contours_roi.empty()不為空說明識別到了黃線,該執(zhí)行轉(zhuǎn)彎動作,返回值第二個bool置為true,其他為false,如果這部分contours_roi.empty()仍為空,說明完成了全部線的跟隨,進(jìn)入了停止?fàn)顟B(tài),返回值最后一個置為true,其他為false。


          4.偏差計算


          那返回的偏差是如何計算的呢,這里是用2倍的中心點x坐標(biāo)減去圖片寬度最后比上圖片寬度實現(xiàn)的即:


          last_center_x / (float)camera_width) * 2 - 1


          這樣的意義可以使得中心點在左邊時,值為負(fù),在右邊時值為正,更為方便判斷轉(zhuǎn)向,然后將偏差以相應(yīng)的比例賦給相應(yīng)的機器人便可以啦。


          這個偏差也是可以進(jìn)行PID的,這樣控制效果會更好,后續(xù)我看看要不要也分享一下,這次就到此結(jié)束啦!


                 
          下載1:OpenCV-Contrib擴展模塊中文版教程
          在「小白學(xué)視覺」公眾號后臺回復(fù):擴展模塊中文教程,即可下載全網(wǎng)第一份OpenCV擴展模塊教程中文版,涵蓋擴展模塊安裝、SFM算法、立體視覺、目標(biāo)跟蹤、生物視覺、超分辨率處理等二十多章內(nèi)容。

          下載2:Python視覺實戰(zhàn)項目52講
          小白學(xué)視覺公眾號后臺回復(fù):Python視覺實戰(zhàn)項目,即可下載包括圖像分割、口罩檢測、車道線檢測、車輛計數(shù)、添加眼線、車牌識別、字符識別、情緒檢測、文本內(nèi)容提取、面部識別等31個視覺實戰(zhàn)項目,助力快速學(xué)校計算機視覺。

          下載3:OpenCV實戰(zhàn)項目20講
          小白學(xué)視覺公眾號后臺回復(fù):OpenCV實戰(zhàn)項目20講,即可下載含有20個基于OpenCV實現(xiàn)20個實戰(zhàn)項目,實現(xiàn)OpenCV學(xué)習(xí)進(jìn)階。

          交流群


          歡迎加入公眾號讀者群一起和同行交流,目前有SLAM、三維視覺、傳感器、自動駕駛、計算攝影、檢測、分割、識別、醫(yī)學(xué)影像、GAN、算法競賽等微信群(以后會逐漸細(xì)分),請掃描下面微信號加群,備注:”昵稱+學(xué)校/公司+研究方向“,例如:”張三 + 上海交大 + 視覺SLAM“。請按照格式備注,否則不予通過。添加成功后會根據(jù)研究方向邀請進(jìn)入相關(guān)微信群。請勿在群內(nèi)發(fā)送廣告,否則會請出群,謝謝理解~


          瀏覽 47
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久无码高清视频 | 高清无码免费观看视频 | 五月婷婷五月 | 老牛吃嫩一区二区三区 | 一般男女中文字幕 |