RoboMaster視覺教程OpenCV(四)開閉運算

RoboMaster視覺教程OpenCV(四)開閉運算

一 Open CV開運算

之後的代碼都在Qt Creator中運行了。畢竟在IDE中運行,比命令行方便許多。當然,刪掉有關Qt的幾行代碼,一樣可以在命令行中運行。

腐蝕與膨脹作爲形態學的基本操作,經過組合後可以很容易的實現更高一級的形態學運算。

形態學(morphology)一詞通常表示生物學的一個分支,該分支主要研究動植物的形態和結構。而我們圖像處理中的形態學,往往指的是數學形態學(Mathematical morphology)。
簡單來講,形態學操作就是基於形狀的一系列圖像處理操作。最基本的形態學操作有兩種,分別是:膨脹(dilate)與腐蝕(erode)。經過互相組合可以形成高級的形態學操作檯運算和閉運算。

詳細內容我們可以去看相關opencv參考書。如毛星雲的書187~194頁

膨脹:卷積求局部最大值。白色部分(高亮部分)更多。

腐蝕:卷積求局部最小值,高亮部分被腐蝕,白色部分(高亮部分)更少。

開運算(Opening Operation):先腐蝕後膨脹的過程。用來消除小物體,在纖細點處分離黑色物體,並且在平滑較大物體的邊界的同時不明顯改變其面積。

閉運算(Closing Operation):先膨脹後腐蝕的過程。算能夠排除小型黑洞(黑色區域)。

開運算代碼如下:

今天發佈的文章有Qt的安裝教程,以後的代碼都在Qt環境裏運行了。新建一個qt控制檯程序,將main.cpp中的內容替換爲以下部分:

#include <QCoreApplication>
#include "opencv2/opencv.hpp"
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
         using namespace cv;

        namedWindow("origin");//用來顯示圖像用的,
        namedWindow("erode");
        namedWindow("dilate");
        namedWindow("Opening_Operation");

        Mat image = imread("/home/dji/Desktop/OpenCV_Logo.png");
        imshow("origin",image);

        Mat element = getStructuringElement(MORPH_RECT,Size(15,15));//生成一個核
        Mat dilate_out;
        dilate(image,dilate_out,element);
        imshow("dilate",dilate_out);

        Mat erode_out;
        erode(image,erode_out,element);
        imshow("erode",erode_out);

        Mat kai_out;
        dilate(erode_out,kai_out,element);
        imshow("Opening_Operation",kai_out);

        Mat bi_out;
         erode(dilate_out,bi_out,element);
        imshow("bi",bi_out);
        waitKey(0);
    return a.exec();
}

1.1 代碼解析

腐蝕膨脹兩步走:

  1. 生成一個核,核的大小以及形狀自己去選擇
  2. 使用腐蝕或者膨脹函數

只需要記住三個函數即可。以代碼中的16到19行爲例。

Mat element = getStructuringElement(MORPH_RECT,Size(15,15));//生成一個核

getStructuringElement()會返回一個卷積用的核,第一個參數用來設置這個核的形狀,第二個參數用來設置這個核的大小。

dilate(image,dilate_out,element);

這個函數是將第一個參數中的圖像作爲原圖,使用第三個參數中的核對原圖進行膨脹處理。處理之後的圖像放到第二個參數中,第二個圖像顯示出來,就是經過膨脹的圖像。

拓展資料:qt中編寫圖形界面調節開閉運算參數 https://blog.csdn.net/qingyang8513/article/details/80675872

namewindow()

namedWindow函數用於創建一個窗口。若是簡單地進行圖片顯示,可以略去namedWindow函數的調用,即先調用imread讀入圖片,然後用imshow直接指定出窗口名進行顯示即可。但需要在顯示窗口之前就用到窗口名時,就需要namedWindow函數先創建出窗口,顯式地規定窗口名稱了。

這裏我們先接觸一下它。

二 Open CV閉運算

  1. qt中編寫閉運算代碼,打開攝像頭,從攝像頭讀取每一幀進行閉運算並顯示,利用CV_CAP_PROP_FPS顯示硬件幀率。

  2. 在這裏我們對圖像進行了處理,一旦圖像處理速度跟不上攝像頭採集圖像的速度,幀率會下降,大家可以輸出一下硬件幀率和軟件圖像處理的幀率。

#include <QCoreApplication>
#include "opencv2/opencv.hpp"
#include <time.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    using namespace std;
    using namespace cv;

    namedWindow("origin");//用來顯示圖像用的,
    VideoCapture cap(0);
    int fps_h,fps_s;//硬件幀率和軟件幀率
    //檢查是否成功打開
    if(!cap.isOpened())
    {
        cerr << "Can not open a camera or file." << endl;
        return -1;
    }
    Mat image;
    for(;;)
    {
        clock_t  begin ,end;//解析有介紹
        begin=clock();
        //從 cap 中讀一幀,存到 frame
        cap >> image;
        fps_h = int(cap.get(CV_CAP_PROP_FPS));
        //如果未讀到圖像
        if(image.empty())
        break;
        //顯示結果
        imshow("origin", image);

        Mat element = getStructuringElement(MORPH_RECT,Size(15,15));//生成一個核
        Mat dilate_out;
        dilate(image,dilate_out,element);
        imshow("dilate",dilate_out);

        Mat bi_out;
        erode(dilate_out,bi_out,element);
        imshow("bi",bi_out);
        end=clock();
        fps_s=1/((double)(end - begin) / CLOCKS_PER_SEC);//求軟件幀率
        cout << "Frames per second(Hard):" << fps_h  << ",Frames per second(Soft):"<< fps_s <<endl ;
        //等待 30 秒,如果按鍵則推出循環
        if(waitKey(30) >= 0)
        break;
    }
    return a.exec();
}

