OpenCVによる顔認識(コンソール モード)

 OpenCV による顔認識が、一時期、流行っていたようなので、此処で取り上げてみました。
OpenCVのインストール方法は、このホームページのOpenCV_WabCamera.htmlと、https://github.com/Laex/Delphi-OpenCVを参照してください。
プログラムは、https://github.com/Laex/Delphi-OpenCVからダウンロードしたファイルの"Samples"ホルダーの中に入っているものです。

 Delphi XE7 の場合、オリジナルのまゝでは動作しないので、一部修正をしています。
プログラムの起動時、 カメラから画像を取得するための設定が完了するのに時間がかかり、その時間分、遅延タイマーで待たないとダメなようです。
特に、コンソールモード(コマンドプロンプトモード)の時に顕著に表れるようで、通常のモードの時は遅延タイマーは必要ないようです。
(XE7のバージョンでしか確認していません、オリジナルには遅延タイマーは無いので他のバージョンだと問題ないのかもしれません。)

 此処でのプログラム例でのDLLは、VC2013の場合です。
新しく delphi用のopenCvをダウンロードすると、新しいバージョンのVCのDLLを使用しているのでそちらを使用してください。
無料のVC2017をインストールした場合は、***2413.dllのみでOKです。
Release 時
 msvcp120.dll,msvcr120.dll,opencv_core2413.dll,opencv_highgui2413.dll,opencv_imgproc2413.dll,opencv_objdetect2413.dll
Debug 時
 msvcp120d.dll,msvcr120d.dll,opencv_core2413d.dll,opencv_highgui2413d.dll,opencv_imgproc2413d.dll,opencv_objdetect2413d.dll

msvcp120.dll,msvcr120.dllは、既にインストールされている可能性があり、不要な場合があります。

プログラム行中の
 cvEqualizeHist(gray, gray);
  
は無いほうが良い場合があります。
又、グレイに変換しなくても、特に問題なく顔認識は出来るようです、色々試してみると良いと思います。

サンプルプログラム 1

program FaceDetect;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.Character,
  System.SysUtils,
  System.Math,
  ocv.core.types_c,
  ocv.core_c,
  ocv.highgui_c,
  ocv.objdetect_c,
  ocv.imgproc_c,
  ocv.imgproc.types_c,
  ocv.utils;

const
  // ******************************************************************************/
  // setup the cameras properly based on OS platform
  CAMERA_INDEX = CV_CAP_ANY;
  // ******************************************************************************/
  windowName = 'Haar Cascade Detection'; // window name
  cascade_name: pCVChar = 'cascades/haarcascade_frontalface_alt.xml'; // cascade file
//  cascade_name: pCVChar = 'cascades/haarcascade_eye.xml'; // cascade file
var
  img: pIplImage = nil; // image object
  frame: pIplImage = nil;
  capture: pCvCapture = nil; // capture object
  detected_objects: pCvSeq = nil; // list of detected items
  key: Integer; // user input
  EVENT_LOOP_DELAY: Integer = 100; // delay for GUI window  40 ms equates to 1000ms/25fps=40ms per frame
  cascade: pCvHaarClassifierCascade;
  storage: pCvMemStorage;
  gray: pIplImage;
  imgcopy: pIplImage;
  i: Integer;
  r: pCvRect;
  isCapture: Boolean = false;

begin
  try
    // if command line arguments are provided try to read image/video_name
    // otherwise default to capture from attached H/W camera
    if ParamCount = 1 then
    begin
      img := cvLoadImage(c_str(ParamStr(1)), CV_LOAD_IMAGE_UNCHANGED);
      if not Assigned(img) then
      begin
        capture := cvCreateFileCapture(c_str(ParamStr(1)));
        isCapture := True;
      end;
    end;

    if (not Assigned(img)) and (not Assigned(capture)) then
    begin
      capture := cvCreateCameraCapture(CAMERA_INDEX);
      isCapture := True;
    end;
    if not Assigned(capture) then
      Halt(1);

    // 画像のサイズ設定 カメラによって値を修正します           追加
    cvSetCaptureProperty(Capture, CV_CAP_PROP_FRAME_WIDTH, 1280);
    cvSetCaptureProperty(Capture, CV_CAP_PROP_FRAME_HEIGHT, 720);

    // create window object (use flag:=0 to allow resize, 1 to auto fix size)
    cvNamedWindow(windowName, CV_WINDOW_AUTOSIZE);
    // 此処で少し待たないとエラーになります                    追加
    cvWaitkey(3000);
