2

I see the QImage.bits() function, and the QImage.constBits(), but both return a voidptr and I'm not sure in python what you can do with that. I am familiar with the C++ syntax, but not for python.

As for data type, i mean this: A black and white pixel would be equal to [[0,0,0],[255,255,255]] where the QImage is Format.RGB888

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Jonathan
  • 6,741
  • 7
  • 52
  • 69
  • Have nested loops for x and y, and go through the image pixel by pixel? Also, what do you mean by "pixel", what data type? – hyde Aug 17 '15 at 20:47
  • I know you could do that, but that sounds terribly slow. In C++ you can do a no-op pointer cast. – Jonathan Aug 17 '15 at 20:48
  • Well, if you want a Python list, you have to write the loops. You could probably do them,in C++ or C, I assume Python has API for adding to lists from native code. Or then you could look for an image manipulation library, which can take the raw QImage data and give you better Python API. Depends on what you actually want to achieve. Btw, QImage::bits seems to give uchar pointer, not void pointer (or is voidptr some Python thing?). – hyde Aug 17 '15 at 20:58
  • I think it's a sipwrapper concept. It has something to do with how C++ got converted to python. – Jonathan Aug 17 '15 at 20:59
  • @JonathanLeaders. Of course it's going to be relatively slow: do you really think all those python list objects will cost nothing to create? Maybe you should re-think whether you actually need that specific data structure. I suggest you take a look at the docs for the [sip module](http://pyqt.sourceforge.net/Docs/sip4/python_api.html) and see what can be achieved by manipulating the [voidptr](http://pyqt.sourceforge.net/Docs/sip4/python_api.html#sip.voidptr) object directly. – ekhumoro Aug 17 '15 at 21:53
  • I guess I wish there was a 2D UCHAR* array in SIP, but it looks like there isn't – Jonathan Aug 17 '15 at 22:45
  • 1
    I guess you could make one, where you override __index__() or something? – Jonathan Aug 17 '15 at 22:51
  • 2
    `sip.voidptr` has an `asarray` method which returns a sequence of unsigned bytes – Tim Wakeham Aug 18 '15 at 04:02

2 Answers2

1

See the youtube video

I hope that I understand your needs, here I put together an app. That will convert QImage to List of Quadruplets RGBA Value, I hope that it help you at least to figure out how to do it, you may implement it different for your needs, you may use matrix/matrix manipulation lib. Like NumPy or any other matrix manipulation lib.

But, in this case I did not create any dependency, because I think is better to stay simple and if you need something more complex you can dig in...

You going to need an image, to keep it simple I will provide 3 images, each of them with different size and pixel colors Image 1:
Image 1Image 2:
Image 2Image 3:
Image 3

You can also check my website to see the code

Here Is the code:

from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys


