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