diff --git a/plugins/basic_change/main.py b/plugins/basic_change/main.py index 34f1728..5fec53c 100644 --- a/plugins/basic_change/main.py +++ b/plugins/basic_change/main.py @@ -5,19 +5,18 @@ from rscder.plugins.basic import BasicPlugin from PyQt5.QtWidgets import QAction, QDialog, QHBoxLayout, QVBoxLayout, QPushButton from PyQt5.QtCore import pyqtSignal from PyQt5.QtGui import QIcon -from rscder.utils.project import BasicLayer, Project, PairLayer, ResultPointLayer, RasterLayer +from rscder.utils.project import BasicLayer, Project, RasterLayer from rscder.gui.layercombox import PairLayerCombox -from osgeo import gdal, gdal_array +from osgeo import gdal from threading import Thread import numpy as np -from basic_change.otsu import OTSU class MyDialog(QDialog): def __init__(self, parent=None): super().__init__(parent) - self.setWindowTitle('BasicChange') + self.setWindowTitle('差分法') self.setWindowIcon(QIcon(":/icons/logo.png")) self.setFixedWidth(500) @@ -27,11 +26,11 @@ class MyDialog(QDialog): # self.number_input = QLineEdit(self) - self.ok_button = QPushButton('OK', self) + self.ok_button = QPushButton('确定', self) self.ok_button.setIcon(QIcon(":/icons/ok.svg")) self.ok_button.clicked.connect(self.on_ok) - self.cancel_button = QPushButton('Cancel', self) + self.cancel_button = QPushButton('取消', self) self.cancel_button.setIcon(QIcon(":/icons/cancel.svg")) self.cancel_button.clicked.connect(self.on_cancel) @@ -41,7 +40,7 @@ class MyDialog(QDialog): self.main_layout = QVBoxLayout() self.main_layout.addWidget(self.layer_select) - # self.main_layout.addWidget(self.number_input) + self.main_layout.addLayout(self.button_layout) self.setLayout(self.main_layout) @@ -82,10 +81,9 @@ class BasicMethod(BasicPlugin): self.message_send.connect(self.send_message) - self.gap = 250 - def setup(self): self.basic_diff_method.setEnabled(True) + self.otsu_method.setEnabled(True) def send_message(self, s): self.message_box.info(s) @@ -111,8 +109,6 @@ class BasicMethod(BasicPlugin): band = ds1.RasterCount yblocks = ysize // cell_size[1] - geo = ds1.GetGeoTransform() - driver = gdal.GetDriverByName('GTiff') out_tif = os.path.join(Project().cmi_path, 'temp.tif') out_ds = driver.Create(out_tif, xsize, ysize, 1, gdal.GDT_Float32) @@ -151,11 +147,11 @@ class BasicMethod(BasicPlugin): self.message_send.emit('归一化概率中...') temp_in_ds = gdal.Open(out_tif) - out_normal_tif = os.path.join(Project().cmi_path, '{}-{}.tif'.format(layer1.layer_parent.name, int(np.random.rand() * 100000))) + out_normal_tif = os.path.join(Project().cmi_path, '{}_{}_cmi.tif'.format(layer1.layer_parent.name, int(np.random.rand() * 100000))) out_normal_ds = driver.Create(out_normal_tif, xsize, ysize, 1, gdal.GDT_Byte) out_normal_ds.SetGeoTransform(ds1.GetGeoTransform()) out_normal_ds.SetProjection(ds1.GetProjection()) - hist = np.zeros(256, dtype=np.int32) + # hist = np.zeros(256, dtype=np.int32) for j in range(yblocks+1): block_xy = (0, j * cell_size[1]) if block_xy[1] > ysize: @@ -167,57 +163,21 @@ class BasicMethod(BasicPlugin): block_data = (block_data - min_diff) / (max_diff - min_diff) * 255 block_data = block_data.astype(np.uint8) out_normal_ds.GetRasterBand(1).WriteArray(block_data, *block_xy) - hist_t, _ = np.histogram(block_data, bins=256, range=(0, 256)) - hist += hist_t - print(hist) + # hist_t, _ = np.histogram(block_data, bins=256, range=(0, 256)) + # hist += hist_t + # print(hist) del temp_in_ds try: os.remove(out_tif) except: pass - self.gap = OTSU(hist) - self.message_send.emit('OTSU:' + str(self.gap)) - - out_normal_ds.FlushCache() - del out_normal_ds - self.message_send.emit('完成归一化概率') - - self.message_send.emit('计算变化表格中...') - out_csv = os.path.join(Project().bcdm_path, '{}-{}.csv'.format(layer1.layer_parent.name, int(np.random.rand() * 100000))) - xblocks = xsize // cell_size[0] - - normal_in_ds = gdal.Open(out_normal_tif) - - with open(out_csv, 'w') as f: - f.write('x,y,diff,status\n') - for j in range(yblocks): - block_xy = (0, j * cell_size[1]) - block_size = (xsize, cell_size[1]) - if block_xy[1] + block_size[1] > ysize: - block_size = (xsize, ysize - block_xy[1]) - block_data = normal_in_ds.ReadAsArray(*block_xy, *block_size) - for i in range(xblocks): - start_x = i * cell_size[0] - end_x = start_x + cell_size[0] - if end_x > xsize: - end_x = xsize - block_data_xy = block_data[:, start_x:end_x] - if block_data_xy.mean() > self.gap: - center_x = start_x + cell_size[0] // 2 - center_y = j * cell_size[1] + cell_size[1] // 2 - center_x = center_x * geo[1] + geo [0] - center_y = center_y * geo[5] + geo [3] - f.write(f'{center_x},{center_y},{block_data_xy.mean() / 255 * 100},1\n') - - - point_result_lalyer = ResultPointLayer(out_csv, enable=False, proj = layer1.proj, geo = layer1.geo) raster_result_layer = RasterLayer(None, True, out_normal_tif, BasicLayer.BOATH_VIEW) - layer1.layer_parent.add_result_layer(point_result_lalyer) + # layer1.layer_parent.add_result_layer(point_result_lalyer) layer1.layer_parent.add_result_layer(raster_result_layer) - self.message_send.emit('完成计算变化表格') + # self.message_send.emit('完成计算变化表格') self.message_send.emit('差分法计算完成') @@ -232,8 +192,6 @@ class BasicMethod(BasicPlugin): return # layer:PairLayer = list(self.project.layers.values())[0] - - t = Thread(target=self.run_basic_diff_alg, args=(layer1, layer2)) t.start() diff --git a/plugins/evaluation/__init__.py b/plugins/evaluation/__init__.py new file mode 100644 index 0000000..f4d7cff --- /dev/null +++ b/plugins/evaluation/__init__.py @@ -0,0 +1 @@ +from evaluation.main import * \ No newline at end of file diff --git a/plugins/evaluation/main.py b/plugins/evaluation/main.py new file mode 100644 index 0000000..4fddc25 --- /dev/null +++ b/plugins/evaluation/main.py @@ -0,0 +1,36 @@ +from rscder.plugins.basic import BasicPlugin +from PyQt5.QtWidgets import QAction, QDialog, QHBoxLayout, QVBoxLayout, QPushButton +from PyQt5.QtGui import QIcon +from PyQt5.QtCore import Qt + +class EvalutationDialog(QDialog): + + def __init__(self, parent=None): + super().__init__(parent) + + self.setWindowTitle('精度评估') + self.setWindowIcon(QIcon(":/icons/logo.png")) + + self.setFixedWidth(500) + + self.ok_button = QPushButton('OK', self) + self.ok_button.setIcon(QIcon(":/icons/ok.svg")) + self.ok_button.clicked.connect(self.on_ok) + + self.cancel_button = QPushButton('Cancel', self) + self.cancel_button.setIcon(QIcon(":/icons/cancel.svg")) + self.cancel_button.clicked.connect(self.on_cancel) + + self.button_layout = QHBoxLayout() + self.button_layout.addWidget(self.ok_button) + self.button_layout.addWidget(self.cancel_button) + + self.main_layout = QVBoxLayout() + self.main_layout.addLayout(self.button_layout) + self.setLayout(self.main_layout) + + def on_ok(self): + self.accept() + + def on_cancel(self): + self.reject() \ No newline at end of file diff --git a/plugins/table_result/__init__.py b/plugins/table_result/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins/table_result/main.py b/plugins/table_result/main.py new file mode 100644 index 0000000..612c4a9 --- /dev/null +++ b/plugins/table_result/main.py @@ -0,0 +1 @@ +from table_result.main import * diff --git a/plugins/threshold/__init__.py b/plugins/threshold/__init__.py new file mode 100644 index 0000000..9a18fd3 --- /dev/null +++ b/plugins/threshold/__init__.py @@ -0,0 +1 @@ +from threshold.main import * \ No newline at end of file diff --git a/plugins/threshold/main.py b/plugins/threshold/main.py new file mode 100644 index 0000000..847e62b --- /dev/null +++ b/plugins/threshold/main.py @@ -0,0 +1,157 @@ +import os +import numpy as np +from rscder.gui.actions import ActionManager +from rscder.plugins.basic import BasicPlugin +from PyQt5.QtWidgets import QAction, QDialog, QHBoxLayout, QVBoxLayout, QPushButton +from PyQt5.QtGui import QIcon +from PyQt5.QtCore import Qt +from rscder.gui.layercombox import RasterLayerCombox +from rscder.utils.project import Project, RasterLayer +from threshold.otsu import OTSU +class OTSUDialog(QDialog): + + def __init__(self, parent=None): + super().__init__(parent) + + self.setWindowTitle('OTSU阈值') + self.setWindowIcon(QIcon(":/icons/logo.png")) + + self.setFixedWidth(500) + + self.layercombox = RasterLayerCombox(self) + + + + self.ok_button = QPushButton('确定', self) + self.ok_button.setIcon(QIcon(":/icons/ok.svg")) + self.ok_button.clicked.connect(self.on_ok) + + self.cancel_button = QPushButton('取消', self) + self.cancel_button.setIcon(QIcon(":/icons/cancel.svg")) + self.cancel_button.clicked.connect(self.on_cancel) + + self.button_layout = QHBoxLayout() + self.button_layout.addWidget(self.ok_button) + self.button_layout.addWidget(self.cancel_button) + + self.main_layout = QVBoxLayout() + self.main_layout.addWidget(self.layercombox) + self.main_layout.addLayout(self.button_layout) + self.setLayout(self.main_layout) + + def on_ok(self): + self.accept() + + def on_cancel(self): + self.reject() + + +class OTSUPlugin(BasicPlugin): + + @staticmethod + def info(): + return { + "name": "OTSU", + "description": "OTSU阈值", + "author": "rscder", + "version": "1.0.0" + } + + def set_action(self): + self.action = QAction('OTSU阈值', self.mainwindow) + self.action.triggered.connect(self.run) + ActionManager().postop_menu.addAction(self.action) + + def run(self): + dialog = OTSUDialog(self.mainwindow) + if dialog.exec_() == QDialog.Accepted: + pass + + def run_alg(self, layer:RasterLayer): + if layer is None or layer.path is None: + return + + ds = gdal.Open(layer.path) + band = ds.GetRasterBand(1) + if band > 1: + self.message_box.error('请选择符合要求的图层') + return + hist = np.zeros(256, dtype=np.int) + xsize = ds.RasterXSize + ysize = ds.RasterYSize + + max_pixels = 1e7 + max_rows = max_pixels // xsize + if max_rows < 1: + max_rows = 1 + if max_rows > ysize: + max_rows = ysize + block_count = ysize // max_rows + 1 + for i in range(block_count): + start_row = i * max_rows + end_row = min((i + 1) * max_rows, ysize) + block = band.ReadAsArray(0, start_row, xsize, end_row - start_row) + hist += np.histogram(block.flatten(), bins=256, range=(0, 255))[0] + hist = hist.astype(np.float32) + gap = OTSU(hist) + self.message_box.info('阈值为:{}'.format(gap)) + + out_th = os.path.join(Project().bcdm_path, '{}_otsu_bcdm.tif'.format(layer.name)) + out_ds = gdal.GetDriverByName('GTiff').Create(out_th, xsize, ysize, 1, gdal.GDT_Byte) + out_ds.SetGeoTransform(ds.GetGeoTransform()) + out_ds.SetProjection(ds.GetProjection()) + out_band = out_ds.GetRasterBand(1) + + for i in range(block_count): + start_row = i * max_rows + end_row = min((i + 1) * max_rows, ysize) + block = band.ReadAsArray(0, start_row, xsize, end_row - start_row) + out_band.WriteArray(block > gap, 0, start_row) + out_band.FlushCache() + out_ds = None + ds = None + self.message_box.info('OTSU阈值完成') + + otsu_layer = RasterLayer(path = out_th, style_info={}) + + +# otsu_method = QAction('OTSU阈值分割') +# postop_menu = self.ctx['postop_menu'] +# postop_menu.addAction(otsu_method) +# otsu_method.setEnabled(False) +# point_result_lalyer = ResultPointLayer(out_csv, enable=False, proj = layer1.proj, geo = layer1.geo) +# self.gap = OTSU(hist) + +# self.message_send.emit('OTSU:' + str(self.gap)) + +# out_normal_ds.FlushCache() +# del out_normal_ds +# self.message_send.emit('完成归一化概率') + +# self.message_send.emit('计算变化表格中...') +# out_csv = os.path.join(Project().bcdm_path, '{}-{}.csv'.format(layer1.layer_parent.name, int(np.random.rand() * 100000))) +# xblocks = xsize // cell_size[0] + +# normal_in_ds = gdal.Open(out_normal_tif) + +# with open(out_csv, 'w') as f: +# f.write('x,y,diff,status\n') +# for j in range(yblocks): +# block_xy = (0, j * cell_size[1]) +# block_size = (xsize, cell_size[1]) +# if block_xy[1] + block_size[1] > ysize: +# block_size = (xsize, ysize - block_xy[1]) +# block_data = normal_in_ds.ReadAsArray(*block_xy, *block_size) +# for i in range(xblocks): +# start_x = i * cell_size[0] +# end_x = start_x + cell_size[0] +# if end_x > xsize: +# end_x = xsize +# block_data_xy = block_data[:, start_x:end_x] +# if block_data_xy.mean() > self.gap: +# center_x = start_x + cell_size[0] // 2 +# center_y = j * cell_size[1] + cell_size[1] // 2 +# center_x = center_x * geo[1] + geo [0] +# center_y = center_y * geo[5] + geo [3] +# f.write(f'{center_x},{center_y},{block_data_xy.mean() / 255 * 100},1\n') + \ No newline at end of file diff --git a/plugins/basic_change/otsu.py b/plugins/threshold/otsu.py similarity index 100% rename from plugins/basic_change/otsu.py rename to plugins/threshold/otsu.py diff --git a/rscder/gui/actions.py b/rscder/gui/actions.py index 5e15801..8702a53 100644 --- a/rscder/gui/actions.py +++ b/rscder/gui/actions.py @@ -57,8 +57,6 @@ class ActionManager(QtCore.QObject): self.plugin_menu = menubar.addMenu('&插件') self.help_menu = menubar.addMenu( '&帮助') - - @property def menus(self): return { @@ -216,8 +214,7 @@ class ActionManager(QtCore.QObject): Project().clear() parent = str(Path(project_file[0]).parent) Settings.General().last_path = parent - name = os.path.basename(project_file[0]) - Project().setup(parent, name) + Project().setup(project_file[0]) def project_save(self): if Project().is_init: @@ -230,12 +227,7 @@ class ActionManager(QtCore.QObject): if(file_loader.exec_()): Project().add_layer(file_loader.path1,file_loader.path2,file_loader.style1,file_loader.style2) self.message_box.info('Data loaded') - # file_open = QFileDialog.getOpenFileNames(self.w_parent, '打开数据', Settings.General().last_path, '*.*') - # if file_open[0] != '': - # if len(file_open[0]) != 2: - # self.message_box.warning('请选择两个数据文件') - # return - # Project().add_layer(file_open[0][0], file_open[0][1]) + def view_setting(self): diff --git a/rscder/gui/load.py b/rscder/gui/load.py index 6033dd2..256f849 100644 --- a/rscder/gui/load.py +++ b/rscder/gui/load.py @@ -11,7 +11,7 @@ from rscder.utils.project import RasterLayer class loader(QDialog): def __init__(self, parent=None) -> None: super().__init__(parent) - self.setWindowTitle('Load Data') + self.setWindowTitle('载入数据') self.setWindowIcon(QIcon(":/icons/data_load.png")) self.path1='' self.path2='' diff --git a/rscder/utils/project.py b/rscder/utils/project.py index afa015c..1b8399b 100644 --- a/rscder/utils/project.py +++ b/rscder/utils/project.py @@ -12,7 +12,13 @@ import numpy as np from osgeo import gdal, gdal_array from rscder.utils.icons import IconInstance from rscder.utils.setting import Settings -from qgis.core import QgsRasterLayer, QgsMarkerSymbol, QgsUnitTypes, QgsCategorizedSymbolRenderer, QgsRendererCategory, QgsPalLayerSettings, QgsRuleBasedLabeling, QgsTextFormat, QgsLineSymbol, QgsSingleSymbolRenderer, QgsSimpleLineSymbolLayer, QgsVectorLayer, QgsCoordinateReferenceSystem, QgsFeature, QgsGeometry, QgsPointXY,QgsMultiBandColorRenderer +from qgis.core import (\ + QgsRasterLayer, QgsMarkerSymbol, QgsUnitTypes, + QgsCategorizedSymbolRenderer, QgsRendererCategory, + QgsPalLayerSettings, QgsRuleBasedLabeling, QgsTextFormat, + QgsLineSymbol, QgsSingleSymbolRenderer, QgsSimpleLineSymbolLayer, + QgsVectorLayer, QgsCoordinateReferenceSystem, QgsFeature, + QgsGeometry, QgsPointXY, QgsMultiBandColorRenderer) from PyQt5.QtCore import QObject, pyqtSignal, Qt, QThread from PyQt5.QtWidgets import QTreeWidgetItem, QAction from PyQt5.QtGui import QColor, QIcon, QFont @@ -375,6 +381,8 @@ class GridLayer(BasicLayer): self.set_render() + + class RasterLayer(BasicLayer): def __init__(self, name=None, enable=False, path=None, view_mode=BasicLayer.BOATH_VIEW,style_info={'r':3,'g':2,'b':1,'NIR':3}): @@ -430,6 +438,21 @@ class RasterLayer(BasicLayer): def set_stlye(self,style_info): self.style_info=style_info self.apply_style() + + +class BinnaryRasterLayer(RasterLayer): + def __init__(self, path, enable=True, name='栅格'): + super().__init__(name, enable, icon='') + self.path = path + self.layer = QgsRasterLayer(path, name) + self.set_render() + + def set_render(self): + renderer = QgsSingleBandGrayRenderer(self.layer.dataProvider(), 1) + self.layer.setRenderer(renderer) + self.layer.triggerRepaint() + + class VectorLayer(BasicLayer): pass