Rechnungen (Sättigung setzen) verschnellern

Status
Nicht offen für weitere Antworten.

thE_29

Top Contributor
Hallo!


Also es geht darum, dass ich bei einem Bild die Sättigung verändern muss.
Dazu gucke ich mir jeden Pixel an mit einer LookupTable und änder die Werte!

Zuerst habe ich RGB in HSB geändert und S = Saturaion = Sättingung geändert.

Bei meinem 2 Kerne Hobel flitzt das ganze natürlich dahin und braucht so 450MS. (Bei einer Auflösung von 1280x1024).

So, aber auf dem Teil wo die App laufen soll, braucht das ganze schonmal 2.5 Sekunden :(.


Bin dann hergegangen und wollte rausfinden wie sich die Sättigung auf die RGB Werte äußert!
Auf dieser Seite hier: http://home.comcast.net/~ed-abramson/14ColorTest/HSB-and-RGB-Colors.html kann man sich prima mit den Reglern spielen!

Mir sind dann folgende Dinger aufgefallen!

.) R = G = B == 0% Sättigung == Wert ändert sich NICHT (50% weniger Sättigung von 0 sind nunmal wieder 0)
.) Der höchste Wert von RGB bleibt immer bestehen!
.) Die anderen 2 Werte ändern sich in der Relation:
Zu berechnender Wert += (Höchster Wert - zu berechnender Wert) / 2 == 50% Sättigung.

Also ist R der höchste Wert sieht die Berechnung von G und B so aus

G = G + (R - G) / 2;
B = B + (R - B) / 2;

Das ganze braucht nun auf der Zielmaschine "nur noch" 1.5 Sekunden! Also 1 Sekunde Zeitersparnis dank wegfallender Umrechnung in HSB und wieder zurück in RGB.


Ich müsste das ganze aber noch schneller machen.

Mein Code sieht so aus:
Code:
    //Methode tauscht die Pixelfarben um
    public int[] lookupPixel(int[] src, int[] dest)
    {
      int x = 0, y = 0, z = 0;
      if(src[0] == src[1] && src[1] == src[2])
      {
        return src;
      }
      if(src[0] > src[1] && src[0] > src[2])
      {
        x = 2;
        y = 1;
        z = 0;
      }
      else if(src[1] > src[0] && src[1] > src[2])
      {
        x = 2;
        y = 0;
        z = 1;
      }
      else
      {
        x = 1;
        y = 0;
        z = 2;
      }
      //rechnet die Sättigung zurück (größte Wert von RGB bleibt bestehen, nur die 2 kleineren ändern sich)
      src[x] += (src[z] - src[x]) / INT_SAETTIGER;
      src[y] += (src[z] - src[y]) / INT_SAETTIGER;
      return src;
    }

Wobei INT_SAETTIGER == 2 == 50% ist.

Wie kann ich das ganze noch optimieren?

Habe schon probiert das src[] in eine Hashtable zu putten, aber da ich ne Kopie vom alten src dann brauche, da src wieder das neue ist (und sein muss, da er auf das Array src wieder zurückgreift zum Bild manipulieren und nicht auf das was man returniert), hat sich die Zeit auf meiner Maschine von 400MS auf 780MS fast verdoppelt.

mfg
 

Marco13

Top Contributor
Hm. Ich glaube, so eine LookupOp ist per se nicht unbedingt die schnellste: Je nachdem, welchen Typ das Eingabebild hat, kann das Lesen und Schreiben der src und dst-Arrays glaubich relativ aufwändig sein. Wenn du ein compilierbares Beispiel posten würdest, würde ich später vielleicht nochmal ein bißchen rumspielen...
 
G

Gast2

Gast
Moin,

thE_29 hat gesagt.:
Wie kann ich das ganze noch optimieren?

Du hattest doch schon den richtigen Gedanken ... eine Look-Up-Table ... jeder Berechnungsschritt kostet Zeit und RGB->HSI sind nicht gerade wenig Rechenschritte ... Du musst die LUT allerdings aus gleich passend aufbauen

