Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

31

07.09.2015, 13:50

@BlueCobold
Ich habe nie groß mit SFML gearbeitet. Allerdings erfordert OpenGL auf vielen Plattformen zum Erstellen eines Contexts grundsätzlich ein Fenster. Ich weiß nicht wie SFML das genau macht. Vielleicht erstellt es ein unsichtbares Fenster oder solche Tricks, aber ein Fenster ist notwendig. Außerdem verwendet er SharpGL. Mit SFML.Net könnte er wahrscheinlich überhaupt keine Shader verwenden. Du tust halt irgendwie so, als ob Sampling ein großer Aufwand wäre. Das ist es aber eigentlich nicht. Man nimmt einfach ein paar Punkte und nimmt den Mittelwert der Ergebnisse. Ein paar Zielen. Bei OpenGL hingegen braucht man auch mehrere Zeilen Multisampling korrekt einzurichten und dann gibt hunderte Varianten und das sieht dann in der Regel auch auf jeder GPU anders aus.

Irgendwie finde ich diese Diskussion nicht mehr zielführend. Alles hat halt seine Vor- und Nachteile.

@MitgliedXYZ
Ich würde an deiner Stelle erstmal eine CPU Lösung wegen der Einfachheit und Unabhänigkeit probieren, aber im Prinzip musst du schon selbst wissen, welche Anforderungen du hast. Wenn du wirklich große Bilder oder hohe Qualitätsanforderungen hast, dann kann das schon auf der GPU schneller gehen.

Für den CPU Ansatz, nimmst du einfach die Bitmap, dort kannst du auch direkt auf die Pixeldaten zugreifen. Am Besten in dem du die Pixel im Unsafe Code lockst. Dort kannst du dann am Besten mit einer parallelen Schleifen (Parallel.For ab 4.0) über die Pixel, überprüfst in welchen Viereck er liegt und interpolierst die Farbe entsprechend. Wenn das noch nicht schnell genug ist, kannst du in der neusten Dot.Net Version auch SIMD verwenden.
Die Methode in deinem Link ist ineffizient, weil dort einzelne Linien gerendert werden was schlechter parallelisierbar ist und zu ungünstig zu behebenden Bildfehlern führt. Das schreibt er auch selbst im Artikel: "Because this algorithm works the opposite way that it should."

Wenn du eine GPU Lösung willst, würde ich mir an deiner Stelle entweder Shader ansehen oder OpenCL/DirectCompute einsetzen, mit denen sich für die Bildverarbeitung zuverlässigere Ergebnisse erreichen lassen.

Warum du eine extra Bibliothek für JPEG suchen möchtest, habe ich nicht ganz verstanden. Klar gibt es schnellere Implementierungen des JPEG Algorithmus, allerdings wird die in GDI+ vermutlich schon schnell genug für deine Zwecke sein.

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »Spiele Programmierer« (07.09.2015, 16:26)


32

07.09.2015, 17:28

Morph from one image to another in Visual Basic .NET

Ist zwar VB und langsam, aber tut. Die PointsToST scheint das bilinear inverse zu sein.

MitgliedXYZ

Alter Hase

  • »MitgliedXYZ« ist der Autor dieses Themas

Beiträge: 1 369

Wohnort: Bayern

  • Private Nachricht senden

33

07.09.2015, 18:06

@Spiele Programmierer
Danke für deine ausführliche Antwort.
Ich werde es jetzt dann doch auf der CPU versuchen.
Statt der ineffizienten Methode aus meinem Link, gibt es bestimmt noch andere Algorithmen dafür, oder?

Wegen der parallelen Ausführung, bringt es da mehr wenn parallel an einem Bild gearbeitet wird, oder parallel mehrere Bilder gleichzeitig aber je mit nur einem Thread?

Eine Bibliothek für JPEG brauch ich dann nicht, ich hatte nur befürchtet, dass das was .Net dafür mitliefert sehr langsam ist. Aber wenn die Geschwindigkeit anderer Bibliotheken dabei ähnlich ist, nehme ich einfach die Standardfunktion.

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

34

07.09.2015, 18:39

Ich habe dir doch die Vorgehensweise schon geschildert. Du iterierst einfach über jeden Punkt im Zielbild und bestimmst, in welchen Viereck er liegt. Algorithmus dafür. Da gibt es viele Möglichkeiten und sehr viel Potential zur Optimierung. Erstmal eine grobe Vorauswahl in einem gröberen Array über die Bildpunkte wäre zum Beispiel eine einfache Möglichkeit. Wenn du weißt, in welchem Quad der aktuelle Bildpunkt liegt, übergibst du den aktuellen Bildpunkt und die Eckpunkte des Quads an die "invBilinear" Funktion("vec2" ist einfach eine Struktur mit float x- und y-Koordinate und komponentenweiser Addition/Subtraktion). Die Funktion sagt dir, welcher Pixel im Originalbild an diese Stelle kommt. Den kannst du einfach nachschlagen und an den Zielpixel kopieren.

