Python | PyQtGraph ROIを実装する方法

Python

Python PyQtGraphでROIを実装する方法を説明する。

結論

pg.ROI([x原点, y原点], [x長さ, y長さ])でROIのインスタンス(下記例ではself.roi)を生成する。addPlotのインスタンス(下記例ではself.p)に.addItem(ROIのインスタンス)とする。

self.roi = pg.ROI([10, 20], [100, 200])
self.p.addItem(self.roi)

具体例

  1. ROIの[x原点, y原点], [x長さ, y長さ]を指定してROIを生成する。
  2. ユーザーがROIの位置やサイズを変化させようとしたときのx原点, y原点を整数(正確にはsnapSize(defaul=1)の整数倍)に固定する。
  3. ユーザーがROIのサイズを変化させようとしたときのx長さ, y長さを整数(正確にはsnapSize(defaul=1)の整数倍)に固定する。
#!/usr/bin/env python3

import sys
from PyQt6.QtWidgets import (QApplication, QWidget, QGraphicsProxyWidget,
                            QLabel, QCheckBox)
import pyqtgraph as pg
import numpy as np

class GuiWindow(QWidget):

    def __init__(self, parent=None):
        super().__init__(parent)
        self.ウィンドウを用意する()
        self.画像を準備する()
        self.画像を描画する()
        self.ROIを設置する()
        self.文字表示板を設置する()
        self.チェックボックスを設置する()
        self.スペーサーを設置する()

    def ウィンドウを用意する(self):
        self.graph = pg.GraphicsLayoutWidget(show=True)

    def 画像を準備する(self):
        self.画像データ = np.random.normal(size=(500,500))

    def 画像を描画する(self):
        self.img = pg.ImageItem(self.画像データ)
        self.p = self.graph.addPlot(row=0, col=0, rowspan=10)
        self.p.addItem(self.img)
        self.p.getViewBox().invertY(True)
        self.p.getViewBox().setAspectLocked(True)

    def ROIを設置する(self):
        self.roi = pg.ROI([10, 20], [100, 200], # ? 1
                          translateSnap=True,   # ? 2
                          scaleSnap=True,       # ? 3
                          )
        self.roi.setZValue(10)  # make sure ROI is drawn above image
        self.p.addItem(self.roi)
        self.add_roi_scale_handle()
        self.roi.sigRegionChangeFinished.connect(self.display_roi_info)

    def roiの位置とサイズを取得する(self):
        self.roi_pos_x, self.roi_pos_y = self.roi.pos()
        self.roi_len_x, self.roi_len_y = self.roi.size()

    def display_roi_info(self):
        self.roiの位置とサイズを取得する()
        self.文字表示版_値_x原点.setInnerText(str(int(self.roi_pos_x)))
        self.文字表示版_値_y原点.setInnerText(str(int(self.roi_pos_y)))
        self.文字表示版_値_x長さ.setInnerText(str(int(self.roi_len_x)))
        self.文字表示版_値_y長さ.setInnerText(str(int(self.roi_len_y)))
        roi_data = self.画像データ[int(self.roi_pos_x) : int(self.roi_pos_x + self.roi_len_x),
                                   int(self.roi_pos_y) : int(self.roi_pos_y + self.roi_len_y)]
        self.文字表示版_値_最大.setInnerText(str(int(np.max(roi_data))))
        self.文字表示版_値_最小.setInnerText(str(int(np.min(roi_data))))
        self.文字表示版_値_平均.setInnerText(str(int(np.mean(roi_data))))
        self.文字表示版_値_偏差.setInnerText(str(int(np.std(roi_data))))

    def 文字表示板を設置する(self):
        self.文字表示版_項目_x原点 = FlexibleSpace(app=self, win=self.graph, row=0, col=1, w=50, h=25, text='x原点')
        self.文字表示版_項目_y原点 = FlexibleSpace(app=self, win=self.graph, row=1, col=1, w=50, h=25, text='y原点')
        self.文字表示版_項目_x長さ = FlexibleSpace(app=self, win=self.graph, row=2, col=1, w=50, h=25, text='x長さ')
        self.文字表示版_項目_y長さ = FlexibleSpace(app=self, win=self.graph, row=3, col=1, w=50, h=25, text='y長さ')
        self.文字表示版_項目_最大 = FlexibleSpace(app=self, win=self.graph, row=4, col=1, w=50, h=25, text='最大')
        self.文字表示版_項目_最小 = FlexibleSpace(app=self, win=self.graph, row=5, col=1, w=50, h=25, text='最小')
        self.文字表示版_項目_平均 = FlexibleSpace(app=self, win=self.graph, row=6, col=1, w=50, h=25, text='平均')
        self.文字表示版_項目_偏差 = FlexibleSpace(app=self, win=self.graph, row=7, col=1, w=50, h=25, text='偏差')
        self.文字表示版_値_x原点 = FlexibleSpace(app=self, win=self.graph, row=0, col=2, w=50, h=25)
        self.文字表示版_値_y原点 = FlexibleSpace(app=self, win=self.graph, row=1, col=2, w=50, h=25)
        self.文字表示版_値_x長さ = FlexibleSpace(app=self, win=self.graph, row=2, col=2, w=50, h=25)
        self.文字表示版_値_y長さ = FlexibleSpace(app=self, win=self.graph, row=3, col=2, w=50, h=25)
        self.文字表示版_値_最大 = FlexibleSpace(app=self, win=self.graph, row=4, col=2, w=50, h=25)
        self.文字表示版_値_最小 = FlexibleSpace(app=self, win=self.graph, row=5, col=2, w=50, h=25)
        self.文字表示版_値_平均 = FlexibleSpace(app=self, win=self.graph, row=6, col=2, w=50, h=25)
        self.文字表示版_値_偏差 = FlexibleSpace(app=self, win=self.graph, row=7, col=2, w=50, h=25)
        self.desplay_roi_info()

    def チェックボックスを設置する(self):
        self.チェックボックス = CheckReceiver(app=self, win=self.graph, row=8, col=1, colspan=2, h=25, text='ハンドル')

    def スペーサーを設置する(self):
        self.flexible_space = FlexibleSpace(app=self, win=self.graph, row=9, col=1, colspan=2)

    def add_roi_scale_handle(self):
        self.roi.addScaleHandle([0.5, 1], [0.5, 0]) #第1引数:handle位置/第2引数:伸張基準点
        self.roi.addScaleHandle([1, 0.5], [0, 0.5])
        self.roi.addScaleHandle([0.5, 0], [0.5, 1])
        self.roi.addScaleHandle([0, 0.5], [1, 0.5])

    def remove_roi_scale_handle(self):
        self.roi.removeHandle(0)
        self.roi.removeHandle(1)
        self.roi.removeHandle(-1)
        self.roi.removeHandle(-0)


