Bild einer Wärmebildkamera in PyQt5 Label ausgeben

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
DSY
User
Beiträge: 1
Registriert: Dienstag 1. Juni 2021, 13:34

Dienstag 1. Juni 2021, 13:57

Hallo liebe Community, ich habe folgendes Problem:

Mir wurde folgender Code zur Verfügung gestellt um Temperaturdaten und Videomaterial von einer Wärmebildkamera zu erhalten:

Code: Alles auswählen

from ctypes.util import find_library
import numpy as np
import ctypes as ct
import cv2
import os

#Define EvoIRFrameMetadata structure for additional frame infos
class EvoIRFrameMetadata(ct.Structure):
    
      
    _fields_ = [("counter", ct.c_uint),
                 ("counterHW", ct.c_uint),
                 ("timestamp", ct.c_longlong),
                 ("timestampMedia", ct.c_longlong),
                 ("flagState", ct.c_int),
                 ("tempChip", ct.c_float),
                 ("tempFlag", ct.c_float),
                 ("tempBox", ct.c_float),
                 ]
    
   


    def run(self):
        # load library
        if os.name == 'nt':
                #windows:
                libir = ct.CDLL("C:\\Users\\Danie\\Desktop\\libirimager-8.7.0-windows\\irDirectSDK\\sdk\\x64\\libirimager.dll") 
        else:
                #linux:
                libir = ct.cdll.LoadLibrary(ct.util.find_library("irdirectsdk"))

        #path to config xml file
        pathXml = ct.c_char_p(b'C:\\Users\\Danie\\Desktop\\libirimager-8.7.0-windows\\irDirectSDK\\examples\\python\\generic.xml')

        # init vars
        pathFormat = ct.c_char_p()
        pathLog = ct.c_char_p(b'logfilename')

        palette_width = ct.c_int()
        palette_height = ct.c_int()

        thermal_width = ct.c_int()
        thermal_height = ct.c_int()

        serial = ct.c_ulong()
        

        # init lib
        ret = libir.evo_irimager_usb_init(pathXml, pathFormat, pathLog)
        if ret != 0:
                print("error at init")
                exit(ret)

        # get the serial number
        ret = libir.evo_irimager_get_serial(ct.byref(serial))
        print('serial: ' + str(serial.value))

        # get thermal image size
        libir.evo_irimager_get_thermal_image_size(ct.byref(thermal_width), ct.byref(thermal_height))
        print('thermal width: ' + str(thermal_width.value))
        print('thermal height: ' + str(thermal_height.value))

        # init thermal data container
        np_thermal = np.zeros([thermal_width.value * thermal_height.value], dtype=np.uint16)
        npThermalPointer = np_thermal.ctypes.data_as(ct.POINTER(ct.c_ushort))

        # get palette image size, width is different to thermal image width duo to stride alignment!!!
        libir.evo_irimager_get_palette_image_size(ct.byref(palette_width), ct.byref(palette_height))
        print('palette width: ' + str(palette_width.value))
        print('palette height: ' + str(palette_height.value))

        # init image container
        np_img = np.zeros([palette_width.value * palette_height.value * 3], dtype=np.uint8)
        npImagePointer = np_img.ctypes.data_as(ct.POINTER(ct.c_ubyte))


        # capture and display image till q is pressed
        while chr(cv2.waitKey(1) & 255) != 'q':
                #get thermal and palette image with metadat
                ret = libir.evo_irimager_get_thermal_palette_image_metadata(thermal_width, thermal_height, npThermalPointer, palette_width, palette_height, npImagePointer, ct.byref(metadata))

                if ret != 0:
                        print('error on evo_irimager_get_thermal_palette_image ' + str(ret))
                        continue

                #calculate total mean value
                mean_temp = np_thermal.mean()
                mean_temp = mean_temp / 10. - 100

                print('mean temp: ' + str(mean_temp))

                #display palette image
                frame = np_img.reshape(palette_height.value, palette_width.value, 3)[:,:,::-1]
                cv2.imshow('image',frame)
    
                
             

                
        # clean shutdown
        libir.evo_irimager_terminate()
        cv2.destroyAllWindows()
        
