Problem mit Process(target=, args=)

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
DmonoFET91
User
Beiträge: 2
Registriert: Dienstag 27. Juni 2023, 13:26

Hey,

ich bin neu hier und Progarmmiere erst seit zwei Monaten mit Python und habe deshalb noch viele Verständnisprobleme. Ich habe mir jetzt ein Programm geschrieben, mit dem ich mehrere Kameras im Live ansehen kann und verwende dafür die Process funktion. Wenn ich nun die Funktion wie gedacht verwende "Process(target=display_camera_image, args=(hCam, pcImageMemory, MemID, window_name))" dann werden mir keine Kamera Bilder angezeigt, sondern dieser Punkt wird quasi übersrpungen. Wenn ich jetzt aber "Process(target=display_camera_image(hCam, pcImageMemory, MemID, window_name))" benutze, dann läuft das Programm wie gewünscht. Das wäre soweit auch okay, sobald ich aber versuche Knöpfe in die Bilder einzuprogrammieren, dann werden mir diese nicht angezeigt. Laut ChatGPT liegt der Fehler geanu darin, dass ich die Process Funktion falsch verwende. Ich verstehe aber noch nicht warum es dann nicht funktioniert, wenn ich die Process Funktion richtig verwende.

Hier auch einmal der ganze Code:
from multiprocessing import Process
from pyueye import ueye
import numpy as np
import cv2
import os
import time

# Anzahl der Kameras
num_cameras = 1
Größe_Kam_Bild = 45
Seitenverhältnis = 1
Höhenverhältnis = 1
Reihe = 3 # Wie viel Bilder werden horizontal gestappelt bevor es vertikal weiter geht

# Knöpfe: Status und Minutenzähler
button_states = {}
counter = {}



#Kamera Einstellungen
zoom = 2.02 #minimal 1.00 und maximal 2.02
Belichtungszeit = ueye.DOUBLE(66)
Fokus = ueye.INT(148)

#Einzelne Konstante
rectAOI = ueye.IS_RECT()
print(rectAOI)
pitch = ueye.INT()
nBitsPerPixel = ueye.INT(24) #24: bits per pixel for color mode; take 8 bits per pixel for monochrome
channels = 3 #3: channels for color mode(RGB); take 1 channel for monochrome
m_nColorMode = ueye.INT() # Y8/RGB16/RGB24/REG32
bytes_per_pixel = int(nBitsPerPixel / 8)


# Liste zur Speicherung der Kamera-Handles
hCam_list = []

# Liste zur Speicherung der Image-Memorys und MemIDs
pcImageMemory_list = []
MemID_list = []
sInfo_list = []
cInfo_list = []
width_list = []
height_list = []

# Liste zur Speicherung der Fensterbezeichnungen
window_names = []


# Initialisierung der Kameras
for i in range(num_cameras):
i=i+1
hCam = ueye.HIDS(i) # Geräte ID
hCam_list.append(hCam)

sInfo = ueye.SENSORINFO()
cInfo = ueye.CAMINFO()
pcImageMemory = ueye.c_mem_p()
MemID = ueye.int()

pcImageMemory_list.append(pcImageMemory)
MemID_list.append(MemID)
sInfo_list.append(sInfo)
cInfo_list.append(cInfo)




# Starts the driver and establishes the connection to the camera
nRet = ueye.is_InitCamera(hCam, None)
if nRet != ueye.IS_SUCCESS:
print(f"is_InitCamera ERROR for camera {i}")

# Reads out the data hard-coded in the non-volatile camera memory and writes it to the data structure that cInfo points to
nRet = ueye.is_GetCameraInfo(hCam, cInfo)
if nRet != ueye.IS_SUCCESS:
print(f"is_GetCameraInfo ERROR for camera {i}")

# You can query additional information about the sensor type used in the camera
nRet = ueye.is_GetSensorInfo(hCam, sInfo)
if nRet != ueye.IS_SUCCESS:
print(f"is_GetSensorInfo ERROR for camera {i}")

