class ParamSlider(QWidget): valueChanged = Signal(int) def __init__(self, interval, ticks=10, reset=0, suffix=None, label=None, bold=False, special=None, parent=None): super(ParamSlider, self).__init__(parent) self.slider = QSlider(Qt.Horizontal) self.slider.setRange(interval[0], interval[1]) self.slider.setTickPosition(QSlider.TicksBelow) self.slider.setTickInterval((interval[1] - interval[0] + 1) / ticks) self.slider.setSingleStep(1) self.slider.setPageStep(1) self.slider.setValue(reset) self.slider.mouseDoubleClickEvent = self.doubleClicked self.spin = QSpinBox() self.spin.setRange(interval[0], interval[1]) self.spin.setValue(reset) self.spin.setSuffix(suffix) self.spin.setFixedWidth(55) self.spin.setSpecialValueText(special) self.reset = reset self.slider.valueChanged.connect(self.spin.setValue) self.spin.valueChanged.connect(self.slider.setValue) self.slider.valueChanged.connect(self.valueChanged) layout = QHBoxLayout() if label is not None: lab = QLabel(label) modify_font(lab, bold=bold) layout.addWidget(lab) layout.addWidget(self.slider) layout.addWidget(self.spin) self.setLayout(layout) def doubleClicked(self, _): self.reset_value() def reset_value(self): self.slider.setValue(self.reset) self.spin.setValue(self.reset) def value(self): return self.slider.value() def setValue(self, value): self.spin.setValue(value) self.slider.setValue(value) self.valueChanged.emit(value) def sync(self): self.spin.setValue(self.slider.value()) self.slider.setValue(self.spin.value()) self.valueChanged.emit(self.slider.value())
class FrequencyWidget(ToolWidget): def __init__(self, image, parent=None): super(FrequencyWidget, self).__init__(parent) self.split_spin = QSpinBox() self.split_spin.setRange(0, 100) self.split_spin.setValue(15) self.split_spin.setSuffix(self.tr(" %")) self.smooth_spin = QSpinBox() self.smooth_spin.setRange(0, 100) self.smooth_spin.setValue(25) self.smooth_spin.setSuffix(self.tr(" %")) self.smooth_spin.setSpecialValueText(self.tr("Off")) self.thr_spin = QSpinBox() self.thr_spin.setRange(0, 100) self.thr_spin.setValue(0) self.thr_spin.setSuffix(self.tr(" %")) self.thr_spin.setSpecialValueText(self.tr("Off")) self.zero_label = QLabel() modify_font(self.zero_label, italic=True) self.filter_spin = QSpinBox() self.filter_spin.setRange(0, 15) self.filter_spin.setValue(0) self.filter_spin.setSuffix(self.tr(" px")) self.filter_spin.setSpecialValueText(self.tr("Off")) self.split_spin.valueChanged.connect(self.process) self.smooth_spin.valueChanged.connect(self.process) self.thr_spin.valueChanged.connect(self.process) self.filter_spin.valueChanged.connect(self.postprocess) self.image = image gray = cv.cvtColor(self.image, cv.COLOR_BGR2GRAY) rows, cols = gray.shape height = cv.getOptimalDFTSize(rows) width = cv.getOptimalDFTSize(cols) padded = cv.copyMakeBorder(gray, 0, height - rows, 0, width - cols, cv.BORDER_CONSTANT) self.dft = np.fft.fftshift(cv.dft(padded.astype(np.float32), flags=cv.DFT_COMPLEX_OUTPUT)) self.magnitude0, self.phase0 = cv.cartToPolar(self.dft[:, :, 0], self.dft[:, :, 1]) self.magnitude0 = cv.normalize(cv.log(self.magnitude0), None, 0, 255, cv.NORM_MINMAX) self.phase0 = cv.normalize(self.phase0, None, 0, 255, cv.NORM_MINMAX) self.magnitude = self.phase = None self.low_viewer = ImageViewer(self.image, self.image, self.tr("Low frequency"), export=True) self.high_viewer = ImageViewer(self.image, self.image, self.tr("High frequency"), export=True) self.mag_viewer = ImageViewer(self.image, None, self.tr("DFT Magnitude"), export=True) self.phase_viewer = ImageViewer(self.image, None, self.tr("DFT Phase"), export=True) self.process() self.low_viewer.viewChanged.connect(self.high_viewer.changeView) self.high_viewer.viewChanged.connect(self.low_viewer.changeView) self.mag_viewer.viewChanged.connect(self.phase_viewer.changeView) self.phase_viewer.viewChanged.connect(self.mag_viewer.changeView) top_layout = QHBoxLayout() top_layout.addWidget(QLabel(self.tr("Separation:"))) top_layout.addWidget(self.split_spin) top_layout.addWidget(QLabel(self.tr("Smooth:"))) top_layout.addWidget(self.smooth_spin) top_layout.addWidget(QLabel(self.tr("Threshold:"))) top_layout.addWidget(self.thr_spin) top_layout.addWidget(QLabel(self.tr("Filter:"))) top_layout.addWidget(self.filter_spin) top_layout.addWidget(self.zero_label) top_layout.addStretch() center_layout = QGridLayout() center_layout.addWidget(self.low_viewer, 0, 0) center_layout.addWidget(self.high_viewer, 0, 1) center_layout.addWidget(self.mag_viewer, 1, 0) center_layout.addWidget(self.phase_viewer, 1, 1) main_layout = QVBoxLayout() main_layout.addLayout(top_layout) main_layout.addLayout(center_layout) self.setLayout(main_layout) def process(self): start = time() rows, cols, _ = self.dft.shape mask = np.zeros((rows, cols), np.float32) half = np.sqrt(rows ** 2 + cols ** 2) / 2 radius = int(half * self.split_spin.value() / 100) mask = cv.circle(mask, (cols // 2, rows // 2), radius, 1, cv.FILLED) kernel = 2 * int(half * self.smooth_spin.value() / 100) + 1 mask = cv.GaussianBlur(mask, (kernel, kernel), 0) mask /= np.max(mask) threshold = int(self.thr_spin.value() / 100 * 255) if threshold > 0: mask[self.magnitude0 < threshold] = 0 zeros = (mask.size - np.count_nonzero(mask)) / mask.size * 100 else: zeros = 0 self.zero_label.setText(self.tr("(zeroed coefficients = {:.2f}%)").format(zeros)) mask2 = np.repeat(mask[:, :, np.newaxis], 2, axis=2) rows0, cols0, _ = self.image.shape low = cv.idft(np.fft.ifftshift(self.dft * mask2), flags=cv.DFT_SCALE) low = norm_mat(cv.magnitude(low[:, :, 0], low[:, :, 1])[:rows0, :cols0], to_bgr=True) self.low_viewer.update_processed(low) high = cv.idft(np.fft.ifftshift(self.dft * (1 - mask2)), flags=cv.DFT_SCALE) high = norm_mat(cv.magnitude(high[:, :, 0], high[:, :, 1]), to_bgr=True) self.high_viewer.update_processed(np.copy(high[: self.image.shape[0], : self.image.shape[1]])) self.magnitude = (self.magnitude0 * mask).astype(np.uint8) self.phase = (self.phase0 * mask).astype(np.uint8) self.postprocess() self.info_message.emit(self.tr(f"Frequency Split = {elapsed_time(start)}")) def postprocess(self): kernel = 2 * self.filter_spin.value() + 1 if kernel >= 3: magnitude = cv.GaussianBlur(self.magnitude, (kernel, kernel), 0) phase = cv.GaussianBlur(self.phase, (kernel, kernel), 0) # phase = cv.medianBlur(self.phase, kernel) else: magnitude = self.magnitude phase = self.phase self.mag_viewer.update_original(cv.cvtColor(magnitude, cv.COLOR_GRAY2BGR)) self.phase_viewer.update_original(cv.cvtColor(phase, cv.COLOR_GRAY2BGR))
class FrequencyWidget(ToolWidget): def __init__(self, image, parent=None): super(FrequencyWidget, self).__init__(parent) self.ampl_radio = QRadioButton(self.tr('Amplitude')) self.ampl_radio.setChecked(True) self.phase_radio = QRadioButton(self.tr('Phase')) self.dct_radio = QRadioButton(self.tr('DCT Map')) self.last_radio = self.ampl_radio self.thr_spin = QSpinBox() self.thr_spin.setRange(0, 255) self.thr_spin.setSpecialValueText(self.tr('Off')) self.ratio_label = QLabel() self.filter_check = QCheckBox(self.tr('Filter')) self.ampl_radio.clicked.connect(self.process) self.phase_radio.clicked.connect(self.process) self.dct_radio.clicked.connect(self.process) self.thr_spin.valueChanged.connect(self.process) self.filter_check.stateChanged.connect(self.process) gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY) rows, cols = gray.shape height = cv.getOptimalDFTSize(rows) width = cv.getOptimalDFTSize(cols) padded = cv.copyMakeBorder(gray, 0, height - rows, 0, width - cols, cv.BORDER_CONSTANT).astype(np.float32) planes = cv.merge([padded, np.zeros_like(padded)]) dft = cv.split(np.fft.fftshift(cv.dft(planes))) mag, phase = cv.cartToPolar(dft[0], dft[1]) dct = cv.dct(padded) self.result = [ normalize_mat(img) for img in [cv.log(mag), phase, cv.log(dct)] ] self.image = image self.viewer = ImageViewer(self.image, None) self.process() top_layout = QHBoxLayout() top_layout.addWidget(QLabel(self.tr('Coefficients:'))) top_layout.addWidget(self.ampl_radio) top_layout.addWidget(self.phase_radio) top_layout.addWidget(self.dct_radio) top_layout.addWidget(self.filter_check) top_layout.addStretch() top_layout.addWidget(QLabel(self.tr('Threshold:'))) top_layout.addWidget(self.thr_spin) top_layout.addWidget(self.ratio_label) main_layout = QVBoxLayout() main_layout.addLayout(top_layout) main_layout.addWidget(self.viewer) self.setLayout(main_layout) def process(self): if self.ampl_radio.isChecked(): output = self.result[0] self.last_radio = self.ampl_radio elif self.phase_radio.isChecked(): output = self.result[1] self.last_radio = self.phase_radio elif self.dct_radio.isChecked(): output = self.result[2] self.last_radio = self.dct_radio else: self.last_radio.setChecked(True) return if self.filter_check.isChecked(): output = cv.medianBlur(output, 3) thr = self.thr_spin.value() if thr > 0: _, output = cv.threshold(output, thr, 0, cv.THRESH_TOZERO) zeros = (1.0 - cv.countNonZero(output) / output.size) * 100 else: zeros = 0 self.ratio_label.setText('(masked = {:.1f}%)'.format(zeros)) self.viewer.update_original(cv.cvtColor(output, cv.COLOR_GRAY2BGR))
class MinMaxWidget(ToolWidget): def __init__(self, image, parent=None): super(MinMaxWidget, self).__init__(parent) self.chan_combo = QComboBox() self.chan_combo.addItems([ self.tr('Luminance'), self.tr('Red'), self.tr('Green'), self.tr('Blue'), self.tr('RGB Norm') ]) colors = [ self.tr('Red'), self.tr('Green'), self.tr('Blue'), self.tr('White'), self.tr('Black') ] self.min_combo = QComboBox() self.min_combo.addItems(colors) self.min_combo.setCurrentIndex(1) self.max_combo = QComboBox() self.max_combo.addItems(colors) self.max_combo.setCurrentIndex(0) self.filter_spin = QSpinBox() self.filter_spin.setRange(0, 5) self.filter_spin.setSpecialValueText(self.tr('off')) self.image = image self.viewer = ImageViewer(self.image, self.image) self.low = self.high = None self.preprocess() self.chan_combo.currentIndexChanged.connect(self.preprocess) self.min_combo.currentIndexChanged.connect(self.process) self.max_combo.currentIndexChanged.connect(self.process) self.filter_spin.valueChanged.connect(self.process) top_layout = QHBoxLayout() top_layout.addWidget(QLabel(self.tr('Channel:'))) top_layout.addWidget(self.chan_combo) top_layout.addWidget(QLabel(self.tr('Minimum:'))) top_layout.addWidget(self.min_combo) top_layout.addWidget(QLabel(self.tr('Maximum:'))) top_layout.addWidget(self.max_combo) top_layout.addWidget(QLabel(self.tr('Filter:'))) top_layout.addWidget(self.filter_spin) top_layout.addStretch() main_layout = QVBoxLayout() main_layout.addLayout(top_layout) main_layout.addWidget(self.viewer) self.setLayout(main_layout) @staticmethod def minmax_dev(patch, mask): c = patch[1, 1] minimum, maximum, _, _ = cv.minMaxLoc(patch, mask) if c < minimum: return -1 if c > maximum: return +1 return 0 @staticmethod def blk_filter(img, radius): result = np.zeros_like(img, np.float32) rows, cols = result.shape block = 2 * radius + 1 for i in range(radius, rows, block): for j in range(radius, cols, block): result[i - radius:i + radius + 1, j - radius:j + radius + 1] = np.std(img[i - radius:i + radius + 1, j - radius:j + radius + 1]) return cv.normalize(result, None, 0, 127, cv.NORM_MINMAX, cv.CV_8UC1) def preprocess(self): start = time() channel = self.chan_combo.currentIndex() if channel == 0: img = cv.cvtColor(self.image, cv.COLOR_BGR2GRAY) elif channel == 4: b, g, r = cv.split(self.image.astype(np.float64)) img = cv.sqrt(cv.pow(b, 2) + cv.pow(g, 2) + cv.pow(r, 2)) else: img = self.image[:, :, 3 - channel] kernel = 3 border = kernel // 2 shape = (img.shape[0] - kernel + 1, img.shape[1] - kernel + 1, kernel, kernel) strides = 2 * img.strides patches = np.lib.stride_tricks.as_strided(img, shape=shape, strides=strides) patches = patches.reshape((-1, kernel, kernel)) mask = np.full((kernel, kernel), 255, dtype=np.uint8) mask[border, border] = 0 output = np.array([self.minmax_dev(patch, mask) for patch in patches]).reshape(shape[:-2]) output = cv.copyMakeBorder(output, border, border, border, border, cv.BORDER_CONSTANT) self.low = output == -1 self.high = output == +1 self.process() self.info_message.emit( self.tr('Min/Max Deviation = {}'.format(elapsed_time(start)))) def process(self): minmax = np.zeros_like(self.image) minimum = self.min_combo.currentIndex() maximum = self.max_combo.currentIndex() radius = self.filter_spin.value() if radius > 0: start = time() radius += 2 low = self.blk_filter(self.low, radius) high = self.blk_filter(self.high, radius) if minimum <= 2: minmax[:, :, 2 - minimum] = low else: minmax = low if maximum <= 2: minmax[:, :, 2 - maximum] += high else: # FIXME: minmax ha un solo canale quando si sceglie black come colore minmax += high minmax = normalize_mat(minmax) self.info_message.emit( self.tr('Min/Max Filter = {}'.format(elapsed_time(start)))) else: if minimum == 0: minmax[self.low] = [0, 0, 255] elif minimum == 1: minmax[self.low] = [0, 255, 0] elif minimum == 2: minmax[self.low] = [255, 0, 0] elif minimum == 3: minmax[self.low] = [255, 255, 255] if maximum == 0: minmax[self.high] = [0, 0, 255] elif maximum == 1: minmax[self.high] = [0, 255, 0] elif maximum == 2: minmax[self.high] = [255, 0, 0] elif maximum == 3: minmax[self.high] = [255, 255, 255] self.viewer.update_processed(minmax)
class MinMaxWidget(ToolWidget): def __init__(self, image, parent=None): super(MinMaxWidget, self).__init__(parent) self.chan_combo = QComboBox() self.chan_combo.addItems( [self.tr("Luminance"), self.tr("Red"), self.tr("Green"), self.tr("Blue"), self.tr("RGB Norm")] ) colors = [self.tr("Red"), self.tr("Green"), self.tr("Blue"), self.tr("White"), self.tr("Black")] self.process_button = QPushButton(self.tr("Process")) self.min_combo = QComboBox() self.min_combo.addItems(colors) self.min_combo.setCurrentIndex(1) self.max_combo = QComboBox() self.max_combo.addItems(colors) self.max_combo.setCurrentIndex(0) self.filter_spin = QSpinBox() self.filter_spin.setRange(0, 5) self.filter_spin.setSpecialValueText(self.tr("Off")) self.image = image self.viewer = ImageViewer(self.image, self.image) self.low = self.high = None self.stopped = False self.change() self.process_button.clicked.connect(self.preprocess) self.chan_combo.currentIndexChanged.connect(self.change) self.min_combo.currentIndexChanged.connect(self.process) self.max_combo.currentIndexChanged.connect(self.process) self.filter_spin.valueChanged.connect(self.process) top_layout = QHBoxLayout() top_layout.addWidget(QLabel(self.tr("Channel:"))) top_layout.addWidget(self.chan_combo) top_layout.addWidget(self.process_button) top_layout.addWidget(QLabel(self.tr("Minimum:"))) top_layout.addWidget(self.min_combo) top_layout.addWidget(QLabel(self.tr("Maximum:"))) top_layout.addWidget(self.max_combo) top_layout.addWidget(QLabel(self.tr("Filter:"))) top_layout.addWidget(self.filter_spin) top_layout.addStretch() main_layout = QVBoxLayout() main_layout.addLayout(top_layout) main_layout.addWidget(self.viewer) self.setLayout(main_layout) @staticmethod def minmax_dev(patch, mask): c = patch[1, 1] minimum, maximum, _, _ = cv.minMaxLoc(patch, mask) if c < minimum: return -1 if c > maximum: return +1 return 0 @staticmethod def blk_filter(img, radius): result = np.zeros_like(img, np.float32) rows, cols = result.shape block = 2 * radius + 1 for i in range(radius, rows, block): for j in range(radius, cols, block): result[i - radius : i + radius + 1, j - radius : j + radius + 1] = np.std( img[i - radius : i + radius + 1, j - radius : j + radius + 1] ) return cv.normalize(result, None, 0, 127, cv.NORM_MINMAX, cv.CV_8UC1) def change(self): self.min_combo.setEnabled(False) self.max_combo.setEnabled(False) self.filter_spin.setEnabled(False) self.process_button.setEnabled(True) self.viewer.update_processed(self.image) def preprocess(self): start = time() channel = self.chan_combo.currentIndex() if channel == 0: img = cv.cvtColor(self.image, cv.COLOR_BGR2GRAY) elif channel == 4: b, g, r = cv.split(self.image.astype(np.float64)) img = cv.sqrt(cv.pow(b, 2) + cv.pow(g, 2) + cv.pow(r, 2)) else: img = self.image[:, :, 3 - channel] kernel = 3 border = kernel // 2 shape = (img.shape[0] - kernel + 1, img.shape[1] - kernel + 1, kernel, kernel) strides = 2 * img.strides patches = np.lib.stride_tricks.as_strided(img, shape=shape, strides=strides) patches = patches.reshape((-1, kernel, kernel)) mask = np.full((kernel, kernel), 255, dtype=np.uint8) mask[border, border] = 0 progress = QProgressDialog( self.tr("Computing deviation..."), self.tr("Cancel"), 0, shape[0] * shape[1] - 1, self ) progress.canceled.connect(self.cancel) progress.setWindowModality(Qt.WindowModal) blocks = [0] * shape[0] * shape[1] for i, patch in enumerate(patches): blocks[i] = self.minmax_dev(patch, mask) progress.setValue(i) if self.stopped: self.stopped = False return output = np.array(blocks).reshape(shape[:-2]) output = cv.copyMakeBorder(output, border, border, border, border, cv.BORDER_CONSTANT) self.low = output == -1 self.high = output == +1 self.min_combo.setEnabled(True) self.max_combo.setEnabled(True) self.filter_spin.setEnabled(True) self.process_button.setEnabled(False) self.process() self.info_message.emit(self.tr(f"Min/Max Deviation = {elapsed_time(start)}")) def cancel(self): self.stopped = True def process(self): minmax = np.zeros_like(self.image) minimum = self.min_combo.currentIndex() maximum = self.max_combo.currentIndex() radius = self.filter_spin.value() if radius > 0: start = time() radius += 3 if minimum < 4: low = self.blk_filter(self.low, radius) if minimum <= 2: minmax[:, :, 2 - minimum] = low else: minmax = np.repeat(low[:, :, np.newaxis], 3, axis=2) if maximum < 4: high = self.blk_filter(self.high, radius) if maximum <= 2: minmax[:, :, 2 - maximum] += high else: minmax += np.repeat(high[:, :, np.newaxis], 3, axis=2) minmax = norm_mat(minmax) self.info_message.emit(self.tr(f"Min/Max Filter = {elapsed_time(start)}")) else: if minimum == 0: minmax[self.low] = [0, 0, 255] elif minimum == 1: minmax[self.low] = [0, 255, 0] elif minimum == 2: minmax[self.low] = [255, 0, 0] elif minimum == 3: minmax[self.low] = [255, 255, 255] if maximum == 0: minmax[self.high] = [0, 0, 255] elif maximum == 1: minmax[self.high] = [0, 255, 0] elif maximum == 2: minmax[self.high] = [255, 0, 0] elif maximum == 3: minmax[self.high] = [255, 255, 255] self.viewer.update_processed(minmax)
class NoiseWidget(ToolWidget): def __init__(self, image, parent=None): super(NoiseWidget, self).__init__(parent) self.mode_combo = QComboBox() self.mode_combo.addItems([ self.tr('Median'), self.tr('Gaussian'), self.tr('BoxBlur'), self.tr('Bilateral'), self.tr('NonLocal') ]) self.radius_spin = QSpinBox() self.radius_spin.setRange(1, 10) self.radius_spin.setSuffix(self.tr(' px')) self.radius_spin.setValue(1) self.sigma_spin = QSpinBox() self.sigma_spin.setRange(1, 200) self.sigma_spin.setValue(3) self.levels_spin = QSpinBox() self.levels_spin.setRange(0, 255) self.levels_spin.setSpecialValueText(self.tr('Equalized')) self.levels_spin.setValue(32) self.gray_check = QCheckBox(self.tr('Grayscale')) self.denoised_check = QCheckBox(self.tr('Denoised')) self.image = image self.viewer = ImageViewer(self.image, self.image) self.process() params_layout = QHBoxLayout() params_layout.addWidget(QLabel(self.tr('Mode:'))) params_layout.addWidget(self.mode_combo) params_layout.addWidget(QLabel(self.tr('Radius:'))) params_layout.addWidget(self.radius_spin) params_layout.addWidget(QLabel(self.tr('Sigma:'))) params_layout.addWidget(self.sigma_spin) params_layout.addWidget(QLabel(self.tr('Levels:'))) params_layout.addWidget(self.levels_spin) params_layout.addWidget(self.gray_check) params_layout.addWidget(self.denoised_check) params_layout.addStretch() main_layout = QVBoxLayout() main_layout.addLayout(params_layout) main_layout.addWidget(self.viewer) self.setLayout(main_layout) self.mode_combo.currentTextChanged.connect(self.process) self.radius_spin.valueChanged.connect(self.process) self.sigma_spin.valueChanged.connect(self.process) self.levels_spin.valueChanged.connect(self.process) self.gray_check.stateChanged.connect(self.process) self.denoised_check.stateChanged.connect(self.process) def process(self): start = time() grayscale = self.gray_check.isChecked() if grayscale: original = cv.cvtColor(self.image, cv.COLOR_BGR2GRAY) else: original = self.image radius = self.radius_spin.value() kernel = radius * 2 + 1 sigma = self.sigma_spin.value() choice = self.mode_combo.currentText() if choice == self.tr('Median'): self.sigma_spin.setEnabled(False) denoised = cv.medianBlur(original, kernel) elif choice == self.tr('Gaussian'): self.sigma_spin.setEnabled(False) denoised = cv.GaussianBlur(original, (kernel, kernel), 0) elif choice == self.tr('BoxBlur'): self.sigma_spin.setEnabled(False) denoised = cv.blur(original, (kernel, kernel)) elif choice == self.tr('Bilateral'): self.sigma_spin.setEnabled(True) denoised = cv.bilateralFilter(original, kernel, sigma, sigma) elif choice == self.tr('NonLocal'): if grayscale: denoised = cv.fastNlMeansDenoising(original, None, kernel) else: denoised = cv.fastNlMeansDenoisingColored( original, None, kernel, kernel) else: denoised = None if self.denoised_check.isChecked(): self.levels_spin.setEnabled(False) result = denoised else: self.levels_spin.setEnabled(True) noise = cv.absdiff(original, denoised) levels = self.levels_spin.value() if levels == 0: if grayscale: result = cv.equalizeHist(noise) else: result = equalize_img(noise) else: result = cv.LUT(noise, create_lut(0, 255 - levels)) if grayscale: result = cv.cvtColor(result, cv.COLOR_GRAY2BGR) self.viewer.update_processed(result) self.info_message.emit( self.tr('Noise estimation = {}'.format(elapsed_time(start))))
class PlotsWidget(ToolWidget): def __init__(self, image, parent=None): super(PlotsWidget, self).__init__(parent) choices = ['Red', 'Green', 'Blue', 'Hue', 'Saturation', 'Value'] self.xaxis_combo = QComboBox() self.xaxis_combo.addItems(choices) self.xaxis_combo.setCurrentIndex(3) self.yaxis_combo = QComboBox() self.yaxis_combo.addItems(choices) self.yaxis_combo.setCurrentIndex(4) self.sampling_spin = QSpinBox() levels = int(np.log2(min(image.shape[:-1]))) self.sampling_spin.setRange(0, levels) self.sampling_spin.setSpecialValueText(self.tr('Off')) self.sampling_spin.setValue(1) self.sampling_spin.setSuffix(self.tr(' level(s)')) self.size_spin = QSpinBox() self.size_spin.setRange(1, 10) self.size_spin.setValue(2) self.size_spin.setSuffix(self.tr(' pt')) self.style_combo = QComboBox() self.markers = [ ',', '.', 'o', '8', 's', 'p', 'P', '*', 'h', 'H', 'X', 'D' ] self.style_combo.addItems([ 'pixel', 'point', 'circle', 'octa', 'square', 'penta', 'plus', 'star', 'hexa1', 'hexa2', 'cross', 'diamond' ]) self.alpha_spin = QDoubleSpinBox() self.alpha_spin.setRange(0, 1) self.alpha_spin.setDecimals(2) self.alpha_spin.setSingleStep(0.05) self.alpha_spin.setValue(1) self.colors_check = QCheckBox(self.tr('Show colors')) self.grid_check = QCheckBox(self.tr('Show grid')) self.norm_check = QCheckBox(self.tr('Normalized')) self.total_label = QLabel() img = np.copy(image) self.colors = [None] * (levels + 1) for scale in range(levels + 1): rgb = cv.cvtColor(img.astype(np.float32) / 255, cv.COLOR_BGR2RGB) hsv = cv.cvtColor(rgb, cv.COLOR_RGB2HSV) hsv[:, :, 0] /= 360 shape = (img.shape[0] * img.shape[1], img.shape[2]) self.colors[scale] = np.concatenate( (np.reshape(rgb, shape), np.reshape(hsv, shape)), axis=1) img = cv.pyrDown(img) figure = Figure() plot_canvas = FigureCanvas(figure) self.axes = plot_canvas.figure.subplots() self.redraw() figure.set_tight_layout(True) self.xaxis_combo.currentIndexChanged.connect(self.redraw) self.yaxis_combo.currentIndexChanged.connect(self.redraw) self.sampling_spin.valueChanged.connect(self.redraw) self.size_spin.valueChanged.connect(self.redraw) self.style_combo.currentIndexChanged.connect(self.redraw) self.alpha_spin.valueChanged.connect(self.redraw) self.colors_check.stateChanged.connect(self.redraw) self.grid_check.stateChanged.connect(self.redraw) self.norm_check.stateChanged.connect(self.redraw) bottom_layout = QGridLayout() bottom_layout.addWidget(NavigationToolbar(plot_canvas, self), 0, 0, 1, 5) bottom_layout.addWidget(QLabel(self.tr('X axis:')), 1, 0) bottom_layout.addWidget(self.xaxis_combo, 1, 1) bottom_layout.addWidget(QLabel(self.tr('Y Axis:')), 2, 0) bottom_layout.addWidget(self.yaxis_combo, 2, 1) bottom_layout.addWidget(QLabel(self.tr('Subsampling:')), 3, 0) bottom_layout.addWidget(self.sampling_spin, 3, 1) bottom_layout.addWidget(QLabel(self.tr('Point size:')), 1, 2) bottom_layout.addWidget(self.size_spin, 1, 3) bottom_layout.addWidget(QLabel(self.tr('Point style:')), 2, 2) bottom_layout.addWidget(self.style_combo, 2, 3) bottom_layout.addWidget(QLabel(self.tr('Point alpha:')), 3, 2) bottom_layout.addWidget(self.alpha_spin, 3, 3) bottom_layout.addWidget(self.colors_check, 1, 4) bottom_layout.addWidget(self.grid_check, 2, 4) bottom_layout.addWidget(self.total_label, 3, 4) main_layout = QVBoxLayout() main_layout.addWidget(plot_canvas) main_layout.addLayout(bottom_layout) self.setLayout(main_layout) def redraw(self): start = time() v = self.sampling_spin.value() x = self.colors[v][:, self.xaxis_combo.currentIndex()] y = self.colors[v][:, self.yaxis_combo.currentIndex()] xlim = self.axes.get_xlim() ylim = self.axes.get_ylim() s = self.size_spin.value()**2 c = None if not self.colors_check.isChecked( ) else self.colors[v][:, :3] a = self.alpha_spin.value() m = self.markers[self.style_combo.currentIndex()] self.axes.clear() self.axes.set_facecolor([0.5] * 3 if c is not None else [1.0] * 3) self.axes.scatter(x, y, s, c, m, alpha=a) self.axes.set_xlabel(self.xaxis_combo.currentText()) self.axes.set_ylabel(self.yaxis_combo.currentText()) self.axes.grid(self.grid_check.isChecked(), which='both') self.axes.set_xlim(xlim) self.axes.set_ylim(ylim) self.axes.figure.canvas.draw() self.total_label.setText(self.tr('[{} points]'.format(len(x)))) self.info_message.emit('Plot redraw = {}'.format(elapsed_time(start)))
def _init_simgrs_tab(self, tab): # simgrs list simgrs_label = QLabel(self) simgrs_label.setText('Simulation Manager') simgrs_list = QComboBox(self) self._simgrs_list = simgrs_list simgrs_list.currentIndexChanged.connect(self._on_simgr_selection) pg_layout = QHBoxLayout() pg_layout.addWidget(simgrs_label) pg_layout.addWidget(simgrs_list) # simulation manager information viewer = QSimulationManagerViewer(self.simgr) self._simgr_viewer = viewer viewer.itemClicked.connect(self._on_item_clicked) def _jump_to_state_address(item, column): if not isinstance(item, StateTreeItem): return self.instance.workspace.jump_to(item.state.addr) viewer.itemDoubleClicked.connect(_jump_to_state_address) # # Max settings # lbl_max_active = QLabel() lbl_max_active.setText("Max active") max_active = QSpinBox() max_active.setMinimum(0) max_active.setMaximum(1000) max_active.setSpecialValueText('-') self._max_active = max_active lbl_max_steps = QLabel() lbl_max_steps.setText("Max steps") max_steps = QSpinBox() max_steps.setMinimum(0) max_steps.setMaximum(1000) max_steps.setSpecialValueText('-') self._max_steps = max_steps max_settings_layout = QVBoxLayout() layout = QHBoxLayout() layout.addWidget(lbl_max_active) layout.addWidget(max_active) max_settings_layout.addLayout(layout) layout = QHBoxLayout() layout.addWidget(lbl_max_steps) layout.addWidget(max_steps) max_settings_layout.addLayout(layout) # # Buttons # # step button self.explore_buttons = [] step_button = QPushButton() step_button.setText('&Step actives') step_button.released.connect(self._on_step_clicked) self.explore_buttons.append(step_button) # step until branch step_until_branch_button = QPushButton('Step actives until &branch') step_until_branch_button.released.connect( self._on_step_until_branch_clicked) self.explore_buttons.append(step_until_branch_button) # explore button explore_button = QPushButton('&Explore') explore_button.released.connect(self._on_explore_clicked) self.explore_buttons.append(explore_button) # buttons layout buttons_layout = QVBoxLayout() layout = QHBoxLayout() layout.addWidget(explore_button) buttons_layout.addLayout(layout) layout = QHBoxLayout() layout.addWidget(step_button) layout.addWidget(step_until_branch_button) buttons_layout.addLayout(layout) simgrs_layout = QVBoxLayout() simgrs_layout.addLayout(pg_layout) simgrs_layout.addWidget(viewer) simgrs_layout.addLayout(max_settings_layout) simgrs_layout.addLayout(buttons_layout) frame = QFrame() frame.setLayout(simgrs_layout) tab.addTab(frame, 'General')
class Window(QWidget): def __init__(self): super(Window, self).__init__() self.renderArea = RenderArea() self.shapeComboBox = QComboBox() self.shapeComboBox.addItem("Polygon", RenderArea.Polygon) self.shapeComboBox.addItem("Rectangle", RenderArea.Rect) self.shapeComboBox.addItem("Rounded Rectangle", RenderArea.RoundedRect) self.shapeComboBox.addItem("Ellipse", RenderArea.Ellipse) self.shapeComboBox.addItem("Pie", RenderArea.Pie) self.shapeComboBox.addItem("Chord", RenderArea.Chord) self.shapeComboBox.addItem("Path", RenderArea.Path) self.shapeComboBox.addItem("Line", RenderArea.Line) self.shapeComboBox.addItem("Polyline", RenderArea.Polyline) self.shapeComboBox.addItem("Arc", RenderArea.Arc) self.shapeComboBox.addItem("Points", RenderArea.Points) self.shapeComboBox.addItem("Text", RenderArea.Text) self.shapeComboBox.addItem("Pixmap", RenderArea.Pixmap) shapeLabel = QLabel("&Shape:") shapeLabel.setBuddy(self.shapeComboBox) self.penWidthSpinBox = QSpinBox() self.penWidthSpinBox.setRange(0, 20) self.penWidthSpinBox.setSpecialValueText("0 (cosmetic pen)") penWidthLabel = QLabel("Pen &Width:") penWidthLabel.setBuddy(self.penWidthSpinBox) self.penStyleComboBox = QComboBox() self.penStyleComboBox.addItem("Solid", Qt.SolidLine) self.penStyleComboBox.addItem("Dash", Qt.DashLine) self.penStyleComboBox.addItem("Dot", Qt.DotLine) self.penStyleComboBox.addItem("Dash Dot", Qt.DashDotLine) self.penStyleComboBox.addItem("Dash Dot Dot", Qt.DashDotDotLine) self.penStyleComboBox.addItem("None", Qt.NoPen) penStyleLabel = QLabel("&Pen Style:") penStyleLabel.setBuddy(self.penStyleComboBox) self.penCapComboBox = QComboBox() self.penCapComboBox.addItem("Flat", Qt.FlatCap) self.penCapComboBox.addItem("Square", Qt.SquareCap) self.penCapComboBox.addItem("Round", Qt.RoundCap) penCapLabel = QLabel("Pen &Cap:") penCapLabel.setBuddy(self.penCapComboBox) self.penJoinComboBox = QComboBox() self.penJoinComboBox.addItem("Miter", Qt.MiterJoin) self.penJoinComboBox.addItem("Bevel", Qt.BevelJoin) self.penJoinComboBox.addItem("Round", Qt.RoundJoin) penJoinLabel = QLabel("Pen &Join:") penJoinLabel.setBuddy(self.penJoinComboBox) self.brushStyleComboBox = QComboBox() self.brushStyleComboBox.addItem("Linear Gradient", Qt.LinearGradientPattern) self.brushStyleComboBox.addItem("Radial Gradient", Qt.RadialGradientPattern) self.brushStyleComboBox.addItem("Conical Gradient", Qt.ConicalGradientPattern) self.brushStyleComboBox.addItem("Texture", Qt.TexturePattern) self.brushStyleComboBox.addItem("Solid", Qt.SolidPattern) self.brushStyleComboBox.addItem("Horizontal", Qt.HorPattern) self.brushStyleComboBox.addItem("Vertical", Qt.VerPattern) self.brushStyleComboBox.addItem("Cross", Qt.CrossPattern) self.brushStyleComboBox.addItem("Backward Diagonal", Qt.BDiagPattern) self.brushStyleComboBox.addItem("Forward Diagonal", Qt.FDiagPattern) self.brushStyleComboBox.addItem("Diagonal Cross", Qt.DiagCrossPattern) self.brushStyleComboBox.addItem("Dense 1", Qt.Dense1Pattern) self.brushStyleComboBox.addItem("Dense 2", Qt.Dense2Pattern) self.brushStyleComboBox.addItem("Dense 3", Qt.Dense3Pattern) self.brushStyleComboBox.addItem("Dense 4", Qt.Dense4Pattern) self.brushStyleComboBox.addItem("Dense 5", Qt.Dense5Pattern) self.brushStyleComboBox.addItem("Dense 6", Qt.Dense6Pattern) self.brushStyleComboBox.addItem("Dense 7", Qt.Dense7Pattern) self.brushStyleComboBox.addItem("None", Qt.NoBrush) brushStyleLabel = QLabel("&Brush Style:") brushStyleLabel.setBuddy(self.brushStyleComboBox) otherOptionsLabel = QLabel("Other Options:") self.antialiasingCheckBox = QCheckBox("&Antialiasing") self.transformationsCheckBox = QCheckBox("&Transformations") self.shapeComboBox.activated.connect(self.shapeChanged) self.penWidthSpinBox.valueChanged.connect(self.penChanged) self.penStyleComboBox.activated.connect(self.penChanged) self.penCapComboBox.activated.connect(self.penChanged) self.penJoinComboBox.activated.connect(self.penChanged) self.brushStyleComboBox.activated.connect(self.brushChanged) self.antialiasingCheckBox.toggled.connect(self.renderArea.setAntialiased) self.transformationsCheckBox.toggled.connect(self.renderArea.setTransformed) mainLayout = QGridLayout() mainLayout.setColumnStretch(0, 1) mainLayout.setColumnStretch(3, 1) mainLayout.addWidget(self.renderArea, 0, 0, 1, 4) mainLayout.setRowMinimumHeight(1, 6) mainLayout.addWidget(shapeLabel, 2, 1, Qt.AlignRight) mainLayout.addWidget(self.shapeComboBox, 2, 2) mainLayout.addWidget(penWidthLabel, 3, 1, Qt.AlignRight) mainLayout.addWidget(self.penWidthSpinBox, 3, 2) mainLayout.addWidget(penStyleLabel, 4, 1, Qt.AlignRight) mainLayout.addWidget(self.penStyleComboBox, 4, 2) mainLayout.addWidget(penCapLabel, 5, 1, Qt.AlignRight) mainLayout.addWidget(self.penCapComboBox, 5, 2) mainLayout.addWidget(penJoinLabel, 6, 1, Qt.AlignRight) mainLayout.addWidget(self.penJoinComboBox, 6, 2) mainLayout.addWidget(brushStyleLabel, 7, 1, Qt.AlignRight) mainLayout.addWidget(self.brushStyleComboBox, 7, 2) mainLayout.setRowMinimumHeight(8, 6) mainLayout.addWidget(otherOptionsLabel, 9, 1, Qt.AlignRight) mainLayout.addWidget(self.antialiasingCheckBox, 9, 2) mainLayout.addWidget(self.transformationsCheckBox, 10, 2) self.setLayout(mainLayout) self.shapeChanged() self.penChanged() self.brushChanged() self.antialiasingCheckBox.setChecked(True) self.setWindowTitle("Basic Drawing") def shapeChanged(self): shape = self.shapeComboBox.itemData(self.shapeComboBox.currentIndex(), IdRole) self.renderArea.setShape(shape) def penChanged(self): width = self.penWidthSpinBox.value() style = Qt.PenStyle(self.penStyleComboBox.itemData( self.penStyleComboBox.currentIndex(), IdRole)) cap = Qt.PenCapStyle(self.penCapComboBox.itemData( self.penCapComboBox.currentIndex(), IdRole)) join = Qt.PenJoinStyle(self.penJoinComboBox.itemData( self.penJoinComboBox.currentIndex(), IdRole)) self.renderArea.setPen(QPen(Qt.blue, width, style, cap, join)) def brushChanged(self): style = Qt.BrushStyle(self.brushStyleComboBox.itemData( self.brushStyleComboBox.currentIndex(), IdRole)) if style == Qt.LinearGradientPattern: linearGradient = QLinearGradient(0, 0, 100, 100) linearGradient.setColorAt(0.0, Qt.white) linearGradient.setColorAt(0.2, Qt.green) linearGradient.setColorAt(1.0, Qt.black) self.renderArea.setBrush(QBrush(linearGradient)) elif style == Qt.RadialGradientPattern: radialGradient = QRadialGradient(50, 50, 50, 70, 70) radialGradient.setColorAt(0.0, Qt.white) radialGradient.setColorAt(0.2, Qt.green) radialGradient.setColorAt(1.0, Qt.black) self.renderArea.setBrush(QBrush(radialGradient)) elif style == Qt.ConicalGradientPattern: conicalGradient = QConicalGradient(50, 50, 150) conicalGradient.setColorAt(0.0, Qt.white) conicalGradient.setColorAt(0.2, Qt.green) conicalGradient.setColorAt(1.0, Qt.black) self.renderArea.setBrush(QBrush(conicalGradient)) elif style == Qt.TexturePattern: self.renderArea.setBrush(QBrush(QPixmap(':/images/brick.png'))) else: self.renderArea.setBrush(QBrush(Qt.green, style))
class SpecifySearchConditionsDialog(BaseDialog): def __init__(self, parent, entity): super().__init__(parent, _("Search criteria"), _("&Start search"), _("&Cancel")) self._entity = entity self._value_widget = None self._value_label = None self._search_expression_parts = [] self._added_condition = False self._populate_fields_tree(self._entity) def create_ui(self): fields_label = QLabel(_("Class fields"), self) self.layout.addWidget(fields_label, 0, 0) self._fields_tree = QTreeWidget(self) fields_label.setBuddy(self._fields_tree) self._fields_tree.currentItemChanged.connect( self.on_fields_tree_sel_changed) self.layout.addWidget(self._fields_tree, 1, 0) operator_label = QLabel(_("Operator"), self) self.layout.addWidget(operator_label, 0, 1) self._operator = QListWidget(self) operator_label.setBuddy(self._operator) self.layout.addWidget(self._operator, 1, 1) self._operator.currentRowChanged.connect(self.on_operator_choice) self._operator.setCurrentRow(0) add_button = QPushButton(_("&Add condition"), self) add_button.clicked.connect(self.on_add_clicked) self.layout.addWidget(add_button, 2, 0, 1, 3) criteria_label = QLabel(_("Current search criteria"), self) self.layout.addWidget(criteria_label, 3, 0, 1, 3) self._criteria_list = QListWidget(self) criteria_label.setBuddy(self._criteria_list) self.layout.addWidget(self._criteria_list, 4, 0, 1, 3) remove_button = QPushButton(_("&Remove condition"), self) remove_button.clicked.connect(self.on_remove_clicked) self.layout.addWidget(remove_button, 5, 0, 1, 3) distance_label = QLabel(_("Search objects to distance"), self) self.layout.addWidget(distance_label, 6, 0) self._distance_field = QSpinBox(self) self._distance_field.setMaximum(100000) self._distance_field.setSuffix(" " + _("meters")) self._distance_field.setSpecialValueText(_("No limit")) distance_label.setBuddy(self._distance_field) self.layout.addWidget(self._distance_field, 6, 1) def _populate_fields_tree(self, entity, parent=None): if parent is None: parent = self._fields_tree.invisibleRootItem() metadata = EntityMetadata.for_discriminator(entity) for field_name, field in sorted( metadata.all_fields.items(), key=lambda i: underscored_to_words(i[0])): child_metadata = None try: child_metadata = EntityMetadata.for_discriminator( field.type_name) except KeyError: pass if child_metadata: name = get_class_display_name(field.type_name) subparent = QTreeWidgetItem([name]) subparent.setData(0, Qt.UserRole, field_name) parent.addChild(subparent) self._populate_fields_tree(field.type_name, subparent) else: item = QTreeWidgetItem([underscored_to_words(field_name)]) item.setData(0, Qt.UserRole, (field_name, field)) parent.addChild(item) def on_fields_tree_sel_changed(self, item): data = item.data(0, Qt.UserRole) if data is not None and not isinstance(data, str): self._field_name = data[0] self._field = data[1] self._operators = operators_for_column_class(self._field.type_name) self._operator.clear() self._operator.addItems([o.label for o in self._operators]) self._added_condition = False def on_operator_choice(self, index): self._added_condition = False if self._value_widget: self.layout.removeWidget(self._value_widget) self._value_widget.deleteLater() if self._value_label: self.layout.removeWidget(self._value_label) self._value_label.deleteLater() self._value_label = None operator = self._operators[self._operator.currentRow()] value_label = self._create_value_label( operator.get_value_label(self._field)) self._value_widget = operator.get_value_widget(self, self._field) if not self._value_widget: return QWidget.setTabOrder(self._operator, self._value_widget) self._value_label = value_label if self._value_label: self._value_label.setBuddy(self._value_widget) self.layout.addWidget(self._value_label, 0, 2) self.layout.addWidget(self._value_widget, 1, 2) def _create_value_label(self, label): if not label: return label = QLabel(label, self) return label def on_add_clicked(self, evt): if not hasattr(self, "_field_name"): return self._added_condition = True json_path = [] parent_item = self._fields_tree.currentItem().parent() parent_data = parent_item.data(0, Qt.UserRole) if parent_item else None if isinstance(parent_data, str): json_path.append(parent_data) json_path.append(self._field_name) json_path = ".".join(json_path) operator_obj = self._operators[self._operator.currentRow()] expression = operator_obj.get_comparison_expression( self._field, FieldNamed(json_path), self._value_widget) self._search_expression_parts.append(expression) self._criteria_list.addItem( f"{underscored_to_words(self._field_name)} {operator_obj.label} {operator_obj.get_value_as_string(self._field, self._value_widget)}" ) @property def distance(self): return self._distance_field.value() def create_conditions(self): conditions = [] if self._search_expression_parts: for part in self._search_expression_parts: conditions.append(part) return conditions def on_remove_clicked(self, evt): selection = self._criteria_list.currentRow() if selection < 0: return del self._search_expression_parts[selection] self._criteria_list.removeItemWidget(self._criteria_list.currentItem()) def ok_clicked(self): if not self._added_condition: if QMessageBox.question( self, _("Question"), _("It appears that you forgot to add the current condition to the conditions list. Do you want to add it before starting the search?" )): self.on_add_clicked(None) super().ok_clicked()
class Window(QWidget): def __init__(self): super(Window, self).__init__() self.renderArea = RenderArea() self.shapeComboBox = QComboBox() self.shapeComboBox.addItem("Polygon", RenderArea.Polygon) self.shapeComboBox.addItem("Rectangle", RenderArea.Rect) self.shapeComboBox.addItem("Rounded Rectangle", RenderArea.RoundedRect) self.shapeComboBox.addItem("Ellipse", RenderArea.Ellipse) self.shapeComboBox.addItem("Pie", RenderArea.Pie) self.shapeComboBox.addItem("Chord", RenderArea.Chord) self.shapeComboBox.addItem("Path", RenderArea.Path) self.shapeComboBox.addItem("Line", RenderArea.Line) self.shapeComboBox.addItem("Polyline", RenderArea.Polyline) self.shapeComboBox.addItem("Arc", RenderArea.Arc) self.shapeComboBox.addItem("Points", RenderArea.Points) self.shapeComboBox.addItem("Text", RenderArea.Text) self.shapeComboBox.addItem("Pixmap", RenderArea.Pixmap) shapeLabel = QLabel("&Shape:") shapeLabel.setBuddy(self.shapeComboBox) self.penWidthSpinBox = QSpinBox() self.penWidthSpinBox.setRange(0, 20) self.penWidthSpinBox.setSpecialValueText("0 (cosmetic pen)") penWidthLabel = QLabel("Pen &Width:") penWidthLabel.setBuddy(self.penWidthSpinBox) self.penStyleComboBox = QComboBox() self.penStyleComboBox.addItem("Solid", Qt.SolidLine) self.penStyleComboBox.addItem("Dash", Qt.DashLine) self.penStyleComboBox.addItem("Dot", Qt.DotLine) self.penStyleComboBox.addItem("Dash Dot", Qt.DashDotLine) self.penStyleComboBox.addItem("Dash Dot Dot", Qt.DashDotDotLine) self.penStyleComboBox.addItem("None", Qt.NoPen) penStyleLabel = QLabel("&Pen Style:") penStyleLabel.setBuddy(self.penStyleComboBox) self.penCapComboBox = QComboBox() self.penCapComboBox.addItem("Flat", Qt.FlatCap) self.penCapComboBox.addItem("Square", Qt.SquareCap) self.penCapComboBox.addItem("Round", Qt.RoundCap) penCapLabel = QLabel("Pen &Cap:") penCapLabel.setBuddy(self.penCapComboBox) self.penJoinComboBox = QComboBox() self.penJoinComboBox.addItem("Miter", Qt.MiterJoin) self.penJoinComboBox.addItem("Bevel", Qt.BevelJoin) self.penJoinComboBox.addItem("Round", Qt.RoundJoin) penJoinLabel = QLabel("Pen &Join:") penJoinLabel.setBuddy(self.penJoinComboBox) self.brushStyleComboBox = QComboBox() self.brushStyleComboBox.addItem("Linear Gradient", Qt.LinearGradientPattern) self.brushStyleComboBox.addItem("Radial Gradient", Qt.RadialGradientPattern) self.brushStyleComboBox.addItem("Conical Gradient", Qt.ConicalGradientPattern) self.brushStyleComboBox.addItem("Texture", Qt.TexturePattern) self.brushStyleComboBox.addItem("Solid", Qt.SolidPattern) self.brushStyleComboBox.addItem("Horizontal", Qt.HorPattern) self.brushStyleComboBox.addItem("Vertical", Qt.VerPattern) self.brushStyleComboBox.addItem("Cross", Qt.CrossPattern) self.brushStyleComboBox.addItem("Backward Diagonal", Qt.BDiagPattern) self.brushStyleComboBox.addItem("Forward Diagonal", Qt.FDiagPattern) self.brushStyleComboBox.addItem("Diagonal Cross", Qt.DiagCrossPattern) self.brushStyleComboBox.addItem("Dense 1", Qt.Dense1Pattern) self.brushStyleComboBox.addItem("Dense 2", Qt.Dense2Pattern) self.brushStyleComboBox.addItem("Dense 3", Qt.Dense3Pattern) self.brushStyleComboBox.addItem("Dense 4", Qt.Dense4Pattern) self.brushStyleComboBox.addItem("Dense 5", Qt.Dense5Pattern) self.brushStyleComboBox.addItem("Dense 6", Qt.Dense6Pattern) self.brushStyleComboBox.addItem("Dense 7", Qt.Dense7Pattern) self.brushStyleComboBox.addItem("None", Qt.NoBrush) brushStyleLabel = QLabel("&Brush Style:") brushStyleLabel.setBuddy(self.brushStyleComboBox) otherOptionsLabel = QLabel("Other Options:") self.antialiasingCheckBox = QCheckBox("&Antialiasing") self.transformationsCheckBox = QCheckBox("&Transformations") self.shapeComboBox.activated.connect(self.shapeChanged) self.penWidthSpinBox.valueChanged.connect(self.penChanged) self.penStyleComboBox.activated.connect(self.penChanged) self.penCapComboBox.activated.connect(self.penChanged) self.penJoinComboBox.activated.connect(self.penChanged) self.brushStyleComboBox.activated.connect(self.brushChanged) self.antialiasingCheckBox.toggled.connect( self.renderArea.setAntialiased) self.transformationsCheckBox.toggled.connect( self.renderArea.setTransformed) mainLayout = QGridLayout() mainLayout.setColumnStretch(0, 1) mainLayout.setColumnStretch(3, 1) mainLayout.addWidget(self.renderArea, 0, 0, 1, 4) mainLayout.setRowMinimumHeight(1, 6) mainLayout.addWidget(shapeLabel, 2, 1, Qt.AlignRight) mainLayout.addWidget(self.shapeComboBox, 2, 2) mainLayout.addWidget(penWidthLabel, 3, 1, Qt.AlignRight) mainLayout.addWidget(self.penWidthSpinBox, 3, 2) mainLayout.addWidget(penStyleLabel, 4, 1, Qt.AlignRight) mainLayout.addWidget(self.penStyleComboBox, 4, 2) mainLayout.addWidget(penCapLabel, 5, 1, Qt.AlignRight) mainLayout.addWidget(self.penCapComboBox, 5, 2) mainLayout.addWidget(penJoinLabel, 6, 1, Qt.AlignRight) mainLayout.addWidget(self.penJoinComboBox, 6, 2) mainLayout.addWidget(brushStyleLabel, 7, 1, Qt.AlignRight) mainLayout.addWidget(self.brushStyleComboBox, 7, 2) mainLayout.setRowMinimumHeight(8, 6) mainLayout.addWidget(otherOptionsLabel, 9, 1, Qt.AlignRight) mainLayout.addWidget(self.antialiasingCheckBox, 9, 2) mainLayout.addWidget(self.transformationsCheckBox, 10, 2) self.setLayout(mainLayout) self.shapeChanged() self.penChanged() self.brushChanged() self.antialiasingCheckBox.setChecked(True) self.setWindowTitle("Basic Drawing") def shapeChanged(self): shape = self.shapeComboBox.itemData(self.shapeComboBox.currentIndex(), IdRole) self.renderArea.setShape(shape) def penChanged(self): width = self.penWidthSpinBox.value() style = Qt.PenStyle( self.penStyleComboBox.itemData( self.penStyleComboBox.currentIndex(), IdRole)) cap = Qt.PenCapStyle( self.penCapComboBox.itemData(self.penCapComboBox.currentIndex(), IdRole)) join = Qt.PenJoinStyle( self.penJoinComboBox.itemData(self.penJoinComboBox.currentIndex(), IdRole)) self.renderArea.setPen(QPen(Qt.blue, width, style, cap, join)) def brushChanged(self): style = Qt.BrushStyle( self.brushStyleComboBox.itemData( self.brushStyleComboBox.currentIndex(), IdRole)) if style == Qt.LinearGradientPattern: linearGradient = QLinearGradient(0, 0, 100, 100) linearGradient.setColorAt(0.0, Qt.white) linearGradient.setColorAt(0.2, Qt.green) linearGradient.setColorAt(1.0, Qt.black) self.renderArea.setBrush(QBrush(linearGradient)) elif style == Qt.RadialGradientPattern: radialGradient = QRadialGradient(50, 50, 50, 70, 70) radialGradient.setColorAt(0.0, Qt.white) radialGradient.setColorAt(0.2, Qt.green) radialGradient.setColorAt(1.0, Qt.black) self.renderArea.setBrush(QBrush(radialGradient)) elif style == Qt.ConicalGradientPattern: conicalGradient = QConicalGradient(50, 50, 150) conicalGradient.setColorAt(0.0, Qt.white) conicalGradient.setColorAt(0.2, Qt.green) conicalGradient.setColorAt(1.0, Qt.black) self.renderArea.setBrush(QBrush(conicalGradient)) elif style == Qt.TexturePattern: self.renderArea.setBrush(QBrush(QPixmap(':/images/brick.png'))) else: self.renderArea.setBrush(QBrush(Qt.green, style))
class GradientWidget(ToolWidget): def __init__(self, image, parent=None): super(GradientWidget, self).__init__(parent) self.levels_spin = QSpinBox() self.levels_spin.setRange(-1, 16) self.levels_spin.setSpecialValueText(self.tr('Auto')) self.levels_spin.setValue(-1) self.invert_check = QCheckBox(self.tr('Invert')) self.abs_check = QCheckBox(self.tr('Absolute')) self.grad_viewer = ImageViewer(image, image) self.image = image self.process() self.levels_spin.valueChanged.connect(self.process) self.invert_check.toggled.connect(self.process) self.abs_check.toggled.connect(self.process) top_layout = QHBoxLayout() top_layout.addWidget(QLabel(self.tr('Levels:'))) top_layout.addWidget(self.levels_spin) top_layout.addWidget(self.invert_check) top_layout.addWidget(self.abs_check) top_layout.addStretch() main_layout = QVBoxLayout() main_layout.addLayout(top_layout) main_layout.addWidget(self.grad_viewer) self.setLayout(main_layout) def process(self): intensity = self.levels_spin.value() invert = self.invert_check.isChecked() absolute = self.abs_check.isChecked() self.levels_spin.setEnabled(not absolute) self.invert_check.setEnabled(not absolute) gray = cv.cvtColor(self.image, cv.COLOR_BGR2GRAY) diff_x = cv.Scharr(gray, cv.CV_32F, 1, 0) diff_y = cv.Scharr(gray, cv.CV_32F, 0, 1) diff_z = cv.normalize(np.abs(diff_x + diff_y), None, 0, 255, cv.NORM_MINMAX) diff_z = cv.LUT(diff_z.astype(np.uint8), create_lut(0, 90)) grad_x = +diff_x if invert else -diff_x grad_y = +diff_y if invert else -diff_y grad_z = diff_z if not absolute: min_x, max_x, _, _ = cv.minMaxLoc(grad_x) lim_x = max(abs(min_x), abs(max_x)) grad_x = (grad_x / lim_x + 1) * 127 min_y, max_y, _, _ = cv.minMaxLoc(grad_y) lim_y = max(abs(min_y), abs(max_y)) grad_y = (grad_y / lim_y + 1) * 127 else: grad_x = cv.normalize(np.abs(grad_x), None, 0, 255, cv.NORM_MINMAX) grad_y = cv.normalize(np.abs(grad_y), None, 0, 255, cv.NORM_MINMAX) grad_x = grad_x.astype(np.uint8) grad_y = grad_y.astype(np.uint8) if intensity >= 0 and not absolute: max_intensity = self.levels_spin.maximum() low = 127 - (max_intensity - intensity) high = 127 + (max_intensity - intensity) lut_xy = create_lut(low, high) grad_x = cv.LUT(grad_x, lut_xy) grad_y = cv.LUT(grad_y, lut_xy) else: grad_x = cv.equalizeHist(grad_x) grad_y = cv.equalizeHist(grad_y) gradient = np.stack((grad_z, grad_x, grad_y), axis=2) self.grad_viewer.update_processed(gradient)
class PlotsWidget(ToolWidget): def __init__(self, image, parent=None): super(PlotsWidget, self).__init__(parent) choices = ["Red", "Green", "Blue", "Hue", "Saturation", "Value"] self.xaxis_combo = QComboBox() self.xaxis_combo.addItems(choices) self.xaxis_combo.setCurrentIndex(3) self.yaxis_combo = QComboBox() self.yaxis_combo.addItems(choices) self.yaxis_combo.setCurrentIndex(4) self.zaxis_combo = QComboBox() self.zaxis_combo.addItems(choices) self.zaxis_combo.setCurrentIndex(5) self.sampling_spin = QSpinBox() levels = int(np.log2(min(image.shape[:-1]))) self.sampling_spin.setRange(0, levels) self.sampling_spin.setSpecialValueText(self.tr("Off")) # self.sampling_spin.setSuffix(self.tr(' level(s)')) self.sampling_spin.setValue(1) self.size_spin = QSpinBox() self.size_spin.setRange(1, 10) self.size_spin.setValue(1) self.size_spin.setSuffix(self.tr(" pt")) self.markers = [ ",", ".", "o", "8", "s", "p", "P", "*", "h", "H", "X", "D" ] self.alpha_spin = QDoubleSpinBox() self.alpha_spin.setRange(0, 1) self.alpha_spin.setDecimals(2) self.alpha_spin.setSingleStep(0.05) self.alpha_spin.setValue(1) self.colors_check = QCheckBox(self.tr("Show colors")) self.grid_check = QCheckBox(self.tr("Show grid")) self.norm_check = QCheckBox(self.tr("Normalized")) self.total_label = QLabel() img = np.copy(image) self.colors = [None] * (levels + 1) for scale in range(levels + 1): rgb = cv.cvtColor(img.astype(np.float32) / 255, cv.COLOR_BGR2RGB) hsv = cv.cvtColor(rgb, cv.COLOR_RGB2HSV) hsv[:, :, 0] /= 360 shape = (img.shape[0] * img.shape[1], img.shape[2]) self.colors[scale] = np.concatenate( (np.reshape(rgb, shape), np.reshape(hsv, shape)), axis=1) img = cv.pyrDown(img) figure2 = Figure() plot2_canvas = FigureCanvas(figure2) self.axes2 = plot2_canvas.figure.subplots() toolbar2 = NavigationToolbar(plot2_canvas, self) plot2_layout = QVBoxLayout() plot2_layout.addWidget(plot2_canvas) plot2_layout.addWidget(toolbar2) plot2_widget = QWidget() plot2_widget.setLayout(plot2_layout) figure3 = Figure() plot3_canvas = FigureCanvas(figure3) self.axes3 = plot3_canvas.figure.add_subplot(111, projection="3d") toolbar3 = NavigationToolbar(plot3_canvas, self) plot3_layout = QVBoxLayout() plot3_layout.addWidget(plot3_canvas) plot3_layout.addWidget(toolbar3) plot3_widget = QWidget() plot3_widget.setLayout(plot3_layout) self.tab_widget = QTabWidget() self.tab_widget.addTab(plot2_widget, "2D Plot") self.tab_widget.addTab(plot3_widget, "3D Plot") self.redraw() figure2.set_tight_layout(True) figure3.set_tight_layout(True) self.xaxis_combo.currentIndexChanged.connect(self.redraw) self.yaxis_combo.currentIndexChanged.connect(self.redraw) self.zaxis_combo.currentIndexChanged.connect(self.redraw) self.sampling_spin.valueChanged.connect(self.redraw) self.size_spin.valueChanged.connect(self.redraw) self.alpha_spin.valueChanged.connect(self.redraw) self.colors_check.stateChanged.connect(self.redraw) self.grid_check.stateChanged.connect(self.redraw) self.norm_check.stateChanged.connect(self.redraw) self.tab_widget.currentChanged.connect(self.redraw) params_layout = QGridLayout() params_layout.addWidget(QLabel(self.tr("X axis:")), 0, 0) params_layout.addWidget(self.xaxis_combo, 0, 1) params_layout.addWidget(QLabel(self.tr("Y axis:")), 1, 0) params_layout.addWidget(self.yaxis_combo, 1, 1) params_layout.addWidget(QLabel(self.tr("Z axis:")), 2, 0) params_layout.addWidget(self.zaxis_combo, 2, 1) params_layout.addWidget(QLabel(self.tr("Subsampling:")), 0, 2) params_layout.addWidget(self.sampling_spin, 0, 3) params_layout.addWidget(QLabel(self.tr("Point size:")), 1, 2) params_layout.addWidget(self.size_spin, 1, 3) params_layout.addWidget(QLabel(self.tr("Point alpha:")), 2, 2) params_layout.addWidget(self.alpha_spin, 2, 3) params_layout.addWidget(self.colors_check, 0, 4) params_layout.addWidget(self.grid_check, 1, 4) params_layout.addWidget(self.total_label, 2, 4) bottom_layout = QHBoxLayout() bottom_layout.addLayout(params_layout) bottom_layout.addStretch() main_layout = QVBoxLayout() main_layout.addWidget(self.tab_widget) main_layout.addLayout(bottom_layout) self.setLayout(main_layout) def redraw(self): start = time() v = self.sampling_spin.value() x = self.colors[v][:, self.xaxis_combo.currentIndex()] y = self.colors[v][:, self.yaxis_combo.currentIndex()] s = self.size_spin.value()**2 c = None if not self.colors_check.isChecked( ) else self.colors[v][:, :3] if self.tab_widget.currentIndex() == 0: self.zaxis_combo.setEnabled(False) self.grid_check.setEnabled(True) self.alpha_spin.setEnabled(True) a = self.alpha_spin.value() xlim = self.axes2.get_xlim() ylim = self.axes2.get_ylim() self.axes2.clear() self.axes2.set_facecolor([0.5] * 3 if c is not None else [1.0] * 3) self.axes2.scatter(x, y, s, c, ".", alpha=a) self.axes2.set_xlabel(self.xaxis_combo.currentText()) self.axes2.set_ylabel(self.yaxis_combo.currentText()) self.axes2.grid(self.grid_check.isChecked(), which="both") self.axes2.set_xlim(xlim) self.axes2.set_ylim(ylim) self.axes2.figure.canvas.draw() else: self.zaxis_combo.setEnabled(True) self.grid_check.setEnabled(False) self.alpha_spin.setEnabled(False) z = self.colors[v][:, self.zaxis_combo.currentIndex()] self.axes3.clear() self.axes3.set_facecolor([0.5] * 3 if c is not None else [1.0] * 3) self.axes3.scatter(x, y, z, s=s, c=c, marker=".", depthshade=True) self.axes3.set_xlabel(self.xaxis_combo.currentText()) self.axes3.set_ylabel(self.yaxis_combo.currentText()) self.axes3.set_zlabel(self.zaxis_combo.currentText()) self.axes3.grid(self.grid_check.isChecked(), which="both") self.axes3.figure.canvas.draw() self.total_label.setText(self.tr(f"[{len(x)} points]")) self.info_message.emit(f"Plot redraw = {elapsed_time(start)}")