metadata = EvoIRFrameMetadata()
metadata.run()
Die Grundsätzlich ausgabe mit opencv funktioniert reibungslos. Gerne würde ich das Videomaterial und die Temperaturdaten jedoch in einer GUI(PyQt5) ausgeben. Dieses gelingt mir bisher nicht. Mit anderen Kameras war es mir bereits möglich mit Hilfe einer Kombination von Thread und Pixmap Videomaterial in einem Label ausgeben zu lassen. Dieses lässt sich jedoch leider nicht analog auf dieses Problem übertragen. Vielleicht hat ja jemand eine Idee, einen Tipp oder einen Denkanstoß für mich.
__deets__
User
Beiträge: 10079
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dienstag 1. Juni 2021, 17:10

Der Frame ist doch einfach ein numpy Array. Was ist daran anders als an all den anderen, bei denen es geht? Was versuchst du, und was geht nicht?
Sirius3
User
Beiträge: 14808
Registriert: Sonntag 21. Oktober 2012, 17:20

Mittwoch 2. Juni 2021, 08:04

Eingerückt wird immer mit 4 Leerzeichen pro Ebene, nicht mal 4 und mal 8.
Die Klasse `EvoIRFrameMetadata` ist seltsam. Du erzeugst ein leere Struktur und benutzt die nie.
`run` sollte also eine ganz normale Funktion sein.

Statt ctypes-Typen anzulegen, solltest Du den C-Funktionen Funktionssignaturen geben und ctypes die Konvertierung automatisch machen lassen.
`exit` sollte nicht irgendwo mitten drin auftauchen. Benutze Exceptions.
String setzt man nicht per str und + zusammen, sondern benutzt Formatstrings.

Code: Alles auswählen

from ctypes.util import find_library
import numpy as np
import ctypes as ct
import cv2
import os

#Define EvoIRFrameMetadata structure for additional frame infos
class EvoIRFrameMetadata(ct.Structure):
    _fields_ = [("counter", ct.c_uint),
                 ("counterHW", ct.c_uint),
                 ("timestamp", ct.c_longlong),
                 ("timestampMedia", ct.c_longlong),
                 ("flagState", ct.c_int),
                 ("tempChip", ct.c_float),
                 ("tempFlag", ct.c_float),
                 ("tempBox", ct.c_float),
                 ]


def load_lib():
    # load library
    if os.name == 'nt':
        #windows:
        libir = ct.CDLL("C:\\Users\\Danie\\Desktop\\libirimager-8.7.0-windows\\irDirectSDK\\sdk\\x64\\libirimager.dll") 
    else:
        #linux:
        libir = ct.cdll.LoadLibrary(ct.util.find_library("irdirectsdk"))

    libir.evo_irimager_usb_init.argtypes = [ct.c_char_p, ct.c_char_p, ct.char_p]
    libir.evo_irimager_get_serial.argtypes = [ct.POINTER(ct.c_ulong)]
    libir.evo_irimager_get_thermal_image_size.argtypes = [ct.POINTER(ct.c_int), ct.POINTER(ct.c_int)]
    libir.evo_irimager_get_palette_image_size.argtypes = [ct.POINTER(ct.c_int), ct.POINTER(ct.c_int)]
    libir.evo_irimager_get_thermal_palette_image_metadata.argtypes = [
        ct.c_int. ct.c_int, ct.POINTER(ct.c_ushort),
        ct.c_int, ct.c_int, ct.POINTER(ct.c_ubytes),
        ct.POINTER(EvoIRFrameMetadata),
    ]
    return libir

