I have a pg.GraphicsLayoutWidget
with some images and some ROIs displayed . I would like to get obtain a numpy
array for what is currently on the scene, just like the export
command in the context menu of the viewbox
.
Let an arbitrary RGB image (i.e. a numpy array) image
be given. For example , I am using
You will notice that an ROI has been added. It has nothing to do with my problem. I added it to only to emphasize I need what is currently on the scene. That is, the part in yellow dotted lines when right clicking on pg.GraphicsLayoutWidget
.
In steps:
- Copy paste the above code and write a command to read the image.
- Scroll the mouse or drag the image or drag the ROI or toggle the ROI. Do whatever you like inside the
pg.GraphicsLayoutWidget
(it does not matter what exactly you do). We may end up in a situation like:
- Move the mouse inside
pg.GraphicsLayoutWidget
and right click. We get the context menu:
- Click
Export...
will give (the region inside yellow dotted lines is what I called on the scene):
- For the export format, choose "Image File (PNG,TIF,JPG,...)". Now click export will save an image like this:
- Observe that this saved image is exactly the same as the part in step4 inside the yellow dotted lines.
My goal is to programmatically read an RGB numpy array upon user button click such that, if saved, is exactly this image. That is, whenever user clicks button, the program reads what is currently on the scene.
I know this is probably just a matter of how to copy exportDialog.py
in the GraphicsScene
folder . The lines of interest are :
def exportItemChanged(self, item, prev):
if item is None:
return
if item.gitem is self.scene:
newBounds = self.scene.views()[0].viewRect()
else:
newBounds = item.gitem.sceneBoundingRect()
self.selectBox.setRect(newBounds)
self.selectBox.show()
self.updateFormatList()
But I get stuck: I don't understand how to get the scene from my viewbox or how to get the array from the scene.
CodePudding user response:
In my previous answer I was using ROI to get image data. However as you refine your question, it's clear that you need 1:1 copy of graphics view with possibly other objects (not just copy of Image data in a view).
You can use pyqtgraph's ImageExporter
object with toBytes
parameter.
Then you have to transform QImage into RGB numpy array.
Here is working example:
import numpy as np
import pyqtgraph as pg
from PIL import Image
from PyQt5.QtGui import QImage
from PyQt5.QtWidgets import *
from numpy import asarray
from pyqtgraph.exporters import ImageExporter
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.layout = QVBoxLayout()
self.button = QPushButton("Save")
self.button.clicked.connect(self.get_numpy_array)
self.layout.addWidget(self.button)
image = asarray(Image.open("image.jpg"))
self.graphics = Graphics(image, pg.ImageItem(image))
self.layout.addWidget(self.graphics)
self.setLayout(self.layout)
def get_numpy_array(self):
# Export current viewvbox into bytes
exporter = ImageExporter(self.graphics.viewbox)
data = exporter.export(toBytes=True)
# Convert QIMage into RGB image
input_img = data.convertToFormat(QImage.Format_RGB888)
width = input_img.width()
height = input_img.height()
# Get pointer to data
ptr = input_img.bits()
ptr.setsize(input_img.byteCount())
# Create numpy array from data
arr = np.array(ptr).reshape(height, width, 3)
# This part transforms array back to image
# img = Image.fromarray(arr, 'RGB')
# img.save("./slice.png")
return arr
class Graphics(pg.GraphicsLayoutWidget):
def __init__(self, image, image_item):
super().__init__()
layout = self.addLayout()
self.image = image
self.shape = image.shape
self.viewbox = layout.addViewBox(lockAspect=True)
self.image_item = image_item
self.viewbox.addItem(self.image_item)
self.viewbox.setLimits(minXRange=0,
minYRange=0,
maxXRange=self.shape[0],
maxYRange=self.shape[1])
x, h = 300, 50
polyline = pg.PolyLineROI(
[[x, x], [x h, x], [x h, x h], [x, x h]], pen=pg.mkPen("b", width=5), closed=True, rotatable=False)
self.viewbox.addItem(polyline)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
main = MainWindow()
main.show()
app.exec()