Захват видеоизображения. Преобразование цветного изображения в черно-белое

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

При этом следует выполнять следующие требования (эти требования носят эвристический характер и сформулированы на основе опыта автора):

  1. Для хранения видеоизображения необходимо использовать одномерный однобайтовый массив данных, в формате 256 оттенков серого. Массив имеет размерность WxH (W - длина изображения (ImageWidth) [пикс.]; H - высота изображения (ImageHeight) [пикс.]).
  2. Для доступа к группе последовательных пикселей изображения следует избегать операций вычисления эффективного адреса каждого пикселя, заданного координатами x, y. (в двухмерных массивах эта операция производится в неявном виде при обращении к элементу массива). Вместо этого необходимо один раз сформировать переменную-указатель на какой-либо базовый пиксель изображения (обычно на пиксель 0:0), а затем использовать операторы увеличения или уменьшения переменной-указателя. В этом случае скорость доступа к ячейке памяти можно повысить с 18 до 5 машинных тактов (для процессоров 80x86).

Обращаем Ваше внимание на то, что изображение в формате RGB24 (цветное изображение) и GrayScale8 (черно-белое изображение) хранится в формате DIB, который используется в BMP-файлах. Согласно этому формату, строки изображения хранятся снизу вверх, а размер каждой строки должен быть кратен 4 байтам. Для достяжения последнего требования следует правильно выбирать ширину изображения (ImageWidth) так, чтобы в нем не было пустых байтов.

В цветном изображении (RGB24) на каждый пиксель отводится 3 байта. Цветовые составляющие хранятся в последовательности СИНИЙ, ЗЕЛЕНЫЙ, КРАСНЫЙ. При этом каждая составляющая является байтом и изменяется в диапазоне от 0 до 255 (0 - минимальная интенсивность цвета, 255 - максимальная интенсивность).

В черно-белом изображении (Grayscale8) на каждый пиксель приходится 1 байт. Яркость пикселя определяется значением 0 до 255 (0-максимально темный пиксель, 255-максимально светлый).

Захват видеоизображения описанным ниже способом возможен для большинства карт видеозахвата (TV-тюнеров) и определяется наличием соответствующего драйвера. Код проверялся для карт видеозахвата DE-18 (DigitEys-18) (Драйвер только для Win95,98,Me), FlyVideo-98 (Драйвер только для Win95,98,Me), EZCapture, AverMedia.

Пример захвата видеоизображения представлен на языке Delphi:


unit Main;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, VidCap, ExtCtrls;

type
  TMainForm = class(TForm)
    Timer1: TTimer;
    PaintBox1: TPaintBox;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    VideoWND : HWND;
  end;

const
  ImageWidth  = 384;  // ширина изображения. Если умножить на 3 должно быть кратным 4
  ImageHeight = 284;  // высота изображения
  DriverIndex = 0;    // номер карты видеозахвата (от 0 до 9).

type
  // тип данных цветного изображения
  TColorImage = packed array[0..ImageHeight-1,0..ImageWidth-1,0..2] of Byte;

  // тип данных для черно-белого изображения
  TBWImage    = array[0..ImageHeight-1,0..ImageWidth-1] of Byte;

  // вспомогательный тип данных
  TBitmapInfo256 = record
    bmiHeader : TBitmapInfoHeader;
    bmiColors : array[0..255,0..3] of Byte;
  end;

var
  MainForm: TMainForm;

  VideoOk    : boolean;     // признак удачного захвата кадра
  ColorImage : TColorImage; // область памяти для хранения цветного изображения
  BWImage    : TBWImage;    // область памяти для хранения черно-белого изображения

var
  BitmapInfo256 : TBitmapInfo256;
  BitmapInfo    : TBitmapInfo absolute BitmapInfo256; // BitmapInfo и BitmapInfo256 лежат в одной области памяти

implementation

{$R *.DFM}

// callback-функция для захвата видео
procedure VideoCallback(Wnd:HWND; Hdr:PVIDEOHDR); stdcall;
begin
  Move(Hdr.lpData^, ColorImage, Hdr.dwBytesUsed);
  VideoOk:=true;
end;

// инициализирует структуру BitmapInfo
procedure InitBitmapInfo;
var
  i : integer;