def main():
    libir = load_lib()

    result = libir.evo_irimager_usb_init(
        b'C:\\Users\\Danie\\Desktop\\libirimager-8.7.0-windows\\irDirectSDK\\examples\\python\\generic.xml', 
        b'',
        b'logfilename')
    if result != 0:
        raise RuntimeError("error at init", result)
    
    # get the serial number
    serial = ct.c_ulong()
    libir.evo_irimager_get_serial(ct.byref(serial))
    print(f'serial: {serial.value}')

    # get thermal image size
    thermal_width = ct.c_int()
    thermal_height = ct.c_int()
    libir.evo_irimager_get_thermal_image_size(ct.byref(thermal_width), ct.byref(thermal_height))
    print(f'thermal width: {thermal_width.value}')
    print(f'thermal height: {thermal_height.value}')

    # get palette image size, width is different to thermal image width duo to stride alignment!!!
    palette_width = ct.c_int()
    palette_height = ct.c_int()
    libir.evo_irimager_get_palette_image_size(ct.byref(palette_width), ct.byref(palette_height))
    print(f'palette width: {palette_width.value}')
    print(f'palette height: {palette_height.value}')

    
    # init thermal data container
    thermal_image = np.zeros([thermal_width.value, thermal_height.value], dtype=np.uint16)
    # init image container
    palette_image = np.zeros([palette_width.value, palette_height.value, 3], dtype=np.uint8)


    # capture and display image till q is pressed
    metadata = EvoIRFrameMetadata()
    while chr(cv2.waitKey(1) & 255) != 'q':
            #get thermal and palette image with metadat
            result = libir.evo_irimager_get_thermal_palette_image_metadata(
                thermal_width, thermal_height, thermal_image.ctypes.data_as(ct.POINTER(ct.c_ushort)),
                palette_width, palette_height, palette_image.ctypes.data_as(ct.POINTER(ct.c_ubyte)),
                ct.byref(metadata)
            )

            if ret != 0:
                print(f'error on evo_irimager_get_thermal_palette_image {result}')
            else:
                #calculate total mean value
                mean_thermal = thermal_image.mean() / 10 - 100
                print(f'mean temp: {mean_temp}')

                #display palette image
                cv2.imshow('image', palette_image[:,:,::-1])
            
    # clean shutdown
    libir.evo_irimager_terminate()
    cv2.destroyAllWindows()

if __name__ == "__main__":
    main()
Benutzeravatar
__blackjack__
User
Beiträge: 8869
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Mittwoch 16. Juni 2021, 15:09

Im nächsten Schritt könnte man die Low-Level-Details der C-API und des `ctypes`-Wrappers vom Code in der `main()` trennen. Da sollte man nicht so viel mit `ctypes`-Typen hantieren müssen und statt Rückgabewerte zu prüfen kann man den `ctypes`-Wrapper so schreiben, das Ausnahmen ausgelöst werden. Die C-API ist ja so entworfen, das *jede* der Funktionen ``int`` als Rückgabetyp hat und ein Wert ≠0 einen Fehler signalisiert.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import ctypes as ct
import os
from collections import namedtuple
from ctypes.util import find_library

import cv2
import numpy as np

if os.name == "nt":
    LIB_IR_IMAGER = ct.CDLL(
        "C:/Users/Danie/Desktop/libirimager-8.7.0-windows/irDirectSDK/sdk/x64/libirimager.dll"
    )
else:
    LIB_IR_IMAGER = ct.cdll.LoadLibrary(find_library("irdirectsdk"))


class IRImagerError(RuntimeError):
    def __init__(self, message, error_number):
        RuntimeError.__init__(self, message)
        self.error_number = error_number


class EvoIRFrameMetadata(ct.Structure):
    _fields_ = [
        ("counter", ct.c_uint),
        ("counterHW", ct.c_uint),
        ("timestamp", ct.c_longlong),
        ("timestampMedia", ct.c_longlong),
        ("flagState", ct.c_int),
        ("tempChip", ct.c_float),
        ("tempFlag", ct.c_float),
        ("tempBox", ct.c_float),
    ]


def _check_result(value):
    if value != 0:
        raise IRImagerError(f"error {value}", value)


_usb_init = LIB_IR_IMAGER.evo_irimager_usb_init
_usb_init.argtypes = [ct.c_char_p, ct.c_char_p, ct.c_char_p]
_usb_init.restype = _check_result

_terminate = LIB_IR_IMAGER.evo_irimager_terminate
_terminate.argtypes = []
_terminate.restype = _check_result

