Skip to content

Commit

Permalink
Merge pull request #153 from PUTvision/devel
Browse files Browse the repository at this point in the history
devel -> master
  • Loading branch information
przemyslaw-aszkowski authored Feb 23, 2024
2 parents 3cf6255 + cfd70a6 commit b397127
Show file tree
Hide file tree
Showing 32 changed files with 328 additions and 181 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/python-app-ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ jobs:
- name: Install dependencies
run: |
pip install --upgrade pip
pip install --upgrade -r requirements.txt
pip install --upgrade -r requirements_development.txt
pip install --upgrade -r ./src/deepness/python_requirements/requirements.txt
pip install --upgrade -r ./src/deepness/python_requirements/requirements_development.txt
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
Expand All @@ -56,7 +56,7 @@ jobs:
# apparently there is some issue with opencv-python-headless, we need to reinstall it
pip uninstall opencv-python-headless --yes
pip install opencv-python-headless==4.6.0.66
pip install --upgrade -r ./src/deepness/python_requirements/requirements.txt
# run one this without pytest, because pytest creates obfuscated error messages
# we need 'xvfb-run' to simulate UI - otherwise qgis crushes
xvfb-run python3 test/test_map_processor_segmentation.py
Expand Down
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ compile_commands.json

# End of https://www.toptal.com/developers/gitignore/api/python,pycharm,qt


grid_data
.idea/

src/deepness/python3.10/
_autosummary/
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ ln -s $PROJECT_DIR/src/deepness ~/.local/share/QGIS/QGIS3/profiles/default/pytho

```bash
. venv/bin/activate
pip install -r requirements.txt
pip install -r ./src/deepness/python_requirements/requirements.txt
```

- Run QGis in the virtual environment:
Expand Down
2 changes: 0 additions & 2 deletions docs/source/creators/creators_add_metadata_to_model.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ List of parameters parsed by plugin
+----------------------+-------+---------------------------------------+-------------------------------------------------------------+
| det_type | str | :code:`YOLO_v5_or_v7_default` | Detector: type of the detector model format |
+----------------------+-------+---------------------------------------+-------------------------------------------------------------+
| det_remove_overlap | bool | :code:`True` | Detector: Whether overlapping detection should be removed |
+----------------------+-------+---------------------------------------+-------------------------------------------------------------+

=======
Example
Expand Down
68 changes: 37 additions & 31 deletions docs/source/main/main_installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,6 @@ Installation
============


============
Requirements
============

* Ubuntu

* (option 1) Install requirements using system Python interpreter:

.. code-block::
python3 -m pip install opencv-python-headless onnxruntime-gpu
* (option 2) Run QGIS and Python Console. Then call command:

.. code-block::
import pip; pip.main(['install', 'opencv-python-headless', 'onnxruntime-gpu'])
* Windows

