Постоянно захватывает область экрана - PullRequest
0 голосов
/ 17 октября 2018

Я хочу непрерывно снимать область экрана.

Я могу сделать это, используя LockBits, BitBlt и т. Д., Но все мои измерения показали, что это занимает в среднем,30 миллисекунд для захвата одного кадра.Это выглядит очень похоже на VSynch ... который будет пытаться поддерживать постоянную скорость обновления экрана 30 мс ...

Однако я только что натолкнулся на следующую запись .

В нем кто-то утверждает, что получил 1000 кадров за 95 миллисекунд ... что неслыханно (для меня!).Вот код, который он опубликовал:

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Diagnostics;
namespace WindowsFormsApplication1
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }
    private readonly Stopwatch sw = new Stopwatch();
    private static Bitmap CaptureImage(int x, int y)
    {
      Bitmap b = new Bitmap(100, 100);
      using (Graphics g = Graphics.FromImage(b))
      {
        g.CopyFromScreen(x, y, 0, 0, new Size(100, 100), CopyPixelOperation.SourceCopy);
        g.DrawLine(Pens.Black, new Point(0, 27), new Point(99, 27));
        g.DrawLine(Pens.Black, new Point(0, 73), new Point(99, 73));
        g.DrawLine(Pens.Black, new Point(52, 0), new Point(52, 99));
        g.DrawLine(Pens.Black, new Point(14, 0), new Point(14, 99));
        g.DrawLine(Pens.Black, new Point(85, 0), new Point(85, 99));
      }
      return b;
    }
    private void button1_Click(object sender, EventArgs e)
    {
      Bitmap bmp = null;
      sw.Restart();
      for (int i = 0; i < 1000; i++)
      {
         bmp = CaptureImage(390, 420);
      }
      sw.Stop();
      Console.WriteLine(sw.ElapsedMilliseconds);
    }
  }
}

Я попробовал это, и угадайте, что ... Я получаю ~ 30k миллисекунд, напечатанных на консоль.Ясно, что этот точно такой же код все еще только захватывает со скоростью 30 мс / кадр в моей системе.В своем посте он говорит:

в вашей системе должно быть что-то радикально неправильное, если одной и той же программе требуется 35 миллисекунд для захвата 1 кадра.

Это заставило меня задуматься, поэтому я обновил драйверы моей видеокарты.Тот же результат.Тогда я подумал, что, возможно, моя видеокарта старая ... Хорошо, это: AMD Radeon HD 5700 Series.Так как я подозревал VSync, я установил AMD Catalyst Software Suite, отключил Vsyc и перезапустил.Запустил тест снова, и я получил тот же результат: ~ 30 тыс. Миллисекунд для 1000 кадров.

Затем я загрузил свой Amazon EC2 g2.2xlarge instance с установленными драйверами Windows 10 и NVIDIA GRID K520 (в актуальном состоянии).Тот же результат: 30 мс на кадр.

Может кто-нибудь с немного большим опытом объяснить мне, что здесь происходит?

Разве невозможно сделать то, что утверждает автор, и захватить 1000 кадровв 95 мс?

Что-то не так с моей системой?Я не знаю, что еще попробовать.

1 Ответ

0 голосов
/ 19 октября 2018

С существующей технологией практически невозможно достичь 100000 кадров в секунду.Ссылка на social.msdn.microsoft.com включает в себя утверждения о достижении таких результатов, но они ошибочны или, возможно, перепутаны между копированием из памяти в память и копированием с экрана в память.

В приведенном ниже примере используется ядро ​​WinAPIфункции.На моем компьютере (с дешевой встроенной видеокартой) он достигает только 60 кадров в секунду для небольшой области с разрешением 100 x 100 пикселей.Это будет немного быстрее, чем рассматриваемый код, но вы, вероятно, достигнете того же результата с .net, если очистите свой код.

Тем не менее, это приводит только к небольшому увеличению, 60 FPS или, возможно, 100 FPS для вашего компьютера (или меньше, если вы пытаетесь скопировать весь экран)

Обратите внимание, что копирование из памятив память намного быстрее.Я получаю более 100000 кадров в секунду, но это не фактический кадр на экране.Это просто копирование из памяти в память.Эта скорость уменьшится, если вы увеличите площадь до размера, превышающего 100x100 пикселей.

Тестирование на облачных компьютерах не является надежным.Я даже не уверен, что значит скопировать экран на облачный компьютер.Попробуйте провести тестирование на настольном компьютере с лучшей видеокартой.

class Program
{
    [DllImport("user32.dll")]
    static extern IntPtr GetDC(IntPtr ptr);

    [DllImport("user32.dll")]
    static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);

    [DllImport("Gdi32.dll")]
    static extern IntPtr CreateCompatibleDC(IntPtr hDC);

    [DllImport("Gdi32.dll")]
    static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int w, int h);

    [DllImport("Gdi32.dll")]
    static extern IntPtr BitBlt(IntPtr hDC, int x, int y, int w, int h, IntPtr hSrc, int sx, int sy, int mode);

    [DllImport("Gdi32.dll")]
    static extern IntPtr SelectObject(IntPtr hdc, IntPtr obj);

    [DllImport("Gdi32.dll")]
    static extern int DeleteObject(IntPtr hdc);

    [DllImport("Gdi32.dll")]
    static extern int DeleteDC(IntPtr hdc);

    static void Main(string[] args)
    {
        const int SRCCOPY = 0x00CC0020;
        int w = 100;
        int h = 100;

        //get screen dc:
        IntPtr hdc = GetDC((IntPtr)0);

        //create memory dc and bitmap, select bitmap in to memory:
        IntPtr memdc = CreateCompatibleDC(hdc);
        IntPtr hbitmap = CreateCompatibleBitmap(hdc, w, h);
        IntPtr holdbmp = SelectObject(memdc, hbitmap);

        //create another memory dc and bitmap:
        IntPtr memdc2 = CreateCompatibleDC(hdc);
        IntPtr hbitmap2 = CreateCompatibleBitmap(hdc, w, h);
        IntPtr holdbmp2 = SelectObject(memdc2, hbitmap2);

        Stopwatch sw = new Stopwatch();

        //copy the screen for 1 second
        sw.Restart();
        int fps = 0; //frames per second

        for (fps = 0; sw.ElapsedMilliseconds < 1000; fps++)
            BitBlt(memdc, 0, 0, w, h, hdc, 0, 0, SRCCOPY);
        sw.Stop();
        Console.WriteLine($"screen capture, FPS: {fps}");

        //copy from memory to memory for 1 second
        sw.Restart();
        fps = 0;
        for (fps = 0; sw.ElapsedMilliseconds < 1000; fps++)
            BitBlt(memdc, 0, 0, w, h, memdc2, 0, 0, SRCCOPY);
        sw.Stop();
        Console.WriteLine($"memory to memory copy, FPS: {fps}");

        //cleanup:
        SelectObject(memdc, holdbmp);
        SelectObject(memdc2, holdbmp2);
        DeleteObject(hbitmap);
        DeleteObject(hbitmap2);
        DeleteDC(memdc);
        DeleteDC(memdc2);
        ReleaseDC((IntPtr)0, hdc);
    }
}
...