das Ganze ist zwar kein Java ... sollte aber nicht das Problem sein
Code:
	// HSI <-> RGB
	RGB2HSI = new int __gc [0x1000000];	// sollten 64 MB sein
	HSI2RGB = new int __gc [0x1000000];	// sollten 64 MB sein

	Diagnostics::Debug::Write("RGB2HSI - ");

	int prozent = 0; // RGB -> HSI
	for(int r = 0; r < 256; r++)
	{
		int p = (int) ( ((double) r / 255.0) * (double) 100 );
		if (p != prozent)
		{
			prozent = p;
			Diagnostics::Debug::Write(".");
			__raise InitProgress(p);
			if (!(p % 10)) Diagnostics::Debug::Write(p.ToString());
		}
		
		for(int g = 0; g < 256; g++)
		{
			for(int b = 0; b < 256; b++)
			{
				double R, G, B;
				double min, max;
				double H;
				double S;
				double V;

				R = r / 255.0;
				G = g / 255.0;
				B = b / 255.0;

				// min / max festlegen
				max = (R > B) ? R : B;
				max = (max > G) ? max : G;
				min = (R < B) ? R : B;
				min = (min < G) ? min : G;

				// H berechnen
				if (max == min)
				{
					H = 0;
				} else
				{
					if (R == max) H = (0 + (G - B) / (max - min)) * 60;
					if (G == max) H = (2 + (B - R) / (max - min)) * 60;
					if (B == max) H = (4 + (R - G) / (max - min)) * 60;
				}
				if (H < 0) H += 360;
				// ---> ??? ---> if (h > 360) H -= 360;
				if (H > 360) H -= 360;
				//H = (H / 360.0) * 255.0;	// wird in 2 Byte gespeichert

				// S berechnen
				if (max == 0)
				{
					S = 0;
				} else
				{
					S = (max - min) / max;
				}
				S = S * 100;

				// V brechnen ... sehr komplex
				V = max * 100;

				// das Ganze jetzt wieder packen und in den Wert schreiben
				//col = ((((int) Math::Round(H, 0) << 8) + (int) Math::Round(S, 0)) << 8) + (int) Math::Round(V, 0);
				int hsi = ((int) Math::Round(S, 0)) << 8;	// Saturation
				hsi |= (((int) Math::Round(H, 0)) << 15);	// Hue in die Bytes von Saturation mit rein !!
				hsi += (int) Math::Round(V, 0);

				RGB2HSI[(((r << 8) + g) << 8) + b] = hsi;
			}
		}
	}
	Diagnostics::Debug::WriteLine("");

	Diagnostics::Debug::Write("HSI2RGB - ");
	prozent = 0;	// HSI -> RGB
	for(int hue = 0; hue < 360; hue++)
	{
		int p = (int) ( ((double) hue / 360.0) * (double) 100 );
		if (p != prozent)
		{
			prozent = p;
			Diagnostics::Debug::Write(".");
			__raise InitProgress(p + 100);	// weil schon der nächste Schritt
			if (!(p % 10)) Diagnostics::Debug::Write(p.ToString());
		}
		
		for(int saturation = 0; saturation <= 100; saturation++)
		{
			for(int value = 0; value <= 100; value++)
			{
				double R, G, B;
				int h1 = (hue / 60);

				double d_hue = hue / 1.0;
				double d_value = value / 100.0;
				double d_saturation = saturation / 100.0;
				
				double f = (( (double) hue / 60.0) - (double) h1);
				double p = (d_value * (1 - d_saturation));
				double q = (d_value * (1 - (d_saturation * f)));
				double t = (d_value * (1 - (d_saturation * (1 - f))));

				switch (h1)
				{
					case 0:
					{
						R = d_value;
						G = t;
						B = p;
						break;
					}
					case 1:
					{
						R = q;
						G = d_value;
						B = p;
						break;
					}
					case 2:
					{
						R = p;
						G = d_value;
						B = t;
						break;
					}
					case 3:
					{
						R = p;
						G = q;
						B = d_value;
						break;
					}
					case 4:
					{
						R = t;
						G = p;
						B = d_value;
						break;
					}
					case 5:
					{
						R = d_value;
						G = p;
						B = q;
						break;
					}
				}
				R *= 255.0;
				G *= 255.0;
				B *= 255.0;

				int col = (pixel) Math::Round(R, 0) << 16;
				col += (pixel) Math::Round(G, 0) << 8;
				col += (pixel) Math::Round(B, 0);

				// das unterste Bit von Hue landet im Byte für Saturation !!!
				int hsi = (saturation & 0xff) << 8;	// Saturation
				hsi |= ((hue & 0x1ff) << 15);			// hier jetzt nur 15 Bit verschieben
				hsi += (value & 0xff);				// jetzt noch Value ... spart gesammt 64 MB

				HSI2RGB[hsi & 0xffffff] = col;
			}
		}
	}
	Diagnostics::Debug::WriteLine("");

anschließend brauchst Du nur Deinen RGB-Wert über die Tabelle in HSI konvertieren ... ziehst Dir S über Bit-Opperationen, änderst S und holst Dir den RGB-Wert über die 2. Tabelle

hand, mogel
 

thE_29