nRet = ueye.is_ResetToDefault( hCam)
if nRet != ueye.IS_SUCCESS:
print("is_ResetToDefault ERROR")

# Set display mode to DIB
nRet = ueye.is_SetDisplayMode(hCam, ueye.IS_SET_DM_DIB)

# Set the right color mode
if int.from_bytes(sInfo.nColorMode.value, byteorder='big') == ueye.IS_COLORMODE_BAYER:
# setup the color depth to the current windows setting
ueye.is_GetColorDepth(hCam, nBitsPerPixel, m_nColorMode)
bytes_per_pixel = int(nBitsPerPixel / 8)
print("IS_COLORMODE_BAYER: ", )
print("\tm_nColorMode: \t\t", m_nColorMode)
print("\tnBitsPerPixel: \t\t", nBitsPerPixel)
print("\tbytes_per_pixel: \t\t", bytes_per_pixel)
print()

elif int.from_bytes(sInfo.nColorMode.value, byteorder='big') == ueye.IS_COLORMODE_CBYCRY:
# for color camera models use RGB32 mode
m_nColorMode = ueye.IS_CM_BGRA8_PACKED
nBitsPerPixel = ueye.INT(32)
bytes_per_pixel = int(nBitsPerPixel / 8)
print("IS_COLORMODE_CBYCRY: ", )
print("\tm_nColorMode: \t\t", m_nColorMode)
print("\tnBitsPerPixel: \t\t", nBitsPerPixel)
print("\tbytes_per_pixel: \t", bytes_per_pixel)
print()

elif int.from_bytes(sInfo.nColorMode.value, byteorder='big') == ueye.IS_COLORMODE_MONOCHROME:
# for color camera models use RGB32 mode
m_nColorMode = ueye.IS_CM_MONO8
nBitsPerPixel = ueye.INT(8)
bytes_per_pixel = int(nBitsPerPixel / 8)
print("IS_COLORMODE_MONOCHROME: ", )
print("\tm_nColorMode: \t\t", m_nColorMode)
print("\tnBitsPerPixel: \t\t", nBitsPerPixel)
print("\tbytes_per_pixel:", bytes_per_pixel)
print()

else:
# for monochrome camera models use Y8 mode
m_nColorMode = ueye.IS_CM_MONO8
nBitsPerPixel = ueye.INT(8)
bytes_per_pixel = int(nBitsPerPixel / 8)
print("else")

# Can be used to set the size and position of an "area of interest"(AOI) within an image
nRet = ueye.is_AOI(hCam, ueye.IS_AOI_IMAGE_GET_AOI, rectAOI, ueye.sizeof(rectAOI))
if nRet != ueye.IS_SUCCESS:
print(f"is_AOI ERROR for camera {i}")

# Get the width and height of the image
width = rectAOI.s32Width
height = rectAOI.s32Height
#prinT(rectAOI)
#struct IS_RECT {
#s32X [c_int] = 0;
#s32Y [c_int] = 0;
#s32Width [c_int] = 1280;
#s32Height [c_int] = 720;
#};
width_list.append(width)
height_list.append(height)

#Sets the zoom Factor of the Camera
nRet = ueye.is_Zoom(hCam, ueye.ZOOM_CMD_DIGITAL_SET_VALUE, ueye.double(zoom), ueye.sizeof(ueye.double(zoom)))
if nRet != ueye.IS_SUCCESS:
print(f"is_Zoom ERROR for camera {i}")

#Sets the exposure Time of the camera
nRet = ueye.is_Exposure(hCam, ueye.IS_EXPOSURE_CMD_SET_EXPOSURE, Belichtungszeit, ueye.sizeof(Belichtungszeit))
if nRet != ueye.IS_SUCCESS:
print(f"is_Expsoure ERROR for camera {i}")

#Sets the Focus of the camera
nRet = ueye.is_Focus(hCam, ueye.FOC_CMD_SET_DISABLE_AUTOFOCUS, Fokus, ueye.sizeof(Fokus))
nRet = ueye.is_Focus(hCam, ueye.FOC_CMD_SET_MANUAL_FOCUS, Fokus, ueye.sizeof(Fokus))
if nRet != ueye.IS_SUCCESS:
print(f"is_Expsoure ERROR for camera {i}")

