[Gelöst] ScanLine() Problem

Alles rund um die Programmierung mit Qt
Antworten
abcmann74
Beiträge: 31
Registriert: 3. Mai 2010 11:03

[Gelöst] ScanLine() Problem

Beitrag von abcmann74 »

Hi

Habe hier ien kleines Problem mit der scanLine() Fkt.
Ich möchte gerne einen bestimmten Bereich eines Bildes pixelweise auslesen und den Kontrast für diesen Bereich ändern. Anfangs habe ich das ganze über pixel() und setPixel() gemacht, was mir aber zu lange gedauert hat. Deswegen möchte ich nun direkt auf den Speicher des Bildes zugreifen.

Ich komme einfach nicht darauf was ich in meinem Code falsch mache. Ich habe mir schon bei beiden Varianten (setPixel() , scanLine()) die Farbwerte ausgeben lassen. Diese waren immer identisch. Das heißt die Funktion scanLine () arbeitet richtig. Ich denke, dass ich beim setzen der neuen Pixel in den Speicher einen Fehler mache.

Code: Alles auswählen

void ImageWindow::EditImage(double contrast)
{
   TempEditImage_.convertToFormat(QImage::Format_RGB32,Qt::AutoColor);
	Contrast(contrast);

	QGraphicsItem *oldbackground = itemAt(0,0);
	removeItem(oldbackground);
	delete oldbackground;

	addPixmap(QPixmap::fromImage(TempEditImage_));

	QGraphicsItem *background = itemAt(0,0);
	background->setZValue(0);

	View_->setScene(this);
}

Code: Alles auswählen

void ImageWindow::Contrast(double contrast)
{
	contrast = (100.0 + contrast) / 100.0;
	contrast *= contrast;

        quint32 y=(Rect_.topLeft()).y();
	quint32 x=(Rect_.topLeft()).x();
	quint32 h=Rect_.height()+(Rect_.topLeft()).y();;
	quint32 w=Rect_.width()+(Rect_.topLeft()).x();

	for (;y<=h;y++)
	{
		QRgb *scanLine = (QRgb *)TempEditImage_.scanLine(y);
		for(;x<=w;x++)
		{
			QRgb q = scanLine[x];
			QColor qc ( q );
			double r=(double)qc.red()/255.0;
			r -= 0.5;
			r *= contrast;
			r += 0.5;
			r *= 255.0;
			if (r < 0) r = 0;
			if (r > 255) r = 255;

			double g=(double)qc.green()/255.0;
			g -= 0.5;
			g *= contrast;
			g += 0.5;
			g *= 255.0;
			if (g < 0) g = 0;
			if (g > 255) g = 255;

			double b=(double)qc.blue()/255.0;
			b -= 0.5;
			b *= contrast;
			b += 0.5;
			b *= 255.0;
			if (b < 0) b = 0;
			if (b > 255) b = 255;

			QRgb qnew = qRgb( r, g, b);

			scanLine[x]=qnew;
		}
	}
}
vllt kann mir ja jemand weiterhelfen.

grüße
Zuletzt geändert von abcmann74 am 18. Juni 2010 08:31, insgesamt 1-mal geändert.
abcmann74
Beiträge: 31
Registriert: 3. Mai 2010 11:03

Beitrag von abcmann74 »

Habe das Problem gerade selber gefunden und lösen können.
Der y Wert in der 1. for Schleife wurde überschrieben. Daher wurde immer nur eine Zeile manipuliert.

Lösung

Code: Alles auswählen

