WPF BitmapSource-ra rajzolás, magas erőforrás igény
2019-04-01T08:13:29+02:00
2019-04-01T22:48:57+02:00
2022-08-11T11:30:30+02:00
ExtSol
Sziasztok!

Ismét én... Most hogy lassan belerázódom a WPF-be, elkezdtem átültetni egy WinForms projektem rá.

cél: kapok egy videó folyamot a webkamerától, Bitmap kép kockák formájában (AForge.NET). Ezen a képen ki akarok jelölni x darab (10-30db) területet, amelyeket a képre rajzolt négyzetekkel lehet kijelölni. Ez WinForms alatt így ment:

public static void KeretekRajzolasa() { Graphics g = Graphics.FromImage(MunkaAsztalKep); for (int index = 0; index < KeretLista.Count; index++) { //pozíciók állapot négyzeteinek kirajzolása if (KijeloltKeretIndex == index) { KijeloloKeret.X = KeretLista[index].KijeloltTeruletKeret.X + Vonalvastagsag; KijeloloKeret.Y = KeretLista[index].KijeloltTeruletKeret.Y + Vonalvastagsag; KijeloloKeret.Width = KeretLista[index].KijeloltTeruletKeret.Width - (Vonalvastagsag * 2); KijeloloKeret.Height = KeretLista[index].KijeloltTeruletKeret.Height - (Vonalvastagsag * 2); g.DrawRectangle(new Pen(KijeloloKeretSzine, Vonalvastagsag), KijeloloKeret); } if (KeretLista[index].ElemzesEredmenyeOK) { if (KeretLista[index].ValtozasDetektalva) g.DrawRectangle(new Pen(ValtozasDetektalasSzine, Vonalvastagsag), KeretLista[index].KijeloltTeruletKeret); else g.DrawRectangle(new Pen(OK_szine, Vonalvastagsag), KeretLista[index].KijeloltTeruletKeret); } else g.DrawRectangle(new Pen(NOK_szine, Vonalvastagsag), KeretLista[index].KijeloltTeruletKeret); } if (MunkaAsztalKepMegjelenitoFrissitesEsemeny != null) MunkaAsztalKepMegjelenitoFrissitesEsemeny(MunkaAsztalKep); }
A KeretLista List<Keret> típusú. Ez a lista tárolja a négyzeteket a képen és a keretek ezzel a kóddal minden beérkező frame-re felrajzolódnak.
Ez CPU: 15% és GPU: 0%
30FPS@1280x720

WPF kód:
Mivel Bitmap típust kapok ezt átkonvertálom BitmapSource-ra hogy tudjam egyáltalán a képet WPF-ben használni. Majd ezután felrajzolok most jelenleg 1db négyzetet a képre.
Kód:

#region bitmap kompatibilitás WinForms -> WPF public BitmapSource BitmapToBitmapSource(System.Drawing.Bitmap bitmap) { var bitmapData = bitmap.LockBits( new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bitmap.PixelFormat); var bitmapSource = BitmapSource.Create( bitmapData.Width, bitmapData.Height, bitmap.HorizontalResolution, bitmap.VerticalResolution, PixelFormats.Bgr24, null, bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride); bitmap.UnlockBits(bitmapData); bitmapSource.Freeze(); return bitmapSource; } #endregion
Rajzolás:

public static BitmapSource Rajzolas(BitmapSource kameraKep) { RenderTargetBitmap rtb = new RenderTargetBitmap(kameraKep.PixelWidth, kameraKep.PixelHeight, kameraKep.DpiX, kameraKep.DpiY, PixelFormats.Pbgra32); DrawingVisual dv = new DrawingVisual(); using (DrawingContext dc = dv.RenderOpen()) { dc.DrawImage(kameraKep, new Rect(0, 0, kameraKep.Width, kameraKep.Height)); //dc.DrawLine(new Pen(Brushes.White, 1), new Point(0, 0), new Point(kameraKep.Width, kameraKep.Height)); dc.DrawRectangle(null, new Pen(Brushes.Red, 1), new Rect(50, 50, 100, 100)); } rtb.Render(dv); rtb.Freeze(); return rtb; }
Mivel sajnos a WPF nem tud már fagyasztott BitmapSource-ra rajzolni így kell egy új BitmapSource amelyre felrajzolom az eredeti képet (ez másolás) + felrajzolok jelenleg 1db négyzetet.

Itt az erőforrás használat így alakul:
CPU: 24%
GPU: 5.4%
! 25FPS@1280x720 ! -> teljesítmény csökkenés

a hardver:
Intel Core i7-4900MQ @ 2.8Ghz 4 mag 8 szál
NVIDIA Quadro K2100M

2 utat látok megoldásnak:
- valahogy gyorsabbá tenni a kódot, kiküszöbölni legalább a másolást, a másolástól is és a konvertálástól is nő meg a hardver igény.

- a négyzeteket nem a képre, hanem csak a felületre rajzolni az Image control felé. Hogyan lehetséges a Lista tartalmától függően négyzeteket rajzolni dinamikusan a felületre?
Mutasd a teljes hozzászólást!
Az egészet át kéne írni JávaSzkript-be, mert az itt a hírek alapján évrôl évre egyre gyorsabb. 

Vagy ha mindenképpen kell a .net, akkor unmanaged módon kéne csinálni a realtime képkezelést.
Ez pl. jol hangzik: "// sample 1 - wrapping .NET image into unmanaged without // making extra copy of image in memory"
-> UnmanagedImage Class
Mutasd a teljes hozzászólást!

  • Ha nem rajzolsz rá, akkor nincs gond a teljesítménnyel? Jól értem?

    Ha így jó, akkor próbáld meg azt, hogy canvasba teszed az egészet:

    <Canvas x:Name="RajzVaszon"> <Image x:Name="KameraKepe"/> <Rectangle Stroke="LightBlue" Canvas.Left="25" Canvas.Top="25" Width="50" Height="50" /> <Rectangle Stroke="LightCoral" Canvas.Left="50" Canvas.Top="50" Width="50" Height="50" /> <Rectangle Stroke="LightCyan" Canvas.Left="75" Canvas.Top="75" Width="50" Height="50" /> </Canvas>
    Az Image-be megy értelem szerűen a kamera képe, és fölé rajzolod a téglalapokat. Első körben így hardkódolva is meg tudod nézni, hogy nincs-e gond a teljesítménnyel. Utána pedig innen kiszedve, a kódból is hozzá tudod adni téglalapokat a RajzVaszon canvasnak. ;)
    Mutasd a teljes hozzászólást!
  • Ha nem rajzolok a képre, maga a Bitmap -> BitmapSource konvertálás, videó kép megjelenités kb. 6-12% CPU, az belefér. A kép megjelenítése sem késik.

    Ezt a kódot ki fogom próbálni, szerintem így nem lesz gond. Viszont így annál több gondom lesz méretarányosan a kép felé rajzolni a négyzeteket, mert ezeket a négyzeteket egérrel át kell tudnom helyezni ill. át kell tudnom méretezni is.

    Esetleg van még egy ötletem. Ahogy nézegetem az AForge kódot, ott a Bitmap egy IntPtr pointerből és egy hosszból lesz amely a memóriában van. Egy lépést ki tudnék hagyni ha nem Bitmapra konvertálódna a kép hanem egyből BitmapSource-ra. Úgy nézem hogy a BitmapSource.Create metódussal ez megvalósítható. Ezt is ki fogom próbálni.

    (viszont nem tudom hogy az AForge.NET LGPLv3 licensz engedi-e a forrás módosítását)
    Mutasd a teljes hozzászólást!
  • Ezt az AForge.NET-et nem ismerem, de akkor csináld azt, hogy rakj még egy Image-t az Image fölé, a háttere legyen transparent, és azon használd az AForge.NET-et. :)

    <Canvas x:Name="RajzVaszon"> <Image x:Name="KameraKepe"/> <Image x:Name="AForgeNETcanvas"/> </Canvas>
    Mutasd a teljes hozzászólást!
  • Az egészet át kéne írni JávaSzkript-be, mert az itt a hírek alapján évrôl évre egyre gyorsabb. 

    Vagy ha mindenképpen kell a .net, akkor unmanaged módon kéne csinálni a realtime képkezelést.
    Ez pl. jol hangzik: "// sample 1 - wrapping .NET image into unmanaged without // making extra copy of image in memory"
    -> UnmanagedImage Class
    Mutasd a teljes hozzászólást!
  • Csatoltam a teszt projektet. A felrajzolt fehér négyzetet lehet egérrel áthelyezni és a négyzet követi az ablak átméretezését is.

    Azért nem akarom Canvassal mert ugyan ott vagyok akkor mint most, vagyis az átméretezésről és a méretarányról kézzel kell gondoskodni. Nem lehet ezt valahogy úgy megoldani hogy a felrajzolt négyzet pozíciója és méretei is ugyan úgy automatikusan méreteződjenek mint az Image control?

    megj: ezt akartam elkerülni azzal hogy magára a képre rajzolom a négyzetet
    Mutasd a teljes hozzászólást!
    Csatolt állomány
  • ez most új kérdés, vagy én vesztettem el a fonalat?

    ha megnyugtat, akkor egy régi xeonos gépen (w3520), quadro 2000-es kártyával, Win 7 Pro SP1, FullHD-n nem tudtam így rángatni, hogy a CPU usage 2% fölé menjen...
    Mutasd a teljes hozzászólást!
  • Nem új kérdés ez a tesztje az eredeti kérdés megoldásának egyik lehetőségére
    Mutasd a teljes hozzászólást!
  • Teljesen igazad lett. De sajna az Accord.NET az lGPLv2 licenccel szerintem nem engedi a zárt forráskódot... így marad a saját unmanaged kód mert a .NET ebben tetű lassú.

    Így értem el a legkevesebb erőforrás felemésztést:

    kellett egy 180 fokos fordítás mert a kamerakép fejjel lefelé jött

    unsafe { fixed (byte* rawKepAdatPtr = rawKepAdat) { byte* cel = (byte*)rawKepAdatPtr + (stride * (Height - 1)); byte* forras = (byte*)buffer.ToPointer(); for (int y = 0; y < Height; y++) { Win32.memcpy(cel, forras, stride); cel -= stride; forras += stride; } } }
    ezután megírtam a saját raw kép tároló osztályomat (inkább csatolom mert sokat kellene javítani, nem működik normálisan forrás kód beszúráskor a behúzás az oldalon).

    Ez vissza tudja adni a BitmapSource típust, meg még ezután fogom felruházni pl egy négyzet felrajzolás metódussal.

    eredmény:
    CPU: 9%
    GPU: 5%
    1280x720@30FPS

    ha nem kellene a forgatással is bajlódni akkor még jobb lenne.

    A .NET nem realtime image process-re való.

    Köszi mindenkinek!
    Mutasd a teljes hozzászólást!
    Csatolt állomány
abcd