class FlexibleSpace(QWidget):
    def __init__(self, app, win, row, col, rowspan=1, colspan=1, w=0, h=0, name='', text=''):
        super().__init__()
        self.set_layout(win=win, row=row, col=col, rowspan=rowspan, colspan=colspan)
        self.set_object(app=app, w=w, h=h, name=name, text=text)
        self.set_proxy()
        self.setInnerText(text=text)

    def set_layout(self, win, row, col, rowspan, colspan):
        self.p = win.addLayout(row=row, col=col, rowspan=rowspan, colspan=colspan)
        self.p.setContentsMargins(10,0,10,0)  # 左,上,右,下

    def set_object(self, app, w, h, name, text):
        def make_object():
            self.object = QLabel()

        def set_style():
            style = ('QLabel{'
                    'background-color: rgba(0,0,0,0);'
                    'color: darkgray;'
                    '}')
            self.object.setStyleSheet(style)

        def set_size(w, h):
            def set_wsize(w):
                self.object.setMinimumWidth(w)
                self.object.setMaximumWidth(w)

            def set_hsize(h):
                self.object.setMinimumHeight(h)
                self.object.setMaximumHeight(h)

            if w > 0:
                set_wsize(w)
            else:
                pass  # free size
            if h > 0:
                set_hsize(h)
            else:
                pass  # free size

        make_object()
        set_style()
        set_size(w=w, h=h)

    def set_proxy(self):
        self.proxy = QGraphicsProxyWidget()
        self.item = self.p.addItem(self.proxy)
        self.proxy.setWidget(self.object)

    def setInnerText(self, text):
        self.object.setText(text)


class CheckReceiver(QWidget):
    def __init__(self, app, win, row, col, rowspan=1, colspan=1, w=0, h=0, name='', text=''):
        super().__init__()
        self.set_layout(win=win, row=row, col=col, rowspan=rowspan, colspan=colspan)
        self.set_object(app=app, w=w, h=h, name=name, text=text)
        self.set_proxy()
        self.setInnerText(text=text)

    def set_layout(self, win, row, col, rowspan, colspan):
        self.p = win.addLayout(row=row, col=col, rowspan=rowspan, colspan=colspan)
        self.p.setContentsMargins(10,0,10,0)  # 左,上,右,下

    def set_object(self, app, w, h, name, text):
        def make_object():
            self.object = QCheckBox(text)
            self.object.setChecked(True)

        def set_style():
            style = ('QCheckBox{'
                    'background-color: rgba(0,0,0,0);'
                    'color: darkgray;'
                    'text-align: left;'
                    '}')
            self.object.setStyleSheet(style)

        def set_size(w, h):
            def set_wsize(w):
                self.object.setMinimumWidth(w)
                self.object.setMaximumWidth(w)

            def set_hsize(h):
                self.object.setMinimumHeight(h)
                self.object.setMaximumHeight(h)

            if w > 0:
                set_wsize(w)
            else:
                pass  # free size
            if h > 0:
                set_hsize(h)
            else:
                pass  # free size

        def connect_signal_slot():
            self.object.stateChanged.connect(onChanged)

        def onChanged():
            if self.object.isChecked():
                app.add_roi_scale_handle()
            else:
                app.remove_roi_scale_handle()

        make_object()
        set_style()
        set_size(w=w, h=h)
        connect_signal_slot()

    def set_proxy(self):
        self.proxy = QGraphicsProxyWidget()
        self.item = self.p.addItem(self.proxy)
        self.proxy.setWidget(self.object)

    def setInnerText(self, text):
        self.object.setText(text)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = GuiWindow()
    sys.exit(app.exec())

まとめ

Python PyQtGraphでROIを実装する方法を説明した。

コメント

タイトルとURLをコピーしました