2

I have a grayscale image in PyQt and want to get the color of a specific pixel. Grayscale images use a color table with up to 256 entries.

from PyQt4 import QtGui

def test():
    image = QtGui.QImage(100, 100, QtGui.QImage.Format_Indexed8)
    image.load("d:/1.bmp")
    print image.pixel(1, 1)
    print image.pixelIndex(1, 1)

    image.setColorTable(list([i] for i in range(256)))
    print image.colorTable()

import sys
app = QtGui.QApplication(sys.argv)
window = test()
sys.exit(app.exec_())

This is d:/1.bmp: This is d:/1.bmp.

I have the following issues:

  • image.colorTable() returns a list of 256 times the number 4294967295L (which is 2^32-1) altough I have just set the color table to 0 to 255.

  • image.pixelIndex(1, 1) gives the message "QImage::pixelIndex: Not applicable for 32-bpp images (no palette)" although the format is set to Indexed8 (and isGrayscale() returns true).

  • image.pixel(1, 1) returns 4278190080 (also when I set the format to Format_RGB32). What is this color? (It should be black.)

New code according to the answer by ekhumoro:

from PyQt4 import QtGui

def test():
    image = QtGui.QImage(100, 100, QtGui.QImage.Format_Indexed8)
    image.load("d:/1.bmp")
    image2 = image.convertToFormat(QtGui.QImage.Format_Indexed8)
    print "format:", image2.format()
    print "pixel color:", QtGui.qGray(image2.pixel(1, 1))

    image2.setColorTable(list([QtGui.qRgb(i, i, i)] for i in range(256)))
    print "color table:", image2.colorTable()

import sys
app = QtGui.QApplication(sys.argv)
window = test()
sys.exit(app.exec_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Michael Westwort
  • 914
  • 12
  • 24

1 Answers1

3

The docs for QImage have all the answers to your questions:

  • From the entry for setColorTable():

    Sets the color table used to translate color indexes to QRgb values, to the specified colors. When the image is used, the color table must be large enough to have entries for all the pixel/index values present in the image, otherwise the results are undefined.

  • From the entry for load():

    The loader attempts to read the image using the specified format, e.g., PNG or JPG. If format is not specified (which is the default), the loader probes the file for a header to guess the file format.

    So the format you passed to the QImage constructor is irrelevant, and I would predict that print image.format() will output a value > 3. Also, looking at the entry for pixelIndex() I see this:

    If position is not valid, or if the image is not a paletted image (depth() > 8), the results are undefined.

  • From the entry for pixel():

    QRgb QImage::pixel(int x, int y) const.

    So this function returns a value of type QRgb, which the docs describe thus:

    An ARGB quadruplet on the format #AARRGGBB, equivalent to an unsigned int.

    Handily, Qt provides some functions for extracting the various components of QRgb values. One of these is qGray, which is described thus:

    Returns a gray value (0 to 255) from the given ARGB quadruplet rgb.

    (NB: these functions are in the global namespace, so in PyQt, you'll find them in the QtGui module).

ekhumoro
  • 115,249
  • 20
  • 229
  • 336
  • Okay, I understand the return value of index. I replaced `image.pixel(1, 1)` by `QtGui.qGray(image.pixel(1, 1))`. For the color table regarding your answer I thought that `image.setColorTable(list([QtGui.qRgb(i, i, i)] for i in range(256)))` would create a suitable color table (containing all possible grayscale values) but still `image.colorTable()` returns undefined values. And I don't understand what the color table has to do with the format of the loaded image: The format in load refers to jpg, bmp, png etc. which I think is independent from monochrome, 8 bit or 32 bit. – Michael Westwort Aug 06 '16 at 13:28
  • Regarding `image.format`: Indeed 4 (RGB32) is returned. So I added `image.convertToFormat(QtGui.QImage.Format_Indexed8)` after loading the bmp file. However, `image.format` still returns 4, and the color table is still nonsense. – Michael Westwort Aug 06 '16 at 14:02
  • 1
    @MichaelWestwort. Please see the first line of my answer ;-) The `convertToFormat()` function does not do in-place modification. – ekhumoro Aug 06 '16 at 19:24
  • Also very true. So I amended the code accordingly and create now a new image. The format is 3 (Indexed8) now, indeed. However, the color table is still [4294967295L]*256. This is although isGrayscale() returns true and the color table contains all 256 grayscale colors (as far as I understand now correctly how colors are defined). – Michael Westwort Aug 06 '16 at 21:37
  • No, I was wrong. isGrayscale returns false for image2, image2 is shown all white when drawn, however reading all pixels returns the correct values for red, green and blue, all three being equal for each pixel. – Michael Westwort Aug 06 '16 at 22:03
  • isGrayscale returns false because for 8 bit images it gets that information from the color table (which is still wrong). However, if I set image2 to RGB32 isGrayscale returns true as it should be. – Michael Westwort Aug 06 '16 at 22:15