testPython/PY_NTNDA_Viewer

2020.06.26

Author:
Marty Kraimer
WARNING
This is a work in progress

This product is available via an open source license

Table of Contents

Introduction

This document briefly discusses: 1) ADViewers/NTNDA_Viewer, a Python Image viewer that comes with areaDetector/ADViewers. 2) testPython/NTNDA_Viewer, another implementation, 3) ImageJ and 4) QImage

See index for installation instructions.

The version that comes with areaDetector is PVAPY_NTNDA_Viewer with documentation at: PVAPY_NTNDA_Viewer.html

After starting ADSimDetector, as described in the previous link, you are ready the run the example, which is located in directory testPython/PY_NTNDA_Viewer

Run the following:

python PVAPY_NTNDA_Viewer.py &

ADViewers/NTNDA_Viewer

ADViewer uses pyqtgraph.widgets.RawImageWidget

The following code is used to display an image

class Image_Display(RawImageWidget,QWidget) :
    def __init__(self,parent=None, **kargs):
        RawImageWidget.__init__(self, parent=parent,scaled=True)
 """ THIS COMMENT IS FROM RawImageWidget
        Setting scaled=True will cause the entire image to be displayed within the boundaries of the widget.
        This also greatly reduces the speed at which it will draw frames.
"""
...
    def display(self,image,pixelLevels) :
        self.setImage(image,levels=pixelLevels)
"" THIS COMMENT IS FROM RawImageWidget
        img must be ndarray of shape (x,y), (x,y,3), or (x,y,4).
        Extra arguments are sent to functions.makeARGB
