QImage Berechnung in einem Thread

Alles rund um die Programmierung mit Qt
Antworten
slash-ex
Beiträge: 239
Registriert: 30. März 2005 21:40

QImage Berechnung in einem Thread

Beitrag von slash-ex »

Ich wollte die Berechnung meines Bildes in einen Thread packen, da diese relativ zeitaufwändig ist. In der run()-Funktion wird ein Signal geworfen das mein Bild dann an die behinhaltende Klasse senden soll.
Das Ganze passiert immer dann wenn, ich über eine Funktion die Variablen m_iMin, m_iMax ändere. Allerdings scheint das Programm immer dann abzustürzen, wenn diese geändert werden, bevor die Berechnung eines Bildes abgeschlossen ist. Ich habe mal die Dateien mit der auskommentierten QThread-Klasse angehangen. Im Header sind zwei Entwürfe, die obere ist die aktuelle die auch zur Implementation passt. Es wäre schön, wenn man die Bildberechnung in einen Thread packen könnte.

Code: Alles auswählen

void DisplayThread::run() {
	while(true) {
		m_pParent->slChangeConfig(m_iMin, m_iMax );
		QImage m_pImage = QImage( (const uchar *)m_pParent->GetImg()->imageData,
								m_pParent->GetImg()->width,
								m_pParent->GetImg()->height,
								QImage::Format_RGB888);
		QString NrColLbl = QString::number(m_pParent->GetNrColonies()) + " surfaces";
		emit ImageProcessed(m_pImage, NrColLbl);

		mutex.lock();
		condition.wait(&mutex);
		mutex.unlock();
	}
}

Code: Alles auswählen

void DisplayThread::SetConfig(int iMin, int iMax) {
	QMutexLocker locker(&mutex);

	m_iMin = iMin;
	m_iMax = iMax;

	if (!isRunning()) {
		start(LowPriority);
	} else {
		restart = true;
		condition.wakeOne();
	}
}
Dateianhänge
Contours.h
(2.49 KiB) 109-mal heruntergeladen
Contours.cpp
(4.16 KiB) 124-mal heruntergeladen
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Warum das emit nicht funktioniert, kann man nicht sagen. Du hältst die Sourcen zurück, wie sie gerade sind, ich seh z.B. gar keinen connect auf das SIGNAL.
Beide Thread-Implementierungen in den hchgeladenen Dateien greifen böserweise schreiben auf die GUI aus einem anderen als dem Hauptthread zu

Code: Alles auswählen

m_pParent->m_pImageLbl->setPixmap(QPixmap::fromImage(m_pParent->m_QImg));
Dein Code ersetzt das durch den emit. Wir haben den passenden SLOT nicht.
Desweiteren rufst du CViewerPrivate::slChangeConfig() aus dem run() deines Thread (und damit aus einem anderen als dem Hauptthread) auf - keine AHnung ob die dort getätigten Operationen threadsafe sind. Merke: Eine Funktion läuftimmer in dem Threadcontext, in dem sie aufgerufen wurde, unabhängig von Thread in dem das Objekt lebt!

Um hier Ordnung reinzubringen, wäre es nicht schlecht, du bastelst ein minimales, kompilierbares Beispiel, welches dein Problem verdeutlicht und vor allem 1:1 deinem Aufbau entspricht. Wenn es möglich ist, erstze die ganzen OpenCV-Sachen durch eigene Funktionen, nicht jeder hat OpenCV oder Webcams o.Ä. (ich z.B.), und kann deshalb dein Programm nicht kompilieren und den Fehler nicht reproduzieren.
slash-ex
Beiträge: 239
Registriert: 30. März 2005 21:40

Beitrag von slash-ex »

hmm ich wollte euch nicht mit den code langweilen. ich denke ich habe das problem gefunden. ich hänge mal den funktionierenden code an.
das ganze scheint wegen dem QLabel abzustürzen. wenn ich eine überprüfung hinzufüge und dadurch NICHT aktualisiere wenn der thread grade läuft, dann funkioniert das programm ohne absturz.
allerdings müsste das ganze auch ohne diese überprüfung laufen, da über das signal immer ein fertiges bild übertragen wird. ich bastle morgen evtl mal ein bsp. zusammen. da ich ein paar abh. zu opencv habe.