* Go to QGIS installation path (for example :code:`C:\Program Files\QGIS 3.26.3\`)
* Run :code:`OSGeo4W.bat` and type installation command:

.. code-block::
python3 -m pip install opencv-python-headless onnxruntime-gpu
* MacOS - SOON

===================
Plugin installation
===================
Expand Down Expand Up @@ -69,3 +38,40 @@ Plugin installation
* Select the ZIP file using system prompt

* Click the Install Plugin button

============
Requirements
============

The plugin should install all required dependencies automatically during the first run. However, if you want to install them manually, you can use the following commands:

.. note::

The plugin requirements and versions are listed in the `requirements.txt <https://github.com/PUTvision/qgis-plugin-deepness/blob/master/src/deepness/python_requirements/requirements.txt>`_ file.

* Ubuntu

* (option 1) Install requirements using system Python interpreter:

.. code-block::
python3 -m pip install opencv-python-headless onnxruntime-gpu
* (option 2) Run QGIS and Python Console. Then call command:

.. code-block::
import pip; pip.main(['install', 'opencv-python-headless', 'onnxruntime-gpu'])
* Windows

* Go to QGIS installation path (for example :code:`C:\Program Files\QGIS 3.26.3\`)
* Run :code:`OSGeo4W.bat` and type installation command:

.. code-block::
python3 -m pip install opencv-python-headless onnxruntime-gpu
* MacOS - SOON
1 change: 1 addition & 0 deletions docs/source/main/model_zoo/MODEL_ZOO.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ The [Model ZOO](https://chmura.put.poznan.pl/s/2pJk4izRurzQwu3) is a collection
| [Agriculture segmentation RGB+NIR](https://chmura.put.poznan.pl/s/wf5Ml1ZDyiVdNiy) | 256 | 30 | Trained on the [Agriculture Vision 2021 dataset](https://www.agriculture-vision.com/agriculture-vision-2021/dataset-2021). 4 channels input (RGB + NIR). 9 output classes within agricultural field (weed_cluster, waterway, ...). Uses X-UNet. | [Image](https://chmura.put.poznan.pl/s/35A5ISUxLxcK7kL) |
| [Fire risk assesment](https://chmura.put.poznan.pl/s/NxKLdfdr9s9jsVA) | 384 | 100 | Trained on the FireRisk dataset (RGB data). Classifies risk of fires (ver_high, high, low, ...). Uses ConvNeXt XXL. Val F1-score 65.5. | [Image](https://chmura.put.poznan.pl/s/Ijn3VgG76NvYtDY) |
| [Roads Segmentation](https://chmura.put.poznan.pl/s/y6S3CmodPy1fYYz) | 512 | 21 | The model segments the Google Earth satellite images into 'road' and 'not-road' classes. Model works best on wide car roads, crossroads and roundabouts. | [Image](https://chmura.put.poznan.pl/s/rln6mpbjpsXWpKg) |
| [Solar PV Segmentation](https://owncloud.fraunhofer.de/index.php/s/Ph9TC6BTxPi5oZZ) | 512 | 3 | Model trained by M Kleebauer et al. in "[Multi-resolution segmentation of solar photovoltaic systems using deep learning](https://www.mdpi.com/2596164) on a diverse range of image data, spanning UAV, aerial, and satellite imagery at both native and aggregated resolutions of 0.1 m, 0.2 m, 0.3 m, 0.8 m, 1.6 m, and 3.2 m. | [Image](https://github.com/Kleebaue/multi-resolution-pv-system-segmentation/blob/main/figures/prediction_multi_res.png) |

## Regression models

Expand Down
9 changes: 0 additions & 9 deletions requirements.txt

This file was deleted.

1 change: 0 additions & 1 deletion src/deepness/common/config_entry_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ class ConfigEntryKey(enum.Enum):

DETECTION_CONFIDENCE = enum.auto(), 0.5
DETECTION_IOU = enum.auto(), 0.5
DETECTION_REMOVE_OVERLAPPING = enum.auto(), True
DETECTOR_TYPE = enum.auto(), 'YOLO_v5_v7_DEFAULT'

DATA_EXPORT_DIR = enum.auto(), ''
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,5 @@ class DetectionParameters(MapProcessingParameters):

confidence: float
iou_threshold: float
remove_overlapping_detections: bool # whether overlapping detections can be deleted

detector_type: DetectorType = DetectorType.YOLO_v5_v7_DEFAULT # parameters specific for each model type
3 changes: 2 additions & 1 deletion src/deepness/deepness.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
MapProcessingResultFailed,
MapProcessingResultSuccess)
from deepness.processing.map_processor.map_processor_training_data_export import MapProcessorTrainingDataExport
from deepness.processing.models.model_types import ModelDefinition

cv2 = LazyPackageLoader('cv2')

Expand Down Expand Up @@ -252,6 +251,8 @@ def _run_training_data_export(self, training_data_export_parameters: TrainingDat
self._display_processing_started_info()

def _run_model_inference(self, params: MapProcessingParameters):
from deepness.processing.models.model_types import ModelDefinition # import here to avoid pulling external dependencies to early

if not self._are_map_processing_parameters_are_correct(params):
return

Expand Down
44 changes: 26 additions & 18 deletions src/deepness/deepness_dockwidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from deepness.common.config_entry_key import ConfigEntryKey
from deepness.common.defines import IS_DEBUG, PLUGIN_NAME
from deepness.common.errors import OperationFailedException
from deepness.common.lazy_package_loader import LazyPackageLoader
from deepness.common.processing_overlap import ProcessingOverlap, ProcessingOverlapOptions
from deepness.common.processing_parameters.detection_parameters import DetectionParameters, DetectorType
from deepness.common.processing_parameters.map_processing_parameters import (MapProcessingParameters, ModelOutputFormat,
Expand All @@ -23,12 +24,11 @@
from deepness.common.processing_parameters.segmentation_parameters import SegmentationParameters
from deepness.common.processing_parameters.superresolution_parameters import SuperresolutionParameters
from deepness.common.processing_parameters.training_data_export_parameters import TrainingDataExportParameters
from deepness.processing.models.detector import Detector
from deepness.processing.models.model_base import ModelBase
from deepness.processing.models.model_types import ModelDefinition, ModelType
from deepness.widgets.input_channels_mapping.input_channels_mapping_widget import InputChannelsMappingWidget
from deepness.widgets.training_data_export_widget.training_data_export_widget import TrainingDataExportWidget


FORM_CLASS, _ = uic.loadUiType(os.path.join(
os.path.dirname(__file__), 'deepness_dockwidget.ui'))

Expand Down Expand Up @@ -113,7 +113,6 @@ def _load_ui_from_config(self):

self.doubleSpinBox_confidence.setValue(ConfigEntryKey.DETECTION_CONFIDENCE.get())
self.doubleSpinBox_iouScore.setValue(ConfigEntryKey.DETECTION_IOU.get())
self.checkBox_removeOverlappingDetections.setChecked(ConfigEntryKey.DETECTION_REMOVE_OVERLAPPING.get())
self.comboBox_detectorType.setCurrentText(ConfigEntryKey.DETECTOR_TYPE.get())
except Exception:
logging.exception("Failed to load the ui state from config!")
Expand Down Expand Up @@ -146,7 +145,6 @@ def _save_ui_to_config(self):

ConfigEntryKey.DETECTION_CONFIDENCE.set(self.doubleSpinBox_confidence.value())
ConfigEntryKey.DETECTION_IOU.set(self.doubleSpinBox_iouScore.value())
ConfigEntryKey.DETECTION_REMOVE_OVERLAPPING.set(self.checkBox_removeOverlappingDetections.isChecked())
ConfigEntryKey.DETECTOR_TYPE.set(self.comboBox_detectorType.currentText())

self._input_channels_mapping_widget.save_ui_to_config()
Expand All @@ -158,6 +156,8 @@ def _rlayer_updated(self):
def _setup_misc_ui(self):
""" Setup some misceleounous ui forms
"""
from deepness.processing.models.model_types import ModelDefinition # import here to avoid pulling external dependencies to early

self._show_debug_warning()
combobox = self.comboBox_processedAreaSelection
for name in ProcessedAreaType.get_all_names():
Expand All @@ -168,6 +168,8 @@ def _setup_misc_ui(self):

self.mMapLayerComboBox_inputLayer.setFilters(QgsMapLayerProxyModel.RasterLayer)
self.mMapLayerComboBox_areaMaskLayer.setFilters(QgsMapLayerProxyModel.VectorLayer)

self.mGroupBox_8.setCollapsed(True) # collapse the group by default
self._set_processed_area_mask_options()
self._set_processing_overlap_enabled()

Expand Down Expand Up @@ -212,6 +214,8 @@ def _create_connections(self):
self.radioButton_processingTileOverlapPixels.toggled.connect(self._set_processing_overlap_enabled)

def _model_type_changed(self):
from deepness.processing.models.model_types import ModelType # import here to avoid pulling external dependencies to early

model_type = ModelType(self.comboBox_modelType.currentText())

segmentation_enabled = False
Expand Down Expand Up @@ -251,11 +255,11 @@ def _model_output_format_changed(self):
model_output_format = ModelOutputFormat(txt)
class_number_selection_enabled = bool(model_output_format == ModelOutputFormat.ONLY_SINGLE_CLASS_AS_LAYER)
self.comboBox_outputFormatClassNumber.setEnabled(class_number_selection_enabled)

def _set_processing_overlap_enabled(self):
overlap_percentage_enabled = self.radioButton_processingTileOverlapPercentage.isChecked()
self.spinBox_processingTileOverlapPercentage.setEnabled(overlap_percentage_enabled)

overlap_pixels_enabled = self.radioButton_processingTileOverlapPixels.isChecked()
self.spinBox_processingTileOverlapPixels.setEnabled(overlap_pixels_enabled)

Expand Down Expand Up @@ -284,15 +288,15 @@ def _browse_query_image_path(self):
)
if file_path:
self.lineEdit_recognitionPath.setText(file_path)

def _load_default_model_parameters(self):
"""
Load the default parameters from model metadata
"""
value = self._model.get_metadata_resolution()
if value is not None:
self.doubleSpinBox_resolution_cm_px.setValue(value)

value = self._model.get_model_batch_size()
if value is not None:
self.spinBox_batchSize.setValue(value)
Expand Down Expand Up @@ -339,15 +343,13 @@ def _load_default_model_parameters(self):
if value is not None:
self.doubleSpinBox_iouScore.setValue(value)

value = self._model.get_metadata_detection_remove_overlapping()
if value is not None:
self.checkBox_removeOverlappingDetections.setChecked(value)

def _load_model_with_type_from_metadata(self, model_class_from_ui, file_path):
"""
If model has model_type in metadata - use this type to create proper model class.
Otherwise model_class_from_ui will be used
"""
from deepness.processing.models.model_types import ModelDefinition, ModelType # import here to avoid pulling external dependencies to early

model_class = model_class_from_ui

model_type_str_from_metadata = ModelBase.get_model_type_from_metadata(file_path)
Expand All @@ -365,6 +367,9 @@ def _load_model_and_display_info(self, abort_if_no_file_path: bool = False):
"""
Tries to load the model and display its message.
"""
from deepness.processing.models.model_types import ModelType # import here to avoid pulling external dependencies to early
import deepness.processing.models.detector as detector_module # import here to avoid pulling external dependencies to early

file_path = self.lineEdit_modelPath.text()

if not file_path and abort_if_no_file_path:
Expand All @@ -387,13 +392,13 @@ def _load_model_and_display_info(self, abort_if_no_file_path: bool = False):
# TODO idk how variable input will be handled
self.spinBox_tileSize_px.setValue(input_size_px)
self.spinBox_tileSize_px.setEnabled(False)

if batch_size is not None:
self.spinBox_batchSize.setValue(batch_size)
self.spinBox_batchSize.setEnabled(False)
else:
self.spinBox_batchSize.setEnabled(True)

self._input_channels_mapping_widget.set_model(self._model)

# super resolution
Expand All @@ -418,7 +423,7 @@ def _load_model_and_display_info(self, abort_if_no_file_path: bool = False):

self.label_modelInfo.setText(txt)

if isinstance(self._model, Detector):
if isinstance(self._model, detector_module.Detector):
detector_type = DetectorType(self.comboBox_detectorType.currentText())
self._model.set_model_type_param(detector_type)

Expand Down Expand Up @@ -470,10 +475,12 @@ def _get_pixel_classification_threshold(self):
return 0
return self.doubleSpinBox_probabilityThreshold.value()

def get_selected_model_class_definition(self) -> ModelDefinition:
def get_selected_model_class_definition(self): # -> ModelDefinition: # we cannot import it here yet
"""
Get the currently selected model class (in UI)
"""
from deepness.processing.models.model_types import ModelDefinition, ModelType # import here to avoid pulling external dependencies to early

model_type_txt = self.comboBox_modelType.currentText()
model_type = ModelType(model_type_txt)
model_definition = ModelDefinition.get_definition_for_type(model_type)
Expand All @@ -483,6 +490,8 @@ def get_inference_parameters(self) -> MapProcessingParameters:
""" Get the parameters for the model interface.
The returned type is derived from `MapProcessingParameters` class, depending on the selected model type.
"""
from deepness.processing.models.model_types import ModelType # import here to avoid pulling external dependencies to early

map_processing_parameters = self._get_map_processing_parameters()

if self._model is None:
Expand Down Expand Up @@ -541,14 +550,13 @@ def get_recognition_parameters(self, map_processing_parameters: MapProcessingPar
query_image_path=self.lineEdit_recognitionPath.text(),
)
return params

def get_detection_parameters(self, map_processing_parameters: MapProcessingParameters) -> DetectionParameters:

params = DetectionParameters(
**map_processing_parameters.__dict__,
confidence=self.doubleSpinBox_confidence.value(),
iou_threshold=self.doubleSpinBox_iouScore.value(),
remove_overlapping_detections=self.checkBox_removeOverlappingDetections.isChecked(),
model=self._model,
detector_type=DetectorType(self.comboBox_detectorType.currentText()),
)
Expand Down
Loading

0 comments on commit b397127

Please sign in to comment.