_get_serial = LIB_IR_IMAGER.evo_irimager_get_serial
_get_serial.argtypes = [ct.POINTER(ct.c_ulong)]
_get_serial.restype = _check_result

_get_thermal_image_size = LIB_IR_IMAGER.evo_irimager_get_thermal_image_size
_get_thermal_image_size.argtypes = [ct.POINTER(ct.c_int), ct.POINTER(ct.c_int)]
_get_thermal_image_size.restype = _check_result

_get_palette_image_size = LIB_IR_IMAGER.evo_irimager_get_palette_image_size
_get_palette_image_size.argtypes = [ct.POINTER(ct.c_int), ct.POINTER(ct.c_int)]
_get_palette_image_size.restype = _check_result

_get_thermal_palette_image_metadata = (
    LIB_IR_IMAGER.evo_irimager_get_thermal_palette_image_metadata
)
_get_thermal_palette_image_metadata.argtypes = [
    ct.c_int,
    ct.c_int,
    ct.POINTER(ct.c_ushort),
    ct.c_int,
    ct.c_int,
    ct.POINTER(ct.c_ubyte),
    ct.POINTER(EvoIRFrameMetadata),
]
_get_thermal_palette_image_metadata.restype = _check_result


CaptureResult = namedtuple(
    "CaptureResult", "thermal_image palette_image metadata"
)


class IRImager:
    def __init__(self, xml_config, formats_def=None, log_file=None):
        _usb_init(
            xml_config.encode(),
            formats_def.encode() if formats_def else formats_def,
            log_file.encode() if log_file else log_file,
        )

    def __enter__(self):
        return self

    def __exit__(self, _type, _value, _traceback):
        self.close()

    @property
    def serial(self):
        serial = ct.c_ulong()
        _get_serial(ct.byref(serial))
        return serial.value

    @property
    def thermal_image_size(self):
        width = ct.c_int()
        height = ct.c_int()
        _get_thermal_image_size(ct.byref(width), ct.byref(height))
        return width.value, height.value

    @property
    def palette_image_size(self):
        width = ct.c_int()
        height = ct.c_int()
        _get_palette_image_size(ct.byref(width), ct.byref(height))
        return width.value, height.value

    def capture(self):
        thermal_size = self.thermal_image_size
        thermal_image = np.zeros(thermal_size, dtype=np.uint16)
        palette_size = self.palette_image_size
        palette_image = np.zeros((*palette_size, 3), dtype=np.uint8)
        metadata = EvoIRFrameMetadata()
        _get_thermal_palette_image_metadata(
            *thermal_size,
            thermal_image.ctypes.data_as(ct.POINTER(ct.c_ushort)),
            *palette_size,
            palette_image.ctypes.data_as(ct.POINTER(ct.c_ubyte)),
            ct.byref(metadata),
        )
        return CaptureResult(thermal_image, palette_image, metadata)

    close = _terminate


def main():
    with IRImager(
        "C:\\Users\\Danie\\Desktop\\libirimager-8.7.0-windows\\irDirectSDK\\examples\\python\\generic.xml",
        log_file="logfilename",
    ) as imager:
        print(f"serial: {imager.serial}")

        thermal_width, thermal_height = imager.thermal_image_size
        print(f"thermal width: {thermal_width}")
        print(f"thermal height: {thermal_height}")
        #
        # Get palette image size, width can be different to thermal image width
        # due to stride alignment!
        #
        palette_width, palette_height = imager.palette_image_size
        print(f"palette width: {palette_width}")
        print(f"palette height: {palette_height}")

        while chr(cv2.waitKey(1) & 255) != "q":
            try:
                result = imager.capture()
            except IRImagerError as error:
                print(f"error on capture: {error}")
            else:
                thermal_mean = result.thermal_image.mean() / 10 - 100
                print(f"mean temp: {thermal_mean}")
                #
                # The negative step in the slicing is there to reorder the RGB
                # values for OpenCV.
                #
                cv2.imshow("image", result.palette_image[:, :, ::-1])

    cv2.destroyAllWindows()


if __name__ == "__main__":
    main()
Q: What is the volume of a pizza of radius z and thickness a?
A: pi·z·z·a
Antworten