Top Contributor
Achso, du meinst also ich soll schon mal alle Wert vorberechnen und dann halt diese schon zurückgeben?

Nur die Frage ist halt, ob er zum Nachschaun wieder nicht zulange Zeit braucht!
Der Weg übers Hashtable war ja auch net gerade ein Vorteil.
 
G

Gast2

Gast
Moin,

thE_29 hat gesagt.:
Achso, du meinst also ich soll schon mal alle Wert vorberechnen und dann halt diese schon zurückgeben?

Nur die Frage ist halt, ob er zum Nachschaun wieder nicht zulange Zeit braucht!
Der Weg übers Hashtable war ja auch net gerade ein Vorteil.

sehe da nicht das zeitliche Problem ... die LUT arbeitet mit O(1) ... die Hashtable mit O(ln)

Code:
int rgb = 0xff00f0;
int hsi = RGB2HSI[rgb];

hand, mogel
 

thE_29

Top Contributor
Naja, das Problem ist nicht die Berechnung ;)

Also ich brauche kein HSI/HSB/HSV für die Sättigung zum Ausrechnen!
 
G

Gast2

Gast
Moin,

thE_29 hat gesagt.:
Also ich brauche kein HSI/HSB/HSV für die Sättigung zum Ausrechnen!

so wie ich das verstanden habe, willst Du die Sättigung ändern

Code:
int rgb = 0xff00f0;  // Farbe "holen"
int hsi = RGB2HSI[rgb]; // nach HSI "rechnen"
int s = (hsi & 0xff00) >> 8; // Sättingung holen ... !!! Bits stimmen nicht !!!
s += 5; // Sättigung ändern
hsi = (hsi & 0xff00ff) & (s << 8);  // zurück schreiben
rgb = HSI2RGB[hsi]; // von HSI nach RGB

hand, mogel
 

thE_29

Top Contributor
Mein Code oben ändert schon die Sättigung ;)

Man braucht für die Sättigung kein HSB/HSV! Anscheinend weiß das keiner auf der Welt außer mir :bae:

Wenn du meinen Post liest, wirst du sehen das ich die Formel beschrieben habe, wie ich die Sättigung ändern kann.

Mein Problem besteht darin, dass auch das zulange dauert. Von RGB in HSB/HSI/HSV rechnen und wieder zurückrechnen will ich gar nicht reden (das braucht noch länger).

Mir gehts darum, mein obiges Codesegment zu verschnellern!!
 

Marco13

Top Contributor
Ja, schneller geht es, wenn man sich direkt eine LookupTable für die Pixel baut
Code:
    private int lut[];

    void doit()
    {
       if (lut == null) initLUT();

       // beide Images vom Typ BufferedImage.TYPE_INT_RGB
        int in[] = (int[])originalImage.getRaster().getDataElements(0,0,w,h,null);
        int out[] = (int[])modifiedImage.getRaster().getDataElements(0,0,w,h,null);
        for (int i=0; i<in.length; i++)
        {
            out[i] = lut[in[i]];
        }
        modifiedImage.getRaster().setDataElements(0,0,w,h,out);
    }

    private void initLUT()
    {
        lut = new int[256*256*256];
        for (int i=0; i<lut.length; i++)
        {
            int b[] = new int[3];
            b[0] = (int)((i >>> 16) & 0xFF);
            b[1] = (int)((i >>>  8) & 0xFF);
            b[2] = (int)((i >>>  0) & 0xFF);
            saturationLookupTable.lookupPixel(b, null); // Deine LookupOp, jaja, ich bin faul....
            int rgb = (b[0] << 16) | (b[1] <<  8) | (b[2] <<  0);
            lut[i] = rgb;
        }

    }

Bei der LookupOp.filter-Methode braucht's bei mir bei einem Bild 250ms, mit dieser Methode nur 50...
 

thE_29

Top Contributor
Da kommt leider das hier raus:
java.lang.ArrayIndexOutOfBoundsException: -8355712


Und zwar in der Zeile

out = lut[in];


Nachtrag: Also bei mir ist in durch die Bank negativ! Da ist kein einziger Eintrag positiv...

Selbst wenn ich nur ein schwarzes Rechteck hinmale, geht es nicht..


Könntest du mir dein Bsp zukommen lassen wo es funktioniert?
 

Marco13

Top Contributor
Verwendest du als Eingaben wirklich BufferedImages vom Type BufferedImage.TYPE_INT_RGB und NICHT BufferedImage.TYPE_INT_ARGB?

