Wie bekomme ich eine Bitmap performant auf den Schirm?



  • Jo. Topic sagt schon fast alles. Fast 🙂
    Ich bekomme Bilddaten als YUY2 rein. Diese in RGB24 oder RGB32 umzuwandeln ist grundsätzlich kein Problem, dafür hab' ich eine DLL die das sehr performant erledigt. Ist auch schon eingebunden und funktioniert.

    Jetzt ist bloss die Frage, wie bekomme ich das ohne viel Rechenzeit zu verschwenden in ein Control (System.Windows.Forms.Control) gezeichnet?

    Derzeit habe ich eine Bitmap die ich mit einem IntPtr auf ein gepinntes Array konstruiert habe, und in diesen Speicher lasse ich von der "YUY2 -> RGB DLL" direkt hineinmalen. Funktionieren tut das fein. Bis dahin geht auch alles noch schön schnell. Wenn ich allerdings dann ein Invalidate auf das PictureBox Control mache welches diese Bitmap als "Image" eingetragen hat ... dauert das recht lange. Bei der derzeitigen Bildrate (25fps) geht dabei ein Core meines Q6600 auf 100% (wenn ich die PictureBox Fullscreen habe, also fast 1600x1200). Und das sollte schneller werden. (Achja, die PictureBox ist natürlich auf "stretch" gestellt, da das Bild auch "füllend" angezeigt werden muss.)

    Also... die Zeit geht wirklich nur beim neu Zeichnen drauf, lasse ich das Invalidate() auf die PictureBox z.B. nur alle 20 Frames oder so machen sinkt die CPU Auslastung auf "fast nix", obwohl alles andere noch läuft (also z.B. die YUY2 -> RGB Konvertierung und der ganze Rest der mir diese Bilddaten überhaupt erst besorgt.)

    Die Bildschirmauflösung/Farbtiefe ist 1600x1200x32bpp, und die .NET Framework Bitmap verwendet als Pixelformat Format32bppRgb (ohne "A" - brauch ich auch nicht). Sollte IMO optimal sein, da gleiches Format und alle Pixel DWORD aligned und so.

    Kennt jmd. von euch eine halbwegs einfache Lösung das performanter hinzubekommen? Ich habe mir schon überlegt managed DirectX zu verwenden, allerdings hab' ich damit genau noch nie irgendetwas gemacht, und ich hab' nicht allzuviel Zeit da rumzuexperimentieren.

    Nochmal vonwegen Farbtiefe: man kann ruhig davon ausgehen dass diese immer 32bpp ist, eine Methode die also nur bei 32bpp performant funktioniert wäre völlig OK, solange sie nicht GANZ versagt bei anderen Farbtiefen.

    Und nochwas zum Schluss: es muss *zumindest* bilinear gefiltert werden (besser ist OK aber nicht unbedingt nötig). Wenn es was bringen würde liesse es sich allerdings einrichten dass das Bild von der "YUY2 -> RGB DLL" bereits passend skaliert wird, und im .NET Teil dadurch immer nur ein 1:1 Pixel-Mapping gemacht werden muss beim Zeichnen.
    Ob das ohne weitere Änderungen etwas bringt muss ich allerdings erst noch ausprobieren (hatte noch keine Zeit das zu versuchen).

    p.S.: danke fürs Lesen und für eure Zeit 🙂



  • Ich hab solche ähnlichen Spielchen nur einmal machen müssen, das war aber auf einen WindowsCE Gerät.
    Ich hatte mich hier der Methode BitBlt aus der WinAPI bedient. Ob Dir diese weiterhilft kann ich Dir aber nicht sagen :o( (Bei meinem Problem ging es eher darum, die Rohdaten zügig auf die Forum zu klecksen.)



  • Vieleicht kannst du den Grafikkarten Overlay der auch zum Videoanzeigen benutzt wird aber keine Ahnung wie man den Anspricht in DirectShow nimmste das einfach als Render Filter.



  • @Knuddlbaer: Danke für den Hinweis. Allerdings... weiss ich nicht wie ich am schnellsten meine "Rohdaten" in einen DC reinbekomme (bzw. in eine "unmanaged" Bitmap die in einen DC selektiert ist). Ist zwar nicht Rocket-Science, aber es würde auch wieder etwas an Zeit dabei draufgehen mich da durchzukämpfen, speziell in Hinblick darauf dass ich das in .NET machen muss ... und es gibt lustigeres als PInvoke zu verwenden 😉

    @DaRpH: DirectShow möchte ich nicht verwenden. Erstens weil es keine offizielle Anbindung für .NET gibt, zweitens weil ich kein gutes Gefühl dabei habe bei jedem Fenster-auf-Fenster-zu einen DirectShow graphen anzulegen und wieder niederzureissen, drittens weil ich auch keine Lust habe einen DirectShow Source Filter dafür zu schreiben (bzw. genauer: keine Zeit), und schliesslich und endlich auch weil ich einige solche Controls gleichzeitig am Schirm braucht (10, 20, 30, ...), und Grafikkarten AFAIK nicht allzuviele Overlays gleichzeitig unterstützen.
    Trotzdem Danke für den Hinweis.



  • Schau Dir mal CreateBitmap an.

    HBITMAP CreateBitmap(
    int nWidth, // bitmap width, in pixels
    int nHeight, // bitmap height, in pixels
    UINT cPlanes, // number of color planes
    UINT cBitsPerPel, // number of bits to identify color
    CONST VOID *lpvBits // color data array
    );

    public void CreateMyBitmap(uint bits, byte[] ImageBuffer, int dwWidth, int dwHeight)
            {
                IntPtr hBmp, hOrgBmp, hdc, hdcMem;
    
                hdc = GetDCCE(getHwnd());
    
                hBmp = CreateBitmap(dwWidth, dwHeight, 1, bits, ImageBuffer);
                hdcMem = CreateCompatibleDC( hdc );
    
                hOrgBmp = SelectObjectCE( hdcMem, hBmp );
    
                Point p = calcPos(dwWidth, dwHeight);
                BitBlt(hdc, p.X, p.Y, dwWidth, dwHeight, hdcMem, 0, 0, 0x00CC0020);
    
                SelectObjectCE( hdcMem, hOrgBmp );
                DeleteObject( hBmp );
                DeleteDCCE( hdcMem );
    
                ReleaseDCCE(getHwnd(), hdc);
    
                Application.DoEvents();
            }
    

    Ein zusammengestückelter Code aus der HW Routine auf dem Scanner. Zugegeben wird hier aber etwas Zeit draufgehen, das so zusammen zu pusseln das es Dir weiterhilft. (Wenn überhaaupt).

    Eventuell könnte man die Frage mal im WinAPI Forum stellen, die PInvokegeschickten kann man sich auf pinvoke.net zusammen stellen.

    DirectX: IMHO gibt es ein Managed DirectX zeugs.

    Schau Dich mal hier um: http://www.mycsharp.de/wbb2/board.php?boardid=25
    http://www.mycsharp.de/wbb2/thread.php?threadid=56234



  • Da du da von Quadcore sprichst... würde was dagegensprechen das Bild vierzuteilen und zu zeichnen bzw das nächste Bild vorzuladen? Weis leider nicht ob das ohne große Syncronisationseinbuße in .NET Framework machbar ist.



  • @DaRpH: die 2-4 Cores auf dem Zielsystem werden schon gut ausgenutzt werden, da mache ich mir keine Sorgen. Die Anzeige der Videobilder ist ja auch nur ein Teil der laufen soll. Von daher besteht garkeine Not ein Bild irgendwie zu splitten. Und es sollte auch eigentlich nur im User-Interface-Thread gezeichnet werden. Erstens ist das viel viel einfacher als alles andere, und zweitens kann der UI Thread auch immer nur einen Core "zu machen", was vorteilhaft ist, da wie gesagt sonst auch noch *einiges* laufen soll.

    @Knuddlbaer:
    Ich hab' das mit dem Bitmaps nur kurz probiert, allerdings bin ich damit noch nicht ganz auf einen grünen Zweig gekommen. Die Funktion Bitmap.GetHbitmap kann man leider gänzlich vergessen (zu langsam, und erzeugt eine Kopie anstatt eine Bitmap die auf die Original-Daten "zeigt", d.h. müsste pro Frame ausgeführt werden).

    Danke übrigens für den pinvoke.net Tip, der war sehr hilfreich. Mittlerweile hab' ich auch einigermassen heraussen wie man diverse Typen marshallen muss bzw. kann, ist eigentlich auch halbwegs logisch.

    z.T. Managed DirectX, ja das gibts. Allerdings hab ich damit noch keine Erfahrung, daher weiss ich auch nicht wie man bestimmte Dinge am einfachsten Regelt. Ich muss z.B. ein Control basteln welches halbwegs eigenständig funktioniert, und wovon gleichzeitig mehrere Instanzen sichtbar sein können. Klar wird das mit D3D irgendwie gehen, bloss eben WIE ... welches Window Handle verwende ich für das Device, worauf muss ich achten, ... das alles eben. 😞

    ----

    Ansonsten... was ich bis jetzt ausprobiert/herausbekommen habe: die DLL die wir zum Skalieren der Bilder verwenden ist leider auch etwas "lahm". Zeichnen mit GDI+ scheint völlig aussichtslos zu sein, VIEL zu langsam. Im Moment verwende ich die DrawDib Funktionen (DrawDibOpen, DrawDibDraw, DrawDibClose - MSVFW32.DLL). Das geht zumindest halbwegs schnell, allerdings auch immer noch langsam verglichen mit z.B. dem Direct3D Renderer vom Media Player Classic.



  • Kannst du nicht der DLL das Handle auf die PictureBox geben und die DLL da direkt rein zeichnen lassen?



  • @geeky:
    Nö, die (fertige) DLL kann nicht zeichnen, die kann bloss Farmraum konvertieren (YUY2->BGRX) und skalieren (bilinear).

    Wenn ich eine Lösung hätte wie ich das mit WinAPI selbst implementieren kann wäre das allerdings auch OK. Mittlerweile hab ich ganz gut den ganzen PInvoke/fixed Kram raus, also das ginge schon. Zur not würde ich es in C++/CLI machen, womit ich auch schon erfolgreiche Gehversuche gemacht habe.

    Bloss bin ich mir nicht sicher welches der erfolgversprechendste Weg ist. Ich möchte halt nicht unbedingt mit z.B. Direct3D9 anfangen und dann draufkommen dass es da irgendwie Probleme gibt die sich nicht oder nur schwer umschiffen lassen.

    DirectDraw7 würde mir zusagen, da weiss ich inetwa worauf ich aufpassen muss und wie man diverse Dinge macht, da ich schon relativ viel damit gemacht habe. Und ich wüsste z.B. dass es mal grundsätzlich keine Probleme gibt wenn ich mit einer DirectDraw Instanz auf mehrere Fenster zeichnen möchte. Zumindest nicht wenn die alle zu einem Thread gehören. Neuen Clipper, HWND in Clipper reingesteckt, Clipper in Primary-Surface reingesteckt, zeichnen, fertig.

    Das einzige was ich nicht weiss ist wie ich in DirectDraw7 schön bilinear gefiltert skaliere und ggf. noch die Farbraumkonvertierung mache 😞



  • Sure, I agree. But I also agree with this statement: Breathing is good, not breathing is bad.,



  • get back on track,



  • Having moved the head off do not cry for hairs,



  • fit like a glove,



  • Being in a hurry one can make people laugh,



  • get one’s jaws tight,



  • ready_sep15-r115-1_7.txt;20;30



  • ready_sep15-r115-1_7.txt;20;30



  • ready_sep15-r115-1_7.txt;20;30



  • ready_sep15-r115-1_7.txt;20;30



  • ready_sep15-r115-1_7.txt;20;30


Anmelden zum Antworten