Qt5&OpenCV3.2 Canny邊緣檢測+Hough變換
省電賽需求,要在微控制器平臺上使用攝像頭模組採集影像,透過串列埠通訊或者其他通訊方式傳輸到上位機,由上位機來識別併傳送指令給下位機。識別目標是綠色的未成熟柚子,如下圖。透過顏色的識別是不太現實的了,但幸好柚子形狀近似圓形,所以想到透過使用Hough變換檢測圓,從而檢測柚子。
用來模擬在樹上的未成熟柚子
1.硬體平臺
STM32F103ZET6微控制器
OV7670帶FIFO攝像頭模組,最高支援320*240解析度
一臺有USB介面的電腦
2.上位機軟體
我使用Qt進行開發。這個我個人還是比較有經驗的。這裡主要是RGB565轉RGB888編碼演算法和串列埠通訊的編寫。
RGB565是一種顏色編碼方式,相對於一般的RGB888(紅綠藍各8位,三個位元組)的儲存方式更節省空間,一般用在儲存空間比較少的場合,比如嵌入式系統。她可以儲存256*256=65536種顏色,圖片質量相比而言不會受到太大影響。
RGB565
RGB565轉RGB888的主要思路是擷取有效的5/6/5位,然後透過左移和右移使其實際只有8位(高8位為0),最後拼接到代表RGB888編碼的uint型別變數。
/************顏色編碼轉換*************/#define RGB888_RED 0x00ff0000#define RGB888_GREEN 0x0000ff00#define RGB888_BLUE 0x000000ff#define RGB565_RED 0xf800#define RGB565_GREEN 0x07e0#define RGB565_BLUE 0x001f//編碼轉換unsigned short RGB888ToRGB565(unsigned int n888Color){ unsigned short n565Color = 0; // 獲取RGB單色,並擷取高位 unsigned char cRed = (n888Color & RGB888_RED) >> 19; unsigned char cGreen = (n888Color & RGB888_GREEN) >> 10; unsigned char cBlue = (n888Color & RGB888_BLUE) >> 3; // 連線 n565Color = (cRed << 11) + (cGreen << 5) + (cBlue << 0); return n565Color; }unsigned int RGB565ToRGB888(unsigned short n565Color){ unsigned int n888Color = 0; // 獲取RGB單色,並填充低位 unsigned char cRed = (n565Color & RGB565_RED) >> 8; unsigned char cGreen = (n565Color & RGB565_GREEN) >> 3; unsigned char cBlue = (n565Color & RGB565_BLUE) << 3; // 連線 n888Color = (cRed << 16) + (cGreen << 8) + (cBlue << 0); return n888Color; }
在MainWindow種初始化一些引數
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); //初始化時獲取可用的串列埠 initSerialPort(); //建立一個串列埠物件 tc = QTextCodec::codecForName("GBK"); //支援漢字編碼 serial = new QSerialPort; //當串列埠準備好讀資料時,呼叫讀取資料的函式 connect(serial,SIGNAL(readyRead()),this,SLOT(readSerialData())); //接收資料框設定為只讀模式 // ui->textEdit->setReadOnly(true); //初始化波特率,預設921600 baudBox=new QComboBox; QStringList baudItems; baudItems<<"1382400"<<"921600"<<"46080"<<"256000"<<"230400" <<"128000"<<"115200"<<"76800"<<"57600"<<"43000"<<"38400"<<"19200" <<"14400"<<"9600"<<"4800"<<"2400"<<"1200"; ui->baudBox->addItems(baudItems); ui->baudBox->setCurrentIndex(1); ui->sendButton->setEnabled(false);//開啟串列埠之前限制傳送按鈕}
不斷重新整理串列埠列表
//滑鼠點選重新整理當前存在的串列埠void MainWindow::mousePressEvent(QMouseEvent *e) { serial->clear(); //清掉所有串列埠,重新檢測當前串列埠 ui->portBox->clear();//刪除當前所以已經存在的串列埠號 initSerialPort(); }void MainWindow::initSerialPort() { QList<QSerialPortInfo> infos = QSerialPortInfo::availablePorts(); if(infos.isEmpty()) { // QMessageBox::information(this,"提示資訊","當前沒有可用的串列埠"); return; } //將搜尋串列埠號新增到下拉選單中 foreach (QSerialPortInfo info, infos) { ui->portBox->addItem(info.portName()); } }
開啟串列埠時根據下拉框的引數進行初始化
//開啟或者關閉串列埠void MainWindow::on_openSerialButton_toggled(bool checked) { if(ui->connectPushButton->text() == "斷開") { QMessageBox::information(this,"提示資訊","請關閉WIFI連線"); return; } //這裡可以輸出一些串列埠出錯的資訊 if(serial->error()==serial->DeviceNotFoundError) { QMessageBox::information(this,"提示資訊","串列埠開啟失敗"); return; } if(checked) { ui->openSerialButton->setText(tr("關閉串列埠")); //設定串列埠名 serial->setPortName(ui->portBox->currentText()); //開啟串列埠 serial->open(QIODevice::ReadWrite); //設定波特率 serial->setBaudRate(ui->baudBox->currentText().toInt()); //設定資料位 serial->setDataBits(QSerialPort::Data8); //設定奇偶校驗 serial->setParity(QSerialPort::NoParity); //設定停止位 serial->setStopBits(QSerialPort::OneAndHalfStop); //關閉串列埠設定按鈕,使能資料傳送按鈕 ui->portBox->setEnabled(false); ui->baudBox->setEnabled(false); ui->sendButton->setEnabled(true); } else { ui->openSerialButton->setText(tr("開啟串列埠")); //關閉串列埠 serial->clear(); serial->close(); //使能串列埠設定按鈕,關閉資料傳送按鈕 ui->portBox->setEnabled(true); ui->baudBox->setEnabled(true); ui->sendButton->setEnabled(false); } }
讀取和清除
//串列埠讀取資料void MainWindow::readSerialData() {//接收的時候要將QByteArray轉換成String才能夠方便的顯示在textEdit中 QByteArray buf; buf=serial->readAll(); if(buf!=NULL) { data+=(tc->toUnicode(buf)); } buf.clear(); }//清除資料接收區的資料void MainWindow::on_clearDataButton_clicked() { data.clear(); random_color(); ui->leftLabel->setText("柚子呢?"); ui->rightLabel->setText("柚子呢?"); update(); // ui->textEdit->clear();}
3.影像識別
由於不需要顏色資訊,我在OV7670模式選擇中使用黑白模式,這樣噪點會比較少
原始影像
有時依舊會有不少噪點,先來一發高斯模糊再說
//讀取影像並定義處理過程中使用的MatMat originalImageL =imread("/original_left.png"); Mat cannyImageL; Mat closeImageL; Mat contoursImageL = Mat::zeros(originalImageL.size(),CV_8U);;vector<vector<Point>> contoursL; Mat outputImageL = originalImageL.clone();//濾波去除噪點GaussianBlur(originalImageL,originalImageL,cv::Size(BLUR_SIZE,BLUR_SIZE),1.5);
cv::Size(BLUR_SIZE,BLUR_SIZE)是卷積核的大小,最後一個引數是高斯函式的σ。效果如下,稍微有點糊但無傷大雅。
高斯模糊去噪
Canny邊緣檢測。引數要多次嘗試
//Canny邊緣檢測Canny(originalImageL,cannyImageL,CANNY_VAR/2,CANNY_VAR);
Canny運算元通常基於Soble運算元。透過一個高閾值的Sobel和低閾值的Sobel綜合考量可以得到比較令人滿意的結果。柚子的大概輪廓出來了,雖然周圍的樹葉的輪廓會對識別有干擾,但沒關係。
Canny
閉運算去除細小紋理
//結構元素Mat element5(M_R_SIZE,M_R_SIZE,CV_8U,cv::Scalar(1)); morphologyEx(cannyImageL,closeImageL,MORPH_CLOSE,element5);
閉運算的定義是對影像先膨脹後腐蝕。比結構元素小的空隙和間隙會被閉合濾波器消除。可以看到柚子周圍的小輪廓基本消除了。
Close
閉運算的定義是對影像先膨脹後腐蝕。比結構元素小的空隙和間隙會被閉合濾波器消除。
提取連續區域中的輪廓(非必需)
findContours(closeImageL,contoursL, CV_RETR_LIST,CV_CHAIN_APPROX_NONE); drawContours(contoursImageL, contoursL, -1, Scalar(255, 0, 255));
第三個引數使用CV_RETR_LIST 指提取所有輪廓。其他的值可以提取其中的層次結構,這裡不介紹。經實驗發現這一步可以略過,但加上的話更加直觀。
findContours
Hough變換檢測圓形
std::vector<cv::Vec3f> circlesL; HoughCircles(contoursImageL,circlesL,HOUGH_GRADIENT,2,CIRCLES_BAR, CANNY_VAR,HOUGH_STD,R_MIN,R_MAX);std::vector<cv::Vec3f>::const_iterator itcL = circlesL.begin();while(itcL!=circlesL.end()) { cv::circle(outputImageL,cv::Point((*itcL)[0],(*itcL)[1]),(*itcL)[2], cv::Scalar(188),4); cout << cv::Point((*itcL)[0],(*itcL)[1]) << (*itcL)[2] <<endl; ++itcL; }
HoughCircles這個函式比較有意思,有必要看看函式原型。
void HoughCircles( InputArray image, OutputArray circles, int method, double dp, double minDist, double param1 = 100, double param2 = 100, int minRadius = 0, int maxRadius = 0 );
原始碼中引數解釋的翻譯大概如下
§ method 使用的檢測方法,一般使用霍夫梯度法,它的識別符號為CV_HOUGH_GRADIENT
§ dp 用來檢測圓心的累加器影像的解析度於輸入影像之比的倒數
§ minDist 圓之間最小的距離
§ param1 第三個引數 method設定的檢測方法的對應的第一個引數,對於CV_HOUGH_GRADIENT為傳遞給canny邊緣檢測運算元的高閾值
§ param2 第三個引數 method設定的檢測方法的對應的第二個引數,表示在檢測階段圓心的累加器閾值,越低檢測出的圓越多
§ minRadius 圓最小半徑
§ minRadius 圓最大半徑
作者:Jacob楊幫幫
連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1817/viewspace-2819352/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 【OpenCV】影像的變換(三)-Canny邊緣檢測OpenCV
- 3.Canny邊緣檢測
- 邊緣檢測演算法——Canny和LoG邊緣檢測演算法演算法
- opencv——自適應閾值Canny邊緣檢測OpenCV
- OPENCV例程2 :CANNY運算元邊緣檢測OpenCV
- OpenCV(iOS)的邊緣檢測和Canny運算元OpenCViOS
- Hough變換
- 影像邊緣檢測
- 詳解 Hough 變換(基本原理與直線檢測)
- 影像的邊緣檢測
- Hough變換與FCM演算法相結合檢測車道線演算法
- 大津法——邊緣檢測
- OpenCV-Python教程(6)(7)(8): Sobel運算元 Laplacian運算元 Canny邊緣檢測OpenCVPython
- opencv學習之邊緣檢測OpenCV
- 影象特徵提取:Sobel邊緣檢測特徵
- Marr-Hildreth邊緣檢測器
- 【OpenCV】影像變換(二)邊緣檢測:梯度運算元、Sobel運算元和Laplace運算元OpenCV梯度
- python+opencv邊緣檢測方法整理PythonOpenCV
- Laplacian(iOS)運算元(邊緣檢測)iOS
- 3. OpenCV-Python——影像梯度演算法、邊緣檢測、影像金字塔與輪廓檢測、直方圖與傅立葉變換OpenCVPython梯度演算法直方圖
- 卷積神經網路1-邊緣檢測卷積神經網路
- 詳解數字影像的濾波和邊緣檢測
- 影象邊緣檢測—sobel運算元(灰度影象,彩色影象)
- 霍夫變換檢測圓
- php 實現一個簡單的圖片邊緣檢測PHP
- 經典影象邊緣檢測(模板法思想)——Kirsch運算元
- [機器視覺]halcon應用例項 邊緣檢測視覺
- 【2017年10月最新深度學習方面的邊緣檢測論文和程式碼】【CASENet:深度類別感知語義邊緣檢測】深度學習SENet
- 【OpenCV】影像變換(四-2)霍夫變換圓檢測OpenCV
- opencv 邊緣羽化,邊緣過渡OpenCV
- 邊緣計算如何改變企業IT
- 【OpenCV】影像變換(四-1)-霍夫變換線段檢測OpenCV
- 輪廓檢測論文解讀 | 整體巢狀邊緣檢測HED | CVPR | 2015巢狀
- 邊緣計算2.0時代,“雲邊緣”與“邊緣雲”你分清了嗎?
- OpenCvSharp手繪ROI區域+模板匹配+霍夫變換檢測圓的邊界OpenCV
- OpenGL模型檢視變換、投影變換、視口變換模型
- 秒懂邊緣雲 | 邊緣雲技術進階
- 邊緣計算?