Elektroda.pl
Elektroda.pl
X
Elektroda.pl
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

[VB.NET] Najefektywniejsze robienie screenshotów - directx?

28 Jun 2010 19:19 1404 3
  • Level 27  
    Witam,

    Pracuję nad pewnym projektem który ma symulować działanie efektu ambilight. Częśc sprzętowa jest już gotowa (urządzenie HID-USB), została kwestia próbkowania kolorów z brzegów ekranu. Obecnie jest to realizowane przez funkcje GDI+ - robię screenshot całego ekranu, wycinam fragmenty z krawędzi i uśredniam kolor poprzez zmianę wielkości obrazu do 1x1px - kolor z tego pixela wyświetlam na ledach RGB.

    Problem w tym że na dość mocnym komputerze mogę wyciągnąć max 10 screenshotów obrazu Full-HD na sekundę, a to troszkę mało. Czy ktoś zna lub pisał coś szybszego? Może jakaś podpowiedź co do directx-a czy opengl?

    Żeby pokazać że już coś działa:
    [VB.NET] Najefektywniejsze robienie screenshotów - directx?
  • Level 27  
    Testowałem i zrzut całkowity + wycinanie, i zrzuty fragmentów... niestety efekt jest ciągle taki sam. Do pełnej płynności dużo brakuje, a w obecnym kodzie nie ma co optymalizować:

    Code:

            Public Function FromArea(ByVal x As Integer, ByVal y As Integer, ByVal area_width As Integer, ByVal area_height As Integer) As Color
                ' (x,y)  - współrzędne punktu startowego
                ' width  - szerokość pobieranego fragmentu
                ' height -  wysokość pobieranego fragmentu
                '
                ' zwracam uśredniony kolor RGB fragmentu ekranu

                ' screenshot fragmentu ekranu
                Dim Img As New Bitmap(area_width, area_height)
                Dim gr As Graphics = Graphics.FromImage(Img)
                gr.CopyFromScreen(x, y, 0, 0, Img.Size)

                ' resize do 1x1 px
                Dim thumb As Bitmap = New Bitmap(1, 1)
                Using g As Graphics = Graphics.FromImage(thumb)
                    g.DrawImage(Img, 0, 0, thumb.Width, thumb.Height)
                End Using

                ' pobierz sam kolor
                Dim GrabbedColor As Color
                GrabbedColor = thumb.GetPixel(0, 0)

                ' sprzatam pamiec
                gr.Dispose()
                Img.Dispose()
                thumb.Dispose()

                Return GrabbedColor

            End Function


    Ja myślę że po prostu ta metoda jest powolna - aplikacja wcina max 10% czasu CPU i w porywach do 2MB Pamięci, więc zapas mocy jest spory.
  • Level 27  
    OK, co do directx-a to udało mi się sklecić coś takiego

    Code:

    Imports System.Drawing
    Imports Microsoft.DirectX
    Imports Microsoft.DirectX.Direct3D
    Imports System.Threading

    Public Class Form1

        ' Direct3D objects
        Dim device As Direct3D.Device
        Dim backbuffer As Direct3D.Surface
        Dim pSurface As Surface
        Dim t As Thread

        Const SCREENW As Integer = 1680
        Const SCREENH As Integer = 1080


        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click


            t = New Thread(AddressOf Me.BackgroundProcess)
            t.Start()

        End Sub

        Private Sub INIT_Dx3d()

            ' get the desktop display mode
            Dim adapterNumber As Integer = Manager.Adapters.Default.Adapter
            Dim mode As DisplayMode = Manager.Adapters.Default.CurrentDisplayMode


            ' set up the presentation parameters
            Dim params As New PresentParameters
            params.Windowed = True
            params.SwapEffect = SwapEffect.Copy
            params.AutoDepthStencilFormat = DepthFormat.D16
            params.EnableAutoDepthStencil = True
            params.MultiSample = MultiSampleType.None
            params.BackBufferCount = 1
            params.BackBufferWidth = SCREENW
            params.BackBufferHeight = SCREENH

            ' check video card capabilities
            Dim flags As CreateFlags
            flags = CreateFlags.HardwareVertexProcessing
            flags += CreateFlags.PureDevice

            ' create the Direct3D device
            device = New Device(adapterNumber, DeviceType.Hardware, Me, flags, params)

            ' get reference to the back buffer
            'backbuffer = device.GetBackBuffer(0, 0, BackBufferType.Mono)
            pSurface = device.CreateOffscreenPlainSurface(Windows.Forms.Screen.PrimaryScreen.Bounds.Width, _
                                                                         Windows.Forms.Screen.PrimaryScreen.Bounds.Height, _
                                                                         Format.A8R8G8B8, _
                                                                         Pool.Scratch)

        End Sub

        Private Sub BackgroundProcess()
            Dim i As Integer
            Dim filename As String

            Label1.Text = DateTime.Now.ToLongTimeString()

            For i = 0 To 1000
                filename = "c:\demo\" & i.ToString & ".bmp"
                device.GetFrontBufferData(0, pSurface)
                SurfaceLoader.Save(filename, ImageFileFormat.Bmp, pSurface)
            Next i

            Label2.Text = DateTime.Now.ToLongTimeString()

        End Sub

        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            INIT_Dx3d()
        End Sub

        Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
            t.Abort()
        End Sub
    End Class




    Po uruchomieniu, program zapisuje 1000 screenshotów i wyświetla czas przed i po zakończeniu procesu. Teraz pytanie, jak manipulować obrazem, który jest w buforze pSurface? Potrzebuję zmieniać jego rozmiar i wycinać fragmenty.

    Acha... przyrost prędkości to jakieś 22screenshoty/sek (bez zapisywania na dysku - przy zapisie około 16/sek).