Falls du das Bild (z.B. ein PNG) mit ImageIO liest, könntest du es mit
Code:
BufferedImage input = ImageIO.read(new File("C:/image.jpg"));
BufferedImage image = new BufferedImage(input.getWidth(), input.getHeight(), BufferedImage.TYPE_INT_RGB);
image.getGraphics().drawImage(input, 0,0,null);
in ein passendes Bild umwandeln.

Falls du Transparenz brauchst, müßte die LUT entsprechend größer sein (dann aber schon SEHR groß :? ) Man hat natürlich immer den Tradeoff zwischen Speicherplatz und Rechenzeit, da könnte man dann noch in die eine oder andere Richtung dran schrauben...
 

WieselAc

Top Contributor
Zwei simple Ansätze habe ich noch:

Zum einen könntest du mal testen, ob es einen Zeitvorteil bringt das Bild von mehreren Threads parallel bearbeiten zu lassen... ich vermute zwar, dass du es schon probiert hast, aber da es hier noch nicht stand wollt ich es zumindest mal erwähnen.

Zum anderen könntest du je nach Art der Bilder versuchen die Anzahl der zu berechnenden Pixel zu reduzieren. zB.: Nur für jedes zweite Pixel die Sättigung berechnen und dazwischen interpolieren... Ob der Ansatz überhaupt Sinn macht und welche Strategie man da am besten benutzt hängt allerdings sehr stark von den Bildern ab.
 

beastofchaos

Bekanntes Mitglied
Ich weiß der Thread ist alt, aber ich hätte noch eine zeitsparendere Version, die aber die selbe Berechnung wie thE_29 benutzt.
Wenn sich R am höchsten sind, verändern sich die beiden anderen Werte nach der selben Formel, weshalb, du nicht für beide, sondern nur für eine beliebige "Zweitfarbe" deine tolle Formel anwenden musst.
Also hast du ein LUT mit "nur noch" 256² Einträgen. Die intensivste Farbe von 0..256 und eine beliebige andere, die sich nach dieser richtet, auch im Bereich 0...256.

Ich denke, ich muss nicht betonen, dass 256³ - 256² Berechnungen weniger ein Großteil an Zeit einsparen :)

Ich arbeite selbst an einem Grafikprogramm und probier das jetzt mal aus, was ich mir theoretisch erdacht habe und melde mich mit einem funktionierendem Codeausschnitt zurück ;)

Gruß, Thomas
 

beastofchaos

Bekanntes Mitglied
Die angegebene Formel ("G = G + (R - G) / 2") stimmt leider nicht immer.
Also zwei Farbanteile lassen sich schon leicht definieren:

Die "größte" Farbe (MAX) bleibt immer gleich, während die "kleinste" Farbe (MIN) sich nach folgenden Formel richtet:

MIN = MAX * ( 1 - Saturation / 100)

Wie die mittlere Farbe (MID) sich verändert, kann ich leider nicht eindeutig erkennen, aber ich hab schon folgende Formel, für die man halt wissen muss, wie die Farbe bei Sättigung = 100 aussieht. Sprich der hier verwendete Wert MID_2 ist der Wert MID bei Sättigung = 100.

x = 100 / (MAX/MID_2 - 1)) + 100
MID = MAX * ( 1 - Saturation / x)

Um MID_2 zu umgehen, müsste man wissen, in welchen Schritten sich MID verändert...

Gruß, Thomas
 
Status
Nicht offen für weitere Antworten.
Ähnliche Java Themen
  Titel Forum Antworten Datum