Code: Alles auswählen

	QObject::connect(m_pDispThread, SIGNAL(ImageProcessed(const QImage &, QString)), this, SLOT(slDisplayImageThrdd(const QImage &, QString) ) );
	// slots rufen SetConfig auf
	QObject::connect(&m_MinSizeSl, SIGNAL(valueChanged(int)), m_pDispThread, SLOT(slChangeMin(int) ) );
	QObject::connect(&m_MaxSizeSl, SIGNAL(valueChanged(int)), m_pDispThread, SLOT(slChangeMax(int) ) );

Code: Alles auswählen

void ContourViewer::slDisplayImageThrdd(const QImage &Image, const QString &str) {
	m_pNrColLbl->setText( str );

	if( !m_pDispThread->isRunning() )
		m_pImageLbl->setPixmap( QPixmap::fromImage( Image ) );
}

Code: Alles auswählen

void DisplayThread::SetConfig(int iMin, int iMax) {
	m_iMin = iMin;
	m_iMax = iMax;

	if ( !isRunning() ) {
		this->start();
	}
}

void DisplayThread::run() {
	m_pParent->slChangeConfig( m_iMax, m_iMin );
	QImage Image = QImage( 	m_pParent->GetData(),
							m_pParent->GetWidth(),
							m_pParent->GetHeight(),
							QImage::Format_RGB888);
	QString NrColLbl = QString::number(m_pParent->GetNrColonies()) + " surfaces";
	emit ImageProcessed(Image, NrColLbl);
}
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

slash-ex hat geschrieben:..
das ganze scheint wegen dem QLabel abzustürzen. wenn ich eine überprüfung hinzufüge und dadurch NICHT aktualisiere wenn der thread grade läuft, dann funkioniert das programm ohne absturz.
Als Entwickler solltest du deinen Code verstehen, nicht durch herumprobieren einen mehr oder weniger stabilen Zustand hinkriegen.

Also:
Ich tippe darauf, dass du "moveToThread()" vergessen hast oder nicht weisst, wozu das gebraucht wird.
Weil dadurch die Klasse des Threads zur GUI gehört, macht Qt aus deinen Signals/Slots eine DirectConnection.
Also wird

Code: Alles auswählen

void DisplayThread::run() {
   while(true) {
      ...
      emit ImageProcessed(m_pImage, NrColLbl); // hier
     ...
   }
}
gleich in den GUI-Code gesprungen und "slDisplayImageThrdd()" im Kontext des Threads aufgerufen.. und nicht im Kontext der GUI.
Wenn die GUI parallel auch darauf rumreitet gibts ein Crash. Wenn nicht, hattest du Glück.. es ist also zufällig, ob das Programm da durchkommt oder stirbt..

hth!
slash-ex
Beiträge: 239
Registriert: 30. März 2005 21:40

Beitrag von slash-ex »

Als Entwickler solltest du deinen Code verstehen, nicht durch herumprobieren einen mehr oder weniger stabilen Zustand hinkriegen.
das sehe ich genauso und darum habe ich den forenbeitrag verfasst. es ist halt doof wenn man mit threads noch nicht so viel erfahrung hat.
deine problemvermutung stimmt natürlich, allerdings kann ich kein moveToThread() benutzen, weil die instanz von qthread connected bleiben soll.
ich setze jetzt die bedingung mit der die run() funktion aufgerufen werden kann nun in der hauptgui. damit umgehe ich alle probleme.
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

slash-ex hat geschrieben:..allerdings kann ich kein moveToThread() benutzen, weil die instanz von qthread connected bleiben soll...
Wie meinst du das? Warum sollte ein QThread nach moveToThread() nicht mehr "connected" sein?
Antworten