//    cvResizeWindow(windowName, 640, 360);
    // load the trained haar cascade classifier from file
    // and create storage required for detections
    cascade := cvLoad(cascade_name, nil, nil, nil);

    if Assigned(cascade) then
      Writeln('LOADED : ', cascade_name)
    else
    begin
      Writeln('ERROR: Could not load classifier cascade : ', cascade_name);
      Halt(1);
    end;
    try
      storage := cvCreateMemStorage(0);

      // if capture object in use (i.e. video/camera)
      // get initial image from capture object
      if Assigned(capture) then
      begin
        // cvQueryFrame is just a combination of cvGrabFrame
        // and cvRetrieveFrame in one call.
        img := cvQueryFrame(capture);
        if not Assigned(img) then
        begin
          if ParamCount = 1 then
            Writeln('End of video file reached')
          else
            Writeln('ERROR: cannot get next fram from camera');
          Halt(0);
        end;
      end;

      // create a greyscale image upon which to run the classifier
      gray := cvCreateImage(cvSize(img^.width, img^.height), img^.depth, 1);

      // create a copy of the image upon which to do detection and box drawing
      imgcopy := cvCloneImage(img);

      // start main loop
      while True do
      begin
        // if capture object in use (i.e. video/camera)
        // get image from capture object
        if Assigned(capture) then
        begin
          // cvQueryFrame is just a combination of cvGrabFrame
          // and cvRetrieveFrame in one call.
          frame := cvQueryFrame(capture);
          if not Assigned(frame) then
          begin
            if ParamCount = 1 then
              Writeln('End of video file reached')
            else
              Writeln('ERROR: cannot get next fram from camera');
            Halt(0);
          end;
        end
        else
        begin
          // if not a capture object set event delay to zero so it waits
          // indefinitely (as single image file, no need to loop)
          EVENT_LOOP_DELAY := 40;
        end;

        // N.B. as haar features are orientation dependent (and
        // the haar function operate directly on the pixel
        // array in memory) we'll just check the image
        // is using Top-Left origin in actual memory
        // and if not flip it (use a copy which is not the
        // capture object buffer)
        if (img^.origin = IPL_ORIGIN_TL) then
        begin
          cvCopy(frame, imgcopy);
        end
        else
        begin
          cvFlip(frame, imgcopy);
          imgcopy^.origin := IPL_ORIGIN_TL;
        end;
        gray^.origin := imgcopy^.origin;

        // convert input image to grayscale
        cvCvtColor(imgcopy, gray, CV_BGR2GRAY);

        // histogram equalize it also to maximize the region differences
        cvEqualizeHist(gray, gray);

        // run the haar cascade detection
        // with parameters scale:=1.2, neighbours := 4 and with Canny pruning
        // turned on with minimum detection scale 30x30 pixels
        detected_objects := cvHaarDetectObjects(gray, cascade, storage, 1.2, 2, CV_HAAR_DO_CANNY_PRUNING, cvSize(50, 50),
          cvSize(0, 0));

        // draw a red rectangle around any detected objects
        i := 0;
        While i < ifthen(Assigned(detected_objects), detected_objects^.total, 0) do
        begin
          r := pCvRect(cvGetSeqElem(detected_objects, i));
          cvRectangle(imgcopy, cvPoint(r^.x, r^.y), cvPoint((r^.x) + (r^.width), (r^.y) + (r^.height)),
            CV_RGB(255, 0, 0), 2, 8, 0);
          Inc(i);
        end;
        // if Assigned(detected_objects) then
        // cvClearSeq(detected_objects);
        // cvClearMemStorage(storage);

        // display image in window
        cvShowImage(windowName, imgcopy);

        // start event processing loop (very important,in fact essential for GUI)
        // 40 ms roughly equates to 1000ms/25fps := 4ms per frame
        key := cvWaitKey(EVENT_LOOP_DELAY);
        if key >= 1 then
        begin
          // if user presses 'ESC' then exit
          Writeln('Keyboard exit requested : exiting now - bye!');
          Break;
        end;
      end;

      // destroy window objects
      // (triggered by event loop *only* window is closed)
      cvDestroyAllWindows();
      cvReleaseImage(gray);
      cvReleaseImage(imgcopy);
    finally
      if Assigned(img) and (not isCapture) then
        cvReleaseImage(img);
      // destroy image objects (if it does not originate from a capture object)
      if Assigned(capture) then
        cvReleaseCapture(capture);
      if Assigned(storage) then
        cvReleaseMemStorage(storage);
    end;

  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