Wenn du tatsächlich mehrere Bilder gleichzeitig verarbeiten kannst, ist das prinzipiell natürlich noch einfacher. Allerdings sollte klar sein, dass es nur dann schneller wird, wenn du wirklich einige Bilder gleichzeitig hast und ein einzelnes Bild länger dauert.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Spiele Programmierer« (07.09.2015, 18:46)


MitgliedXYZ

Alter Hase

  • »MitgliedXYZ« ist der Autor dieses Themas

Beiträge: 1 369

Wohnort: Bayern

  • Private Nachricht senden

35

07.09.2015, 19:07

Ah, okay der Link war also ein Algorithmus dafür. Ich dachte erst das wäre ein Shader-Code gewesen, weil die Vorschau auf der Seite ShadederToy verwendet. Aber dann hatte ich das nur verwechselt...

Die Funktion aus dem Artikel:

C#-Quelltext

1
vec2 invBilinear( in vec2 p, in vec2 a, in vec2 b, in vec2 c, in vec2 d )


Ist Übergabewert p der Punkt im original Bild, oder im Zielbild?

Und a, b, c und d sind die Vektoren des Ziel Polygons, aber woher weiß die Funktion, wie Groß das Ursprungsbild ist, bzw. dessen Vektoren kennt es doch nicht?

Oder macht die Funktion was anderes?

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

36

07.09.2015, 20:12

Ja, der Punkt P ist der Bildpunkt für den die Ursprungswerte der Interpolation gesucht werden.

Texturkoordinaten bzw. die Gewichtungen der bilinearen Interpolation reichen von 0 bis 1. Der Code gibt also Koordinaten im Bereich 0 bis 1 zurück. Da dein Ursprungsbild größer als ein einzelner Pixel ist, musst du den Wert einfach mit der Bildgröße multiplizieren damit der Bereich von 0 bis 1 auf 0 bis Bildgröße vergrößert wird. Dann hast du Pixelkoordinaten.

MitgliedXYZ

Alter Hase

  • »MitgliedXYZ« ist der Autor dieses Themas

Beiträge: 1 369

Wohnort: Bayern

  • Private Nachricht senden

37

07.09.2015, 20:53

Super, dann teste ich das morgen gleich mal.

Woher kanntest du diesen Algorithmus eigentlich, hast du den zufällig auch schon mal für ein Projekt gebraucht, oder kam das in einer Vorlesung vor?

MitgliedXYZ

Alter Hase

  • »MitgliedXYZ« ist der Autor dieses Themas

Beiträge: 1 369

Wohnort: Bayern

  • Private Nachricht senden

38

08.09.2015, 17:12

Der Algorithmus funktioniert und ist schneller als der aus dem Codeproject Artikel.
Für ein 2600x1600px Bild braucht er auf noch ohne Optimierungen und mit nur einem Thread auf meinem i5 ca. 600 Millisekunden, das ist schnell genug, ich werde deine vorgeschlagenen Optimierungsmöglichkeiten bzgl. Multitasking trotzdem noch ausprobieren.

Im Blog ist er nur in C++ beschrieben, ist zwar nicht viel Arbeit den für C# umzuschreiben, aber vielleicht hilfts mal jemandem der diesen Thread durch Google findet oder so:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
public float cross(Vec2 a, Vec2 b)
        {
            return a.x * b.y - a.y * b.x;
        }

        public Vec2 invBilinear(Vec2 p, Vec2 a, Vec2 b, Vec2 c, Vec2 d)
        {
            Vec2 e = new Vec2();
            e.x = b.x - a.x;
            e.y = b.y - a.y;

            Vec2 f = new Vec2();
            f.x = d.x - a.x;
            f.y = d.y - a.y;

            Vec2 g = new Vec2();
            g.x = a.x - b.x + c.x - d.x;
            g.y = a.y - b.y + c.y - d.y;

            Vec2 h = new Vec2();
            h.x = p.x - a.x;
            h.y = p.y - a.y;


            float k2 = cross(g, f);
            float k1 = cross(e, f) + cross(h, g);
            float k0 = cross(h, e);

            float w = k1 * k1 - 4.0f * k0 * k2;
            if (w < 0.0)
            {
                return new Vec2(-1.0f, -1.0f);
            }
            w = (float)Math.Sqrt(w);

            float v1 = (-k1 - w) / (2.0f * k2);
            float u1 = (h.x - f.x * v1) / (e.x + g.x * v1);

            float v2 = (-k1 + w) / (2.0f * k2);
            float u2 = (h.x - f.x * v2) / (e.x + g.x * v2);

            float u = u1;
            float v = v1;

            if (v < 0.0 || v > 1.0 || u < 0.0 || u > 1.0) { u = u2; v = v2; }
            if (v < 0.0 || v > 1.0 || u < 0.0 || u > 1.0) { u = -1.0f; v = -1.0f; }

            return new Vec2(u, v);
        }

