float -> int ab- UND aufrunden
-
Hi.
Ich hab mal was leichtes für euch ( mein buch sowie die blöde board-google suche haben mir leider nicht geholfen)
Was ich möchte ist von 2 Integerwerten den Durchschnitt zu bilden ... welcher ebenfalls Integer ist.
Das Problem:
Wenn nach Integer gecastet wird, wird auch immer abgerundet.
Das macht sich aber schlecht wenn ich zwei Farbwerte (0-255) mischen möchte, da so immer zu Gunsten des dunkleren Wertes entschieden wird. Und es fällt besonders ins Gewicht wenn es >100 mal die Sekunde so mischt.Muss ich irgentwetwas beim Compiler einstellen, oder muss ich eine bestimmte Funktion nutzen, oder muss ich mir selbst was schreiben, was am Ende doch zu Rechenintensiv ist?
In dem Fall müsste ich auf float Farbwerte umsteigen.Danke schonmal.
EDIT: Ach man....wie blöd...^^
float h=0.66; cout << (int)(h+0.5);
...aber geht das nicht auch weniger umständlich? ... auch wenns recht banal einfach ist...aber ich versuch meinen thread noch einen Hauch von Leben zu verpassen
-
((int1 + int2)*10/2+5)/10
Zuerst fügst du eine Stelle hinzu, dann teilst du durch 2, um den durchschnitt auszurechnen, dann rechnest du plus 5, um zu runden, am ende wieder durch 10 um wieder auf 3 Stellen zu kommen.
mfg.
-
Äh.
int r = (a+b+1)/2;
Nen?
Wieso fällt es mehr ins Gewicht wenn du 100 mal pro Sekunde mit abrunden mischt? Machst du einen auf Feedback oder wie? Hmmm...
-
Habs mit dem was ich im edit geschrieben habe schon gelöst.
Hier meine Mischfunktion:
bool mixColori( int ir, int ig, int ib, float am) // am = amount //deckkraft { if( ir >= 0 && ir <= 255 && ig >= 0 && ig <= 255 && ib >= 0 && ib <= 255 && am >= 0 && am <= 1) { r = (int)((float)ir*am + (float)r*(1-am)+0.5); g = (int)((float)ig*am + (float)g*(1-am)+0.5); b = (int)((float)ib*am + (float)b*(1-am)+0.5); } else return false; }
So und warum fällt das ins Gewicht(auf wenn >100 übertrieben war ^^):
Ich habe einen Brush programmiert der die Farbe vom Brush mit dem des Bildes(bzw. der jeweilige Pixel der darunter liegt) mischt.
Wenn ich nun eine 100 Pixel lange Linie ziehe werden immer im gleichen Abstand(ca 2 Pixel) Kreise gezeichnet, bei denen eben jeweils immer die Farbe gemischt wird.
Und da sich diese Kreise überlagern, wird der Mischvorgang mehrmals an einer Stelle durchgeführt.
Was zur Folge hat, dass ein heller Farbton viel schneller den jeweilig dunkleren Mischwert annimmt.(200 + 255) / 2 = 227.5 -> 227
(200 + 227) / 2 = 213.5 -> 213
(200 + 213) / 2 = 206.5 -> 206
(200 + 206) / 2 = 203 -> 203
(200 + 203) / 2 = 201.5 -> 201
(200 + 201) / 2 = 200.5 -> 200 -> nach so wenigen Durchgängen wurde der dunklere Mischwert wurde angenommen.
Bei richtigem Runden, würde dieser Vorgang, bei dem theoretisch niemals der dunklere Mischwert angenommen werden sollte, beträchtlich verlängert. Außerdem wenn ein hellerer Farbwert konstant ist, wird gleich schnell angeglichen.
Das hier gezeigte Beispiel findet zwar nicht wirklich so beim Malen statt, Ich wollte allerdings zeigen, dass sich mit jedem falschen Abrunden sich die Ungenauigkeit noch mehr vergrößert.Außerdem Plotzenhotz, dein Code rundet in jedem Fall auf. Ich wollte nur abrunden wenn es nötig ist
Dankö
-
der code sieht furchtbar aus.
benutze eine funktion zum runden: http://www.c-plusplus.net/forum/viewtopic-var-t-is-39342.htmldamit das ganze halbwegs lesbar bleibt
-
Ich habe einen Brush programmiert der die Farbe vom Brush mit dem des Bildes mischt.
Wenn ich nun eine 100 Pixel lange Linie ziehe werden Kreise gezeichnet, bei denen eben jeweils immer die Farbe gemischt wird.
Und da sich diese Kreise überlagern, wird der Mischvorgang mehrmals an einer Stelle durchgeführt.der code sieht furchtbar aus. benutze eine funktion zum runden
wenn dieser "brush" einigermassen fluessig arbeiten koennen soll, kann man sich das floating-point-zeug sowieso nicht leisten;
geschweige denn pro farbkomponente eine funktion zum runden aufzurufen...
-
hellihjb schrieb:
wenn dieser "brush" einigermassen fluessig arbeiten koennen soll, kann man sich das floating-point-zeug sowieso nicht leisten;
geschweige denn pro farbkomponente eine funktion zum runden aufzurufen...Das mag vor zehn Jahren der Fall gewesen sein, aber du wirst dich wundern, was heutige CPUs zu leisten vermögen.
Edit:
Obwohl ich beim besten Willen nicht einsehen kann, wozu die Farbwerte gerundet werden müssen.
-
VHASE schrieb:
Außerdem Plotzenhotz, dein Code rundet in jedem Fall auf. Ich wollte nur abrunden wenn es nötig ist
Der Code von Plotzenhotz macht schon das Richtige! Hier ein kleines Beispiel:
#include <iostream> using namespace std; int main() { float am = 2.3f; int ir = 2; int r = static_cast<int>((am + ir + 1) / 2); //Das ist dasselbe wie static_cast<int>((am + ir)/2 + 0.5)!!! cout << r << endl; }
-
Das mag vor zehn Jahren der Fall gewesen sein, aber du wirst dich wundern, was heutige CPUs zu leisten vermögen.
danke, ich bin mir ziemlich bewusst darueber, was aktuelle prozessoren leisten koennen und ich gebe dir in soweit recht, dass man bei mehreren milliarden taktzyklen pro sekunde durchaus mal gedankenlos code hinschludern kann, der pro pixel einige hundert takte fuer unsinn verschwendet, ohne dass man es ueberhaupt bemerken wuerde.
dennoch sollte man das nicht zur tugend erklaeren: waeren professionelle anwendungen, wie zb ein photoshop, so implementiert, koennte man damit auch auf aktuellen cpus nicht vernuenftig arbeiten.
fuer eine lineare interpolation zwischen zwei bytes braucht man keine gleitkomma-praezision; aber darum geht es ja hier auch gar nicht, also schwamm drueber
-
@Shade Of Mine: ^^ für mich ist mein Code lesbar...Aber Danke für den Link, sieht nützlich aus
@Konstruktör: Oh...Dankeschön für die Klarstellung.
@hellihjb: Also bis jetzt läuft es noch flüssig. Auch wenn der Code bis jetzt größtenteils unoptimiert ist.
Dazu bin ich noch ein C++ Neuling...Ich kann mir auch vorstellen dass Photoshop mit mehreren Threads arbeitet, da wenn man eine sehr große Linie(große brushsize) zieht, diese mit einer Verzögerung gezogen wird. Eben um die Mausposition trotz großer Belastung immer schnell abfragen zu können.
Dadurch kann man sich noch einige Spielereien leisten, auch wenn die Verzögerung das Malen nicht unbedingt zur Freude macht.
-
VHASE schrieb:
@Shade Of Mine: ^^ für mich ist mein Code lesbar...
schau dir nur einmal diese 2 codes an:
f=(int)((float)ir*am + (float)r*(1-am)+0.5);
f=round(ir*am + r*(1-am));
und dann sag mir das obere ist gut zu lesen.
du kannst es lesen weil du weisst was es macht. aber da sind alleine soviele klammern, da kann man kaum die prioritaeten ausmachen ohne genau hinschauen zu muessen.
-
Dann rat mal warum ich "^^" davor geschrieben habe
-
nach so wenigen Durchgängen wurde der dunklere Mischwert angenommen:
1. (200 + 255) / 2 = 227.5 -> 227
2. (200 + 227) / 2 = 213.5 -> 213
3. (200 + 213) / 2 = 206.5 -> 206
4. (200 + 206) / 2 = 203 -> 203
5. (200 + 203) / 2 = 201.5 -> 201
6. (200 + 201) / 2 = 200.5 -> 200ueberleg mal genau, was du da machst:
du bewegst dich zwischen zwei schranken (200..255) durch binaerteilung.
und dafuer benoetigst du eben log2(255-200)= ~6 schritte.
auch anderes rundungsverhalten wird daran nichts aendern!aber was soll das ueberhaupt?
wenn's weniger stark transparent sein soll, benutz' doch einfach 'nen hoeheren faktor...
-
VHASE schrieb:
Dann rat mal warum ich "^^" davor geschrieben habe
Sorry, aber ^^ ist kein smiley das eine besondere bedeutung hat. und schon garnicht ironie anzeigt...
-
@Shade Of Mine: "^^" ist anscheinend "CounterStriker-Slang" und versteht sich als "*zwinker*" oder so. Nicht dass ich sowas spielen würde, aber ich hab mich mal erkundigt was das soll und wurde dahingehend belehrt
@VHASE: Tip: nimm nen Integer als Mischverhältnis.
Wertebereich 0-2^n, also z.B. 0 bis 256 oder auch 0 bis 1024. Dann mischt du wie folgt:long rMixed = (r1 + ( ((r2-r1) * a) >> 8 )); long gMixed = ... long bMixed = ...
Dafür muss a == 0 sein wenn du r2 haben willst und a == 256 sein wenn du r1 haben willst. Wenn du a z.B. von 0 bis 1024 haben willst dann halt ">> 8" durch ">> 10" ersetzen. Wenn du 8 Bit Werte hast macht es im Übrigen auch keinen Sinn mehr als einen "8 Bit + 1 Wert" (257 Levels - 0 bis 256 eben) Alpha-Kanal zu verwenden - die effektiven Blending-Levels erhöhen sich dadurch nicht.
Weiters brauchst du bloss eine Integer Multiplikation statt 2 float Multiplikationen. Viel viel schneller. Verwende ich so zum Beispiel zum Alpha-Blenden in einer einfachen Sprite-Lib.Und damit fällt auch das Problem weg dass du immer in Richtung "dunkel" rundest, du rundest dann nämlich immer in Richtung r1! Wenn du in Richtung r2 runden willst musst du einfach die mix-Richtung und "a" umdrehen, also:
long rMixed = (r2 + ( ((r1-r2) * (256-a)) >> 8 ));
Das "(256-a)" ziehst du natürlich üblicherweise vor die Schleife, wenn nicht dann macht das hoffentlich der Compiler für dich (loop invariant subexpression elimination).
Wenn du willst kannst du dann noch versuchen erst einmal in Richtung r1 zu runden und dann in Richtung r2 etc.
Photoshop 5 rundet im übrigen auch nicht besser, und soweit ich mich erinnere immer in Richtung dunkel. Bei neueren kann ich nix sagen, die kenne ich nicht.
-
p.S.: Falls du's noch nicht so machst: für jede Brush anhand ihrer Grösse (Durchmesser/Radius) eine "Stepgrösse" bestimmen. Du willst z.B. sicher nicht mit einer 100x100 Brush bei Bewegen um 1 Pixel gleich wieder neu blenden. Durchmesser/8 oder sowas in der Richtung könnte hinkommen. PS macht das auch so. Die Step-Breite kann man dort natürlich auch einstellen (Eigenschaft der Brush), damit man wenn man doch was anderes will eben nicht "angeschissen" ist
Nur weils mir grad zum Thema Brushes einfällt, hat natürlich nixe mit dem eigentlichen Blenden zu tun.
-
wenn man an performance denken moechte, sollte man erstmal diese fuerchterliche bereichspruefung entfernen:
if( ir >= 0 && ir <= 255 && ig >= 0 && ig <= 255 && ib >= 0 && ib <= 255 && am >= 0 && am <= 1) // ...
illegale transparenzfaktoren und overflows in den farbkomponenten des pinsels entfernt man einmal initial und ueberprueft sie nicht fuer jeden pixel, negative farbanteile sollte man pauschal ausschliessen und ueberlaeufe sind bei dieser speziellen blend-formel (lineare interpolation zwsichen zwei legalen farben) unmoeglich.
wenn tatsaechlich ueberlaeufe stattfinden koennen, sollte man an mmx denken - vgl hier(r1*a + r2*(256-a)) >> 8 = r2 + ((r1-r2)*a>>8)
ueberraschender weise ist ersteres haeufig schneller.
-
Hui, die ganzen "ifs" hab ich in meine "Hui, ich weiss was" Rausch ganz "übersehen"
Klar müssen die weg.Und die zwei-mul Variante ist ziemlich sicher schneller wenn entweder (r1 und a) oder (r2 und a) innerhalb der Schleife konstant sind und der Compiler es überreisst. Wenn es garkeine Schleife gibt, bzw. die zu weit weg ist mag das auch so sein, aber dann ist sowieso wieder egal, dann werden beide Varianten langsam sein.
Die ein-mul Variante sollte nach meiner Einschätzung auf jeden Fall schneller sein wenn beide Teilausdrücke "r1*a" und "r2*(256-a)" nicht konstant sind. Ein mul ist immernoch teuer wenn es zu knapp auf ein anderes mul folgt, oder man das Ergebnis zu schnell braucht um damit weiterzumachen. Ich werde das aber wenn ich nicht vergesse ausprobieren... das will ich jetzt genau wissen
Also ja, @VHASE: die ganzen ifs "müssen" weg (zumindest aus der Schleife raus), und das blending "muss" direkt in einer Schleife über die betroffenen Pixel erfolgen. Zumindest wenn du das schnell haben willst.
Wenns dir fürn Anfang zu kompliziert ist dann lass es so wie es ist, bloss eines würde ich trotzdem empfehlen, nämlich mit integers zu blenden.
-
Klingt alles sehr gut was ihr hier schreibt, allerdings bin ich mit der Nutzung von Shift-Operatoren noch nicht wirklich vertraut.
Das Programm läuft zur Zeit recht flüssig, selbst auf einem Laptop mit schlappen 1ghz ^^ (ja "Counterstrike Slang" .. *hust*)Das Blending, so wie ich es noch nutze, bringt mir auch nicht wirklich Leistungseinbrüche. Das wird hauptsächlich durch das Darstellen selbst verursacht. Zeichne dementsprechend blos die Pixel neu, welche verändert wurden.(hui)
Ist für mich persönlich was Neues so Leistungsoptimiert zu programmieren, allerdings bringt es ja auch viele Vorteile.
@Plotzenhotz: Jo ich hatte den Space abhängig von der Größe des Brush gemacht.
Das Blending erfolgt auch blos bei den betroffenen Bildpunkten.Die ifs habe ich jetzt auch erstmal rausgenommen. Da es ja nur bereits überprüfte Werte nutzt.
...Ich werd sicher nochmal irgentwann mit mehr Wissen auf den Thread zurückgreifen.
PS: Wen es interessiert wie sich so mein Projekt entwickelt, kann ja mal codename Neverdry besuchen