Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/Ultimaker/Cura.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGhostkeeper <rubend@tutanota.com>2020-03-25 20:00:14 +0300
committerGhostkeeper <rubend@tutanota.com>2020-03-25 20:00:14 +0300
commit4c770587e2d9d7c8a10fe6e6c828c7d58803dfb2 (patch)
treea7cd8cd0d970cd709053613a491beaf8c72b0075 /plugins/SolidView
parenta4fbf3c613bfe8f91b1bb63b472e2f9ff22b69bb (diff)
Fix getting additional bytes from QImage
QImage's bytes are aligned to memory words per column of pixels. That means that one of these columns contains 99% valid image data, but with several bytes of unassigned noise at the end. How many of these padding bytes there are would depend on the image size, i.e. Cura's window size. In the end, the total number of bytes in the image ends up slightly more than w*h*3. As a result, Cura would crash because it couldn't reshape the image. Reshaping was completely unnecessary anyway, but this random noise was giving false positives also. But how do you then get only the actual pixels from each column of data? We can't just go iterating over this array, as that would be an iteration of thousands of columns which is prohibitively slow in Python. No, we're going to do some Numpy magic. We're going to create a class that pretends to be a Numpy array. Give this class some data and say that this data has a certain pixel size but also a certain STRIDE LENGTH. This stride length can be the length of the actual pixel data. As a result when Numpy sees this object it will read out the data using these strides, all done efficiently within the C code of Numpy. Framerate is fantastic on my computer. No problems at all. Pretty powerful computer though. But also a big 5k screen. Still no problem for Numpy. Seems to be decently efficient. Took me quite a while to figure all of this out. Contributes to issue CURA-7262.
Diffstat (limited to 'plugins/SolidView')
-rw-r--r--plugins/SolidView/SolidView.py29
1 files changed, 25 insertions, 4 deletions
diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py
index 65ae03d140..6acd72e615 100644
--- a/plugins/SolidView/SolidView.py
+++ b/plugins/SolidView/SolidView.py
@@ -267,10 +267,31 @@ class SolidView(View):
xray_img = self._xray_pass.getOutput()
xray_img = xray_img.convertToFormat(QImage.Format.Format_RGB888)
- ptr = xray_img.bits()
- ptr.setsize(xray_img.byteCount())
- reds = np.array(ptr).reshape(xray_img.height(), xray_img.width(), 3)[:,:,0] # Copies the data
- bad_pixel_count = np.sum(np.mod(reds, 2)) # check number of pixels with an odd intersection count
+
+ # We can't just read the image since the pixels are aligned to internal memory positions.
+ # xray_img.byteCount() != xray_img.width() * xray_img.height() * 3
+ # The byte count is a little higher sometimes. We need to check the data per line, but fast using Numpy.
+ # See https://stackoverflow.com/questions/5810970/get-raw-data-from-qimage for a description of the problem.
+ # We can't use that solution though, since it doesn't perform well in Python.
+ class QImageArrayView:
+ """
+ Class that ducktypes to be a Numpy ndarray.
+ """
+ def __init__(self, qimage):
+ self.__array_interface__ = {
+ "shape": (qimage.height(), qimage.width()),
+ "typestr": "|u4", # Use 4 bytes per pixel rather than 3, since Numpy doesn't support 3.
+ "data": (int(qimage.bits()), False),
+ "strides": (qimage.bytesPerLine(), 3), # This does the magic: For each line, skip the correct number of bytes. Bytes per pixel is always 3 due to QImage.Format.Format_RGB888.
+ "version": 3
+ }
+ array = np.asarray(QImageArrayView(xray_img)).view(np.dtype({
+ "r": (np.uint8, 0, "red"),
+ "g": (np.uint8, 1, "green"),
+ "b": (np.uint8, 2, "blue"),
+ "a": (np.uint8, 3, "alpha") # Never filled since QImage was reformatted to RGB888.
+ }), np.recarray)
+ bad_pixel_count = np.sum(np.mod(array.r, 2)) # check number of pixels in the red channel with an odd intersection count
if bad_pixel_count > 10: # allow for 10 pixels to be erroneously marked as problematic
self._next_xray_checking_time = time.time() + self._xray_warning_cooldown