...
for (quint32 y=(Rect_.topLeft()).y();y<=h;y++)
	{
		QRgb *scanLine = (QRgb *)TempEditImage_.scanLine(y);
		for(quint32 x=(Rect_.topLeft()).x() ;x<=w;x++)
		{
			QRgb q = scanLine[x];
...
Aber sehr viel schneller läuft das ganze leider nicht. Hatte mir mehr erhofft. Kennt vllt. jemand noch eine effektivere Variante?

grüße
N&#164;X
Beiträge: 77
Registriert: 21. September 2009 12:24

Beitrag von N&#164;X »

Also normalerweise bringt das schon was an Geschwindigkeit, zumindest wenn die Bildgrößen mal in den Megapixelbereich kommen.
Was ich vllt noch anders machen würde:

Code: Alles auswählen

double r = qRed(scanLine[x])/255.0;
bzw gleich alles ein wenig verkürzen, damit nicht ständig in die Variablen geschrieben wird (Ich hab in Grundlagen der Programmiersprachen nicht soo gut aufgepasst, aber ich glaub das machts nicht nur unleserlicher sondern auch etwas schneller, falls das der Compiler nicht eh so optimiert...)

Code: Alles auswählen

scanLine[x] = qRgb(
    qBound(0, qRound(((((qRed(scanLine[x])/255.0)-0.5)*contrast)+0.5)*255.0), 255),
    qBound(0, qRound(((((qGreen(scanLine[x])/255.0)-0.5)*contrast)+0.5)*255.0), 255),
    qBound(0, qRound(((((qBlue(scanLine[x])/255.0)-0.5)*contrast)+0.5)*255.0), 255)
    );
oder auch ohne Umweg über den Bereich [0.0, 1.0]:

Code: Alles auswählen

scanLine[x] = qRgb(
    qBound(0, qRound((double(qRed(scanLine[x]))-127.5)*contrast+127.5), 255),
    qBound(0, qRound((double(qGreen(scanLine[x]))-127.5)*contrast+127.5), 255),
    qBound(0, qRound((double(qBlue(scanLine[x]))-127.5)*contrast+127.5), 255)
    );
;)

(Alternativ kannst du natürlich auch deins weiterbenutzen, aber dann würd ich die ganzen Variablendeklarationen außerhalb der Schleife machen, der Computer hat auch nicht unbedingt bock die selbe Variable tausend mal zu erstellen und zu zerstören und zu erstellen und zu zerstören...)
mfg N¤X
abcmann74
Beiträge: 31
Registriert: 3. Mai 2010 11:03

Beitrag von abcmann74 »

danke für den tip.

wie sieht es mit einer Pixmap aus? Könnte es damit schneller gehen?

grüße
N&#164;X
Beiträge: 77
Registriert: 21. September 2009 12:24

Beitrag von N&#164;X »

Nein, zum bearbeiten immer ein QImage nehmen, zum Anzeigen dann ne QPixmap. Die Doku sagt das auch klipp und klar:
"QImage is designed and optimized for I/O, and for direct pixel access and manipulation, while QPixmap is designed and optimized for showing images on screen."

Wie groß sind denn die Ausschnitte die du bearbeitest, und wie lange dauert das denn? (Zeit natürlich immer beim Release-Build messen!)
mfg N¤X
abcmann74
Beiträge: 31
Registriert: 3. Mai 2010 11:03

Beitrag von abcmann74 »

die bilder sind über 3500 x 2500 pixel groß.
ich habe halt einen slider eingebaut den ich verschieben kann. wenn ich jetzt jedesmal, wenn sich der wert vom slider ändert, die funktion aufrufe, kommt er schon ins schwitzen. es ergeben sich "stockende" übergänge wenn ich das ganze im view anzeige. ich denke ich werde den slider event erst dann auslösen wenn ich den slider loslasse. dann läufts relativ schnell (auch wenn ich das ganze bild ausgewählt habe).
N&#164;X
Beiträge: 77
Registriert: 21. September 2009 12:24

Beitrag von N&#164;X »

Naja, bei solch großen Bildern wirst du es in der Tat nicht schaffen sowas in Echtzeit (<16ms) zu machen. Seltener aktualisieren ist eine Möglichkeit, ansonsten kannst du vllt noch mehr CPU-Kerne nutzen, indem du die Scanlines in eine Liste steckst und mit dem Concurrent-Framework von Qt drauf losgehst. Wenns wirklich nur flackert könnte das bei Multicore-Systemen vllt schon helfen.

Ansonsten bietet sich für solch einen massiven Einsatz von Punktoperatoren natürlich die SIMD-Architektur der GPU an, aber jetzt extra CUDA oder GLSL oder ähnliches zu nutzen ist vllt ein bisschen viel Aufwand ^^
(Ein Kumpel hat letztes Jahr für die Uni ein kleines Bildbearbeitungsprogramm geschrieben, das solche Sachen wie Skalierung, Blending und sonstige Punktoperationen über Shader auf der GPU berechnet hat, aber ich glaub er hat damals Probleme mit Qt und GLee gekriegt. Oo)
mfg N¤X
Antworten