2014-11-26 3 views
0

Я знаю, что есть несколько таких «способов получить цвет пикселя экрана»? вопросы, но когда я пытаюсь их решения, я не получаю достаточно хороших результатов.Как получить максимальный цвет пикселя экрана (C#)

Я делаю приложение, которое снова и снова обнаруживает цвета 4 разных пикселей и обрабатывает результаты. Проблема в том, что когда я пытаюсь выполнить код, он может запускать всего несколько циклов в секунду, и мне нужно не менее 100 циклов в секунду (это означает, что 400 пиксельных обнаружений в секунду).

[DllImport("user32.dll", SetLastError = true)] 
public static extern IntPtr GetDesktopWindow(); 
[DllImport("user32.dll", SetLastError = true)] 
public static extern IntPtr GetWindowDC(IntPtr window); 
[DllImport("gdi32.dll", SetLastError = true)] 
public static extern uint GetPixel(IntPtr dc, int x, int y); 
[DllImport("user32.dll", SetLastError = true)] 
public static extern int ReleaseDC(IntPtr window, IntPtr dc); 

private Color GetColorAt(int x, int y) 
    { 
     IntPtr desk = GetDesktopWindow(); 
     IntPtr dc = GetWindowDC(desk); 
     int a = (int)GetPixel(dc, x, y); 
     ReleaseDC(desk, dc); 
     return Color.FromArgb(255, (a >> 0) & 0xff, (a >> 8) & 0xff, (a >> 16) & 0xff); 
    } 

private void record() 
    { 
     sw.Start(); 
     while(isRunning) 
     { 
      Color cK1 = GetColorAt(1857, 488); 
      Color cK2 = GetColorAt(1857, 556); 
      Color cM1 = GetColorAt(1857, 624); 
      Color cM2 = GetColorAt(1857, 692); 

      if (cK1.R + cK1.G + cK1.B > 750 && !k1pressed) 
      { 
       k1pressed = true; 
       addEvent("DOWN", "K1"); 
      } 
      else if (cK1.R + cK1.G + cK1.B < 30 && k1pressed) 
      { 
       k1pressed = false; 
       addEvent("UP", "K1"); 
      } 

      if (cK2.R + cK2.G + cK2.B > 750 && !k2pressed) 
      { 
       k2pressed = true; 
       addEvent("DOWN", "K2"); 
      } 
      else if (cK2.R + cK2.G + cK2.B < 30 && k2pressed) 
      { 
       k2pressed = false; 
       addEvent("UP", "K2"); 
      } 

      if (cM1.R + cM1.G + cM1.B > 750 && !m1pressed) 
      { 
       m1pressed = true; 
       addEvent("DOWN", "M1"); 
      } 
      else if (cM1.R + cM1.G + cM1.B < 30 && m1pressed) 
      { 
       m1pressed = false; 
       addEvent("UP", "M1"); 
      } 

      if (cM2.R + cM2.G + cM2.B > 750 && !m2pressed) 
      { 
       m2pressed = true; 
       addEvent("DOWN", "M2"); 
      } 
      else if (cM2.R + cM2.G + cM2.B < 30 && m2pressed) 
      { 
       m2pressed = false; 
       addEvent("UP", "M2"); 
      } 
      labelStatus.Text = "Recording: " + sw.ElapsedMilliseconds.ToString(); 
      Application.DoEvents(); 
     } 
    } 

Для объяснения, мое приложение захватывает 4 пикселя, и каждый из этих пикселей представляет собой один (виртуальных) клавишной клавиатуры или мыши кнопки (скажем, A, B, LMB, ПКМ) и addEvent (ул НТР) только путы информация о нажатии или отпускании клавиши в строке и после остановки записи сохраняет строку в файл.

Есть ли способ сделать что-то подобное 100 раз в секунду? Потому что я думал, что работа с 4 пикселями должна быть действительно быстрой.

+1

Нет смысла делать это с более высокой скоростью, чем частота обновления видео. –

ответ

2

Много проблем с вашим кодом.

  1. Вы не должны использовать петлю с DoEvents. Попробуйте async функцию, которая ждет Task.Yield тогда называет себя, или в более ранних версиях .NET, функция, которая использует BeginInvoke на себя и т.д.

  2. Не обновлять метку так часто, что это очень медленно.

  3. Получить окно рабочего стола и создать DC только один раз, а затем получить все значения пикселей столько раз, сколько вам нужно. Выводите DC только после завершения просмотра экрана.

Например:

private Color GetColorAt(IntPtr dc, int x, int y) 
{ 
    int a = (int)GetPixel(dc, x, y); 
    return Color.FromArgb(a | 0xFF000000); 
} 

double lastTextBoxUpdate; 
private async void record() 
{ 
    IntPtr desk = GetDesktopWindow(); 
    IntPtr dc = GetWindowDC(desk); 

    sw.Start(); 
    lastTextBoxUpdate = 0.0; 
    while(isRunning) 
    { 
     Color cK1 = GetColorAt(dc, 1857, 488); 
     Color cK2 = GetColorAt(dc, 1857, 556); 
     Color cM1 = GetColorAt(dc, 1857, 624); 
     Color cM2 = GetColorAt(dc, 1857, 692); 

     // ... 

     double currentElapsed = sw.ElapsedMilliseconds; 
     if (currentElapsed > lastTextBoxUpdate + 500) { 
      labelStatus.Text = "Recording: " + currentElapsed.ToString(); 
      lastTextBoxUpdate = currentElapsed; 
     } 
     await Task.Yield(); 
    } 
    ReleaseDC(desk, dc); 
} 

Кроме того, это может быть на самом деле быстрее блитирования ограничительного прямоугольника в локальный растровый называют LockBits на растровом изображении, и считывать значения оттуда. Это связано с тем, что доступ к графической памяти из ЦП имеет высокую стоимость для настройки передачи и невысокую стоимость отправки дополнительных данных.

+0

Я знаю, что DoEvents() неправильно используется в этом случае и меняет ярлык. Текст также довольно часто, но мне нужно отслеживать время, когда моя программа запущена. Не могли бы вы объяснить или связать, как сделать «асинхронный fynction», и как использовать LockBits? Я не смог найти простой способ использовать LockBits и вернуть System.Drawing.Color ... – user3043260

+0

@ user3043260: LockBits возвращает структуру с указателем на данные. Если вы указали этот указатель на 'int *', вы можете индексировать его как массив, и вы получите то же значение, что и p/invoke GetPixel. –

+0

@ user3043260: Я добавил пример того, как вы могли бы реализовать три точки маркера. –

Смежные вопросы