mirror of
https://github.com/Laex/Delphi-OpenCV.git
synced 2024-11-15 15:55:53 +01:00
a016c463c5
Signed-off-by: Laentir Valetov <laex@bk.ru>
430 lines
14 KiB
ObjectPascal
430 lines
14 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:
|
||
// http://blog.vidikon.com/?p=213
|
||
// ***************************************************************
|
||
|
||
program cv_ExtractSURF;
|
||
|
||
{$APPTYPE CONSOLE}
|
||
{$POINTERMATH ON}
|
||
{$R *.res}
|
||
|
||
uses
|
||
System.SysUtils,
|
||
Winapi.Windows,
|
||
ocv.highgui_c,
|
||
ocv.core_c,
|
||
ocv.core.types_c,
|
||
ocv.imgproc_c,
|
||
ocv.imgproc.types_c,
|
||
ocv.compat,
|
||
ocv.calib3d_c,
|
||
ocv.nonfree,
|
||
uResourcePaths;
|
||
|
||
// cðàâíåíèå äâóõ îcîáåííîcòåé
|
||
// comparison of the two features
|
||
function compareSURFDescriptors(const d1: PSingle; const d2: PSingle; best: Double; length: Integer): Double;
|
||
var
|
||
total_cost: Double;
|
||
i: Integer;
|
||
t0, t1, t2, t3: Double;
|
||
begin
|
||
total_cost := 0;
|
||
assert(length mod 4 = 0);
|
||
i := 0;
|
||
While i < length - 1 do
|
||
begin
|
||
t0 := d1[i] - d2[i];
|
||
t1 := d1[i + 1] - d2[i + 1];
|
||
t2 := d1[i + 2] - d2[i + 2];
|
||
t3 := d1[i + 3] - d2[i + 3];
|
||
total_cost := total_cost + t0 * t0 + t1 * t1 + t2 * t2 + t3 * t3;
|
||
if (total_cost > best) then
|
||
break;
|
||
i := i + 4;
|
||
end;
|
||
result := total_cost;
|
||
end;
|
||
|
||
// cðàâíèâàåò îäíó îcîáåííîcòü îáúåêòà cî âcåìè îcîáåííîcòÿìè cöåíû
|
||
// compares one feature of the scene with all the features
|
||
function naiveNearestNeighbor(const vec: PSingle; laplacian: Integer; const model_keypoints: pCvSeq;
|
||
const model_descriptors: pCvSeq): Integer;
|
||
Var
|
||
length, i, neighbor: Integer;
|
||
d, dist1, dist2: Double;
|
||
reader, kreader: TCvSeqReader;
|
||
kp: pCvSURFPoint;
|
||
mvec: PSingle;
|
||
begin
|
||
length := model_descriptors.elem_size div sizeof(single);
|
||
neighbor := -1;
|
||
dist1 := 1E6;
|
||
dist2 := 1E6;;
|
||
// Íà÷àëüíàÿ îcîáåííîcòü cöåíû
|
||
// The initial feature scenes
|
||
cvStartReadSeq(model_keypoints, @kreader, 0);
|
||
cvStartReadSeq(model_descriptors, @reader, 0);
|
||
// Ïåðåáîð âcåõ îcîáåííîcòåé cöåíû
|
||
// Iterating through all features of the scene
|
||
for i := 0 to model_descriptors.total - 1 do
|
||
begin
|
||
kp := pCvSURFPoint(kreader.ptr);
|
||
mvec := PSingle(reader.ptr);
|
||
CV_NEXT_SEQ_ELEM(kreader.seq.elem_size, kreader);
|
||
CV_NEXT_SEQ_ELEM(reader.seq.elem_size, reader);
|
||
// Äëÿ ócêîðåíèÿ cíà÷àëà cðàâíèâàåòcÿ ëàïëccèàí îcîáåííîcòåé
|
||
// To accelerate the first compared Laplacian features
|
||
if (laplacian <> kp.laplacian) then
|
||
continue;
|
||
// cðàâíåíèå îcîáåííîcòåé
|
||
// comparison of the features
|
||
d := compareSURFDescriptors(vec, mvec, dist2, length);
|
||
if (d < dist1) then
|
||
begin
|
||
// Íàéäåíà ëó÷øåå cîâïàäåíèå îcîáåííîcòåé
|
||
// Found a better match features
|
||
dist2 := dist1;
|
||
dist1 := d;
|
||
neighbor := i;
|
||
end
|
||
|
||
else if (d < dist2) then
|
||
dist2 := d;
|
||
end;
|
||
if (dist1 < 0.6 * dist2) then
|
||
Exit(neighbor);
|
||
result := -1;
|
||
end;
|
||
|
||
// Ôóíêöèÿ èùåò cîâïàäàþùèå ïàðû
|
||
// Function searches for matching pairs
|
||
procedure findPairs(const objectKeypoints: pCvSeq; const objectDescriptors: pCvSeq; const imageKeypoints: pCvSeq;
|
||
const imageDescriptors: pCvSeq; Var ptpairs: TArray<Integer>);
|
||
var
|
||
i: Integer;
|
||
reader, kreader: TCvSeqReader;
|
||
kp: pCvSURFPoint;
|
||
descriptor: PSingle;
|
||
nearest_neighbor: Integer;
|
||
begin
|
||
// Ócòàíîâêà íà÷àëüíîé îcîáåííîcòè îáúåêòà ðccïîçíàâàíèÿ
|
||
// Sets the initial features of object recognition
|
||
cvStartReadSeq(objectKeypoints, @kreader);
|
||
cvStartReadSeq(objectDescriptors, @reader);
|
||
SetLength(ptpairs, 0);
|
||
// Ïåðåáîð âcåõ îcîáåííîcòåòåé îáúåêòà
|
||
// Iterating through all features of the object
|
||
for i := 0 to objectDescriptors.total - 1 do
|
||
begin
|
||
kp := pCvSURFPoint(kreader.ptr);
|
||
descriptor := PSingle(reader.ptr);
|
||
CV_NEXT_SEQ_ELEM(kreader.seq.elem_size, kreader);
|
||
CV_NEXT_SEQ_ELEM(reader.seq.elem_size, reader);
|
||
// cðàâíåíèå òåêóùåé îcîáåííîcòè cî âcåìè îcîáåííîcòÿìè èç cöåíû
|
||
// comparison of the current features with all the features of the scene
|
||
nearest_neighbor := naiveNearestNeighbor(descriptor, kp.laplacian, imageKeypoints, imageDescriptors);
|
||
if (nearest_neighbor >= 0) then
|
||
begin
|
||
// Íàøëîcü cîâïàäåíèå îcîáåííîcòåé
|
||
// Match the features found
|
||
SetLength(ptpairs, length(ptpairs) + 2);
|
||
ptpairs[High(ptpairs) - 1] := i;
|
||
ptpairs[High(ptpairs)] := nearest_neighbor;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
// Ãðóáîå íàõîæäåíèå ìåcòîïîëîæåíèÿ îáúåêòà
|
||
// Finding rough position of the object
|
||
function locatePlanarObject(const objectKeypoints: pCvSeq; const objectDescriptors: pCvSeq; const imageKeypoints: pCvSeq;
|
||
const imageDescriptors: pCvSeq; const src_corners: TArray<TCvPoint>; dst_corners: TArray<TCvPoint>): Integer;
|
||
|
||
var
|
||
h: array [0 .. 8] of Double;
|
||
_h: TCvMat;
|
||
ptpairs: TArray<Integer>;
|
||
pt1, pt2: TArray<TCvPoint2D32f>;
|
||
_pt1, _pt2: TCvMat;
|
||
i, n: Integer;
|
||
x, y, _Z, _X, _Y: Double;
|
||
|
||
begin
|
||
_h := cvMat(3, 3, CV_64F, @h);
|
||
// Èùåì ïàðû îcîáåííîcòåé íà îáåèõ êàðòèíêàõ, êîòîðûå cîîòâåòcòâóþò äðóã äðóãó
|
||
// We are looking for a pair of features on each image that correspond to each other
|
||
findPairs(objectKeypoints, objectDescriptors, imageKeypoints, imageDescriptors, ptpairs);
|
||
n := length(ptpairs) div 2;
|
||
// Åcëè ïàð ìàëî, çíà÷èò íàäî âûõîäèòü – îáúåêò íå íàéäåí
|
||
// If found little pair, then have to go - object not found
|
||
if (n < 4) then
|
||
Exit(0);
|
||
// Âûäåëÿåì ïàìÿòü
|
||
SetLength(pt1, n);
|
||
SetLength(pt2, n);
|
||
// c÷èòûâàåì êîîðäèíàòû «îcîáûõ»òî÷åê
|
||
// read the coordinates of the "singular" points
|
||
for i := 0 to n - 1 do
|
||
begin
|
||
pt1[i] := pCvSURFPoint(cvGetSeqElem(objectKeypoints, ptpairs[i * 2])).pt;
|
||
pt2[i] := pCvSURFPoint(cvGetSeqElem(imageKeypoints, ptpairs[i * 2 + 1])).pt;
|
||
end;
|
||
|
||
// Ïî ïîëó÷åííûì âåêòîðàì cîçäà¸ì ìàòðèö
|
||
// Using computed vectors - creating a matrix
|
||
_pt1 := cvMat(1, n, CV_32FC2, @pt1[0]);
|
||
_pt2 := cvMat(1, n, CV_32FC2, @pt2[0]);
|
||
// Íàõîäèì òðàícôîðìàöèþ ìåæäó ècõîäíûì èçîáðàæåíèåì è c òåì, êîòîðîå èùåì
|
||
// Find the transformation between the original image and the fact that looking
|
||
if (cvFindHomography(@_pt1, @_pt2, @_h, CV_RANSAC, 5) = 0) then
|
||
Exit(0);
|
||
// Ïî ïîëó÷åííîìó çíà÷åíèþ òðàícôîðìàöèè (â ìàòðèöó _h) íàõîäèì
|
||
// êîîðäèíàòû ÷åòûð¸õóãîëüíèêà, õàðàêòåðèçóþùåãî îáúåêò
|
||
// Using the values transformation (in the matrix _h) find
|
||
// the coordinates of a quadrilateral, indicative of the object
|
||
for i := 0 to 3 do
|
||
begin
|
||
x := src_corners[i].x;
|
||
y := src_corners[i].y;
|
||
_Z := 1. / (h[6] * x + h[7] * y + h[8]);
|
||
_X := (h[0] * x + h[1] * y + h[2]) * _Z;
|
||
_Y := (h[3] * x + h[4] * y + h[5]) * _Z;
|
||
dst_corners[i] := cvPoint(cvRound(_X), cvRound(_Y));
|
||
end;
|
||
Exit(1);
|
||
end;
|
||
|
||
Var
|
||
object_filename, scene_filename: AnsiString;
|
||
storage: pCvMemStorage;
|
||
colors: array [0 .. 8] of TCvScalar = (
|
||
(
|
||
val:
|
||
(
|
||
0,
|
||
0,
|
||
255,
|
||
0
|
||
)
|
||
),
|
||
(
|
||
val:
|
||
(
|
||
0,
|
||
128,
|
||
255,
|
||
0
|
||
)
|
||
),
|
||
(
|
||
val:
|
||
(
|
||
0,
|
||
255,
|
||
255,
|
||
0
|
||
)
|
||
),
|
||
(
|
||
val:
|
||
(
|
||
0,
|
||
255,
|
||
0,
|
||
0
|
||
)
|
||
),
|
||
(
|
||
val:
|
||
(
|
||
255,
|
||
128,
|
||
0,
|
||
0
|
||
)
|
||
),
|
||
(
|
||
val:
|
||
(
|
||
255,
|
||
255,
|
||
0,
|
||
0
|
||
)
|
||
),
|
||
(
|
||
val:
|
||
(
|
||
255,
|
||
0,
|
||
0,
|
||
0
|
||
)
|
||
),
|
||
(
|
||
val:
|
||
(
|
||
255,
|
||
0,
|
||
255,
|
||
0
|
||
)
|
||
),
|
||
(
|
||
val:
|
||
(
|
||
255,
|
||
255,
|
||
255,
|
||
0
|
||
)
|
||
)
|
||
);
|
||
|
||
_object, image, object_color: pIplImage;
|
||
objectKeypoints, objectDescriptors, imageKeypoints, imageDescriptors: pCvSeq;
|
||
i: Integer;
|
||
params: TCvSURFParams;
|
||
tt: Double;
|
||
src_corners, dst_corners: TArray<TCvPoint>;
|
||
correspond: pIplImage;
|
||
r1, r2: TCvPoint;
|
||
ptpairs: TArray<Integer>;
|
||
_r1, _r2: pCvSURFPoint;
|
||
r: pCvSURFPoint;
|
||
center: TCvPoint;
|
||
radius: Integer;
|
||
|
||
begin
|
||
try
|
||
initModule_nonfree;
|
||
// Èíèöèàëèçàöèÿ ïàðàìåòðîâ
|
||
// initialization parameters
|
||
object_filename := iif(ParamCount = 2, ParamStr(1), cResourceMedia + 'box.png');
|
||
scene_filename := iif(ParamCount = 2, ParamStr(2), cResourceMedia + 'box_in_scene.png');
|
||
storage := cvCreateMemStorage(0);
|
||
cvNamedWindow('Object', 1);
|
||
cvNamedWindow('Object Correspond', 1);
|
||
// Çàãðóçêà èçîáðàæåíèé
|
||
// Loading Images
|
||
_object := cvLoadImage(pcvChar(@object_filename[1]), CV_LOAD_IMAGE_GRAYSCALE);
|
||
image := cvLoadImage(pcvChar(@scene_filename[1]), CV_LOAD_IMAGE_GRAYSCALE);
|
||
|
||
if (not Assigned(_object)) or (not Assigned(image)) then
|
||
begin
|
||
WriteLn(Format('Can not load %s and/or %s', [object_filename, scene_filename]));
|
||
WriteLn('Usage: find_obj [<object_filename> <scene_filename>]');
|
||
Halt;
|
||
end;
|
||
// Ïåðåâîä â ãðàäàöèè cåðîãî
|
||
// Translation grayscale
|
||
object_color := cvCreateImage(cvGetSize(_object), 8, 3);
|
||
cvCvtColor(_object, object_color, CV_GRAY2BGR);
|
||
// Èíèöèàëèçàöèÿ còðóêòóðû CvSURFParams c ðàçìåðîì äåcêðèïòîðîâ â 128 ýëåìåíòîâ
|
||
// Initialization of the structure CvSURFParams c size descriptors 128 items
|
||
params := CvSURFParams(500, 1);
|
||
// Çàcåêàåì âðåìÿ
|
||
// note the time
|
||
tt := cvGetTickCount();
|
||
// Èùåì îcîáåííîcòè îáúåêòà ðccïîçíàâàíèÿ
|
||
// We are looking for particular object recognition
|
||
cvExtractSURF(_object, nil, @objectKeypoints, @objectDescriptors, storage, params);
|
||
WriteLn(Format('Object Descriptors: %d', [objectDescriptors.total]));
|
||
// Èùåì îcîáåííîcòè cöåíû
|
||
// We are looking for particular scenes
|
||
cvExtractSURF(image, nil, @imageKeypoints, @imageDescriptors, storage, params);
|
||
WriteLn(Format('Image Descriptors: %d', [imageDescriptors.total]));
|
||
// cêîëüêî ïîòðåáîâàëîcü âðåìåíè (Ó ìåíÿ 167 ìèëëè cåêóíä)
|
||
// how long it took (I 167 milliseconds)
|
||
tt := cvGetTickCount() - tt;
|
||
WriteLn(Format('Extraction time = %gms', [tt / (cvGetTickFrequency() * 1000)]));
|
||
// Ócòàíàâëèâàåì ãðàíèöû èçîáðàæåíèé, âíóòðè êîòîðûõ áóäóò cðàâíèâàòücÿ îcîáåííîcòè
|
||
// Set the image borders, within which features will be compared
|
||
SetLength(src_corners, 4);
|
||
src_corners[0] := cvPoint(0, 0);
|
||
src_corners[1] := cvPoint(_object.width, 0);
|
||
src_corners[2] := cvPoint(_object.width, _object.height);
|
||
src_corners[3] := cvPoint(0, _object.height);
|
||
SetLength(dst_corners, 4);
|
||
// cîçäàíèå äîïîëíèòåëüíîãî èçîáðàæåíèå (â í¸ì áóäåò cöåíà è îáúåêò)
|
||
// Çàïócòèòå ïðèìåð è ïîéì¸òå î ÷¸ì ðå÷ü
|
||
// creation of an additional image (it will be a scene and object)
|
||
// Run the example and you will understand what I mean
|
||
correspond := cvCreateImage(cvSize(image.width, _object.height + image.height), 8, 1);
|
||
cvSetImageROI(correspond, cvRect(0, 0, _object.width, _object.height));
|
||
cvCopy(_object, correspond);
|
||
cvSetImageROI(correspond, cvRect(0, _object.height, correspond.width, correspond.height));
|
||
cvCopy(image, correspond);
|
||
cvResetImageROI(correspond);
|
||
// Âûçûâàåì ôóíêöèþ, íàõîäÿùóþ îáúåêò íà ýêðàíå
|
||
// Call the function that retrieves the object on the screen
|
||
if (locatePlanarObject(objectKeypoints, objectDescriptors, imageKeypoints, imageDescriptors, src_corners, dst_corners) <> 0)
|
||
then
|
||
begin
|
||
// Îáâîäèì íóæíûé ÷åòûð¸õóãîëüíèê
|
||
// Draw out the desired quadrangle
|
||
for i := 0 to 3 do
|
||
begin
|
||
r1 := dst_corners[i mod 4];
|
||
r2 := dst_corners[(i + 1) mod 4];
|
||
cvLine(correspond, cvPoint(r1.x, r1.y + _object.height), cvPoint(r2.x, r2.y + _object.height), colors[8]);
|
||
end;
|
||
end;
|
||
// Åcëè â ýòîì ìåcòå âûâåcòè ðåçóëüòàò íà ýêðàí, òî ïîëó÷èòücÿ òî, ÷òî ïîêàçàíî íà ðècóíêå 23.3.
|
||
// cíîâà èùóòcÿ âcå cîâïàäàþùèå ïàðû îcîáåííîcòåé â îáåèõ êàðòèíêàõ
|
||
// again finds all the matching pairs of features in both pictures
|
||
findPairs(objectKeypoints, objectDescriptors, imageKeypoints, imageDescriptors, ptpairs);
|
||
// Ìåæäó ïàðàìè îcîáåííîcòåé íà ðècóíêå ïðîâîäÿòcÿ ïðÿìûå
|
||
// Between pairs of features in the figure are held straight
|
||
i := 0;
|
||
While i < length(ptpairs) do
|
||
begin
|
||
_r1 := pCvSURFPoint(cvGetSeqElem(objectKeypoints, ptpairs[i]));
|
||
_r2 := pCvSURFPoint(cvGetSeqElem(imageKeypoints, ptpairs[i + 1]));
|
||
cvLine(correspond, cvPointFrom32f(_r1.pt), cvPoint(cvRound(_r2.pt.x), cvRound(_r2.pt.y + _object.height)), colors[8]);
|
||
i := i + 2;
|
||
end;
|
||
// Ðåçóëüòàò ìîæíî ïîcìîòðåòü íà ðècíóêå 23.4.
|
||
cvShowImage('Object Correspond', correspond);
|
||
// Âûäåëÿåì îcîáåííîcòèî îêðóæíîcòÿìè (Ðèc. 23.5)
|
||
// Highlight features of circles
|
||
for i := 0 to objectKeypoints.total - 1 do
|
||
begin
|
||
r := pCvSURFPoint(cvGetSeqElem(objectKeypoints, i));
|
||
center.x := cvRound(r.pt.x);
|
||
center.y := cvRound(r.pt.y);
|
||
radius := cvRound(r.size * 1.2 / 9 * 2);
|
||
cvCircle(object_color, center, radius, colors[0], 1, 8, 0);
|
||
end;
|
||
cvShowImage('Object', object_color);
|
||
cvWaitKey(0);
|
||
cvDestroyAllWindows;
|
||
except
|
||
on E: Exception do
|
||
WriteLn(E.ClassName, ': ', E.Message);
|
||
end;
|
||
|
||
end.
|