diff --git a/CMakeLists.txt b/CMakeLists.txt index add4deb..0287743 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,6 +72,8 @@ add_executable(PixelAnnotationTool MACOSX_BUNDLE WIN32 src/image_canvas.h src/label_widget.cpp src/label_widget.h + src/slic_parameter.cpp + src/slic_parameter.h src/main.cpp ${UI_TEST_HDRS}) target_link_libraries(PixelAnnotationTool Qt5::Gui Qt5::Widgets ${OpenCV_LIBS}) diff --git a/CMakeLists.txt.user b/CMakeLists.txt.user new file mode 100644 index 0000000..e7d401c --- /dev/null +++ b/CMakeLists.txt.user @@ -0,0 +1,346 @@ + + + + + + EnvironmentId + {820f2194-d6b4-4e7b-83ed-9145b2160249} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + true + false + 0 + true + true + 0 + 8 + true + 1 + true + true + true + false + + + + ProjectExplorer.Project.PluginSettings + + + true + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop + {b81ddff5-7510-401e-a1bd-02304d536f10} + 0 + 0 + 0 + + + CMAKE_BUILD_TYPE:STRING=Debug + CMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} + CMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} + CMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX} + QT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable} + + /home/rome/Dokumente/Evaluation-Annotier-Tools/fork_PixelAnnotationTool/build-PixelAnnotationTool-Desktop-Debug + + + + + all + + true + CMakeProjectManager.MakeStep + + 1 + Erstellen + Erstellen + ProjectExplorer.BuildSteps.Build + + + + + + clean + + true + CMakeProjectManager.MakeStep + + 1 + Bereinigen + Bereinigen + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + CMakeProjectManager.CMakeBuildConfiguration + + + + CMAKE_BUILD_TYPE:STRING=Release + CMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} + CMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} + CMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX} + QT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable} + + /home/rome/Dokumente/Evaluation-Annotier-Tools/fork_PixelAnnotationTool/build-PixelAnnotationTool-Desktop-Release + + + + + all + + true + CMakeProjectManager.MakeStep + + 1 + Erstellen + Erstellen + ProjectExplorer.BuildSteps.Build + + + + + + clean + + true + CMakeProjectManager.MakeStep + + 1 + Bereinigen + Bereinigen + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + CMakeProjectManager.CMakeBuildConfiguration + + + + CMAKE_BUILD_TYPE:STRING=RelWithDebInfo + CMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} + CMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} + CMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX} + QT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable} + + /home/rome/Dokumente/Evaluation-Annotier-Tools/fork_PixelAnnotationTool/build-PixelAnnotationTool-Desktop-RelWithDebInfo + + + + + all + + true + CMakeProjectManager.MakeStep + + 1 + Erstellen + Erstellen + ProjectExplorer.BuildSteps.Build + + + + + + clean + + true + CMakeProjectManager.MakeStep + + 1 + Bereinigen + Bereinigen + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release mit Debuginformationen + CMakeProjectManager.CMakeBuildConfiguration + + + + CMAKE_BUILD_TYPE:STRING=MinSizeRel + CMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} + CMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} + CMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX} + QT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable} + + /home/rome/Dokumente/Evaluation-Annotier-Tools/fork_PixelAnnotationTool/build-PixelAnnotationTool-Desktop-MinSizeRel + + + + + all + + true + CMakeProjectManager.MakeStep + + 1 + Erstellen + Erstellen + ProjectExplorer.BuildSteps.Build + + + + + + clean + + true + CMakeProjectManager.MakeStep + + 1 + Bereinigen + Bereinigen + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release (kleinstmöglich) + CMakeProjectManager.CMakeBuildConfiguration + + 4 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + dwarf + + cpu-cycles + + + 250 + + -e + cpu-cycles + --call-graph + dwarf,4096 + -F + 250 + + -F + true + 4096 + false + false + 0 + + true + + false + false + false + false + true + 0.01 + 10 + true + kcachegrind + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + PixelAnnotationTool + CMakeProjectManager.CMakeRunConfiguration.PixelAnnotationTool + PixelAnnotationTool + + false + + false + true + true + false + false + true + + /home/rome/Dokumente/Evaluation-Annotier-Tools/fork_PixelAnnotationTool/build-PixelAnnotationTool-Desktop-Debug + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/src/about_dialog.cpp b/src/about_dialog.cpp index c027b26..df54754 100644 --- a/src/about_dialog.cpp +++ b/src/about_dialog.cpp @@ -47,7 +47,7 @@ AboutDialog::AboutDialog(QWidget *parent):QDialog(parent) { "" "

Version Info : " INFO_PIXEL_ANNOTATION_TOOL_VERSION "

" "


" - "

Author: Amaury Bréhéret

" + "

Author: Amaury Breheret

" "

Software use : OpenCV and Qt.

" "

Make donation : Donate

" "


" diff --git a/src/image_canvas.cpp b/src/image_canvas.cpp index f2f0bef..24ed420 100644 --- a/src/image_canvas.cpp +++ b/src/image_canvas.cpp @@ -7,22 +7,23 @@ ImageCanvas::ImageCanvas(MainWindow *ui) : QLabel() , - _ui(ui){ + _ui(ui){ _scroll_parent = new QScrollArea(ui); setParent(_scroll_parent); - resize(800,600); - _scale = _ui->spinbox_scale->value(); - _alpha = _ui->spinbox_alpha->value(); - _pen_size = _ui->spinbox_pen_size->value(); - _initPixmap(); - setScaledContents(true); - setMouseTracking(true); - _button_is_pressed = false; - - _undo_list.clear(); - _undo_index = 0; - _undo = false; + resize(800,600); + _scale = _ui->spinbox_scale->value(); + _alpha = _ui->spinbox_alpha->value(); + _pen_size = _ui->spinbox_pen_size->value(); + updateSlicParams(); + _initPixmap(); + setScaledContents(true); + setMouseTracking(true); + _button_is_pressed = false; + + _undo_list.clear(); + _undo_index = 0; + _undo = false; _scroll_parent->setBackgroundRole(QPalette::Dark); _scroll_parent->setWidget(this); @@ -34,248 +35,292 @@ ImageCanvas::~ImageCanvas() { } void ImageCanvas::_initPixmap() { - QPixmap newPixmap = QPixmap(width(), height()); - newPixmap.fill(Qt::white); - QPainter painter(&newPixmap); - const QPixmap * p = pixmap(); - if (p != NULL) - painter.drawPixmap(0, 0, *pixmap()); - painter.end(); - setPixmap(newPixmap); + QPixmap newPixmap = QPixmap(width(), height()); + newPixmap.fill(Qt::white); + QPainter painter(&newPixmap); + const QPixmap * p = pixmap(); + if (p != NULL) + painter.drawPixmap(0, 0, *pixmap()); + painter.end(); + setPixmap(newPixmap); } void ImageCanvas::loadImage(const QString &filename) { - if (!_image.isNull() ) - saveMask(); - - _img_file = filename; - QFileInfo file(_img_file); - if (!file.exists()) return; - - _image = mat2QImage(cv::imread(_img_file.toStdString())); - - _mask_file = file.dir().absolutePath()+ "/" + file.baseName() + "_mask.png"; - _watershed_file = file.dir().absolutePath()+ "/" + file.baseName() + "_watershed_mask.png"; - - _watershed = ImageMask(_image.size()); - _undo_list.clear(); - _undo_index = 0; - if (QFile(_mask_file).exists()) { - _mask = ImageMask(_mask_file,_ui->id_labels); + if (!_image.isNull() ) + saveMask(); + + _img_file = filename; + QFileInfo file(_img_file); + if (!file.exists()) return; + + _image = mat2QImage(cv::imread(_img_file.toStdString())); + + _mask_file = file.dir().absolutePath()+ "/" + file.baseName() + "_mask.png"; + _watershed_file = file.dir().absolutePath()+ "/" + file.baseName() + "_watershed_mask.png"; + + _watershed = ImageMask(_image.size()); + _undo_list.clear(); + _undo_index = 0; + if (QFile(_mask_file).exists()) { + _mask = ImageMask(_mask_file,_ui->id_labels); _ui->runWatershed(this);// button_watershed->released()); - _ui->checkbox_manuel_mask->setChecked(true); - _undo_list.push_back(_mask); - _undo_index++; - } else { - clearMask(); - } - _ui->undo_action->setEnabled(false); - _ui->redo_action->setEnabled(false); - - setPixmap(QPixmap::fromImage(_image)); - resize(_scale *_image.size()); + _ui->checkbox_manuel_mask->setChecked(true); + _undo_list.push_back(_mask); + _undo_index++; + } else { + clearMask(); + } + _ui->undo_action->setEnabled(false); + _ui->redo_action->setEnabled(false); + + setPixmap(QPixmap::fromImage(_image)); + resize(_scale *_image.size()); } void ImageCanvas::saveMask() { - if (isFullZero(_mask.id)) - return; + if (isFullZero(_mask.id)) + return; - _mask.id.save(_mask_file); - if (!_watershed.id.isNull()) { + _mask.id.save(_mask_file); + if (!_watershed.id.isNull()) { QImage watershed = _watershed.id; // if (!_ui->checkbox_border_ws->isChecked()) { // watershed = removeBorder(_watershed.id, _ui->id_labels); // } - watershed.save(_watershed_file); - QFileInfo file(_img_file); - QString color_file = file.dir().absolutePath() + "/" + file.baseName() + "_color_mask.png"; - idToColor(watershed, _ui->id_labels).save(color_file); - } + watershed.save(_watershed_file); + QFileInfo file(_img_file); + QString color_file = file.dir().absolutePath() + "/" + file.baseName() + "_color_mask.png"; + idToColor(watershed, _ui->id_labels).save(color_file); + } _undo_list.clear(); _undo_index = 0; _ui->setStarAtNameOfTab(false); } void ImageCanvas::scaleChanged(double scale) { - _scale = scale ; - resize(_scale * _image.size()); - repaint(); + _scale = scale ; + resize(_scale * _image.size()); + repaint(); + + adjustScrollBars(); +} + +void ImageCanvas::adjustScrollBars() +{ + // x ------> + // _________ + // y |.........| + // | |.........| + // | |.........| + // | |.........| + // v |.........| + // --------- + QPointF mPos = _mouse_pos; + QSize imSize = _act_im_size; + + + if(_scroll_parent->verticalScrollBar()) + { + auto posHeightRel = (mPos.y()/imSize.height()); //Relation Mauspos to Height of Image + + //set vertical scrollbar to mouse position + QScrollBar * verticalScroll = _scroll_parent->verticalScrollBar(); + double vertScrollSpace = verticalScroll->maximum() - verticalScroll->minimum(); // general calculating of moving-space + verticalScroll->setValue(vertScrollSpace*posHeightRel); + //alternative: QWheelEvent::angleDelta().y() -> see example!!! : https://doc.qt.io/qt-5/qwheelevent.html#angleDelta + } + + if(_scroll_parent->horizontalScrollBar()) + { + auto posWidthRel = (mPos.x()/imSize.width()); ////Relation Mauspos to Width of Image + + //set horizontal scrollbar to mouse position + QScrollBar * horizontalScroll = _scroll_parent->horizontalScrollBar(); + double horizScrollSpace = horizontalScroll->maximum() - horizontalScroll->minimum(); // general calculating of moving-space + horizontalScroll->setValue(horizScrollSpace*posWidthRel); + } } + void ImageCanvas::alphaChanged(double alpha) { - _alpha = alpha; - repaint(); + _alpha = alpha; + repaint(); } void ImageCanvas::paintEvent(QPaintEvent *event) { - QPainter painter(this); - painter.setRenderHint(QPainter::Antialiasing, false); - QRect rect = painter.viewport(); - QSize size = _scale * _image.size(); - if (size != _image.size()) { - rect.size().scale(size, Qt::KeepAspectRatio); - painter.setViewport(rect.x(), rect.y(), size.width(), size.height()); - painter.setWindow(pixmap()->rect()); - } - painter.drawImage(QPoint(0, 0), _image); - painter.setOpacity(_alpha); - - if (!_mask.id.isNull() && _ui->checkbox_manuel_mask->isChecked()) { - painter.drawImage(QPoint(0, 0), _mask.color); - } - - if (!_watershed.id.isNull() && _ui->checkbox_watershed_mask->isChecked()) { - painter.drawImage(QPoint(0, 0), _watershed.color); - } - - if (_mouse_pos.x() > 10 && _mouse_pos.y() > 10 && - _mouse_pos.x() <= QLabel::size().width()-10 && - _mouse_pos.y() <= QLabel::size().height()-10) { - painter.setBrush(QBrush(_color.color)); - painter.setPen(QPen(QBrush(_color.color), 1.0)); - painter.drawEllipse(_mouse_pos.x() / _scale - _pen_size / 2, _mouse_pos.y() / _scale - _pen_size / 2, _pen_size, _pen_size); - painter.end(); - } + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing, false); + QRect rect = painter.viewport(); + QSize size = _scale * _image.size(); + if (size != _image.size()) { + rect.size().scale(size, Qt::KeepAspectRatio); + painter.setViewport(rect.x(), rect.y(), size.width(), size.height()); + painter.setWindow(pixmap()->rect()); + } + painter.drawImage(QPoint(0, 0), _image); + painter.setOpacity(_alpha); + + if (!_mask.id.isNull() && _ui->checkbox_manuel_mask->isChecked()) { + painter.drawImage(QPoint(0, 0), _mask.color); + } + + if (!_watershed.id.isNull() && _ui->checkbox_watershed_mask->isChecked()) { + painter.drawImage(QPoint(0, 0), _watershed.color); + } + + if (_mouse_pos.x() > 10 && _mouse_pos.y() > 10 && + _mouse_pos.x() <= QLabel::size().width()-10 && + _mouse_pos.y() <= QLabel::size().height()-10) { + painter.setBrush(QBrush(_color.color)); + painter.setPen(QPen(QBrush(_color.color), 1.0)); + painter.drawEllipse(_mouse_pos.x() / _scale - _pen_size / 2, _mouse_pos.y() / _scale - _pen_size / 2, _pen_size, _pen_size); + painter.end(); + } } void ImageCanvas::mouseMoveEvent(QMouseEvent * e) { - _mouse_pos.setX(e->x()); - _mouse_pos.setY(e->y()); + _mouse_pos.setX(e->x()); + _mouse_pos.setY(e->y()); + + if (_button_is_pressed) + _drawFillCircle(e); - if (_button_is_pressed) - _drawFillCircle(e); + _act_im_size = _image.size() * _scale; //important for adjusting the scrollBars - update(); + //using statusbar to show actual _mouse_pos + _ui->statusbar->showMessage("X: " + QString::number(_mouse_pos.x()) + " " + + "Y: " + QString::number(_mouse_pos.y()) ); + update(); } void ImageCanvas::setSizePen(int pen_size) { - _pen_size = pen_size; + _pen_size = pen_size; } void ImageCanvas::mouseReleaseEvent(QMouseEvent * e) { - if(e->button() == Qt::LeftButton) { - _button_is_pressed = false; - - if (_undo) { - QMutableListIterator it(_undo_list); - int i = 0; - while (it.hasNext()) { - it.next(); - if (i++ >= _undo_index) - it.remove(); - } - _undo = false; - _ui->redo_action->setEnabled(false); - } - _undo_list.push_back(_mask); - _undo_index++; + if(e->button() == Qt::LeftButton) { + _button_is_pressed = false; + + if (_undo) { + QMutableListIterator it(_undo_list); + int i = 0; + while (it.hasNext()) { + it.next(); + if (i++ >= _undo_index) + it.remove(); + } + _undo = false; + _ui->redo_action->setEnabled(false); + } + _undo_list.push_back(_mask); + _undo_index++; _ui->setStarAtNameOfTab(true); - _ui->undo_action->setEnabled(true); - } - - if (e->button() == Qt::RightButton) { // selection of label - QColor maskColor = _mask.id.pixel(_mouse_pos / _scale); - QColor watershedColor = _watershed.id.pixel(_mouse_pos / _scale); - const LabelInfo * label = _ui->id_labels[maskColor.red()] != NULL? _ui->id_labels[maskColor.red()] : _ui->id_labels[watershedColor.red()]; - if (label != NULL) - { - if (!_watershed.id.isNull() && _ui->checkbox_watershed_mask->isChecked()) { - QColor color = QColor(_watershed.id.pixel(_mouse_pos / _scale)); - QMap::const_iterator it = _ui->id_labels.find(color.red()); - if (it != _ui->id_labels.end()) { - label = it.value(); - } - } - if (label->item != NULL) - emit(_ui->list_label->currentItemChanged(label->item, NULL)); - refresh(); - } - } - - if (e->button() == Qt::MiddleButton) - { - int x, y; - if (_pen_size > 0) { - x = e->x() / _scale; - y = e->y() / _scale; - } - else { - x = (e->x() + 0.5) / _scale; - y = (e->y() + 0.5) / _scale; - } - - _mask.exchangeLabel(x, y, _ui->id_labels, _color); - update(); - } + _ui->undo_action->setEnabled(true); + } + + if (e->button() == Qt::RightButton) { // selection of label + QColor maskColor = _mask.id.pixel(_mouse_pos / _scale); + QColor watershedColor = _watershed.id.pixel(_mouse_pos / _scale); + const LabelInfo * label = _ui->id_labels[maskColor.red()] != NULL? _ui->id_labels[maskColor.red()] : _ui->id_labels[watershedColor.red()]; + if (label != NULL) + { + if (!_watershed.id.isNull() && _ui->checkbox_watershed_mask->isChecked()) { + QColor color = QColor(_watershed.id.pixel(_mouse_pos / _scale)); + QMap::const_iterator it = _ui->id_labels.find(color.red()); + if (it != _ui->id_labels.end()) { + label = it.value(); + } + } + if (label->item != NULL) + emit(_ui->list_label->currentItemChanged(label->item, NULL)); + refresh(); + } + } + + if (e->button() == Qt::MiddleButton) + { + int x, y; + if (_pen_size > 0) { + x = e->x() / _scale; + y = e->y() / _scale; + } + else { + x = (e->x() + 0.5) / _scale; + y = (e->y() + 0.5) / _scale; + } + + _mask.exchangeLabel(x, y, _ui->id_labels, _color); + update(); + } } void ImageCanvas::mousePressEvent(QMouseEvent * e) { - setFocus(); - if (e->button() == Qt::LeftButton) { - _button_is_pressed = true; - _drawFillCircle(e); - } + setFocus(); + if (e->button() == Qt::LeftButton) { + _button_is_pressed = true; + _drawFillCircle(e); + } } void ImageCanvas::_drawFillCircle(QMouseEvent * e) { - if (_pen_size > 0) { - int x = e->x() / _scale - _pen_size / 2; - int y = e->y() / _scale - _pen_size / 2; - _mask.drawFillCircle(x, y, _pen_size, _color); - } else { - int x = (e->x()+0.5) / _scale ; - int y = (e->y()+0.5) / _scale ; - _mask.drawPixel(x, y, _color); - } - update(); + if (_pen_size > 0) { + int x = e->x() / _scale - _pen_size / 2; + int y = e->y() / _scale - _pen_size / 2; + _mask.drawFillCircle(x, y, _pen_size, _color); + } else { + int x = (e->x()+0.5) / _scale ; + int y = (e->y()+0.5) / _scale ; + _mask.drawPixel(x, y, _color); + } + update(); } void ImageCanvas::clearMask() { - _mask = ImageMask(_image.size()); - _watershed = ImageMask(_image.size()); - _undo_list.clear(); - _undo_index = 0; - repaint(); - + _mask = ImageMask(_image.size()); + _watershed = ImageMask(_image.size()); + _undo_list.clear(); + _undo_index = 0; + repaint(); + } void ImageCanvas::wheelEvent(QWheelEvent * event) { - int delta = event->delta() > 0 ? 1 : -1; - if (Qt::ShiftModifier == event->modifiers()) { + int delta = event->delta() > 0 ? 1 : -1; + if (Qt::ShiftModifier == event->modifiers()) { + _scroll_parent->verticalScrollBar()->setEnabled(false); + int value = _ui->spinbox_pen_size->value() + delta * _ui->spinbox_pen_size->singleStep(); + _ui->spinbox_pen_size->setValue(value); + emit(_ui->spinbox_pen_size->valueChanged(value)); + setSizePen(value); + repaint(); + } else if (Qt::ControlModifier == event->modifiers()) { _scroll_parent->verticalScrollBar()->setEnabled(false); - int value = _ui->spinbox_pen_size->value() + delta * _ui->spinbox_pen_size->singleStep(); - _ui->spinbox_pen_size->setValue(value); - emit(_ui->spinbox_pen_size->valueChanged(value)); - setSizePen(value); - repaint(); - } else if (Qt::ControlModifier == event->modifiers()) { - _scroll_parent->verticalScrollBar()->setEnabled(false); - double value = _ui->spinbox_scale->value() + delta * _ui->spinbox_scale->singleStep(); - value = std::min(_ui->spinbox_scale->maximum(),value); - value = std::max(_ui->spinbox_scale->minimum(), value); - - _ui->spinbox_scale->setValue(value); - scaleChanged(value); - repaint(); - } else { + double value = _ui->spinbox_scale->value() + delta * _ui->spinbox_scale->singleStep(); + value = std::min(_ui->spinbox_scale->maximum(),value); + value = std::max(_ui->spinbox_scale->minimum(), value); + + _ui->spinbox_scale->setValue(value); + scaleChanged(value); + repaint(); + } else { _scroll_parent->verticalScrollBar()->setEnabled(true); - } + } } void ImageCanvas::keyPressEvent(QKeyEvent * event) { - if (event->key() == Qt::Key_Space) { - emit(_ui->button_watershed->released()); - } + if (event->key() == Qt::Key_Space) { + emit(_ui->button_watershed->released()); + } } void ImageCanvas::setWatershedMask(QImage watershed) { - _watershed.id = watershed; - idToColor(_watershed.id, _ui->id_labels, &_watershed.color); + _watershed.id = watershed; + idToColor(_watershed.id, _ui->id_labels, &_watershed.color); } void ImageCanvas::setMask(const ImageMask & mask) { - _mask = mask; + _mask = mask; } void ImageCanvas::setActionMask(const ImageMask & mask) { @@ -287,47 +332,62 @@ void ImageCanvas::setActionMask(const ImageMask & mask) { } void ImageCanvas::setId(int id) { - _color.id = QColor(id, id, id); - _color.color = _ui->id_labels[id]->color; + _color.id = QColor(id, id, id); + _color.color = _ui->id_labels[id]->color; } void ImageCanvas::refresh() { - if (!_watershed.id.isNull() && _ui->checkbox_watershed_mask->isChecked() ) { - emit(_ui->button_watershed->released()); - } - update(); + if (!_watershed.id.isNull() && _ui->checkbox_watershed_mask->isChecked() ) { + emit(_ui->button_watershed->released()); + } + update(); } void ImageCanvas::undo() { - _undo = true; - _undo_index--; - if (_undo_index == 1) { - _mask = _undo_list.at(_undo_index - 1); - _ui->undo_action->setEnabled(false); - refresh(); - } else if (_undo_index > 1) { - _mask = _undo_list.at(_undo_index - 1); - refresh(); - } else { - _undo_index = 0; - _ui->undo_action->setEnabled(false); - } - _ui->redo_action->setEnabled(true); + _undo = true; + _undo_index--; + if (_undo_index == 1) { + _mask = _undo_list.at(_undo_index - 1); + _ui->undo_action->setEnabled(false); + refresh(); + } else if (_undo_index > 1) { + _mask = _undo_list.at(_undo_index - 1); + refresh(); + } else { + _undo_index = 0; + _ui->undo_action->setEnabled(false); + } + _ui->redo_action->setEnabled(true); } void ImageCanvas::redo() { - _undo_index++; - if (_undo_index < _undo_list.size()) { - _mask = _undo_list.at(_undo_index - 1); - refresh(); - } else if (_undo_index == _undo_list.size()) { - _mask = _undo_list.at(_undo_index - 1); - _ui->redo_action->setEnabled(false); - refresh(); - } else { - _undo_index = _undo_list.size(); - _ui->redo_action->setEnabled(false); - } - _ui->undo_action->setEnabled(true); + _undo_index++; + if (_undo_index < _undo_list.size()) { + _mask = _undo_list.at(_undo_index - 1); + refresh(); + } else if (_undo_index == _undo_list.size()) { + _mask = _undo_list.at(_undo_index - 1); + _ui->redo_action->setEnabled(false); + refresh(); + } else { + _undo_index = _undo_list.size(); + _ui->redo_action->setEnabled(false); + } + _ui->undo_action->setEnabled(true); +} + +void ImageCanvas::setSlicMask (QImage rootImage ,QImage slicImage) +{ + _watershed.id = rootImage; + _watershed.color = slicImage; +} + +void ImageCanvas::updateSlicParams (){ + slic_params.algorithmChanged( _ui->spinbox_slic_algo->currentIndex()); + slic_params.regionSizeChanged( _ui->spinbox_slic_region_size->value()); + slic_params.rulerChanged( _ui->spinbox_slic_ruler->value()); + slic_params.connectivityChanged( _ui->spinbox_slic_connectivity->value()); + slic_params.iterationsChanged( _ui->spinbox_slic_iterations->value()); + slic_params.displayModeChanged( _ui->spinbox_slic_display_mode->value()); } diff --git a/src/image_canvas.h b/src/image_canvas.h index 6b5e1d7..dc765d5 100644 --- a/src/image_canvas.h +++ b/src/image_canvas.h @@ -3,77 +3,87 @@ #include "utils.h" #include "image_mask.h" +#include "slic_parameter.h" #include #include #include + class MainWindow; class ImageCanvas : public QLabel { - Q_OBJECT + Q_OBJECT public: - ImageCanvas(MainWindow *ui); + ImageCanvas(MainWindow *ui); ~ImageCanvas(); - void setId(int id); - void setMask(const ImageMask & mask); + void setId(int id); + void setMask(const ImageMask & mask); void setActionMask(const ImageMask & mask); ImageMask getMask() const { return _mask; } QImage getImage() const { return _image; } - void setWatershedMask(QImage watershed); - void refresh(); - void updateMaskColor(const Id2Labels & labels) { _mask.updateColor(labels); } - void loadImage(const QString &file); - QScrollArea * getScrollParent() const { return _scroll_parent; } + void setWatershedMask(QImage watershed); + void setSlicMask (QImage rootImage, QImage slicImage); + void refresh(); + void updateMaskColor(const Id2Labels & labels) { _mask.updateColor(labels); } + void loadImage(const QString &file); + QScrollArea * getScrollParent() const { return _scroll_parent; } bool isNotSaved() const { return _undo_list.size() > 1; } + SlicParameter slic_params ; + void updateSlicParams(); + protected: - void mouseMoveEvent(QMouseEvent * event) override; - void mousePressEvent(QMouseEvent * event) override; - void keyPressEvent(QKeyEvent * event) override; - void wheelEvent(QWheelEvent * event) override; - void mouseReleaseEvent(QMouseEvent * event) override; - void paintEvent(QPaintEvent *event) override; + void mouseMoveEvent(QMouseEvent * event) override; + void mousePressEvent(QMouseEvent * event) override; + void keyPressEvent(QKeyEvent * event) override; + void wheelEvent(QWheelEvent * event) override; + void mouseReleaseEvent(QMouseEvent * event) override; + void paintEvent(QPaintEvent *event) override; public slots : - void scaleChanged(double); - void alphaChanged(double); - void setSizePen(int); - void clearMask(); - void saveMask(); - void undo(); - void redo(); - + void scaleChanged(double); + void alphaChanged(double); + void setSizePen(int); + void clearMask(); + void saveMask(); + void undo(); + void redo(); + private: - MainWindow *_ui; - - void _initPixmap(); - void _drawFillCircle(QMouseEvent * e); - - QScrollArea *_scroll_parent ; - double _scale ; - double _alpha ; - QImage _image ; - ImageMask _mask ; - ImageMask _watershed ; - QList _undo_list ; - bool _undo ; - int _undo_index ; - QPoint _mouse_pos ; - QString _img_file ; - QString _mask_file ; - QString _watershed_file ; - ColorMask _color ; - int _pen_size ; - bool _button_is_pressed; + MainWindow *_ui; + + void _initPixmap(); + void _drawFillCircle(QMouseEvent * e); + + QScrollArea *_scroll_parent ; + double _scale ; + double _alpha ; + QImage _image ; + ImageMask _mask ; + ImageMask _watershed ; + QList _undo_list ; + bool _undo ; + int _undo_index ; + QPoint _mouse_pos ; + QString _img_file ; + QString _mask_file ; + QString _watershed_file ; + ColorMask _color ; + int _pen_size ; + bool _button_is_pressed; + QSize _act_im_size ; + +private slots: + void adjustScrollBars(); }; -#endif //IMAGE_CANVAS_H \ No newline at end of file +#endif //IMAGE_CANVAS_H diff --git a/src/main_window.cpp b/src/main_window.cpp index 53783f2..e09971f 100644 --- a/src/main_window.cpp +++ b/src/main_window.cpp @@ -19,6 +19,8 @@ #include "about_dialog.h" +#include + MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) : QMainWindow(parent, flags) { @@ -64,6 +66,7 @@ MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) tabWidget->clear(); connect(button_watershed , SIGNAL(released()) , this, SLOT(runWatershed() )); + connect(button_slic , SIGNAL(released()) , this, SLOT(runSlic() )); connect(swap_action , SIGNAL(triggered()) , this, SLOT(swapView() )); connect(actionOpen_config_file, SIGNAL(triggered()) , this, SLOT(loadConfigFile())); connect(actionSave_config_file, SIGNAL(triggered()) , this, SLOT(saveConfigFile())); @@ -225,12 +228,15 @@ void MainWindow::changeLabel(QListWidgetItem* current, QListWidgetItem* previous void MainWindow::runWatershed(ImageCanvas * ic) { QImage iwatershed = watershed(ic->getImage(), ic->getMask().id); + if (!checkbox_border_ws->isChecked()) { iwatershed = removeBorder(iwatershed, id_labels); } - ic->setWatershedMask(iwatershed); + + ic->setWatershedMask(iwatershed); + checkbox_watershed_mask->setCheckState(Qt::CheckState::Checked); - ic->update(); + ic->update(); } void MainWindow::runWatershed() { @@ -266,6 +272,12 @@ void MainWindow::updateConnect(const ImageCanvas * ic) { connect(redo_action, SIGNAL(triggered()), ic, SLOT(redo())); connect(save_action, SIGNAL(triggered()), ic, SLOT(saveMask())); connect(checkbox_border_ws, SIGNAL(clicked()), this, SLOT(runWatershed())); + connect(spinbox_slic_algo, SIGNAL(currentIndexChanged(int)), &ic->slic_params, SLOT(algorithmChanged(int))); + connect(spinbox_slic_region_size, SIGNAL(valueChanged(int)), &ic->slic_params, SLOT(regionSizeChanged(int))); + connect(spinbox_slic_ruler, SIGNAL(valueChanged(double)), &ic->slic_params, SLOT(rulerChanged(double))); + connect(spinbox_slic_connectivity, SIGNAL(valueChanged(int)), &ic->slic_params, SLOT(connectivityChanged(int))); + connect(spinbox_slic_iterations, SIGNAL(valueChanged(int)), &ic->slic_params, SLOT(iterationsChanged(int))); + connect(spinbox_slic_display_mode, SIGNAL(valueChanged(int)), &ic->slic_params, SLOT(displayModeChanged(int))); } void MainWindow::allDisconnnect(const ImageCanvas * ic) { @@ -280,6 +292,12 @@ void MainWindow::allDisconnnect(const ImageCanvas * ic) { disconnect(redo_action, SIGNAL(triggered()), ic, SLOT(redo())); disconnect(save_action, SIGNAL(triggered()), ic, SLOT(saveMask())); disconnect(checkbox_border_ws, SIGNAL(clicked()), this, SLOT(runWatershed())); + disconnect(spinbox_slic_algo, SIGNAL(currentIndexChanged(int)), &ic->slic_params, SLOT(algorithmChanged(int))); + disconnect(spinbox_slic_region_size, SIGNAL(valueChanged(int)), &ic->slic_params, SLOT(regionSizeChanged(int))); + disconnect(spinbox_slic_ruler, SIGNAL(valueChanged(double)), &ic->slic_params, SLOT(rulerChanged(double))); + disconnect(spinbox_slic_connectivity, SIGNAL(valueChanged(int)), &ic->slic_params, SLOT(connectivityChanged(int))); + disconnect(spinbox_slic_iterations, SIGNAL(valueChanged(int)), &ic->slic_params, SLOT(iterationsChanged(int))); + disconnect(spinbox_slic_display_mode, SIGNAL(valueChanged(int)), &ic->slic_params, SLOT(displayModeChanged(int))); } ImageCanvas * MainWindow::newImageCanvas() { @@ -302,6 +320,7 @@ void MainWindow::updateConnect(int index) { ImageCanvas * MainWindow::getImageCanvas(int index) { QScrollArea * scroll_area = static_cast(tabWidget->widget(index)); ImageCanvas * ic = static_cast(scroll_area->widget()); + updateSlicParamsView(ic); return ic; } @@ -323,9 +342,9 @@ int MainWindow::getImageCanvas(QString name, ImageCanvas * ic) { QString MainWindow::currentDir() const { QTreeWidgetItem *current = tree_widget_img->currentItem(); if (!current || !current->parent()) - return ""; + return ""; - return current->parent()->text(0); + return current->parent()->text(0); } QString MainWindow::currentFile() const { @@ -345,6 +364,7 @@ void MainWindow::treeWidgetClicked() { allDisconnnect(image_canvas); int index = getImageCanvas(iFile, image_canvas); updateConnect(image_canvas); + std::cout << "Algo: " << image_canvas->slic_params.algorithmSet() << "\t Region Size: " << image_canvas->slic_params.regionSizeSet() << "\t Ruler: " << image_canvas->slic_params.rulerSet() << "\t Connectivity: " << image_canvas->slic_params.connectivitySet() << "\t Iter: " << image_canvas->slic_params.iterationsSet() << std::endl; tabWidget->setCurrentIndex(index); } @@ -353,7 +373,8 @@ void MainWindow::on_tree_widget_img_currentItemChanged(QTreeWidgetItem *current, } void MainWindow::on_actionOpenDir_triggered() { - statusBar()->clearMessage(); + statusBar()->clearMessage(); + curr_open_dir = "/home/rome/Dokumente/Evaluation-Annotier-Tools/freiburg_forest_data_raw/freiburg_forest_raw/"; //RA added ///TODO: Später wieder entfernen QString openedDir = QFileDialog::getExistingDirectory(this, "Choose a directory to be read in", curr_open_dir); if (openedDir.isEmpty()) return; @@ -506,3 +527,25 @@ void MainWindow::onLabelShortcut(int row) { update(); } } + +void MainWindow::runSlic(ImageCanvas * ic){ + QImage iSlic = slic(ic->getImage(), &ic->slic_params); + + + ic->setSlicMask(ic->getImage(),iSlic); + ic->update(); +} + +void MainWindow::runSlic() { + ImageCanvas * ic = image_canvas; + if (ic != NULL) + runSlic(ic); +} + +void MainWindow::updateSlicParamsView(ImageCanvas *ic){ + spinbox_slic_algo->setCurrentIndex( ic->slic_params.algorithmSet() - 100); + spinbox_slic_region_size->setValue( ic->slic_params.regionSizeSet() ); + spinbox_slic_ruler->setValue( ic->slic_params.rulerSet() ); + spinbox_slic_connectivity->setValue( ic->slic_params.connectivitySet() ); + spinbox_slic_iterations->setValue( ic->slic_params.iterationsSet() ); +} diff --git a/src/main_window.h b/src/main_window.h index 92604b1..eb704c9 100644 --- a/src/main_window.h +++ b/src/main_window.h @@ -24,6 +24,7 @@ #include "label_widget.h" #include "labels.h" + class MainWindow : public QMainWindow, public Ui::MainWindow { Q_OBJECT @@ -43,6 +44,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindow { ImageMask _tmp; QVector _shortcuts; bool _isLoadingNewLabels; + void updateSlicParamsView(ImageCanvas *ic); public: ImageCanvas * image_canvas ; @@ -62,6 +64,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindow { QAction * next_file_action; QAction * previous_file_action; QString curr_open_dir; + public: QString currentDir() const; QString currentFile() const; @@ -71,6 +74,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindow { void setStarAtNameOfTab(bool star); void dragEnterEvent(QDragEnterEvent *e) override; void dropEvent(QDropEvent *e) override; + void runSlic(ImageCanvas * ic); public slots: @@ -95,6 +99,7 @@ public slots: void treeWidgetClicked(); void onLabelShortcut(int row); void update(); + void runSlic(); }; #endif diff --git a/src/main_window.ui b/src/main_window.ui index b01c9af..95a9972 100644 --- a/src/main_window.ui +++ b/src/main_window.ui @@ -6,8 +6,8 @@ 0 0 - 1511 - 967 + 1507 + 958 @@ -87,8 +87,8 @@ 0 0 - 1511 - 21 + 1507 + 22 @@ -297,6 +297,210 @@ + + + + + + Qt::LeftToRight + + + SLIC - Segmentation + + + Qt::AlignCenter + + + + + + + + + Algorithm : + + + + + + + false + + + SLIC + + + 3 + + + + SLIC + + + + + SLICO + + + + + MSLIC + + + + + + + + + + + + Region size : + + + + + + + 0 + + + 1000 + + + 5 + + + 50 + + + + + + + + + + + false + + + + + + 3 + + + Ruler : + + + + + + + 100.000000000000000 + + + 1.000000000000000 + + + 20.000000000000000 + + + + + + + + + + + Connectivity : + + + + + + + 1 + + + 100 + + + 5 + + + 30 + + + + + + + + + + + Iterations : + + + + + + + 1 + + + 20 + + + 1 + + + 8 + + + + + + + + + + + Display mode : + + + + + + + 0 + + + 2 + + + 1 + + + 0 + + + + + + + + + Slic + + + + + diff --git a/src/slic_parameter.cpp b/src/slic_parameter.cpp new file mode 100644 index 0000000..a7d0a75 --- /dev/null +++ b/src/slic_parameter.cpp @@ -0,0 +1,51 @@ +#include "slic_parameter.h" +#include + +SlicParameter::SlicParameter() + : QObject(), + _algo(cv::ximgproc::SLIC), + _region_size(100), + _ruler(25.0), + _connectivity(50), + _iterations(8), + _display_mode(0) +{ + +} + +void SlicParameter::algorithmChanged(int choice) +{ + switch (choice) + { + case 0: _algo = cv::ximgproc::SLIC; break; + case 1: _algo = cv::ximgproc::SLICO; break; + case 2: _algo = cv::ximgproc::MSLIC; break; + default: _algo = cv::ximgproc::SLIC; ;break; + } + +} + +void SlicParameter::regionSizeChanged(int region_size) +{ + _region_size = region_size; +} + +void SlicParameter::rulerChanged(double ruler) +{ + _ruler = ruler; +} + +void SlicParameter::connectivityChanged(int connectivity) +{ + _connectivity = connectivity; +} + +void SlicParameter::iterationsChanged(int number_of_iterations) +{ + _iterations = number_of_iterations; +} + +void SlicParameter::displayModeChanged(int display_mode) +{ + _display_mode = display_mode; +} diff --git a/src/slic_parameter.h b/src/slic_parameter.h new file mode 100644 index 0000000..201b9c7 --- /dev/null +++ b/src/slic_parameter.h @@ -0,0 +1,40 @@ +#ifndef SLIC_PARAMETER_H +#define SLIC_PARAMETER_H + +#include +#include + +class SlicParameter : public QObject { + Q_OBJECT + +public: + SlicParameter(); + virtual ~SlicParameter() {}; + + int algorithmSet() const {return _algo;} //SLIC(0),SLICO(1),MSLIC(2) + int regionSizeSet() const {return _region_size ;} + double rulerSet() const {return _ruler ;} + int connectivitySet() const {return _connectivity;} + int iterationsSet() const {return _iterations ;} + int displayModeSet() const {return _display_mode;} + + + +public slots : + void algorithmChanged(int); + void regionSizeChanged(int); + void rulerChanged(double); + void connectivityChanged(int); + void iterationsChanged(int); + void displayModeChanged(int); + +private: + cv::ximgproc::SLICType _algo ; + int _region_size ; + double _ruler ; + int _connectivity; + int _iterations ; + int _display_mode; +}; + +#endif // SLIC_PARAMETER_H diff --git a/src/utils.cpp b/src/utils.cpp index 13019f2..86c723e 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -1,14 +1,25 @@ #include "utils.h" +#include //------------------------------------------------------------------------------------------------------------- QImage mat2QImage(cv::Mat const& src) { cv::Mat temp; // make the same cv::Mat - cv::cvtColor(src, temp, cv::COLOR_BGR2RGB); // cvtColor Makes a copt, that what i need + cv::cvtColor(src, temp, cv::COLOR_BGR2RGB); // cvtColor Makes a copt, that what i need QImage dest((const uchar *)temp.data, temp.cols, temp.rows,int(temp.step), QImage::Format_RGB888); dest.bits(); // enforce deep copy, see documentation // of QImage::QImage ( const uchar * data, int width, int height, Format format ) return dest; } + +QImage mat2QImageGray(cv::Mat const& src) { + cv::Mat temp; // make the same cv::Mat + cv::cvtColor(src, temp, cv::COLOR_GRAY2RGB); // cvtColor Makes a copt, that what i need + QImage dest((const uchar *)temp.data, temp.cols, temp.rows,int(temp.step), QImage::Format_RGB888); + dest.bits(); // enforce deep copy, see documentation + // of QImage::QImage ( const uchar * data, int width, int height, Format format ) + return dest; +} + cv::Mat qImage2Mat(QImage const& src) { cv::Mat tmp(src.height(), src.width(), CV_8UC3, (uchar*)src.bits(), src.bytesPerLine()); cv::Mat result; // deep copy just in case (my lack of knowledge with open cv) @@ -131,7 +142,7 @@ QImage watershed(const QImage& qimage, const QImage & qmarkers_mask) { } cv::watershed(image, markers); cv::Mat new_mask = convertMat32StoRGBC3(markers); - return mat2QImage(new_mask); + return mat2QImage(new_mask); } QImage removeBorder(const QImage & mask_id, const Id2Labels & labels, cv::Size win_size) { @@ -193,3 +204,66 @@ bool isFullZero(const QImage& image) { return true; } //------------------------------------------------------------------------------------------------------------- + +QImage slic(const QImage& qimage, const SlicParameter * sp) +{ + cv::Mat image = qImage2Mat(qimage); + + cv::Mat converted; + cv::cvtColor(image, converted, cv::COLOR_BGR2Lab); + cv::Ptr slic_ptr = cv::ximgproc::createSuperpixelSLIC(converted, sp->algorithmSet(), sp->regionSizeSet(), sp->rulerSet()); + + slic_ptr->iterate(sp->iterationsSet()); + slic_ptr->enforceLabelConnectivity(sp->connectivitySet()); + + static const char* window_name = "SLIC Superpixels"; + cv::namedWindow(window_name, 0); + + cv::Mat mask, result; + slic_ptr->getLabelContourMask(mask,true); + + image.setTo(cv::Scalar(255,0,0), mask); + + result=image; + + cv::Mat labels; + cv::Mat return_mat; + switch (sp->displayModeSet()) + { + case 0: //superpixel contours + result.setTo(cv::Scalar(0, 0, 255), mask); + + return_mat = result; + break; + case 1: //mask + //cv::imshow(window_name, mask); + break; + case 2: //labels array + { + // use the last x bit to determine the color. Note that this does not + // guarantee that 2 neighboring superpixels have different colors. + // retrieve the segmentation result +// cv::Mat labels; + slic_ptr->getLabels(labels); + //std::cout <<"BEFORE LABELS:" << labels; + const int num_label_bits = 2; + labels &= (1 << num_label_bits) - 1; + labels *= 1 << (16 - num_label_bits); + labels.convertTo(labels, CV_8UC3, 1.0/(255.0)); + + cv::cvtColor(labels, return_mat, cv::COLOR_GRAY2RGB,3); + + //std::cout <<"AFTER LABELS:" << return_mat; + return_mat = labels; + break; + } + } + + cv::imshow(window_name, return_mat); + std::cout << "Algo: " << sp->algorithmSet() << "\t Region Size: " << sp->regionSizeSet() << "\t Ruler: " << sp->rulerSet() << "\t Connectivity: " << sp->connectivitySet() << "\t Iter: " << sp->iterationsSet() << std::endl; + + QImage dest((const uchar *)return_mat.data, return_mat.cols, return_mat.rows,int(return_mat.step), QImage::Format_RGB888); + dest.bits(); // enforce deep copy, see documentation + + return dest; +} diff --git a/src/utils.h b/src/utils.h index aee223f..422aff6 100644 --- a/src/utils.h +++ b/src/utils.h @@ -2,11 +2,16 @@ #define PIX_ANN_UTILS_H #include "labels.h" +#include "image_canvas.h" +#include "slic_parameter.h" #include #include +#include #include + + cv::Mat qImage2Mat(QImage const& src); QImage mat2QImage(cv::Mat const& src); QImage idToColor(const QImage &image_id, const Id2Labels& id_label); @@ -22,4 +27,6 @@ bool isFullZero(const QImage& image); int rgbToInt(uchar r, uchar g, uchar b); void intToRgb(int value, uchar &r, uchar &g, uchar &b); +QImage slic(const QImage& qimage, const SlicParameter * sp); + #endif