mirror of
https://github.com/Laex/Delphi-OpenCV.git
synced 2024-11-16 16:25:53 +01:00
ac3dd15da8
[+] TrackColor [*] Some changes in modules [*] To continue the translation of classes [!] Should change the structure of the modules to the structure of the modules installed OpenCV Signed-off-by: Laex <laex@bk.ru>
428 lines
12 KiB
ObjectPascal
428 lines
12 KiB
ObjectPascal
(* /*****************************************************************
|
|
// 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 file:
|
|
// opencv\samples\c\adaptiveskindetector.cpp
|
|
// *************************************************************** *)
|
|
|
|
// JCL_DEBUG_EXPERT_GENERATEJDBG OFF
|
|
// JCL_DEBUG_EXPERT_INSERTJDBG OFF
|
|
// JCL_DEBUG_EXPERT_DELETEMAPFILE OFF
|
|
program cv_AdaptiveSkinDetector;
|
|
|
|
{$APPTYPE CONSOLE}
|
|
{$POINTERMATH ON}
|
|
{$R *.res}
|
|
|
|
uses
|
|
System.SysUtils,
|
|
Windows,
|
|
uLibName in '..\..\..\include\uLibName.pas',
|
|
highgui_c in '..\..\..\include\highgui\highgui_c.pas',
|
|
core_c in '..\..\..\include\core\core_c.pas',
|
|
Core.types_c in '..\..\..\include\core\Core.types_c.pas',
|
|
imgproc.types_c in '..\..\..\include\imgproc\imgproc.types_c.pas',
|
|
imgproc_c in '..\..\..\include\imgproc\imgproc_c.pas',
|
|
legacy in '..\..\..\include\legacy\legacy.pas',
|
|
calib3d in '..\..\..\include\calib3d\calib3d.pas',
|
|
imgproc in '..\..\..\include\imgproc\imgproc.pas',
|
|
haar in '..\..\..\include\objdetect\haar.pas',
|
|
objdetect in '..\..\..\include\objdetect\objdetect.pas',
|
|
tracking in '..\..\..\include\video\tracking.pas',
|
|
Core in '..\..\..\include\core\core.pas',
|
|
contrib in '..\..\..\include\contrib\contrib.pas',
|
|
Mat in '..\..\..\include\core\Mat.pas';
|
|
|
|
Const
|
|
CLOCKS_PER_SEC = 1000;
|
|
|
|
procedure help;
|
|
begin
|
|
Writeln('This program demonstrates the contributed flesh detector CvAdaptiveSkinDetector which can be found in contrib.cpp');
|
|
Writeln('Usage: ');
|
|
Writeln(ExtractFileName(ParamStr(0)), ' fileMask firstFrame lastFrame');
|
|
Writeln('Example:');
|
|
Writeln(ExtractFileName(ParamStr(0)), ' C:\VideoSequences\sample1\right_view\temp_%05d.jpg 0 1000');
|
|
Writeln(' iterates through temp_00000.jpg to temp_01000.jpg');
|
|
Writeln('If no parameter specified, this application will try to capture from the default Webcam.');
|
|
Writeln('Please note: Background should not contain large surfaces with skin tone.');
|
|
Writeln(' ESC will stop');
|
|
Writeln('Using OpenCV version ', CV_VERSION);
|
|
end;
|
|
|
|
Type
|
|
TASDFrameHolder = class
|
|
private
|
|
image: pIplImage;
|
|
timeStamp: double;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure assignFrame(sourceImage: pIplImage; frameTime: double); virtual;
|
|
function getImage: pIplImage;
|
|
function getTimeStamp: double;
|
|
procedure setImage(sourceImage: pIplImage); virtual;
|
|
end;
|
|
|
|
TASDFrameSequencer = class
|
|
public
|
|
destructor Destroy; override;
|
|
function getNextImage: pIplImage; virtual;
|
|
procedure close; virtual;
|
|
function isOpen: bool; virtual;
|
|
procedure getFrameCaption(var caption: AnsiString); virtual;
|
|
end;
|
|
|
|
TASDCVFrameSequencer = class(TASDFrameSequencer)
|
|
protected
|
|
capture: pCvCapture;
|
|
public
|
|
function getNextImage: pIplImage; override;
|
|
procedure close; override;
|
|
function isOpen: bool; override;
|
|
end;
|
|
|
|
TASDFrameSequencerWebCam = class(TASDCVFrameSequencer)
|
|
public
|
|
function open(cameraIndex: Integer): boolean; virtual;
|
|
procedure getFrameCaption(var caption: AnsiString); override;
|
|
end;
|
|
|
|
TASDFrameSequencerVideoFile = class(TASDCVFrameSequencer)
|
|
public
|
|
function open(const fileName: pCvChar): bool; virtual;
|
|
end;
|
|
|
|
TASDFrameSequencerImageFile = class(TASDFrameSequencer)
|
|
private
|
|
sFileNameMask: AnsiString;
|
|
nCurrentIndex, nStartIndex, nEndIndex: Integer;
|
|
public
|
|
procedure open(const fileNameMask: AnsiString; startIndex, endIndex: Integer); virtual;
|
|
procedure getFrameCaption(var caption: AnsiString); override;
|
|
function getNextImage: pIplImage; override;
|
|
procedure close; override;
|
|
function isOpen: bool; override;
|
|
end;
|
|
|
|
{ TASDFrameHolder }
|
|
|
|
procedure TASDFrameHolder.assignFrame(sourceImage: pIplImage; frameTime: double);
|
|
begin
|
|
if Assigned(image) then
|
|
begin
|
|
cvReleaseImage(image);
|
|
image := nil;
|
|
end;
|
|
image := cvCloneImage(sourceImage);
|
|
timeStamp := frameTime;
|
|
end;
|
|
|
|
constructor TASDFrameHolder.Create;
|
|
begin
|
|
image := nil;
|
|
timeStamp := 0;
|
|
end;
|
|
|
|
destructor TASDFrameHolder.Destroy;
|
|
begin
|
|
cvReleaseImage(image);
|
|
inherited;
|
|
end;
|
|
|
|
function TASDFrameHolder.getImage: pIplImage;
|
|
begin
|
|
Result := image;
|
|
end;
|
|
|
|
function TASDFrameHolder.getTimeStamp: double;
|
|
begin
|
|
Result := timeStamp;
|
|
end;
|
|
|
|
procedure TASDFrameHolder.setImage(sourceImage: pIplImage);
|
|
begin
|
|
image := sourceImage;
|
|
end;
|
|
|
|
{ TASDFrameSequencer }
|
|
|
|
procedure TASDFrameSequencer.close;
|
|
begin
|
|
|
|
end;
|
|
|
|
destructor TASDFrameSequencer.Destroy;
|
|
begin
|
|
close;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TASDFrameSequencer.getFrameCaption(var caption: AnsiString);
|
|
begin
|
|
|
|
end;
|
|
|
|
function TASDFrameSequencer.getNextImage: pIplImage;
|
|
begin
|
|
Result := nil;
|
|
end;
|
|
|
|
function TASDFrameSequencer.isOpen: bool;
|
|
begin
|
|
Result := False;
|
|
end;
|
|
|
|
{ TASDFrameSequencerImageFile }
|
|
|
|
procedure TASDFrameSequencerImageFile.close;
|
|
begin
|
|
nCurrentIndex := nEndIndex + 1;
|
|
end;
|
|
|
|
procedure TASDFrameSequencerImageFile.getFrameCaption(var caption: AnsiString);
|
|
begin
|
|
caption := Format(sFileNameMask, [nCurrentIndex]);
|
|
end;
|
|
|
|
function TASDFrameSequencerImageFile.getNextImage: pIplImage;
|
|
Var
|
|
fileName: AnsiString;
|
|
begin
|
|
Inc(nCurrentIndex);
|
|
if (nCurrentIndex > nEndIndex) then
|
|
Exit(nil);
|
|
fileName := Format(sFileNameMask, [nCurrentIndex]);
|
|
Result := cvLoadImage(pCvChar(@fileName[1]));
|
|
end;
|
|
|
|
function TASDFrameSequencerImageFile.isOpen: bool;
|
|
begin
|
|
Result := (nCurrentIndex <= nEndIndex);
|
|
end;
|
|
|
|
procedure TASDFrameSequencerImageFile.open(const fileNameMask: AnsiString; startIndex, endIndex: Integer);
|
|
begin
|
|
nCurrentIndex := startIndex - 1;
|
|
nStartIndex := startIndex;
|
|
nEndIndex := endIndex;
|
|
sFileNameMask := Format('%s', [fileNameMask]);
|
|
end;
|
|
|
|
{ TASDFrameSequencerVideoFile }
|
|
|
|
function TASDFrameSequencerVideoFile.open(const fileName: pCvChar): bool;
|
|
begin
|
|
close();
|
|
capture := cvCreateFileCapture(fileName);
|
|
Result := Assigned(capture);
|
|
end;
|
|
|
|
{ TASDFrameSequencerWebCam }
|
|
|
|
procedure TASDFrameSequencerWebCam.getFrameCaption(var caption: AnsiString);
|
|
begin
|
|
caption := 'Web cam';
|
|
end;
|
|
|
|
function TASDFrameSequencerWebCam.open(cameraIndex: Integer): boolean;
|
|
begin
|
|
close();
|
|
capture := cvCreateCameraCapture(cameraIndex);
|
|
Result := Assigned(capture);
|
|
end;
|
|
|
|
{ TASDCVFrameSequencer }
|
|
|
|
procedure TASDCVFrameSequencer.close;
|
|
begin
|
|
inherited;
|
|
if (capture <> nil) then
|
|
cvReleaseCapture(capture);
|
|
end;
|
|
|
|
function TASDCVFrameSequencer.getNextImage: pIplImage;
|
|
Var
|
|
image: pIplImage;
|
|
begin
|
|
image := cvQueryFrame(capture);
|
|
if (image <> nil) then
|
|
Exit(cvCloneImage(image))
|
|
else
|
|
Exit(nil);
|
|
end;
|
|
|
|
function TASDCVFrameSequencer.isOpen: bool;
|
|
begin
|
|
Result := (capture <> nil);
|
|
end;
|
|
|
|
// ---------------------------------------------------------------------
|
|
procedure putTextWithShadow(img: pIplImage; const str: pCvChar; point: TCvPoint; font: pCvFont;
|
|
color: TCvScalar { = CV_RGB(255, 255, 128) } );
|
|
begin
|
|
cvPutText(img, str, cvPoint(point.x - 1, point.y - 1), font, CV_RGB(0, 0, 0));
|
|
cvPutText(img, str, point, font, color);
|
|
end;
|
|
|
|
procedure ASD_RGB_SET_PIXEL(ptr: pByte; r, g, b: uchar); inline;
|
|
begin
|
|
// (*pointer) = (unsigned char)b;
|
|
ptr[0] := b;
|
|
// (*(pointer+1)) = (unsigned char)g;
|
|
ptr[1] := g;
|
|
// (*(pointer+2)) = (unsigned char)r;
|
|
ptr[2] := r;
|
|
end;
|
|
|
|
procedure ASD_RGB_GET_PIXEL(ptr: pByte; Var r, g, b: uchar); inline;
|
|
begin
|
|
// b = (unsigned char)(*(pointer));
|
|
b := ptr[0];
|
|
// g = (unsigned char)(*(pointer+1));
|
|
g := ptr[1];
|
|
// r = (unsigned char)(*(pointer+2));
|
|
r := ptr[2];
|
|
end;
|
|
|
|
procedure displayBuffer(rgbDestImage: pIplImage; buffer: pIplImage; rValue, gValue, bValue: Integer);
|
|
Var
|
|
x, y, nWidth, nHeight: Integer;
|
|
destX, destY, dx, dy: double;
|
|
c: uchar;
|
|
pSrc: pByte;
|
|
begin
|
|
nWidth := buffer^.width;
|
|
nHeight := buffer^.height;
|
|
|
|
dx := rgbDestImage^.width / nWidth;
|
|
dy := rgbDestImage^.height / nHeight;
|
|
|
|
destX := 0;
|
|
for x := 0 to nWidth - 1 do
|
|
begin
|
|
destY := 0;
|
|
for y := 0 to nHeight - 1 do
|
|
begin
|
|
c := (buffer^.imageData + buffer^.widthStep * y)[x];
|
|
|
|
if (c <> 0) then
|
|
begin
|
|
pSrc := rgbDestImage^.imageData + rgbDestImage^.widthStep * Trunc(destY) +
|
|
(Trunc(destX) * rgbDestImage^.nChannels);
|
|
ASD_RGB_SET_PIXEL(pSrc, rValue, gValue, bValue);
|
|
end;
|
|
destY := destY + dy;
|
|
end;
|
|
destY := 0;
|
|
destX := destX + dx;
|
|
end;
|
|
end;
|
|
|
|
Var
|
|
caption, s, windowName: AnsiString;
|
|
img: pIplImage;
|
|
filterMask: pIplImage = nil;
|
|
filter: TCvAdaptiveSkinDetector;
|
|
sequencer: TASDFrameSequencer;
|
|
base_font: TCvFont;
|
|
clockTotal, numFrames: LongInt;
|
|
clock: Cardinal;
|
|
|
|
begin
|
|
try
|
|
help;
|
|
filter := TCvAdaptiveSkinDetector.Create(1, TCvAdaptiveSkinDetector.MORPHING_METHOD_ERODE_DILATE);
|
|
clockTotal := 0;
|
|
numFrames := 0;
|
|
|
|
if (ParamCount < 3) then
|
|
begin
|
|
help;
|
|
sequencer := TASDFrameSequencerWebCam.Create;
|
|
(sequencer as TASDFrameSequencerWebCam).open(-1);
|
|
if (not sequencer.isOpen()) then
|
|
begin
|
|
Writeln('Error: Cannot initialize the default Webcam');
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
sequencer := TASDFrameSequencerImageFile.Create;
|
|
// A sequence of images captured from video source, is stored here
|
|
(sequencer as TASDFrameSequencerImageFile).open(ParamStr(1), StrToInt(ParamStr(2)), StrToInt(ParamStr(3)));
|
|
end;
|
|
|
|
windowName := 'Adaptive Skin Detection Algorithm for Video Sequences';
|
|
|
|
cvNamedWindow(pCvChar(@windowName[1]), CV_WINDOW_AUTOSIZE);
|
|
cvInitFont(@base_font, CV_FONT_VECTOR0, 0.5, 0.5);
|
|
|
|
// Usage:
|
|
// c:\>CvASDSample 'C:\VideoSequences\sample1\right_view\temp_%05d.jpg' 0 1000
|
|
|
|
Writeln('Press ESC to stop.');
|
|
img := sequencer.getNextImage();
|
|
while Assigned(img) do
|
|
begin
|
|
Inc(numFrames);
|
|
|
|
if not Assigned(filterMask) then
|
|
filterMask := cvCreateImage(cvSize(img^.width, img^.height), IPL_DEPTH_8U, 1);
|
|
clock := GetTickCount;
|
|
filter.process(img, filterMask); // DETECT SKIN
|
|
clockTotal := clockTotal + (GetTickCount - clock);
|
|
|
|
displayBuffer(img, filterMask, 0, 255, 0);
|
|
|
|
sequencer.getFrameCaption(caption);
|
|
s := Format('%s - %d x %d', [caption, img^.width, img^.height]);
|
|
putTextWithShadow(img, pCvChar(@s[1]), cvPoint(10, img^.height - 35), @base_font, CV_RGB(0, 255, 128));
|
|
|
|
s := Format('Average processing time per frame: %5.2fms', [((clockTotal * 1000) / CLOCKS_PER_SEC) / numFrames]);
|
|
putTextWithShadow(img, pCvChar(@s[1]), cvPoint(10, img^.height - 15), @base_font, CV_RGB(255, 255, 128));
|
|
|
|
cvShowImage(pCvChar(@windowName[1]), img);
|
|
cvReleaseImage(img);
|
|
|
|
if (cvWaitKey(1) = 27) then
|
|
break;
|
|
img := sequencer.getNextImage();
|
|
end;
|
|
|
|
sequencer.close();
|
|
sequencer.Free;
|
|
filter.Free;
|
|
|
|
cvReleaseImage(filterMask);
|
|
|
|
cvDestroyAllWindows;
|
|
|
|
Writeln('Finished, ', numFrames, ' frames processed.');
|
|
|
|
except
|
|
on E: Exception do
|
|
Writeln(E.ClassName, ': ', E.Message);
|
|
end;
|
|
|
|
end.
|