class main_window(QDialog):
    def __init__(self):
        QDialog.__init__(self)

        # Create a QTextBrowser
        qtb_rgba = QTextBrowser(self)
        # Create an empty list
        li_pixels = list()


        #!! QImage to list of pixels !! >>> RGB format
        image = QImage()
        load_image = image.load("test_img_3x3", ".jpg")
        # Make sure you got an image named "test_img_3x3" .jpg extension , it needs to sit on your HDD,
        # in the root from where you run the script
        # create it with the size of 3x3 , do it in Paint, or any other 2d image manipulation software

        # An assert error will occur if fails to open your image
        assert load_image == True

        # Find more data about our image
        # In our case is obvios that we create with our hand an image .jpg with a size of 3x3
        # Even so, the functions bellow will may help us sometimes

        # Obtain image size
        image_width = image.width()
        image_height = image.height()
        print("Your image size is:\nWidth: " + str(image_width) + " Height: " + str(image_height) + "\n" + str(image_width) + "x" + str(image_height))


        # Get QRGB values
        qrgb = image.pixel(0, 0)
        print("QRGB Values: " + str(qrgb))
        # Convert it to QColor
        qrgb_to_QCol = QColor(qrgb)
        # Once you got it converted in QColor you got a lot of freedom to choose from,
        # you can convert it to some of the most popular formats like:
        # CMYK, HSL, RGB, RGBA, magenta, or even individual chanels R , G , B  + a lot of other formats, even Floats...

        # Convert it to RGBA
        rgba = qrgb_to_QCol.getRgb()
        print("RGBA Values: " + str(rgba))

        # In order to achieve our goal: list of pixels for the entire image
        # we got many ways to achieve it, depending of your needs you may implelent it different
        # I will continue by producing a list of quadruplets values, 
        # from left to right - top to bottom 

        # The Quadruplets value for the list will be, which mean that 0 will hold value 0 on list , 1 hold 1 and so on...
        # y _ _
       #x|0 1 2
        #|3 4 5
        #|6 7 8

        # And the position will be:
        # y     _     _
       #x|0.0   1.0   2.0
        #|0.1   1.1   2.1
        #|0.2   1.2   2.2

        # So let`s do it

        # Generate a list of numbers for width
        li_num_width = []
        for i in range(image_width):
            li_num_width.append(i)

        # Generate a list of numbers for height
        li_num_height = []
        for i in range(image_height):
            li_num_height.append(i)

        # List for x num
        x = [li_num_width for i in range(len(li_num_height))]
        print("\nX list is:\n" + str(x))

        # List for y num
        for i in range(len(li_num_height)):
            y = [[i]*len(li_num_width) for i in range(len(li_num_height))]
        print("\nY list is:\n" + str(y))

        
        row_el_li = []
        row_el_li_y = []

        # Obtain list numbers for x
        for i in range(len(li_num_height)):
            row = x[i]
            for i in range(len(li_num_width)):
                row_el = row[i]
                #print(row_el)
                row_el_li.append(row_el)

        print("\nRow Elements list x: \n" + str(row_el_li))
        
        # Obtain list numbers for y
        for i in range(len(li_num_height)):
            row_y = y[i]
            for i in range(len(li_num_width)):
                row_el_y = row_y[i]
                #print(row_el_y)
                row_el_li_y.append(row_el_y)

        print("\nRow Elements list y: \n" + str(row_el_li_y))
        
        # Create a list, which eventualy will hold qrgb values, which is our goal
        qrgb_li = []
        # How many values will the list hold? or How many pixels in the image do we have?
        num_pixels = len(li_num_width) * len(li_num_height)
        print("\nNumber of Pixels:" + str(num_pixels))
        
        for i in range(num_pixels):
            ordered_qrgb = image.pixel(row_el_li[i], row_el_li_y[i])
            qrgb_li.append(ordered_qrgb)

        
        # One more step lets convert from QRGB list to RGBA list, which will lead us to the end of this tutorial
        rgba_li = []

        for i in range(len(qrgb_li)):
            qrgb_li_to_QCol = QColor(qrgb_li[i])
            rgba_set = qrgb_li_to_QCol.getRgb()
            rgba_li.append(rgba_set)

        print("\nList of Quadruplets RGBA Value: \n" + str(rgba_li))
        
        
        #!! QImage to list of pixels End !!

        qtb_rgba.setText(str(rgba_li))
        #.......
        self.setFixedSize(250, 300)
        self.setWindowTitle("QImage to list of pixels")

#.......3D Sasu Catalin
app = QApplication(sys.argv)
dialog = main_window()
dialog.show()
sys.exit(app.exec_())

I hope that help you out, and if you need more details about this subject you can visit my website from time to time...

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • This indeed will work. It is slow compared to C++ pointer magic, but hey, I did specify a python list. I guess what I really wanted was to do the conversion via memory hopping on a __index__() operator for speed reasons, but if I'm going to be hopping in a loop this may be the fastest way. Thanks for the answer – Jonathan Aug 30 '15 at 18:55
1

For anyone who still needs to know how to do this, here's some code I'm currently using to get pixel data from PyQt5 to PyOpenCV without having to save and load it.

Since PyOpenCV uses numpy.array in place of OpenCV Mats, this should answer the question.

# Convert to 32-bit RGBA with solid opaque alpha
# and get the pointer numpy will want.
# 
# Cautions:
# 1. I think I remember reading that PyQt5 only has
# constBits() and PySide2 only has bits(), so you may
# need to do something like `if hasattr(...)` for
# portability.
# 
# 2. Format_RGBX8888 is native-endian for your 
# platform and I suspect this code, as-is,
# would break on a big-endian system.
im_in = im_in.convertToFormat(QImage.Format_RGBX8888)
ptr = im_in.constBits()
ptr.setsize(im_in.byteCount())

# Convert the image into a numpy array in the 
# format PyOpenCV expects to operate on, explicitly
# copying to avoid potential lifetime bugs when it
# hasn't yet proven a performance issue for my uses.
cv_im_in = np.array(ptr, copy=True).reshape(
    im_in.height(), im_in.width(), 4)
cv_im_in = cv.cvtColor(cv_im_in, cv.COLOR_BGRA2RGB)

I'm still rapidly experimenting with the prototype, so haven't yet optimized it, but here's how to do so:

  1. Verify that there's no risk of dereferencing a dangling pointer and remove copy=True from the np.array constructor.
  2. Find a format that QImage.convertToFormat can produce which the relevant OpenCV modules can operate on directly and skip the cv.cvtColor step. (I think Format_BGR888 is that format, but I still need to support Qt versions prior to 5.14 in my project at the moment.)
ssokolow
  • 14,938
  • 7
  • 52
  • 57