Programming/Qt

[도서 실습] Qt 5 and OpenCV 4 Computer Vision – ImageEditor (Plugin Mechanism – Erode - 2)

변화의 물결1 2024. 2. 24. 00:06

 

 

안녕하세요.

 

 이전 내용에서 Plugin 인 DLL 파일로 Erode를 만들어 보았습니다. 이제 이 파일을 Mainwindow 프로그램에서 불러오는 것을 만들어 보도록 하겠습니다.

 기존의 ImageEditor에 내용을 추가하는데 소스 입력하는 위치가 어려울 수 있는데 천천히 설명하도록 하겠습니다.

 


 

1. ImageEditor 프로젝트 수정

 

 - ImageEditor.pro에 plugin interface 파일을 추가합니다. (editor_plugin_interface.h)

  

# Input
HEADERS += mainwindow.h \
    editor_plugin_interface.h
SOURCES += main.cpp mainwindow.cpp

 

 

2. Mainwindow.h에 내용 추가

 

 - mainwindow.h에 데이터 구조와 plugin interface 헤더 파일을 추가합니다.

 

#include <QMap>
#include "editor_plugin_interface.h"

  

 - MainWindow class에 2개 멤버 변수를 추가합니다. ( private와 private slots 아래에 추가합니다.

void loadPlugins(): plugins 폴더에 있는 파일들을 불러오는 함수 선언

void pluginPerform(): slot과 연동하여 plugin이 실제 수행하도록 하는 함수 선언

 

private:
   // plugin
    void loadPlugins();

private slots:
    void pluginPerform();

 

 - 불러온 plugin을 관리하기 위해서 QMap 데이터 구조된 저장 합니다. 그러기 위해서 private로 변수를 생성합니다.

 

private :
  QMap<QString, EditorPluginInterface*> editPlugins;

 

 

3. Mainwindow.cpp에 내용 추가

 

  - Plugin 불러오기 위한 해더 파일을 추가합니다.

 

#include <QPluginLoader>

 

 

 - 멤버 함수로 정의한 loadplugins() 함수를 구현합니다.

 - plugins 폴더를 검색해서 .so, .dll파일을 읽어와서 파일리스트를 만들고 plugin 인스턴스를 불러와서 포인터를 변환해서 저장합니다. 만약 문제없이 불러와서 포인터가 생성되었다면 Erode plugin에서 name() 함수로 만든 이름을 불러와서 상단 메뉴 Title 이름으로 사용하고 QMap 키값으로 사용합니다.

 

 editPlugins 변수에 이름(키값)과 plugin을 호출할 수 있는 포인터를 저장합니다. 그리고 메뉴(Action)와 클릭했을 때 수행하는 pluginPerform() 함수를 연결합니다.

 

void MainWindow::loadPlugins()
{

    QDir pluginsDir(QApplication::instance()->applicationDirPath() + "/plugins");
    QStringList nameFilters;

    nameFilters << "*.so" << "*.dylib" << "*.dll";
    QFileInfoList plugins = pluginsDir.entryInfoList(
        nameFilters, QDir::NoDotAndDotDot | QDir::Files, QDir::Name);
    foreach(QFileInfo plugin, plugins) {
      QPluginLoader pluginLoader(plugin.absoluteFilePath(), this);
      EditorPluginInterface *plugin_ptr = dynamic_cast<EditorPluginInterface*>(pluginLoader.instance());
      if(plugin_ptr) {
             QAction *action = new QAction(plugin_ptr->name());
             editMenu->addAction(action);
             editToolBar->addAction(action);
             editPlugins[plugin_ptr->name()] = plugin_ptr;
             connect(action, SIGNAL(triggered(bool)), this, SLOT(pluginPerform()));
             // pluginLoader.unload();
       } else {
             qDebug() << "bad plugin: " << plugin.absoluteFilePath();
       }
    }
}

 

 

 - 멤버 함수로 정의한 pluginPerform() 함수를 구현합니다.

 - 현재 수정할 수 있는 이미지가 있는지 확인하고 없다면 다이얼로그 창을 띄웁니다.

 - 메뉴(Action)에서 보낸 객체를 확인해서 메뉴 타이틀 이름(Key)을 불러와서 선택한 plugin의 포인터를 불러옵니다.

 

 - 이미지와 plugin 포인터가 문제없다면 blur() 함수에서 처럼 OpenCV가 수정할 수 있는 매트릭스 형태로 이미지를 변환해 줍니다. 그리고 plugin의 edit() 함수를 호출합니다. 측, edit 함수에서 erode() 기능이 구현이 되어 있기 때문에 수행이 되고 결과를 mat으로 돌려받게 됩니다.

 

 - 다시 출력해 줄 수 있는 이미지로 변환해 주고 결과를 상태바에 출력해 주는 것으로 마무리합니다. 

 

void MainWindow::pluginPerform()
{
    if (currentImage == nullptr) {
        QMessageBox::information(this, "Information", "No image to edit.");
        return;
    }

    QAction *active_action = qobject_cast<QAction*>(sender());
    EditorPluginInterface *plugin_ptr = editPlugins[active_action->text()];
    if(!plugin_ptr) {
        QMessageBox::information(this, "Information", "No plugin is found.");
        return;
    }

    QPixmap pixmap = currentImage->pixmap();
    QImage image = pixmap.toImage();
    image = image.convertToFormat(QImage::Format_RGB888);
    cv::Mat mat = cv::Mat(
        image.height(),
        image.width(),
        CV_8UC3,
        image.bits(),
        image.bytesPerLine());

    plugin_ptr->edit(mat, mat);

    QImage image_edited(
        mat.data,
        mat.cols,
        mat.rows,
        mat.step,
        QImage::Format_RGB888);
    pixmap = QPixmap::fromImage(image_edited);
    imageScene->clear();
    imageView->resetMatrix();
    currentImage = imageScene->addPixmap(pixmap);
    imageScene->update();
    imageView->setSceneRect(pixmap.rect());
    QString status = QString("(editted image), %1x%2")
        .arg(pixmap.width()).arg(pixmap.height());
    mainStatusLabel->setText(status);
}

 

 

- 마지막으로 plugin들은 Mainwindow가 생성되고 UI가 초기화된 다음에 호출되게 합니다. 

 

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
    , fileMenu(nullptr)
    , viewMenu(nullptr)
    , currentImage(nullptr)
{
    initUI();
    loadPlugins();  // load plugins
}

 

 

4. 최종 결과

 

 - 그냥 Run 시키면 이전과 차이 없이 ImageEditor가 나타납니다.

 

 

 

 

- 여기서 핵심은 plugins 폴더를 만들어서 ErodePlugin.dll 파일을 넣어주어야 합니다. 주의할 점은 소스파일이 있는 경로에 넣어주는 것이 아니라 컴파일하면서 생성된 폴더에 넣어주어야 합니다. 나중에 릴리즈(Release)하는 경우는 지금과 달라질 수 있습니다. 

 

 현재 디버그 모드로 진행하고 있기 때문에 debug 폴더에 plugins 폴더를 만들어 dll파일을 넣어 주어야 합니다.

 

 

 

 

- 다시 Run을 실행하면 Erode 메뉴가 추가된 것을 확인할 수 있습니다.

 

 

 

- 이미지 하나를 불러와서 Erode 기능을 실행해 보겠습니다. Erode 실행 전 원본사진에서는 화살표와 육각형이 뚜렷이 볼 수 있습니다.

 

 

 

 

- Erode 한번 실행한 경우 (어두운 값으로 변환되면서 노이즈와 같은 실선들은 사라지는 효과를 볼 수 있습니다.)

 

 

 

 

- Erode 5번 이상 실행한 경우 (주변이 검은색으로 침식되면서 사각형이 점점 작아지는 것을 볼 수 있습니다.)

 

 

 

 

 

감사합니다.

 

 

<참고자료>

1. [BOOK] Qt-5-and-OpenCV-4-Computer-Vision-Projects

 

- 소스를 참고하실 때는 .pro 파일의 OpenCV 경로를 본의 PC에 맞게 바꿔서 컴파일하시면 문제없을 겁니다.

 

ImageEditor_erode.zip
0.01MB

 

반응형