"""
...

setImage is a method in RawImage Widget that creates a pixarray that can be used to create a QImage. Some notes:

scaled=True
This results in a Python loop accessing the entire pixel array. For example (nx,ny,nz)=(800,800,3) means 1,920,000 elements
levels=pixelLevels
This also results in a Python loop accessing the entire pixel array.
image dtype
numpy is used to convert the image to a numpy array with dtype uint8. Since this is done by numpy it is far more efficent than using a python for loop. But no matter what type the original image has, it is converted to an image with uint8 pixels.
Threads
setImage is called via the QApplication thread. The actual image is generated in another thread and by C/C++ code. Thus there could potentially be multiple images being generated simultaneously. But the two python loops result in the images almost always being generated faster than Python can ask for new images.

testPython/NTNDA_Viewer

ADViewer uses numpyImage
Some Notes:

performance
There are no Python loops accessing the entire pixel array. Thus it has much better performance than ADViewers/NTNDA_Viewer.
pixelLevels
This is not supported.
image dtype
If the image dtype is unit8 the image is not modified. In all other cases numpy converts the image array to have dtype uint8.

NOTE: This seems to work well for ADSimDetector but what about real detectors.
Threads
The first request to display an image is done without waiting. Further requests wait until the image for the previous request has been displayed.

ADViewers/ImageJ/EPICS_areaDetector

For monchrome images the following is done:

uint8
ij.process.ByteProcessor is used.
unit16
ij.process.ShortProcessor is used.
all other types
ij.process.FloatProcessor is used.

For color images ij.process.ColorProcessor is used.

ImageJ

ImageJ states that ImageJ can handle the following:

8-bit Images that can display 256 gray levels (integers only).
16-bit Images that can display 65536 gray levels (integers only).
32-bit Images that can display 4294967296 gray levels (real numbers).
    In 32-bit images, pixels are described by floating point values and can have any intensity value.
RGB Color Color Images that can display 256 values in the Red, Green and Blue channel.
  These are 24-bit color images.
  RGB color images can also be 32-bit color images (24-bit color images with additional eight bits coding alpha blending values,
  i.e., transparency).

QImage

QImage states the following:

ConstantValueDescription
QImage::Format_Invalid0The image is invalid.
QImage::Format_Mono1The image is stored using 1-bit per pixel. Bytes are packed with the most significant bit (MSB) first.
QImage::Format_MonoLSB2The image is stored using 1-bit per pixel. Bytes are packed with the less significant bit (LSB) first.
QImage::Format_Indexed83The image is stored using 8-bit indexes into a colormap.
QImage::Format_RGB324The image is stored using a 32-bit RGB format (0xffRRGGBB).
QImage::Format_ARGB325The image is stored using a 32-bit ARGB format (0xAARRGGBB).
QImage::Format_ARGB32_Premultiplied6The image is stored using a premultiplied 32-bit ARGB format (0xAARRGGBB), i.e. the red, green, and blue channels are multiplied by the alpha component divided by 255. (If RR, GG, or BB has a higher value than the alpha channel, the results are undefined.) Certain operations (such as image composition using alpha blending) are faster using premultiplied ARGB32 than with plain ARGB32.
QImage::Format_RGB167The image is stored using a 16-bit RGB format (5-6-5).
QImage::Format_ARGB8565_Premultiplied8The image is stored using a premultiplied 24-bit ARGB format (8-5-6-5).
QImage::Format_RGB6669The image is stored using a 24-bit RGB format (6-6-6). The unused most significant bits is always zero.
QImage::Format_ARGB6666_Premultiplied10The image is stored using a premultiplied 24-bit ARGB format (6-6-6-6).
QImage::Format_RGB55511The image is stored using a 16-bit RGB format (5-5-5). The unused most significant bit is always zero.
QImage::Format_ARGB8555_Premultiplied12The image is stored using a premultiplied 24-bit ARGB format (8-5-5-5).
QImage::Format_RGB88813The image is stored using a 24-bit RGB format (8-8-8).
QImage::Format_RGB44414The image is stored using a 16-bit RGB format (4-4-4). The unused bits are always zero.
QImage::Format_ARGB4444_Premultiplied15The image is stored using a premultiplied 16-bit ARGB format (4-4-4-4).
QImage::Format_RGBX888816The image is stored using a 32-bit byte-ordered RGB(x) format (8-8-8-8). This is the same as the Format_RGBA8888 except alpha must always be 255. (added in Qt 5.2)
QImage::Format_RGBA888817The image is stored using a 32-bit byte-ordered RGBA format (8-8-8-8). Unlike ARGB32 this is a byte-ordered format, which means the 32bit encoding differs between big endian and little endian architectures, being respectively (0xRRGGBBAA) and (0xAABBGGRR). The order of the colors is the same on any architecture if read as bytes 0xRR,0xGG,0xBB,0xAA. (added in Qt 5.2)
QImage::Format_RGBA8888_Premultiplied18The image is stored using a premultiplied 32-bit byte-ordered RGBA format (8-8-8-8). (added in Qt 5.2)
QImage::Format_BGR3019The image is stored using a 32-bit BGR format (x-10-10-10). (added in Qt 5.4)
QImage::Format_A2BGR30_Premultiplied20The image is stored using a 32-bit premultiplied ABGR format (2-10-10-10). (added in Qt 5.4)
QImage::Format_RGB3021The image is stored using a 32-bit RGB format (x-10-10-10). (added in Qt 5.4)
QImage::Format_A2RGB30_Premultiplied22The image is stored using a 32-bit premultiplied ARGB format (2-10-10-10). (added in Qt 5.4)
QImage::Format_Alpha823The image is stored using an 8-bit alpha only format. (added in Qt 5.5)
QImage::Format_Grayscale824The image is stored using an 8-bit grayscale format. (added in Qt 5.5)
QImage::Format_Grayscale1628The image is stored using an 16-bit grayscale format. (added in Qt 5.13)
QImage::Format_RGBX6425The image is stored using a 64-bit halfword-ordered RGB(x) format (16-16-16-16). This is the same as the Format_RGBA64 except alpha must always be 65535. (added in Qt 5.12)
QImage::Format_RGBA6426The image is stored using a 64-bit halfword-ordered RGBA format (16-16-16-16). (added in Qt 5.12)
QImage::Format_RGBA64_Premultiplied27The image is stored using a premultiplied 64-bit halfword-ordered RGBA format (16-16-16-16). (added in Qt 5.12)
QImage::Format_BGR88829The image is stored using a 24-bit BGR format. (added in Qt 5.14)

Some Comments

ImageJ vs QImage

They appear to have a lot of similaity in the types of images they support. They also have similar codec support.

ADSimDetector

This has data types for signed and unsigned integers of length 8, 16, 32, and 64. It also has data types float and double.

testPython/PY_NTNDA_Viewer

When simulation mode is LinearRamp and gain is anything, testPython/PY_NTNDA_Viewer appears to work well with all data types. When simulation mode is peaks the gain must be set to 255.

ADViewers/PY_NTNDA_Viewer

When simulation mode is peaks the gain must be set to 255 for int8 and uint8, 65535 for int16 and uint16, and who knows what for other datatypes. I think this is a problem with similation mode peaks.

ADViewers/PY_NTNDA_Viewer works well only for int8 and uint8.

Questions

Can PY_NTNDA_Viewer take advantage of more features of QImage?

Can the ImageJ viewer take advantage of more features of ImageJ?