Associative Video Memory - Frequently Asked Questions
Question: I noticed that algorithm is not rotation invariant, am I right?
Answer: Yes, you are right but object may be learned under different angles.
Question: Do you perform per pixel search of your template in the image, as well as in
decreased size images, or you scale your template beforehand in order to obtain some scale invariance?
Answer: Image scan with 25% increase of scale on each step and start it from 75% of key image size
(scaling: 75%, 100%, 125%, 150% ... until size biger than scan image).
Question: Unrecognized events are multiple. Is it possible to make only one event per
stored object id per single recognition / tracking function call?
Answer: For single similarity estimation you can use "Read" method:
#include "stdafx.h"
#include "cv.h"
#include "highgui.h"
#include "AssociativeMemory.h"
void TestAVM()
{
CvRect rect = cvRect(312, 312, 159, 159);
CvRect rectFound;
long id = 1;
IplImage *img1 = cvLoadImage("4_i110.png", 0);
IplImage *img2 = cvLoadImage("4_i120.png", 0);
// Create
CvAssociativeMemory32S *am = new CvAssociativeMemory32S;
am->Create(cvSize(rect.width, rect.height));
// Learn
am->SetImage(img1);
am->Write(rect, &id);
// Recognize
am->SetImage(img2);
long* pId;
double Similarity;
if(am->Read(rect, &rectFound, &pId, NULL, NULL, &Similarity, true)) {
printf("Object recognized, rectFound(%d, %d, %d, %d)\n",
rectFound.x, rectFound.y, rectFound.width, rectFound.height);
}
printf("Id = %lu, Similarity = %f or %lu\n", *pId, Similarity, long(Similarity*10000));
cvReleaseImage(&img1);
cvReleaseImage(&img2);
delete am;
}
int _tmain(int argc, _TCHAR* argv[])
{
TestAVM();
return 0;
}
Question: We have the following problem: when associative memory is stored and then is
loaded similarity is always zero.
................
// Learn and save
am->SetImage(img1);
am->Write(rect, &id);
am->Save("test.model");
................
Answer:
It happens because "Write" method was called only once. Associative tree
optimization was performed before saving and it removed recognition data
from associative tree because must be more three hitting into associative base.
If number of hitting less then associative base remove during optimization process.
I modified interface of "Save" method:
// Saving of recognition data
bool Save(char* aFileName, bool aSaveWithoutOptimization = false);
The program works properly in this case:
..................
// Learn and save
am->SetImage(img1);
am->Write(rect, &id);
// Save without optimization
am->Save("test.model", true);
..................
Question: If interest rectangle in the Write method has different aspect ratio
from the key image rectangle, what happens, does it changes aspect ratio of
input rectangle to fit the key image rectangle, and how this behavior
affects recognition?
Answer: The key image size of associative video memory and size of interest area for "Write"
method should be equal for effective learning process and it is necessary that the aspect
ratio must be the same as in key image size otherwise it not will be able to give effective
learning with different aspect ratio. For recognition in "Read" method the interest area can
has any scaling (it is desirable from 50% and higher) and any aspect ratio.
Question: But in this case there will be difficulties with learning objects of
different sizes as it is in the Amsterdam database.
Of course I can use the biggest rectangle found between the training images,
but how it will affects recognition quality of small objects?
Answer:
I propose another method for evaluation associative video memory on image data base.
The strategy conclude in fixing of key image size and using different interest area
sizes for "Write" and "Read" method (interest area size for write/read is identical).
You can use image key size parameter as tool for control of recognition capacity.
The key image size and recognition accuracy is depended as the biger, the accurater.
But a key image size must be less than an interest area size.
The key image size is depended on sequence 40, 80, 160, 320, 640, 1280... 2^n*10.
Use similar combinations for aspect ratio control:
80x80 - 1:1; 320x320 - 1:1; 80x160 - 1:2; 640x160 - 4:1; ...
Image key size |
Associative levels |
80x80 | 3 |
160x160 | 4 |
320x320 | 5 |
640x640 | 6 |
... | ... |
80x160 | 3 |
640x160 | 4 |
... | ... |
Object number |
Interest area size |
Optimal image key size |
4 | 314x327 | 320x320 |
15 | 500x380 |
18 | 600x500 |
44 | 500x380 |
Command line example for "Similarity test" application:
SimilarityTest.exe -DBPath(../../TestPhoto) -Obj(4) -Rect(223, 211, 314, 327)
SimilarityTest.exe -DBPath(../../TestPhoto) -Obj(15) -Rect(100,150,500,380)
SimilarityTest.exe -DBPath(../../TestPhoto) -Obj(18) -Rect(100, 50, 600, 500)
SimilarityTest.exe -DBPath(../../TestPhoto) -Obj(44) -Rect(100,150,500,380)
Modification in SimilarityTest.cpp file:
Previous text version:
.....................
sprintf(OutStr, "Object number: %lu, image key size: %dx%d pixels",
ObjNumber, InterestArea.width, InterestArea.height);
.....................
CvAssociativeMemory32S* am = new CvAssociativeMemory32S;
am->Create(cvSize(InterestArea.width, InterestArea.height));
.....................
Text after modification:
.....................
sprintf(OutStr, "Object number: %lu, interest area size: %dx%d",
ObjNumber, InterestArea.width, InterestArea.height);
.....................
CvAssociativeMemory32S* am = new CvAssociativeMemory32S;
am->Create(cvSize(320, 320)); // Set a fixed key size
.....................
I made some diagrams for comparing:
|
<- key size 640x640; |
|
<- key size 320x320; |
|
<- key size 160x160; |
|
<- key size 80x80. |
Question: I set the same quadratic region for several different object, however they
are not recognized. Could you check what is the problem.
#include "stdafx.h"
#include "cv.h"
#include "highgui.h"
#include "AssociativeMemory.h"
void TestAVM()
{
CvRect rect = cvRect(223, 211, 315, 315);
CvRect rectFound;
long id;
IplImage *img1 = cvLoadImage("4_i110.png", 0);
IplImage *img2 = cvLoadImage("4_i120.png", 0);
IplImage *img1a = cvLoadImage("32_i110.png", 0);
IplImage *img2a = cvLoadImage("32_i120.png", 0);
IplImage *img1b = cvLoadImage("35_i110.png", 0);
IplImage *img2b = cvLoadImage("35_i120.png", 0);
// Create
CvAssociativeMemory32S *am = new CvAssociativeMemory32S;
am->Create(cvSize(315, 315));
// Learn
am->SetImage(img1);
id = 0;
am->Write(rect, &id);
am->SetImage(img1a);
id = 1;
am->Write(rect, &id);
am->SetImage(img1b);
id = 2;
am->Write(rect, &id);
am->Save("test.model", true);
// Destroy recreate and load
delete am;
am = new CvAssociativeMemory32S;
bool res;
if((res = am->Load("test.model"))) {
printf("\nLoad associative bases from file: %s\n"
"Associative bases ......... %-7lu\n"
"Associative Levels ........ %-7u\n"
"Current base index ........ %-7I64u\n"
"Wr/Rd counter ............. %-7I64u\n\n",
"test.model", am->GetTotalABases(),
am->GetTotalLevels(), am->GetCurIndex(), am->GetWrRdCounter());
}
// Recognize
am->SetImage(img2);
long* pId;
double Similarity;
rect = cvRect(0, 0, img2->width, img2->height);
if(am->Read(rect, &rectFound, &pId, NULL, NULL, &Similarity, true)) {
printf("Object recognized, rectFound(%d, %d, %d, %d)\n",
rectFound.x, rectFound.y, rectFound.width, rectFound.height);
}
printf("Id = %lu, Similarity = %f or %lu\n", *pId, Similarity, long(Similarity*10000));
am->SetImage(img2a);
rect = cvRect(0, 0, img2a->width, img2a->height);
if(am->Read(rect, &rectFound, &pId, NULL, NULL, &Similarity, true)) {
printf("Object recognized, rectFound(%d, %d, %d, %d)\n",
rectFound.x, rectFound.y, rectFound.width, rectFound.height);
}
printf("Id = %lu, Similarity = %f or %lu\n", *pId, Similarity, long(Similarity*10000));
am->SetImage(img2b);
rect = cvRect(0, 0, img2a->width, img2a->height);
if(am->Read(rect, &rectFound, &pId, NULL, NULL, &Similarity, true)) {
printf("Object recognized, rectFound(%d, %d, %d, %d)\n",
rectFound.x, rectFound.y, rectFound.width, rectFound.height);
}
printf("Id = %lu, Similarity = %f or %lu\n", *pId, Similarity, long(Similarity*10000));
cvReleaseImage(&img1);
cvReleaseImage(&img2);
cvReleaseImage(&img1a);
cvReleaseImage(&img2a);
cvReleaseImage(&img1b);
cvReleaseImage(&img2b);
delete am;
}
int _tmain(int argc, _TCHAR* argv[])
{
TestAVM();
return 0;
}
Answer: The Interest area for Write/Read methods is used as key for data access.
It is low level operation and "Read" method can give answer: Is it an object
in interest area or not? But for searching in big image it must be performed
by a multiple calling of "Read" method with different interest area value
(or by calling "ObjectRecognition" method). I modified program text and now
it works correctly.
#include "stdafx.h"
#include "cv.h"
#include "highgui.h"
#include "AssociativeMemory.h"
void doSearchObject(CvAM_State aState, long* apData, CvAM_Parameters* apParam, void* args)
{
switch (aState)
{
case cProcessingIsBegun:
printf("== Begin ==\n");
break;
case cUnknownObject:
//printf("Unknown, Id = %lu, Similarity = %ld\n", *apData, long(apParam->Similarity*10000));
break;
case cRecognizedObject:
printf("Recognized, Id = %lu, Similarity = %ld, rectFound(%d, %d, %d, %d)\n",
*apData, long(apParam->Similarity*10000),
apParam->ObjRect.x, apParam->ObjRect.y,
apParam->ObjRect.width, apParam->ObjRect.height);
break;
case cGeneralizedObject:
case cTrackedObject:
printf("Tracked/Generalized, Id = %lu, Similarity = %ld, rectFound(%d, %d, %d, %d)\n",
*apData, long(apParam->Similarity*10000),
apParam->ObjRect.x, apParam->ObjRect.y,
apParam->ObjRect.width, apParam->ObjRect.height);
break;
case cLearnThisObject:
printf("Learning\n");
break;
case cProcessingIsFinished:
printf("-- Finish --\n");
break;
}
}
void SimplyRecognition(CvAssociativeMemory32S* pAM, CvSize aInputImgSize, CvRcgAMFunc32S RcgAMFunc) {
if(pAM && RcgAMFunc) {
CvRect sRect;int i;
printf("<***> Simply recognition -> begin\n");
for(i=0;;i++) {
float Rate = 0.75f + 0.25f*float(i);
int SearchStep = int(15.0f*Rate);
CvSize KeyImgSize = pAM->GetKeyImageSize();
sRect.width = int(KeyImgSize.width*Rate);
sRect.height = int(KeyImgSize.height*Rate);
if((aInputImgSize.width < sRect.width) || (aInputImgSize.height < sRect.height)) break;
int eX = aInputImgSize.width - sRect.width;
int eY = aInputImgSize.height - sRect.height;
for(int y=0; y < eY; y += SearchStep) {
for(int x=0; x < eX; x += SearchStep) {
sRect.x = x;
sRect.y = y;
long* pData;
CvAM_Parameters Param;
Param.ppUserTrackingInfo = NULL;
Param.TotalABases = pAM->GetTotalABases();
if(pAM->Read(sRect, &Param.ObjRect, &pData, NULL, NULL, &Param.Similarity)) {
RcgAMFunc(cRecognizedObject, pData, &Param, NULL);
}
}
}
}
printf("<***> Simply recognition -> finish\n\n");
}
}
void TestAVM()
{
CvRect rect = cvRect(223, 211, 315, 315);
CvRect rectFound;
long id;
IplImage *img1 = cvLoadImage("4_i110.png", 0);
IplImage *img2 = cvLoadImage("4_i120.png", 0);
IplImage *img1a = cvLoadImage("32_i110.png", 0);
IplImage *img2a = cvLoadImage("32_i120.png", 0);
IplImage *img1b = cvLoadImage("35_i110.png", 0);
IplImage *img2b = cvLoadImage("35_i120.png", 0);
// Create
CvAssociativeMemory32S *am = new CvAssociativeMemory32S;
am->Create(cvSize(315, 315));
// Learn
am->SetImage(img1);
id = 0;
am->Write(rect, &id);
am->SetImage(img1a);
id = 1;
am->Write(rect, &id);
am->SetImage(img1b);
id = 2;
am->Write(rect, &id);
am->Save("test.model", true);
// Destroy recreate and load
delete am;
am = new CvAssociativeMemory32S;
bool res;
if((res = am->Load("test.model"))) {
printf("\nLoad associative bases from file: %s\n"
"Associative bases ......... %-7lu\n"
"Associative Levels ........ %-7u\n"
"Current base index ........ %-7I64u\n"
"Wr/Rd counter ............. %-7I64u\n\n",
"test.model", am->GetTotalABases(),
am->GetTotalLevels(), am->GetCurIndex(), am->GetWrRdCounter());
}
// Recognize
printf("Interest area rect(%d, %d, %d, %d)\n\n", rect.x, rect.y, rect.width, rect.height);
am->SetImage(img2);
long* pId;
double Similarity;
//!!! rect = cvRect(0, 0, img2->width, img2->height);
if(am->Read(rect, &rectFound, &pId, NULL, NULL, &Similarity, true)) {
printf("Object recognized, rectFound(%d, %d, %d, %d)\n",
rectFound.x, rectFound.y, rectFound.width, rectFound.height);
}
printf("Id = %lu, Similarity = %f or %lu\n", *pId, Similarity, long(Similarity*10000));
am->ObjectRecognition(doSearchObject , 0);
SimplyRecognition(am, cvSize(img2->width, img2->height), doSearchObject);
am->SetImage(img2a);
//!!! rect = cvRect(0, 0, img2a->width, img2a->height);
if(am->Read(rect, &rectFound, &pId, NULL, NULL, &Similarity, true)) {
printf("Object recognized, rectFound(%d, %d, %d, %d)\n",
rectFound.x, rectFound.y, rectFound.width, rectFound.height);
}
printf("Id = %lu, Similarity = %f or %lu\n", *pId, Similarity, long(Similarity*10000));
am->ObjectRecognition(doSearchObject , 0);
SimplyRecognition(am, cvSize(img2a->width, img2a->height), doSearchObject);
am->SetImage(img2b);
///!!! rect = cvRect(0, 0, img2b->width, img2b->height);
if(am->Read(rect, &rectFound, &pId, NULL, NULL, &Similarity, true)) {
printf("Object recognized, rectFound(%d, %d, %d, %d)\n",
rectFound.x, rectFound.y, rectFound.width, rectFound.height);
}
printf("Id = %lu, Similarity = %f or %lu\n", *pId, Similarity, long(Similarity*10000));
am->ObjectRecognition(doSearchObject , 0);
SimplyRecognition(am, cvSize(img2b->width, img2b->height), doSearchObject);
cvReleaseImage(&img1);
cvReleaseImage(&img2);
cvReleaseImage(&img1a);
cvReleaseImage(&img2a);
cvReleaseImage(&img1b);
cvReleaseImage(&img2b);
delete am;
}
int _tmain(int argc, _TCHAR* argv[])
{
TestAVM();
return 0;
}
=====================================================================
Results:
Load associative bases from file: test.model
Associative bases ......... 12
Associative Levels ........ 4
Current base index ........ 12
Wr/Rd counter ............. 3
Interest area rect(223, 211, 315, 315)
Object recognized, rectFound(254, 240, 259, 259)
Id = 0, Similarity = 0.680402 or 6804
== Begin ==
Recognized, Id = 0, Similarity = 6630, rectFound(251, 239, 259, 259)
Tracked/Generalized, Id = 0, Similarity = 6630, rectFound(251, 239, 259, 259)
-- Finish --
<***> Simply recognition -> begin
Recognized, Id = 0, Similarity = 6402, rectFound(250, 240, 259, 259)
Recognized, Id = 0, Similarity = 6402, rectFound(255, 240, 259, 259)
Recognized, Id = 0, Similarity = 6630, rectFound(251, 239, 259, 259)
Recognized, Id = 0, Similarity = 6630, rectFound(256, 239, 259, 259)
Recognized, Id = 0, Similarity = 7236, rectFound(253, 240, 259, 259)
Recognized, Id = 0, Similarity = 7475, rectFound(256, 240, 259, 259)
Recognized, Id = 0, Similarity = 7861, rectFound(255, 240, 259, 259)
<***> Simply recognition -> finish
Object recognized, rectFound(254, 240, 259, 259)
Id = 1, Similarity = 0.673710 or 6737
== Begin ==
Recognized, Id = 1, Similarity = 6746, rectFound(251, 239, 259, 259)
Recognized, Id = 1, Similarity = 7351, rectFound(255, 239, 259, 259)
Tracked/Generalized, Id = 1, Similarity = 7048, rectFound(253, 239, 259, 259)
-- Finish --
<***> Simply recognition -> begin
Recognized, Id = 1, Similarity = 6437, rectFound(255, 240, 259, 259)
Recognized, Id = 1, Similarity = 6746, rectFound(251, 239, 259, 259)
Recognized, Id = 1, Similarity = 6746, rectFound(256, 239, 259, 259)
Recognized, Id = 1, Similarity = 7351, rectFound(255, 239, 259, 259)
Recognized, Id = 1, Similarity = 7445, rectFound(253, 240, 259, 259)
Recognized, Id = 1, Similarity = 6862, rectFound(252, 240, 259, 259)
Recognized, Id = 1, Similarity = 7702, rectFound(255, 240, 259, 259)
<***> Simply recognition -> finish
Id = 2, Similarity = 0.412393 or 4123
== Begin ==
Recognized, Id = 2, Similarity = 6666, rectFound(277, 239, 259, 259)
Tracked/Generalized, Id = 2, Similarity = 6666, rectFound(277, 239, 259, 259)
-- Finish --
<***> Simply recognition -> begin
Recognized, Id = 2, Similarity = 6970, rectFound(256, 239, 259, 259)
Recognized, Id = 2, Similarity = 6666, rectFound(277, 239, 259, 259)
<***> Simply recognition -> finish