public class Vec2
{
 public float x, y;

        public Vec2()
        {
        }

        public Vec2(float x, float y)
        {
            this.x = x;
            this.y = y;
        }
}


Edit:
Wenn ich vor dem Abragen des jeweiligen Pixels teste, ob es in dem Polygon liegt, dauert die Ausführung ca 1/3 länger. Es reicht also mit invBilinear() die aktuelle Pixelkoordinate zu bestimmen, ist diese negativ, liegt diese nicht im Polygon.

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 public unsafe int pnpoly(int nvert, float[] vertx, float[] verty, float testx, float testy)
        {
            int i, j, c = 0;
            for (i = 0, j = nvert - 1; i < nvert; j = i++)
            {
                if (((verty[i] > testy) != (verty[j] > testy)) &&
                 (testx < (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + vertx[i]))
                {
                    //c = !c;
                    if (c == 0)
                    {
                        c = 1;
                    }
                    else
                    {
                        c = 0;
                    }
                }
            }
            return c;
        }

Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von »MitgliedXYZ« (08.09.2015, 19:41)


SlinDev

Treue Seele

Beiträge: 142

Wohnort: Lübeck

Beruf: Programmierer

  • Private Nachricht senden

39

09.09.2015, 18:59

Je nach GPU wäre das ganze mit OpenGL in einem Shader vermutlich trotzdem in unter 50ms möglich, mit guter GPU eher in 10ms oder schneller. Da kommst du auch mit Multithreading und 8 Kernen nicht hin.
Die Funktion könntest du auch großartig mit OpenCL implementieren. Das langsame wäre in beiden Fällen wohl weniger das verzerren als das laden und kopieren der Daten. Den Kontext würde man nur einmal erstellen und immer wieder verwenden.

Aber es kommt eben immer darauf an was du brauchst, jede Lösung hat Vor- und Nachteile. Aber wenn es schnell sein soll und du eine GPU zur Verfügung hast ist diese nunmal ideal für das Problem. Wenn nicht, bleibt dir nichts anderes als die CPU und ordentlich zu optimieren. Auch da lässt sich bestimmt mit SIMD noch was rausholen. Eventuell inlinen der Funktion (kp wie C# sowas handled, in C++ sind Funktionsaufrufe pro Pixel recht teuer), Variablen wiederverwenden (für jeden Pixel mehrmals neu Speicher allocieren kann relativ teuer sein) usw...

MitgliedXYZ

Alter Hase

  • »MitgliedXYZ« ist der Autor dieses Themas

Beiträge: 1 369

Wohnort: Bayern

  • Private Nachricht senden

40

09.09.2015, 19:29

Du schreibst man kann die Funktion gut mit OpenGL implementieren, meinst du damit dann mit vielen kleinen Polygonen (so wie ich es ein paar Seiten vorher schon mal versucht habe), oder gibt es in OpenGL auch die Möglichkeit einzelne Pixel einer Textur zu setzen, so dass es dann trotzdem auf der GPU läuft?

Gerade habe ich aber noch ein anderes Problem, SetPixel() mit LockBits wie im Link beschrieben funktioniert, wenn ich beim Bitmap kein Format mit angebe. Mein Programm arbeitet aber bisher mit:

C#-Quelltext

1
PIXEL_FORMAT = System.Drawing.Imaging.PixelFormat.Format32bppArgb;

Und dann sieht das Gitterbild nach dem QuadDistort Algorihmus so aus:


Wenn ich, wie auf StackOverflow vorgeschlagen wurde, aus dem für PixelFormat.Format24bppRgb

C#-Quelltext

1
 ptr[(x * 3) + y * stride] =

dann

C#-Quelltext

1
 ptr[(x * 4) + y * stride] =

mache, bleibt das Bild schwarz.

Fällt jemandem dazu was ein? Kann der Alpha Kanal das Problem sein?

Werbeanzeige