L Rechnungen in Java erstellen & drucken Allgemeine Java-Themen 23
M Datentyp für Geldbeträge,Prozente, Rechnungen Allgemeine Java-Themen 4
berserkerdq2 Was heißt es mit FXML Listener zu setzen ind Buttons zu erstellen? Allgemeine Java-Themen 6
TheSepp Java bestimmtes Array auf den Wert 0 setzen Allgemeine Java-Themen 32
Y Bild in JPanel setzen Allgemeine Java-Themen 2
C javax.mail.Message message.setreplyto() null setzen (keine replyto Adresse) Allgemeine Java-Themen 25
JavaNewbie2.0 Anführungszeichen setzen Allgemeine Java-Themen 3
OnDemand FTP4J Rechte setzen Allgemeine Java-Themen 1
J Mausposition setzen Allgemeine Java-Themen 2
I Setzen und Lesen eines Objektes (Enum?) Allgemeine Java-Themen 10
N event_scheduler richtig setzen? Allgemeine Java-Themen 1
O log4j pfad per umgebungsvariable setzen Allgemeine Java-Themen 5
F JTextArea Cursor in die nächste Zeile setzen Allgemeine Java-Themen 2
G JDK setzen Allgemeine Java-Themen 4
H java.util.logging Formatter Optionen setzen Allgemeine Java-Themen 2
iB0T "goto" Befehl aus Batch in Java und Variablen wert immer wieder neu setzen Allgemeine Java-Themen 4
D ClassLoader für Variablen einer Klasse setzen Allgemeine Java-Themen 24
C Datentypen int in bytes zerlegen und wieder zusammen setzen Allgemeine Java-Themen 13
M Session-Parameter setzen? Allgemeine Java-Themen 10
D Alle Variablen final setzen ? Allgemeine Java-Themen 26
I %AppData% Variable für einen Prozess setzen Allgemeine Java-Themen 23
N java.util.Date - Zeit auf 00:00 setzen Allgemeine Java-Themen 3
T HTTP Skript für Passwortgeschützte Seite :: Häkchen setzen und Download Allgemeine Java-Themen 4
L Variablen Systemvariablen setzen Allgemeine Java-Themen 3
G JNDI Context setzen Allgemeine Java-Themen 3
S "Working Directory" setzen..? Allgemeine Java-Themen 3
A Datei als Object einlesen und das Object als Singleton instance setzen. Allgemeine Java-Themen 13
V Priorität der Event-Handlings setzen Allgemeine Java-Themen 4
N Timeout setzen Allgemeine Java-Themen 10
P Einzelne Bits in einem Byte-Array setzen Allgemeine Java-Themen 2
X JTable mit Checkboxen -> Setzen (true/false) der Checkboxen per Mouseklick... Allgemeine Java-Themen 3
S String intelligent zusammen setzen Allgemeine Java-Themen 8
A Umgebungsvariable unter Windows Vista setzen Allgemeine Java-Themen 60
N ranges setzen Allgemeine Java-Themen 5
O getRuntime().Exec() - Environment - Variablen setzen? Allgemeine Java-Themen 2
Y beliebige Dateigröße setzen Allgemeine Java-Themen 10
G Proxy- Server setzen Allgemeine Java-Themen 2
M nichtreferenzierte Objekte auf NULL setzen -> Performance Allgemeine Java-Themen 4
M Focus setzen - Frage zu FAQ Artikel Allgemeine Java-Themen 6
reibi Priorität setzen bei Thread Allgemeine Java-Themen 11
0 Taste setzen Allgemeine Java-Themen 2
B Listener dynamisch setzen Allgemeine Java-Themen 6
F Werte von Member-Variablen erst im Konstruktor setzen? Allgemeine Java-Themen 7
P Datum in einer DateComboBox setzen Allgemeine Java-Themen 7
MQue Datum in ComboBox setzen Allgemeine Java-Themen 9
0 Timeout für Thread setzen? Allgemeine Java-Themen 2
MQue Objektvariablen setzen Allgemeine Java-Themen 3
MQue Button Attribute setzen Allgemeine Java-Themen 14
L datum nach dem kopiervorgang neu setzen? Allgemeine Java-Themen 2
P JNI_CreateVM und -Xms –Xmx setzen Allgemeine Java-Themen 10
R Array komplett auf null setzen Allgemeine Java-Themen 10
B Choice setzen Allgemeine Java-Themen 5
O Environment Variable Setzen Allgemeine Java-Themen 2
O Schreibrechte setzen? Allgemeine Java-Themen 4
TheJavaKid Focus für KeyListener setzen Allgemeine Java-Themen 5
D Position des Mauszeigers setzen! Allgemeine Java-Themen 3
S Odnericon neu setzen/auslesen etc. Allgemeine Java-Themen 2
G Konsoleneingabe: vordefinierte werte setzen? geht das? Allgemeine Java-Themen 4
foobar java.library.path Property setzen Allgemeine Java-Themen 5
Z Parallel Port, setzen der einzelnen Pins Allgemeine Java-Themen 4
C Setzen der Cursorposition Allgemeine Java-Themen 2
G Standard-Drucker mit Java setzen Allgemeine Java-Themen 4
T Classpath per Java Programm setzen Allgemeine Java-Themen 9
D jdic classpath setzen Allgemeine Java-Themen 2
S Grafiken/ImageIcon setzen Allgemeine Java-Themen 24
S File Encoding im Programm setzen? Allgemeine Java-Themen 3
clemson Umgebungsvariable setzen und lesen Allgemeine Java-Themen 9
F Verfügbaren Heap-Speicher setzen Allgemeine Java-Themen 2
M Content-Type setzen und auslesen? Allgemeine Java-Themen 2
C Objekte null setzen? Allgemeine Java-Themen 7

Ähnliche Java Themen

Neue Themen


Oben