begin
  Fillchar(BitmapInfo256,sizeof(TBitmapInfoHeader),0);
  BitmapInfo256.bmiHeader.biSize:=40;
  BitmapInfo256.bmiHeader.biPlanes:=1;
  BitmapInfo256.bmiHeader.biWidth:=ImageWidth;
  BitmapInfo256.bmiHeader.biHeight:=ImageHeight;
  for i:=0 to 255 do
  begin
    BitmapInfo256.bmiColors[i,0]:=i;
    BitmapInfo256.bmiColors[i,1]:=i;
    BitmapInfo256.bmiColors[i,2]:=i;
    BitmapInfo256.bmiColors[i,3]:=0;
  end;
end;

// рисует на устройстве DC цветное изображение Image
procedure DrawColorImage(DC:HDC; var Image:TColorImage);
begin
  BitmapInfo.bmiHeader.biBitCount:=24;
  SetDIBitsToDevice(DC,0,0,ImageWidth,ImageHeight,0,0,0,ImageHeight,@Image,BitmapInfo,DIB_RGB_COLORS);
end;

// рисует на устройстве DC черно-белое изображение Image
procedure DrawBWImage(DC:HDC; var Image:TBWImage);
begin
  BitmapInfo.bmiHeader.biBitCount:=8;
  SetDIBitsToDevice(DC,0,0,ImageWidth,ImageHeight,0,0,0,ImageHeight,@Image,BitmapInfo,DIB_RGB_COLORS);
end;

// конвертирует цветное изображение в черно-белое
procedure ColorImageToBWImage(const ColorImage : TColorImage; var BWImage:TBWImage); assembler;
asm

          PUSH     EDI
          PUSH     ESI
          PUSH     EBX

          MOV      EDI,EDX
          MOV      ESI,EAX
          MOV      ECX,ImageHeight*ImageWidth
          MOV      EBX,3

@@2:      XOR      EAX,EAX
          LODSB
          MOV      EDX,EAX
          LODSB
          ADD      EDX,EAX
          LODSB
          ADD      EAX,EDX
          DIV      BL

          STOSB
          LOOP     @@2

          POP      EBX
          POP      ESI
          POP      EDI

end;



procedure TMainForm.FormCreate(Sender: TObject);
begin
  // инициализируем структуру BitmapInfo
  InitBitmapInfo;

  // Создаем окно для проекции оверлея
  VideoWND:=capCreateCaptureWindow('Video',WS_CHILD or WS_VISIBLE,
                                   10,10,ImageWidth,ImageHeight,
                                   Handle,0);
  if VideoWND <> 0 then
  begin
    // подключение окна к драйверу
    if SendMessage(VideoWND,WM_CAP_DRIVER_CONNECT,DriverIndex,0)=0 then
    begin
      DestroyWindow(VideoWND);
      VideoWND:=0;
      exit;
    end;
    // задаем формат видеокадра. Большая часть BitmapInfo проинициализирована в InitBitmapInfo
    BitmapInfo.bmiHeader.biBitCount:=24;
    SendMessage(VideoWND,WM_CAP_SET_VIDEOFORMAT ,0,Integer(@BitmapInfo));

    // задаем callback-функцию
    SendMessage(VideoWND,WM_CAP_SET_CALLBACK_FRAME ,0,Integer(@VideoCallback));

    // включаем оверлей
    SendMessage(VideoWND,WM_CAP_SET_OVERLAY,1,0);
    VideoOk:=true;
  end;
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
  SendMessage(VideoWND,WM_CAP_DRIVER_DISCONNECT,0,0)
end;


procedure TMainForm.Timer1Timer(Sender: TObject);
begin
  // захват кадра
  if (VideoWND <> 0) and VideoOk then
  begin
    VideoOk:=False;
    SendMessage(VideoWND,WM_CAP_GRAB_FRAME_NOSTOP,0,0);
  end;
  // цветное в черно-белое
  ColorImageToBWImage(ColorImage,BWImage);

  // Нарисовать для проверки
  DrawBWImage(PaintBox1.Canvas.Handle,BWImage);
end;

end.


Для компиляции кода необходим модуль vidcap.pas.

Скачать полностью все файлы проекта можно здесь (winrar3.2, Delphi 3.0 и выше).




Назад ]
Дизайн: DynSoft 

Рейтинг@Mail.ru
Используются технологии uCoz