/******************************************************************************
imgSeek :: C++ database implementation
---------------------------------------
begin : Fri Jan 17 2003
email : nieder|at|mail.ru
Copyright (C) 2003 Ricardo Niederberger Cabral
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
******************************************************************************/
/* STL Includes */
#include <map>
#include <queue>
#include <list>
#include <fstream>
#include <iostream>
// NOTE: when running build-ext.sh (auto swig wrappers) this namespace line has to be commented
using namespace std;
/* imgSeek includes */
#include "haar.h"
/* Database */
#include "imgdb.h"
/* C Includes */
#include <math.h>
#include <stdio.h>
#include "EXTERN.h"
#include "perl.h"
void cleardb() {
for (sigIterator it = sigs.begin(); it != sigs.end(); it++) {
free((*it).second->sig1);
free((*it).second->sig2);
free((*it).second->sig3);
free((*it).second->avgl);
delete (*it).second;
}
for (int c = 0;c<3;c++) for (int pn=0;pn<2;pn++)
for (int i = 0;i<16384;i++) {
imgbuckets[c][pn][i].clear();
}
}
int addImage(const long int id, unsigned char* red, unsigned char* green, unsigned char* blue) {
/* id is a unique image identifier
filename is the image location
thname is the thumbnail location for this image
doThumb should be set to 1 if you want to save the thumbnail on thname
Images with a dimension smaller than ignDim are ignored
*/
double* avgl = (double*)Perl_safesysmalloc(3*sizeof(double));
int* sig1;
int* sig2;
int* sig3;
double* cdata1, * cdata2, * cdata3;
int i;
New(200, cdata1, 16384, double);
New(200, cdata2, 16384, double);
New(200, cdata3, 16384, double);
New(200, sig1, 40, int);
New(200, sig2, 40, int);
New(200, sig3, 40, int);
sigStruct* nsig = new sigStruct();
nsig->sig1 = sig1;
nsig->sig2 = sig2;
nsig->sig3 = sig3;
nsig->avgl = avgl;
nsig->id = id;
transformChar(red, green, blue, cdata1,cdata2,cdata3);
sigs[id] = nsig;
calcHaar(cdata1,cdata2,cdata3,sig1,sig2,sig3,avgl);
for (i = 0;i<40;i++) { // populate buckets
if (sig1[i]>0) imgbuckets[0][0][sig1[i]].push_back(id);
if (sig1[i]<0) imgbuckets[0][1][-sig1[i]].push_back(id);
if (sig2[i]>0) imgbuckets[1][0][sig2[i]].push_back(id);
if (sig2[i]<0) imgbuckets[1][1][-sig2[i]].push_back(id);
if (sig3[i]>0) imgbuckets[2][0][sig3[i]].push_back(id);
if (sig3[i]<0) imgbuckets[2][1][-sig3[i]].push_back(id);
}
free(cdata1);
free(cdata2);
free(cdata3);
return 1;
}
/* Data:
buckets[3][2][16835]
sigs (hash)
*/
int loaddb(char* filename) {
std::ifstream f(filename, ios::binary);
if (!f.is_open()) return 0;
int sz,coef,c,k;
long int id;
// read buckets
for ( c = 0;c<3;c++) for (int pn=0;pn<2;pn++)
for (int i = 0;i<16384;i++) {
f.read ((char*)&(sz), sizeof(int) );
for ( k = 0;k<sz;k++) {
f.read ((char*)&(id), sizeof(long int) );
imgbuckets[c][pn][i].push_back(id);
}
}
// read sigs
f.read ((char*)&(sz), sizeof(int) );
for ( k = 0;k<sz;k++) {
f.read ((char*)&(id), sizeof(long int) );
sigs[id] = new sigStruct();
sigs[id]->id = id;
sigs[id]->sig1 = (int*)(Perl_safesysmalloc(40*sizeof(int)));
sigs[id]->sig2 = (int*)(Perl_safesysmalloc(40*sizeof(int)));
sigs[id]->sig3 = (int*)(Perl_safesysmalloc(40*sizeof(int)));
sigs[id]->avgl = (double*)Perl_safesysmalloc(3*sizeof(double));
// sig
for ( c = 0;c<40;c++) {
f.read ((char*)&(coef), sizeof( int) );
sigs[id]->sig1[c] = coef;
f.read ((char*)&(coef), sizeof( int) );
sigs[id]->sig2[c] = coef;
f.read ((char*)&(coef), sizeof( int) );
sigs[id]->sig3[c] = coef;
}
// avgl
for ( c = 0;c<3;c++) {
f.read ((char*)&(sigs[id]->avgl[c]), sizeof( double) );
}
}
f.close();
return 1;
}
int savedb(char* filename) {
/*
Serialization order:
for each color {0,1,2}:
for {positive,negative}:
for each 128x128 coefficient {0-16384}:
[int] bucket size (size of list of ids)
for each id:
[long int] image id
[int] number of images (signatures)
for each image:
[long id] image id
for each sig coef {0-39}: (the 40 greatest coefs)
for each color {0,1,2}:
[int] coef index (signed)
for each color {0,1,2}:
[double] average luminance
[int] image height
*/
std::ofstream f(filename, ios::binary);
if (!f.is_open()) return 0;
int sz,c;
long int id;
// save buckets
for ( c = 0;c<3;c++) for (int pn=0;pn<2;pn++)
for (int i = 0;i<16384;i++) {
sz = imgbuckets[c][pn][i].size();
f.write((char*)&(sz), sizeof(int) );
for (long_listIterator it = imgbuckets[c][pn][i].begin(); it != imgbuckets[c][pn][i].end(); it++) {
f.write ((char*)&((*it)), sizeof(long int) );
}
}
// save sigs
sz = sigs.size();
f.write ((char*)&(sz), sizeof(int) );
for (sigIterator it = sigs.begin(); it != sigs.end(); it++) {
id = (*it).first;
f.write ((char*)&(id), sizeof(long int));
// sigs
for ( c = 0;c<40;c++) {
f.write ((char*)&((*it).second->sig1[c]), sizeof( int));
f.write ((char*)&((*it).second->sig2[c]), sizeof( int));
f.write ((char*)&((*it).second->sig3[c]), sizeof( int));
}
// avgl
for ( c = 0;c<3;c++)
f.write ((char*)&((*it).second->avgl[c]), sizeof(double));
}
f.close();
return 1;
}
/* sig1,2,3 are int arrays of lenght 40
avgl is the average luminance
numres is the max number of results
scanned tells which set of weights to use
*/
void queryImgData(int* sig1,int* sig2,int* sig3,double * avgl,int numres,int scanned) {
int idx,c;
bool pn;
int * sig[3] = {sig1,sig2,sig3};
int nres = numres+1;
int i,j;
if (!imgBin[0]) {
for (i = 0;i<128;i++) for (j=0;j<128;j++) imgBin[i*128+j] = min(max(i,j),5);
}
for (sigIterator sit = sigs.begin(); sit!=sigs.end(); sit++) { //#TODO3: do I really need to score every single sig on db?
(*sit).second->score = 0;
for (c = 0; c<3; c++) {
(*sit).second->score += weights[scanned][0][c]*fabs((*sit).second->avgl[c]-avgl[c]);
}
}
for (int b = 0;b<40;b++) { // for every coef on a sig
for ( c = 0;c<3;c++) {
pn = 0;
if (sig[c][b]>0) {
pn = 0;
idx = sig[c][b];
} else {
pn = 1;
idx = -sig[c][b];
}
// update the score of every image which has this coef
for (long_listIterator uit = imgbuckets[c][pn][idx].begin(); uit != imgbuckets[c][pn][idx].end(); uit++) {
sigs[(*uit)]->score -= weights[scanned][imgBin[idx]][c];
}
}
}
while(!pqResults.empty()) pqResults.pop(); // make sure pqResults is empty. TODO: any faster way to empty it ? didn't find any on STL refs.
int cnt = 0;
for (sigIterator it = sigs.begin(); it != sigs.end(); it++) {
cnt++;
pqResults.push(*(*it).second);
if (cnt>nres) {
pqResults.pop();
}
}
}
void queryImgID(long int id,int numres) {
if (!sigs.count(id)) {
return;
}
queryImgData(sigs[id]->sig1,sigs[id]->sig2,sigs[id]->sig3,sigs[id]->avgl, numres, 0);
}