end.

サンプルプログラム 2
 サンプルプログラム1との差は、Wabカメラが無いとき、Jpgファイルを読み込む様になっています。
 旧バージョンの cvCopyImage が cvCopy に変わっています。
 此処でダウンロード出来るファイルは変更していませんので、新しいバージョンで使用する場合は、変更してください。

program FaceDetect;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.Character,
  System.SysUtils,
  ocv.highgui_c,
  ocv.core_c,
  ocv.core.types_c,
  ocv.imgproc_c,
  ocv.imgproc.types_c,
  ocv.objdetect_c;

var
  // Create memory for calculations
  storage: pCvMemStorage = nil;
  // Create a new Haar classifier
  cascade: pCvHaarClassifierCascade = nil;
  // Create a string that contains the cascade name
  cascade_name: AnsiString = 'cascades/haarcascade_frontalface_alt.xml';
      //'haarcascade_russian_plate_number.xml';
      //'haarcascade_frontalface_alt.xml';
      // "haarcascade_profileface.xml";
  //  cascade_name: AnsiString = 'haarcascade_licence_plate_rus_16stages.xml';
      //'haarcascade_russian_plate_number.xml';//'haarcascade_frontalface_alt.xml';
      // "haarcascade_profileface.xml";
  // Input file name for avi or image file.
  input_name: AnsiString = '0';

  // Function prototype for detecting and drawing an object from an image
procedure detect_and_draw(image: pIplImage);
var
  scale: Integer;
//  temp: pIplImage;
  // two points to represent the face locations
  pt1, pt2: TCvPoint;
  i: Integer;
  faces: pCvSeq;
  r: pCvRect;
begin
  scale := 1;
  // Create a new image based on the input image
//  temp := cvCreateImage(cvSize(image^.width div scale, image^.height div scale), 8, 3);
  // Clear the memory storage which was used before
  cvClearMemStorage(storage);

  // Find whether the cascade is loaded, to find the faces. If yes, then:
  if Assigned(cascade) then
  begin
    // There can be more than one face in an image. So create a growable sequence of faces.
    // Detect the objects and store them in the sequence
    faces := cvHaarDetectObjects(image,cascade,storage,1.1,2,CV_HAAR_DO_CANNY_PRUNING,cvSize(40, 40),cvSize(0, 0));

    // Loop the number of faces found.
    for i := 1 to faces^.total do
    begin
      // Create a new rectangle for drawing the face
      r := pCvRect(cvGetSeqElem(faces, i));
      // Find the dimensions of the face,and scale it if necessary
      pt1.x := r^.x * scale;
      pt2.x := (r^.x + r^.width) * scale;
      pt1.y := r^.y * scale;
      pt2.y := (r^.y + r^.height) * scale;

      // Draw the rectangle in the input image
      cvRectangle(image, pt1, pt2, CV_RGB(255, 0, 0), 3, 8, 0);
    end;
  end;

  // Show the image in the window named "result"
  cvShowImage('result', image);

  // Release the temp image created.