2.1 代碼解析

clock()

這個函數會返回一個clock_t類型的變量,打開time.h中的定義:typedef long clock_t;所以,這個clock_t就是long int型,這個數字代表從程序開始,過了多少ms,這個long int型可以連續相加1193小時。而CLOCKS_PER_SEC被宏定義爲1000。

第43行,(end - begin)表示圖像處理的時間,單位爲ms,因此需要除以1000,也就是被宏定義爲1000的CLOCKS_PER_SEC。

便於理解和使用,進行了上面的描述。想看更詳細的解釋,看下方的拓展閱讀。

clock_t 拓展閱讀:https://zhidao.baidu.com/question/18831136.html

此外,Qt中可以使用Qtime類來獲取時間。

2.2 軟件幀率求解方法

  1. 輸出每一幀圖像處理時間的倒數。

    這種方法容易受到某一幀的影響,輸出的幀率跳動比較厲害。

  2. 按照定義計算一秒鐘內輸出了多少幀。

    按照定義輸出的幀率,比較穩定。第一種方法,是直接將上一次時間與這一次時間作差求倒數即爲幀率,而這種方法需要判斷上一次時間和這次時間有沒有滿一秒鐘,滿一秒鐘之後纔會輸出這一秒時間內計算的幀數。

大家可以把這兩種方法都試一下。筆記本硬件幀率應該就是30fps

2.3 硬件幀率與軟件幀率

攝像頭硬件的幀率是說攝像頭一秒鐘能讀進來多少幀。這個東西和內部的CCD以及CMOS等器件有關。對於工業攝像頭可以通過軟件來設置攝像頭的曝光時間時間以及分辨率。設置的曝光時間越短,那麼攝像頭就可以在更短的時間內讀取一幀圖片。圖像分辨率越小,對於線陣相機來說,成像時需要掃描的行數就越少,那麼自然成像的速度也越快,對於面陣相機來說, 雖然成像時是一個面一次完成,但之後傳輸數據還是一行一行輸出來,耗時就在這裏。不過,一旦分辨率以及曝光時間等參數設置好後,攝像頭的幀率就是固定的。也就是曝光時間短,圖片分辨率小,硬件幀率變大。(回學校以後,大家可以連上邁德威視的攝像頭,利用它的上位機調節一下,看一看攝像頭的實時幀率。)

軟件幀率的話,其實就是說程序一秒鐘可以處理多少幀從攝像頭讀過來的圖片。像我們這幾天寫的代碼。處理圖像的時間都很短。因此軟件的幀率其實幾乎等於硬件幀率。而筆記本的硬件幀率我們暫時沒有設置的方式。而像比賽中所使用的Robomaster中的代碼。在程序處理一幀圖像的時間,攝像頭可能已經讀進來三幀。因此也就是我們嘴邊常說的說幀率不夠。(不過提高幀率並不是重點,大家可以想一下從100幀提升到300幀。每一幀圖像每一次做到的發送節省了7ms。而一個30m/s的彈丸(速度已經很快了)。擊打三m以外的裝甲板。卻需要100ms,所以我個人認爲提高幀率自然是多多益善,但是更重要的是座標的預測。)

那麼怎麼提高幀率呢?

  1. 用更好的硬件。CPU的主頻變高之後,處理每一條指令時間都會縮短,那麼程序整體執行的時間就會加快,軟件幀率自然會變高。有些時候掉幀,或者幀率變化幅度很大,和CPU頻率不穩定也有關係。比如妙算GPU幀率跳動很嚴重,而CPU版本只有一兩幀的跳動。
  2. 優化代碼。將不必要的代碼通通刪減掉,可執行的代碼行數減少。時間也會加快。
  3. 及時更改分辨率。根據條件即時變化要處理的圖像分辨率,就是對攝像頭讀進來的frmae進行ROI,比如之前我們學習Mat類時,對矩陣進行裁剪。在圖像處理的時間要遠遠大於攝像頭成像的時間的時候,減小攝像頭硬件的分辨率其實是沒什麼作用的,因爲相同時間內硬件成像加快,但是軟件執行的時間不變。那由於加快硬件成像速度而讀進來的圖片,還是沒有辦法進行處理。
  4. 串口115200波特率發送數據,傳輸包也耗費時間,其實就是也屬於優化代碼的內容。

微信公衆號

歡迎大家關注我的個人公衆號,現階段主要總結Robomaster相關的計算機視覺知識。
公衆號名稱:三豐雜貨鋪
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章