# Prints out some information about the camera and the sensor
print("Camera model:\t\t", sInfo.strSensorName.decode('utf-8'))
print("Camera serial no.:\t", cInfo.SerNo.decode('utf-8'))
print("Maximum image width:\t", width)
print("Maximum image height:\t", height)
print()

# Allocates an image memory for an image having its dimensions defined by width and height and its color depth defined by nBitsPerPixel
nRet = ueye.is_AllocImageMem(hCam, width, height, nBitsPerPixel, pcImageMemory, MemID)
if nRet != ueye.IS_SUCCESS:
print(f"is_AllocImageMem ERROR for camera {i}")
else:
# Makes the specified image memory the active memory
nRet = ueye.is_SetImageMem(hCam, pcImageMemory, MemID)
if nRet != ueye.IS_SUCCESS:
print(f"is_SetImageMem ERROR for camera {i}")
else:
# Set the desired color mode
nRet = ueye.is_SetColorMode(hCam, m_nColorMode)

# Activates the camera's live video mode (free run mode)
nRet = ueye.is_CaptureVideo(hCam, ueye.IS_DONT_WAIT)
if nRet != ueye.IS_SUCCESS:
print("is_CaptureVideo ERROR")

# Enables the queue mode for existing image memory sequences
nRet = ueye.is_InquireImageMem(hCam, pcImageMemory, MemID, width, height, nBitsPerPixel, pitch)
if nRet != ueye.IS_SUCCESS:
print(f"is_InquireImageMem ERROR for camera {i}")
else:
print("Press q to leave the programm")

# Setze Fensterbezeichnung
window_name = f"Live-Bild Anlage #{i}"
window_names.append(window_name)
print(f"i = {i}")

# Funktion zum Anzeigen eines Kamerabildes
def display_camera_image(hCam, pcImageMemory, MemID, window_name):
print("Anzeigen der Kamerabilder...")
global button_states
global counter

