2013-09-25 21:18:23 +02:00
|
|
|
// *****************************************************************
|
|
|
|
// Delphi-OpenCV Demo
|
|
|
|
// Copyright (C) 2013 Project Delphi-OpenCV
|
|
|
|
// ****************************************************************
|
|
|
|
// Contributor:
|
|
|
|
// Laentir Valetov
|
|
|
|
// email:laex@bk.ru
|
|
|
|
// ****************************************************************
|
|
|
|
// You may retrieve the latest version of this file at the GitHub,
|
|
|
|
// located at git://github.com/Laex/Delphi-OpenCV.git
|
|
|
|
// ****************************************************************
|
|
|
|
// The contents of this file are used with permission, subject to
|
|
|
|
// the Mozilla Public License Version 1.1 (the "License"); you may
|
|
|
|
// not use this file except in compliance with the License. You may
|
|
|
|
// obtain a copy of the License at
|
|
|
|
// http://www.mozilla.org/MPL/MPL-1_1Final.html
|
|
|
|
//
|
|
|
|
// Software distributed under the License is distributed on an
|
|
|
|
// "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
|
|
// implied. See the License for the specific language governing
|
|
|
|
// rights and limitations under the License.
|
|
|
|
// *******************************************************************
|
|
|
|
// Original: http://public.cranfield.ac.uk/c5354/teaching/ml/examples/c/haar_cascade/haar_cascade.cc
|
|
|
|
// *******************************************************************
|
|
|
|
//
|
|
|
|
// Example : run haar cascade classifier on image / video / camera
|
|
|
|
// usage: prog {<image_name> | <video_name>}
|
|
|
|
|
2013-03-27 23:20:08 +01:00
|
|
|
program FaceDetect;
|
|
|
|
|
2013-04-09 12:41:27 +02:00
|
|
|
{$APPTYPE CONSOLE}
|
2013-03-27 23:20:08 +01:00
|
|
|
{$R *.res}
|
|
|
|
|
|
|
|
uses
|
|
|
|
System.Character,
|
2013-04-05 13:36:47 +02:00
|
|
|
System.SysUtils,
|
2013-09-25 21:18:23 +02:00
|
|
|
System.Math,
|
2013-05-26 10:50:18 +02:00
|
|
|
Core.types_c,
|
2013-09-25 21:18:23 +02:00
|
|
|
core_c,
|
|
|
|
highgui_c,
|
2014-05-04 13:35:57 +02:00
|
|
|
objdetect_c,
|
2013-05-26 10:50:18 +02:00
|
|
|
imgproc_c,
|
|
|
|
imgproc.types_c,
|
2013-09-25 21:18:23 +02:00
|
|
|
cvUtils;
|
2013-03-27 23:20:08 +01:00
|
|
|
|
2013-09-25 21:18:23 +02:00
|
|
|
Const
|
|
|
|
// ******************************************************************************/
|
|
|
|
// setup the cameras properly based on OS platform
|
|
|
|
CAMERA_INDEX = CV_CAP_ANY;
|
|
|
|
// ******************************************************************************/
|
2014-05-11 15:15:21 +02:00
|
|
|
windowName = 'Haar Cascade Detection'; // window name
|
2013-09-25 21:18:23 +02:00
|
|
|
cascade_name: pCVChar = 'FaceDetectXML\haarcascade_frontalface_alt.xml'; // cascade file
|
2013-03-27 23:20:08 +01:00
|
|
|
|
|
|
|
Var
|
2014-05-11 15:15:21 +02:00
|
|
|
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 = 40; // 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;
|
2013-03-27 23:20:08 +01:00
|
|
|
|
|
|
|
begin
|
|
|
|
try
|
2013-09-25 21:18:23 +02:00
|
|
|
// 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
|
2013-03-27 23:20:08 +01:00
|
|
|
begin
|
2014-05-11 15:15:21 +02:00
|
|
|
img := cvLoadImage(c_str(ParamStr(1)), CV_LOAD_IMAGE_UNCHANGED);
|
2013-09-25 21:18:23 +02:00
|
|
|
if not Assigned(img) then
|
|
|
|
begin
|
|
|
|
capture := cvCreateFileCapture(c_str(ParamStr(1)));
|
2014-05-11 15:15:21 +02:00
|
|
|
isCapture := True;
|
2013-09-25 21:18:23 +02:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
if (not Assigned(img)) and (not Assigned(capture)) then
|
2013-03-27 23:20:08 +01:00
|
|
|
begin
|
2013-09-25 21:18:23 +02:00
|
|
|
capture := cvCreateCameraCapture(CAMERA_INDEX);
|
2014-05-11 15:15:21 +02:00
|
|
|
isCapture := True;
|
2013-03-27 23:20:08 +01:00
|
|
|
end;
|
2013-09-25 21:18:23 +02:00
|
|
|
if not Assigned(capture) then
|
|
|
|
Halt(1);
|
2013-03-27 23:20:08 +01:00
|
|
|
|
2013-09-25 21:18:23 +02:00
|
|
|
// create window object (use flag:=0 to allow resize, 1 to auto fix size)
|
2014-05-11 15:15:21 +02:00
|
|
|
cvNamedWindow(windowName, 0);
|
2013-03-27 23:20:08 +01:00
|
|
|
|
2013-09-25 21:18:23 +02:00
|
|
|
// load the trained haar cascade classifier from file
|
|
|
|
// and create storage required for detections
|
2014-05-11 15:15:21 +02:00
|
|
|
cascade := cvLoad(cascade_name, nil, nil, nil);
|
2013-09-25 21:18:23 +02:00
|
|
|
if Assigned(cascade) then
|
2014-05-11 15:15:21 +02:00
|
|
|
Writeln('LOADED : ', cascade_name)
|
2013-09-25 21:18:23 +02:00
|
|
|
else
|
2013-03-27 23:20:08 +01:00
|
|
|
begin
|
2014-05-11 15:15:21 +02:00
|
|
|
Writeln('ERROR: Could not load classifier cascade : ', cascade_name);
|
2013-03-27 23:20:08 +01:00
|
|
|
Halt(1);
|
|
|
|
end;
|
2013-09-25 21:18:23 +02:00
|
|
|
try
|
|
|
|
storage := cvCreateMemStorage(0);
|
2013-03-27 23:20:08 +01:00
|
|
|
|
2013-09-25 21:18:23 +02:00
|
|
|
// 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;
|
2013-03-27 23:20:08 +01:00
|
|
|
|
2013-09-25 21:18:23 +02:00
|
|
|
// create a greyscale image upon which to run the classifier
|
2014-05-11 15:15:21 +02:00
|
|
|
gray := cvCreateImage(cvSize(img^.width, img^.height), img^.depth, 1);
|
2013-03-27 23:20:08 +01:00
|
|
|
|
2013-09-25 21:18:23 +02:00
|
|
|
// create a copy of the image upon which to do detection and box drawing
|
|
|
|
imgcopy := cvCloneImage(img);
|
2013-03-27 23:20:08 +01:00
|
|
|
|
2013-09-25 21:18:23 +02:00
|
|
|
// start main loop
|
|
|
|
while True do
|
2013-03-27 23:20:08 +01:00
|
|
|
begin
|
2013-09-25 21:18:23 +02:00
|
|
|
// 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 := 0;
|
|
|
|
end;
|
2013-03-27 23:20:08 +01:00
|
|
|
|
2013-09-25 21:18:23 +02:00
|
|
|
// 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
|
2014-05-11 15:15:21 +02:00
|
|
|
cvCopy(frame, imgcopy);
|
2013-09-25 21:18:23 +02:00
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
2014-05-11 15:15:21 +02:00
|
|
|
cvFlip(frame, imgcopy);
|
2013-09-25 21:18:23 +02:00
|
|
|
imgcopy^.origin := IPL_ORIGIN_TL;
|
|
|
|
end;
|
|
|
|
gray^.origin := imgcopy^.origin;
|
2013-03-27 23:20:08 +01:00
|
|
|
|
2013-09-25 21:18:23 +02:00
|
|
|
// convert input image to grayscale
|
2014-05-11 15:15:21 +02:00
|
|
|
cvCvtColor(imgcopy, gray, CV_BGR2GRAY);
|
2013-03-27 23:20:08 +01:00
|
|
|
|
2013-09-25 21:18:23 +02:00
|
|
|
// histogram equalize it also to maximize the region differences
|
2014-05-11 15:15:21 +02:00
|
|
|
cvEqualizeHist(gray, gray);
|
2013-03-27 23:20:08 +01:00
|
|
|
|
2013-09-25 21:18:23 +02:00
|
|
|
// run the haar cascade detection
|
|
|
|
// with parameters scale:=1.2, neighbours := 4 and with Canny pruning
|
|
|
|
// turned on with minimum detection scale 30x30 pixels
|
2014-05-11 15:15:21 +02:00
|
|
|
detected_objects := cvHaarDetectObjects(gray, cascade, storage, 1.2, 4, CV_HAAR_DO_CANNY_PRUNING, cvSize(30, 30),
|
2013-09-25 21:18:23 +02:00
|
|
|
cvSize(0, 0));
|
2013-03-27 23:20:08 +01:00
|
|
|
|
2013-09-25 21:18:23 +02:00
|
|
|
// 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));
|
2014-05-11 15:15:21 +02:00
|
|
|
cvRectangle(imgcopy, cvPoint(r^.x, r^.y), cvPoint((r^.x) + (r^.width), (r^.y) + (r^.height)),
|
|
|
|
CV_RGB(255, 0, 0), 2, 8, 0);
|
2013-09-25 21:18:23 +02:00
|
|
|
Inc(i);
|
|
|
|
end;
|
|
|
|
// if Assigned(detected_objects) then
|
|
|
|
// cvClearSeq(detected_objects);
|
|
|
|
// cvClearMemStorage(storage);
|
2013-03-27 23:20:08 +01:00
|
|
|
|
2013-09-25 21:18:23 +02:00
|
|
|
// display image in window
|
2014-05-11 15:15:21 +02:00
|
|
|
cvShowImage(windowName, imgcopy);
|
2013-03-27 23:20:08 +01:00
|
|
|
|
2013-09-25 21:18:23 +02:00
|
|
|
// 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 = 27 then
|
|
|
|
begin
|
|
|
|
// if user presses 'ESC' then exit
|
|
|
|
Writeln('Keyboard exit requested : exiting now - bye!');
|
|
|
|
Break;
|
|
|
|
end;
|
2013-03-27 23:20:08 +01:00
|
|
|
end;
|
2013-09-25 21:18:23 +02:00
|
|
|
|
|
|
|
// 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);
|
2013-03-27 23:20:08 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
except
|
|
|
|
on E: Exception do
|
2014-05-11 15:15:21 +02:00
|
|
|
Writeln(E.ClassName, ': ', E.Message);
|
2013-03-27 23:20:08 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
end.
|