//  cvReleaseImage(temp);
end;

Var
  // Structure for getting video from camera or avi
  capture: pCvCapture = nil;
  // Images to capture the frame from video or camera or from file
  frame: pIplImage = nil;
  frame_copy: pIplImage = nil;

const
  opt = '--cascade=';

begin
  try
    // Check for the correct usage of the command line              変更
    if ParamCount > 1 then
    begin
      cascade_name := Ansistring(ParamStr(1));
      input_name := Ansistring(ParamStr(2));
    end
    else
    if ParamCount = 1 then
    begin
      Writeln('Usage: facedetect --cascade=<cascade_path> [filename|camera_index]');
      Halt(1);
    end;

    // check facedetect HaarClassifier file name
    if not FileExists(string(cascade_name)) then
    begin
      Writeln('Usage: facedetect --cascade=<cascade_path> [filename]');
      Halt(1);
    end;

    // Load the HaarClassifierCascade
    cascade := cvLoad(pCVChar(@cascade_name[1]), nil, nil, nil);

    // Check whether the cascade has loaded successfully. Else report and error and quit
    if not Assigned(cascade) then
    begin
      Writeln('ERROR: Could not load classifier cascade');
      Halt(1);
    end;

    // Allocate the memory storage
    storage := cvCreateMemStorage(0);

    // Find whether to detect the object from file or from camera.
    if isDigit(input_name, 1) and (Length(input_name) = 1) then
      capture := cvCreateCameraCapture(StrToInt(input_name))
    else
      capture := cvCreateFileCapture(pCVChar(@input_name[1]));

    // 画像のサイズ設定 カメラによって値を修正します                追加
    cvSetCaptureProperty(Capture, CV_CAP_PROP_FRAME_WIDTH, 1280);
    cvSetCaptureProperty(Capture, CV_CAP_PROP_FRAME_HEIGHT, 720);

    // Create a new named window with title: result
    cvNamedWindow('result', 1);

    // 実行時間待ち合わせ   画像サイズが大きい場合 長くします   追加
    cvWaitKey(3000);
    // Find if the capture is loaded successfully or not.

    // If loaded succesfully, then:
    if Assigned(capture) then
    begin
      // Capture from the camera.
      While true do
      begin

        // Capture the frame and load it in IplImage
        frame := cvQueryFrame(capture);
        if not Assigned(frame) then
          Break;

        // Allocate framecopy as the same size of the frame
        if not Assigned(frame_copy) then
          frame_copy := cvCreateImage(cvSize(frame^.width, frame^.height), IPL_DEPTH_8U, frame^.nChannels);

        // Check the origin of image. If top left, copy the image frame to frame_copy.
        if (frame^.origin = IPL_ORIGIN_TL) then
          cvCopy(frame, frame_copy)                    
//          cvCopyImage(frame, frame_copy, nil)     旧バージョンでは "cvCopyImage" 
          // Else flip and copy the image
        else
          cvFlip(frame, frame_copy, 0);

        // Call the function to detect and draw the face
        detect_and_draw(frame_copy);
        // Wait for a while before proceeding to the next frame
        if (cvWaitKey(1) >= 0) then
          Break;

      end;
      // Release the images, and capture memory
      cvReleaseImage(frame_copy);
      cvReleaseCapture(capture);
    end
    else
    // If the capture is not loaded succesfully, then:
    begin // Assume the image to be lena.jpg, or the input_name specified
      input_name := 'lenna.jpg';

      // Load the image from that filename
      frame := cvLoadImage(pCVChar(@input_name[1]), 1);

      // If Image is loaded succesfully, then:
      if Assigned(frame) then
      begin
        // Detect and draw the face
        detect_and_draw(frame);
        //
        // Wait for user input
        cvWaitKey(0);

        // Release the image memory
        cvReleaseImage(frame);
      end;
    end;
    // Destroy the window previously created with filename: "result"
    cvDestroyWindow('result');

  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

end.

    download FaceDetect.zip

画像処理一覧へ戻る

      最初に戻る