def button_callback(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
for i, name in enumerate(window_names):
if name == window_name:
button_states = not button_states.get(i, False)
cv2.namedWindow(window_name, cv2.WINDOW_NORMAL)
cv2.setMouseCallback(window_name, button_callback)

while (nRet == ueye.IS_SUCCESS):
frames = []##############################
for i in range(num_cameras):
hCam = hCam_list
pcImageMemory = pcImageMemory_list
MemID = MemID_list
window_name = window_names


width = width_list[0]
height = height_list[0]

# In order to display the image in an OpenCV window we need to...
# ...extract the data of our image memory
array = ueye.get_data(pcImageMemory, width, height, nBitsPerPixel, pitch, copy=False)

bytes_per_pixel = int(nBitsPerPixel / 8)
try:
# ...reshape it in an numpy array...
frame = np.array(array, dtype=np.uint8).reshape((height.value, width.value, bytes_per_pixel))
frame = cv2.resize(frame,(0,0),fx=0.5, fy=0.5)

text = f"Anlage #{i+1}"
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 1
color = (0,255,0)
thickness = 4
text_size, _ = cv2.getTextSize(text, font, font_scale, thickness)
text_x = 10
text_y = text_size[1] +10
cv2.putText(frame, text, (text_x, text_y), font, font_scale, color, thickness, cv2.LINE_AA)

# Füge Knopf ein
button_state = button_states.get(i, False)
button_text = "Gerade keine Aufnahme"
button_color = (0, 0, 255) #Farbe Aus Knopf rgb
save_image = False #Kein Bild speichern
if button_state:
button_text = "Aufnahme läuft"
button_color = (0, 255, 0)
save_image = True #Bild speichern
button_width = 100
button_height = 50
button_x = width - button_width -10
button_y = 10
cv2.rectangle(frame, (button_x, button_y), (button_x + button_width, button_y + button_height),
button_color, -1)
cv2.putText(frame, button_text, (button_x + 10, button_y + 35), font, 1, (255, 255, 255), 2,
cv2.LINE_AA)
frames.append(frame)
except Exception as e:
print(f"Error in reshape: {e}")
continue
if save_image:
current_time = datetime.now().strftime("%Y%m%d%H%M%S")
image_name = f"{current_time} Minuten.jpg"
cv2.imwrite(image_name, frame)

if frames:
# Staple die Bilder vertikal
rows = []
for j in range(0, len(frames), Reihe):
row = np.hstack(frames[j:j + Reihe])
rows.append(row)

combined_frame = np.vstack(rows)


# ... Hier kommt der Code zum Anzeigen des Bildes in einem separaten Fenster
cv2.namedWindow(window_name, cv2.WINDOW_NORMAL)
cv2.resizeWindow(window_name, int(width.value*Größe_Kam_Bild*0.01*Seitenverhältnis), int(height.value*Größe_Kam_Bild*0.01*Höhenverhältnis))
cv2.imshow(window_name, combined_frame)
cv2.waitKey(1)

# Drücke 'q', um die Schleife zu beenden
if cv2.waitKey(1) & 0xFF == ord('q'):
break
else:
None

# Erstelle und starte separate Prozesse für jede Kamera
processes = []
for i in range(num_cameras):
hCam = hCam_list
pcImageMemory = pcImageMemory_list
MemID = MemID_list
window_name = window_names
process = Process(target=display_camera_image(hCam, pcImageMemory, MemID, window_name))
process.start()
processes.append(process)
print(process)


# Warte auf das Beenden aller Prozesse
for process in processes:
process.join()

# Freigabe der Ressourcen für jede Kamera
for i in range(num_cameras):
hCam = hCam_list
pcImageMemory = pcImageMemory_list[i]
MemID = MemID_list[i]

# Releases an image memory that was allocated using is_AllocImageMem() and removes it from the driver management
ueye.is_FreeImageMem(hCam, pcImageMemory, MemID)

# Disables the hCam camera handle and releases the data structures and memory areas taken up by the uEye camera
ueye.is_ExitCamera(hCam)

# Destroys the OpenCv windows
cv2.destroyAllWindows()

print("fertig")
Benutzeravatar
__blackjack__
User
Beiträge: 14056
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@DmonoFET91: Als erstes muss der ganze Code von der Modulebene verschwinden. Das macht man nicht nur nicht weil es unübersichtlich und fehleranfällig ist, sondern bei `multiprocessing` ist es tatsächlich ein Fehler. Da muss das Modul ohne Seiteneffekte importierbar sein. Steht auch in der Dokumentation von dem Modul.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

``global`` hat in einem sauberen Programm nichts zu suchen. Funktionen und Methoden bekommen alles was sie ausser Konstanten benötigen als Argument(e) übergeben. Ergebnisse werden als Rückgabewert an den Aufrufer zurückgegeben.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Namen sollten keine kryptischen Abkürzungen enthalten oder gar nur daraus bestehen. Der Name soll dem Leser vermitteln was der Wert dahinter im Programm bedeutet, nicht zum rätseln zwingen.

Man nummeriert keine Namen. Dann will man sich entweder bessere Namen überlegen, oder gar keine Einzelnamen/-werte verwenden, sondern eine Datenstruktur. Oft eine Liste.

Grunddatentypen haben nichts in Namen verloren. Den Typen ändert man gar nicht so selten mal während der Programmentwicklung und dann muss man überall im Programm die betroffenen Namen ändern, oder man hat falsche, irreführende Namen im Quelltext.

So etwas wie `hCam_list` sollte beispielsweise `camera_handles` heissen.

``for i in range(len(sequence)):`` nur um dann `i` als Index in `sequence` zu verwenden ist in Python ein „anti pattern“. Man kann direkt über die Werte iterieren, ohne den unnötigen Umweg über einen Laufindex. Sollte man zusätzlich eine laufende ganze Zahl benötigen, gibt es die `enumerate()`-Funktion.

Man verwendet keine ”parallelen” Listen. Wenn man Listen hat bei denen die Elemente am gleichen Index zusammengehören, dann will man das in *einer* Liste speichern wo an einem Index die zusammengehörenden Elemente gespeichert sind. Zum Beispiel als Tupel oder als `collections.namedtuple` oder man schreibt sich dafür einen eigenen Datentyp.

Für diesen ganzen Kamera-Kram mit den `ueye`-Funktionen würde das sicher Sinn machen das etwas ”pythonischer” zu verpacken in einen Datentyp der diese API zu verstecken die sehr nach C aussieht.

Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in aller Regel auch was in der Dokumentation von Python und den verwendeten Bibliotheken steht.

Funktionen werden normalerweise nicht innerhalb von Funktionen oder Methoden definiert. Das macht man nur wenn es wirklich total simpel ist, aber ein ``lambda``-Ausdruck aus technischen Gründen nicht geht, oder wenn man gezielt ein Closure erzeugen will.

Bei ``except Exception:`` muss man *jede* Ausnahme sinnvoll behandeln. Bei einem sehr langen ``try``-Block ein Ausgabe zu machen das „reshape“ nicht ging, ist sicher keine sinnvolle Behandlung von *jeder* Ausnahme die in dem Block auftreten kann.

Um ``continue`` würde ich einen Bogen machen. Das macht das Programm unübersichtlicher und schwerer zu verändern. Und man kann eigentlich *immer* leicht darauf verzichten. ``try``/``except`` kennt beispielsweise auch einen ``else``-Block.

Man muss nicht jedes kleine Zwischenergebnis an einen Namen binden. Da liesse sich einiges kürzen. Und dann beispielsweise an mindestens einer Stelle auch als „list comprehension“ schreiben.

``else: None`` ist sinnfrei und kann weg.

Ich vermute ganz stark, dass man die ganzen Kamera-Ressourcen auch *in* dem jeweiligen Prozess erzeugen muss. Selbst wenn man Handles serialisieren kann, muss ein Handle aus einem Prozess nicht in einem anderen Prozess gültig sein. Für Dateihandles gilt das beispielsweise nicht, und auch nicht für Handles die eigentlich ein opaker Zeiger auf eine Speicheradresse sind.

`os` und `time` werden importiert, aber nirgends verwendet.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
DmonoFET91
User
Beiträge: 2
Registriert: Dienstag 27. Juni 2023, 13:26

Hey __blackjack__,

vielen Dank erstmal für die ganzen Hinweise. Mir war klar, das es bestimmt einiges zu verbessern gibt, aber das ist dann doch etwas mehr als ich dachte.

Was meinst du mit Modulebene (vermutlich eine Frage, welche zeigt wie stark ich Anfänger bin) und Hauptprogramm?

Die Kommentare hatte ich mal ursprünglich für mich selber gemacht, damit ich mich leichter zurechtfinde. Aber macht bestimmt Sinn, die auch nochmal zu überarbeiten (zumndest als Forenbeitrag).

os und time sind importiert, weil ich die in den nächsten Erweiterungen nutzen möchte um Ordner (mit Datum als Namen) zu erzeugen, falls noch nicht vorhanden und darin was abspeichern will.

Ich werde das Programm jetzt erstmal mit deinen Kommentaren überarbeiten. Danke dafür
Benutzeravatar
__blackjack__
User
Beiträge: 14056
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@DmonoFET91: Modulebene ist alles das was nicht eingerückt ist, also ausgeführt wird wenn man das Modul importiert. Da sollte nichts stehen was nicht Konstanten, Funktionen, und Klassen definiert. Alles was irgendwelche weitergehenden Effekte hat, beispielsweise Kameras initialisieren, sollte mindestens in einer Funktion stehen, die natürlich auch nicht direkt oder indirekt aufgerufen werden sollte, nur weil man das Modul importiert. Das ist das übliche:

Code: Alles auswählen

if __name__ == "__main__":
    main()
am Ende des Moduls. `__name__` ist der Name des Moduls als Zeichenkette wenn man es importiert, und "__main__" wenn es nicht importiert, sondern als Programm ausgeführt wird.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Antworten