VS+Qt+Halcon——顯示圖片,實(shí)現(xiàn)鼠標(biāo)縮放、移動(dòng)圖片
點(diǎn)擊下方卡片,關(guān)注“新機(jī)器視覺”公眾號(hào)
視覺/圖像重磅干貨,第一時(shí)間送達(dá)
摘要
本篇博文記錄一下,用VS+Qt+Halcon實(shí)現(xiàn)對(duì)圖片的讀取以及鼠標(biāo)縮放,移動(dòng)(鼠標(biāo)事件調(diào)用了halcon自帶的算子)的過程。以及遇到的坑.....????
先來看一下動(dòng)態(tài)效果圖:

主要控件:
添加一個(gè)Label控件,對(duì)象名設(shè)為label,用于顯示圖片,并將背景設(shè)為黑色,設(shè)置方法為:選中Label控件,在屬性編輯器中找到styleSheet屬性,在其后的值中輸入background-color:black即可;
添加四個(gè)Push Button控件,如上圖所示從左至右,對(duì)象名依次為:btn_prePic、btn_openPic、btn_nextPic,btn_resetPic,用于打開圖片和前后瀏覽,以及恢復(fù)原圖;
添加一個(gè)Label,對(duì)象名設(shè)為label_status,用于實(shí)時(shí)顯示坐標(biāo)和灰度值;
將label_show控件提升為CMyLabel類,用于接收鼠標(biāo)事件。
一,代碼例程
在Visual Studio中新建一個(gè)Qt GUI項(xiàng)目,名稱設(shè)為BrowsePic,并新建Mylabel類(繼承自QLabel)用于label控件的提升。
Mylabel.h
using namespace HalconCpp;class Mylabel :public QLabel{Q_OBJECTpublic:Mylabel(QWidget* parent = Q_NULLPTR);~Mylabel();//設(shè)置Halcon圖像和Halcon窗口句柄,用戶響應(yīng)鼠標(biāo)事件后實(shí)時(shí)更新圖像void setHalconWnd(HObject img, HTuple hHalconID, QLabel* label);//鼠標(biāo)滾輪縮放事件void wheelEvent(QWheelEvent* ev);//鼠標(biāo)按下事件void mousePressEvent(QMouseEvent* ev);//鼠標(biāo)釋放事件void mouseReleaseEvent(QMouseEvent* ev);//鼠標(biāo)移動(dòng)事件void mouseMoveEvent(QMouseEvent* ev);public:HTuple m_labelID; //Qt標(biāo)簽句柄HTuple m_hHalconID; //Halcon窗口句柄HObject m_currentImg; //當(dāng)前的圖像//主界面顯示坐標(biāo)的標(biāo)簽QLabel* m_label;//鼠標(biāo)按下的位置HTuple m_tMouseDownRow;HTuple m_tMouseDownCol;bool m_bIsMove; //是否移動(dòng)圖像標(biāo)識(shí)};
Mylabel.cpp
//定義單步放大倍率Mylabel::Mylabel(QWidget* parent): QLabel(parent){m_bIsMove = false;this->setMouseTracking(true);}Mylabel::~Mylabel(){}//設(shè)置Halcon圖像和Halcon窗口句柄,用戶響應(yīng)鼠標(biāo)事件后實(shí)時(shí)更新圖像void Mylabel::setHalconWnd(HObject img, HTuple hHalconID, QLabel* label){m_hHalconID = hHalconID;m_currentImg = img;m_label = label;}//鼠標(biāo)滾輪縮放事件,用于縮放圖像void Mylabel::wheelEvent(QWheelEvent* ev){double Zoom; //放大或縮小倍率HTuple mouseRow, mouseCol, Button;HTuple startRowBf, startColBf, endRowBf, endColBf, Ht, Wt, startRowAft, startColAft, endRowAft, endColAft;//滾輪前滑,放大if (ev->delta()>0){Zoom = ZOOMRATIO;}else//否則縮小{Zoom = 1 / ZOOMRATIO;}//獲取光標(biāo)在原圖上的位置,注意是原圖坐標(biāo),不是Label下的坐標(biāo)HTuple hv_Exception, hv_ErrMsg;try{GetMposition(m_hHalconID, &mouseRow, &mouseCol, &Button);}catch (HException& HDevExpDefaultException){return;}//獲取原圖顯示的部分,注意也是原圖坐標(biāo)GetPart(m_hHalconID, &startRowBf, &startColBf, &endRowBf, &endColBf);//縮放前顯示的圖像寬高Ht = endRowBf - startRowBf;Wt = endColBf - startColBf;//普通版halcon能處理的圖像最大尺寸是32K*32K。如果無限縮小原圖像,導(dǎo)致顯示的圖像超出限制,則會(huì)造成程序崩潰if (Ht*Wt<20000*20000||Zoom==ZOOMRATIO){//計(jì)算縮放后的圖像區(qū)域startRowAft = mouseRow - ((mouseRow - startRowBf) / Zoom);startColAft = mouseCol - ((mouseCol - startColBf) / Zoom);endRowAft = startRowAft + (Ht / Zoom);endColAft = startColAft + (Wt / Zoom);//如果放大過大,則返回if (endRowAft - startRowAft < 2){return;}if (m_hHalconID != NULL){//如果有圖像,則先清空?qǐng)D像DetachBackgroundFromWindow(m_hHalconID);}SetPart(m_hHalconID, startRowAft, startColAft, endRowAft, endColAft);AttachBackgroundToWindow(m_currentImg, m_hHalconID);}}void Mylabel::mousePressEvent(QMouseEvent* ev){HTuple mouseRow, mouseCol, Button;try{GetMposition(m_hHalconID, &mouseRow, &mouseCol, &Button);}catch (HException){return;}//鼠標(biāo)按下時(shí)的行列坐標(biāo)m_tMouseDownRow = mouseRow;m_tMouseDownCol = mouseCol;m_bIsMove = true;}//鼠標(biāo)釋放事件void Mylabel::mouseReleaseEvent(QMouseEvent* ev){m_bIsMove = false;}//鼠標(biāo)移動(dòng)事件void Mylabel::mouseMoveEvent(QMouseEvent* ev){HTuple startRowBf, startColBf, endRowBf, endColBf, mouseRow, mouseCol, Button;try{GetMposition(m_hHalconID, &mouseRow, &mouseCol, &Button);}catch (HException){return;}//鼠標(biāo)按下并移動(dòng)時(shí),移動(dòng)圖像,否則只顯示坐標(biāo)if (m_bIsMove){//計(jì)算移動(dòng)值double RowMove = mouseRow[0].D() - m_tMouseDownRow[0].D();double ColMove = mouseCol[0].D() - m_tMouseDownCol[0].D();//得到當(dāng)前的窗口坐標(biāo)GetPart(m_hHalconID, &startRowBf, &startColBf, &endRowBf, &endColBf);//移動(dòng)圖像if (m_hHalconID!=NULL){//如果有圖像,則先清空?qǐng)D像DetachBackgroundFromWindow(m_hHalconID);}SetPart(m_hHalconID, startRowBf - RowMove, startColBf - ColMove, endRowBf - RowMove, endColBf - ColMove);AttachBackgroundToWindow(m_currentImg, m_hHalconID);}//獲取灰度值HTuple pointGray;try{GetGrayval(m_currentImg, mouseRow, mouseCol, &pointGray);}catch (HException){m_label->setText(QString::fromLocal8Bit("X坐標(biāo):- Y坐標(biāo):- 灰度值:-"));return;}//設(shè)置坐標(biāo)m_label->setText(QString::fromLocal8Bit("X坐標(biāo):%1 Y坐標(biāo):%2 灰度值:%3").arg(mouseCol[0].D()).arg(mouseRow[0].D()).arg(pointGray[0].D()));}
BrowsePic.h
using namespace HalconCpp;class BrowsePic : public QWidget{Q_OBJECTpublic:BrowsePic(QWidget *parent = Q_NULLPTR);~BrowsePic();//初始化void init();//顯示圖像void showImg();int currentIndex;//顯示圖像的控件idHTuple m_hLabelID; //QLabel控件句柄HTuple m_hHalconID; //Halcon顯示窗口句柄//原始圖像的尺寸HTuple m_imgWidth, m_imgHeight;//圖片路徑列表HTuple m_imgFiles;//當(dāng)前圖像HObject m_hCurrentImg;//縮放后的圖像HObject m_hResizedImg;//縮放系數(shù)HTuple m_hvScaledRate;//縮放后圖像的大小HTuple m_scaledHeight, m_scaledWidth;QToolBar* m_toolBar;public slots://打開圖片void on_btn_openPic_clicked();//瀏覽前一張void on_btn_prePic_clicked();//瀏覽后一張void on_btn_nextPic_clicked();//恢復(fù)圖片void on_btn_resetPic_clicked();private:Ui::BrowsePicClass ui;};
BrowsePic.cpp
BrowsePic::BrowsePic(QWidget *parent): QWidget(parent){ui.setupUi(this);init();}BrowsePic::~BrowsePic(){}void BrowsePic::init(){//設(shè)置halcon的文件路徑為utf8,解決中文亂碼SetSystem("filename_encoding", "utf8");//生成空?qǐng)D像GenEmptyObj(&m_hCurrentImg);m_hHalconID = NULL;m_hLabelID = (Hlong)ui.label->winId();currentIndex = -1;}//顯示圖像void BrowsePic::showImg(){if (m_hHalconID!=NULL){//如果有圖像,則先清空?qǐng)D像DetachBackgroundFromWindow(m_hHalconID);}else{//打開窗口OpenWindow(0, 0, ui.label->width(), ui.label->height(), m_hLabelID, "visible", "", &m_hHalconID);}ui.label-> setHalconWnd(m_hCurrentImg, m_hHalconID, ui.label_status);//獲取圖像大小GetImageSize(m_hCurrentImg, &m_imgWidth, &m_imgHeight);//獲取縮放系數(shù)TupleMin2(1.0 * ui.label->width() / m_imgWidth, 1.0 * ui.label->height() / m_imgHeight, &m_hvScaledRate);//縮放圖像ZoomImageFactor(m_hCurrentImg, &m_hResizedImg, m_hvScaledRate, m_hvScaledRate, "constant");//獲取縮放后的大小GetImageSize(m_hResizedImg, &m_scaledWidth, &m_scaledHeight);//打開窗口if (1.0 * ui.label->width() / m_imgWidth < 1.0 * ui.label->height() / m_imgHeight){SetWindowExtents(m_hHalconID, ui.label->height() / 2.0 - m_scaledHeight / 2.0, 0, ui.label->width(), m_scaledHeight);}else{SetWindowExtents(m_hHalconID, 0, ui.label->width() / 2.0 - m_scaledWidth / 2.0, m_scaledWidth, ui.label->height());}SetPart(m_hHalconID, 0, 0, m_imgHeight - 1, m_imgWidth - 1);AttachBackgroundToWindow(m_hCurrentImg, m_hHalconID);}//打開圖片void BrowsePic::on_btn_openPic_clicked(){QString path = QFileDialog::getOpenFileName(this, "加載圖像", "./", "圖像文件(*.bmp *.png *.jpg)");QFileInfo fileInfo(path);QString dir = fileInfo.path();if (!path.isEmpty()){ListFiles(dir.toStdString().c_str(), "files", &m_imgFiles);TupleRegexpSelect(m_imgFiles, HTuple("\\.bmp|png|jpg").Append("ignore_case"), &m_imgFiles);for (int i = 0; i < m_imgFiles.Length(); i++){QString currentPath = m_imgFiles[i];currentPath.replace("\\", "/");if (currentPath == path){currentIndex = i;ReadImage(&m_hCurrentImg, m_imgFiles[i]);showImg();}}}}//瀏覽前一張void BrowsePic::on_btn_prePic_clicked(){if (currentIndex > 0){currentIndex--;ReadImage(&m_hCurrentImg, m_imgFiles[currentIndex]);showImg();}}//瀏覽后一張void BrowsePic::on_btn_nextPic_clicked(){if (currentIndex >= 0 && currentIndex < m_imgFiles.Length() - 1){currentIndex++;ReadImage(&m_hCurrentImg, m_imgFiles[currentIndex]);showImg();}}//恢復(fù)圖片void BrowsePic::on_btn_resetPic_clicked(){showImg();}
二,關(guān)鍵代碼解釋
1??Qt函數(shù)與Halcon算子獲取的文件路徑字符串的區(qū)別
Halcon算子獲取的文件路徑格式
list_files()的原型如下:

第一個(gè)參數(shù)為路徑,提取的文件路徑格式與參數(shù)Directory的形式有關(guān),在HDevelop中測(cè)試:
– Directory以"\\"分隔時(shí),即list_files ('E:\\TEST', 'files', Files)

– Directory以“/”分隔時(shí),即list_files ('E:/TEST', 'files', Files)

可以發(fā)現(xiàn)兩種方式提取的文件路徑字符串的區(qū)別。
Qt函數(shù)獲取的文件路徑格式
getOpenFileName()獲得的路徑:

如何將二者路徑保持一致?
先讀取halcon算子獲取的路徑:
QString currentPath = m_imgFiles[i];

然后將" \ "全部換成" /":
currentPath.replace("\\", "/");

2??在VS中使用Halcon時(shí)的編碼及中文亂碼問題
默認(rèn)條件下,可使用以下C++語句獲取Halcon的文件名編碼:
HTuple codeType;
get_system("filename_encoding", &codeType);
QString strCodeType = codeType[0].S();

可以發(fā)現(xiàn)默認(rèn)的編碼是locale,此時(shí)用Halcon算子list_files獲取的文件路徑中如果包含中文,則會(huì)出現(xiàn)亂碼

解決方法:將Halcon的文件名編碼格式設(shè)置為utf8,代碼如下:
set_system("filename_encoding", "utf8");

三,項(xiàng)目打包
詳細(xì)過程可參考我的博文:QT從入門到入土(八)——項(xiàng)目打包和發(fā)布 - 唯有自己強(qiáng)大 - 博客園 (cnblogs.com)
需要注意一點(diǎn)的是:對(duì)于halcon庫的引用需要:

來源:https://www.cnblogs.com/xyf327/p/15186230.html
—版權(quán)聲明—
僅用于學(xué)術(shù)分享,版權(quán)屬于原作者。
若有侵權(quán),請(qǐng)聯(lián)系微信號(hào):yiyang-sy 刪除或修改!
