00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #ifndef NNCLASSIFIER_H
00020 #define NNCLASSIFIER_H
00021
00022
00023
00024 #include <vector>
00025 #include "Matrix.h"
00026 #include "Histogram.h"
00027
00028 namespace motld {
00030 class NNPatch
00031 {
00032 public:
00034 Matrix patch;
00036 float avg;
00038 float norm2;
00040 float* histogram;
00042 NNPatch() : patch(), avg(0), norm2(1), histogram(NULL){}
00044 NNPatch(const NNPatch& copyFrom);
00046 NNPatch(const Matrix& curPatch);
00048 NNPatch(const Matrix& curPatch, const ObjectBox& bbox,
00049 const unsigned char * rgb = NULL, const int w = 0, const int h = 0);
00051 NNPatch(const ObjectBox& bbox, const Matrix& curImage, const int patchSize,
00052 const unsigned char * rgb = NULL, const int w = 0, const int h = 0);
00054 NNPatch(std::ifstream & inputStream, const int patchSize);
00056 ~NNPatch();
00058 NNPatch& operator=(const NNPatch& copyFrom);
00060 void saveToStream(std::ofstream & outputStream) const;
00061 };
00062
00065 class NNClassifier
00066 {
00067 public:
00069 NNClassifier(int width, int height, int patchSize, bool useColor = true, bool allowFastChange = false);
00071 NNClassifier(std::ifstream & inputStream);
00073 double getConf(const NNPatch& patch, int objId = 0, bool conservative = false) const;
00075 double getConf(NNPatch& patch, int objId, bool conservative,
00076 const ObjectBox& bbox, const unsigned char * rgb, int w, int h) const;
00078 bool trainNN(const NNPatch& patch, int objId = 0, bool positive = true, bool tmp = false);
00080 void addObject(const NNPatch& patch);
00082 const std::vector<std::vector<NNPatch> > * getPosPatches() const;
00084 const std::vector<NNPatch> * getNegPatches() const;
00086 void removeWarps();
00088 void saveToStream(std::ofstream & outputStream) const;
00089
00090 private:
00091 int ivWidth;
00092 int ivHeight;
00093 int ivPatchSize;
00094 static Histogram * ivHistogram;
00095 std::vector<std::vector<NNPatch> > ivPosPatches;
00096 std::vector<NNPatch> ivNegPatches;
00097 std::vector<char> ivWarpIndices;
00098 bool ivUseColor, ivAllowFastChange;
00099 double getConf(const float* patch, float norm2 = 1.0f, int objId = 0, bool conservative = false) const;
00100 double crossCorr(const float* patchA, const float* patchB, float denom = 1) const;
00101 double cmpHistograms(const float* h1, const float* h2) const;
00102 };
00103
00104
00105
00106
00107
00108
00110
00111
00112 NNPatch::NNPatch(const NNPatch& copyFrom)
00113 {
00114 patch = Matrix(copyFrom.patch);
00115 avg = copyFrom.avg;
00116 norm2 = copyFrom.norm2;
00117 if(copyFrom.histogram != NULL)
00118 {
00119 histogram = new float[NUM_BINS];
00120 memcpy(histogram, copyFrom.histogram, NUM_BINS * sizeof(float));
00121
00122
00123 }else
00124 histogram = NULL;
00125 }
00126
00127 NNPatch::NNPatch(const Matrix& curPatch)
00128 {
00129 patch = curPatch;
00130 avg = patch.avg();
00131 patch += -avg;
00132 norm2 = patch.norm2();
00133 histogram = NULL;
00134 }
00135
00136 NNPatch::NNPatch(const Matrix& curPatch, const ObjectBox& bbox,
00137 const unsigned char * rgb, const int w, const int h)
00138 {
00139 patch = curPatch;
00140 avg = patch.avg();
00141 patch += -avg;
00142 norm2 = patch.norm2();
00143 histogram = (rgb == NULL ? NULL
00144 : Histogram::getInstance()->getColorDistribution(rgb, w, h, bbox));
00145 }
00146
00147 NNPatch::NNPatch(const ObjectBox& bbox, const Matrix& curImage, const int patchSize,
00148 const unsigned char * rgb, const int w, const int h)
00149 {
00150 patch = curImage.getRectSubPix(bbox.x + 0.5 * bbox.width, bbox.y + 0.5 * bbox.height,
00151 round(bbox.width), round(bbox.height));
00152 patch.rescale(patchSize, patchSize);
00153 avg = patch.avg();
00154 patch += -avg;
00155 norm2 = patch.norm2();
00156 histogram = (rgb == NULL ? NULL
00157 : Histogram::getInstance()->getColorDistribution(rgb, w, h, bbox));
00158 }
00159
00160 NNPatch::NNPatch(std::ifstream & inputStream, const int patchSize)
00161 {
00162 patch = Matrix(patchSize, patchSize);
00163 inputStream.read((char*)patch.data(), patchSize*patchSize*sizeof(float));
00164 inputStream.read((char*)&avg, sizeof(float));
00165 inputStream.read((char*)&norm2, sizeof(float));
00166 bool hist;
00167 inputStream.read((char*)&hist, sizeof(bool));
00168 if(hist){
00169 histogram = new float[NUM_BINS];
00170 inputStream.read((char*)histogram, NUM_BINS*sizeof(float));
00171 }else
00172 histogram = NULL;
00173 }
00174
00175 void NNPatch::saveToStream(std::ofstream & outputStream) const
00176 {
00177 outputStream.write((char*)patch.data(), patch.size()*sizeof(float));
00178 outputStream.write((char*)&avg, sizeof(float));
00179 outputStream.write((char*)&norm2, sizeof(float));
00180 bool hist = histogram != NULL;
00181 outputStream.write((char*)&hist, sizeof(bool));
00182 if(hist)
00183 outputStream.write((char*)histogram, NUM_BINS*sizeof(float));
00184 }
00185
00186 NNPatch::~NNPatch()
00187 {
00188 if(histogram != NULL)
00189 delete [] histogram;
00190 histogram = NULL;
00191 }
00192
00193 NNPatch& NNPatch::operator=(const NNPatch& copyFrom)
00194 {
00195 if (this != ©From) {
00196 patch = Matrix(copyFrom.patch);
00197 avg = copyFrom.avg;
00198 norm2 = copyFrom.norm2;
00199 if(copyFrom.histogram != NULL)
00200 {
00201 if(histogram != NULL)
00202 delete [] histogram;
00203 histogram = new float[NUM_BINS];
00204 memcpy(histogram, copyFrom.histogram, NUM_BINS * sizeof(float));
00205 }else
00206 histogram = NULL;
00207 }
00208 return *this;
00209 }
00210
00212
00213
00214 Histogram * NNClassifier::ivHistogram = Histogram::getInstance();
00215
00216 NNClassifier::NNClassifier(int width, int height, int patchSize, bool useColor, bool allowFastChange)
00217 : ivWidth(width), ivHeight(height), ivPatchSize(patchSize),
00218 ivUseColor(useColor), ivAllowFastChange(allowFastChange) {}
00219
00220 NNClassifier::NNClassifier(std::ifstream & inputStream)
00221 {
00222 inputStream.read((char*)&ivWidth, sizeof(int));
00223 inputStream.read((char*)&ivHeight, sizeof(int));
00224 inputStream.read((char*)&ivPatchSize, sizeof(int));
00225 inputStream.read((char*)&ivUseColor, sizeof(bool));
00226 inputStream.read((char*)&ivAllowFastChange, sizeof(bool));
00227 int nNeg, nObs, nPos;
00228 inputStream.read((char*)&nNeg, sizeof(int));
00229 for(int i = 0; i < nNeg; ++i)
00230 ivNegPatches.push_back(NNPatch(inputStream, ivPatchSize));
00231 inputStream.read((char*)&nObs, sizeof(int));
00232 ivPosPatches = std::vector<std::vector<NNPatch> >(nObs);
00233 for(int i = 0; i < nObs; ++i)
00234 {
00235 inputStream.read((char*)&nPos, sizeof(int));
00236 for(int j = 0; j < nPos; ++j)
00237 ivPosPatches[i].push_back(NNPatch(inputStream, ivPatchSize));
00238 }
00239 }
00240
00241 const std::vector<std::vector<NNPatch> > * NNClassifier::getPosPatches() const
00242 {
00243 return &ivPosPatches;
00244 }
00245 const std::vector<NNPatch> * NNClassifier::getNegPatches() const
00246 {
00247 return &ivNegPatches;
00248 }
00249
00250 void NNClassifier::removeWarps()
00251 {
00252 for(unsigned int p = 0; p < ivPosPatches.size(); p++)
00253 if(ivWarpIndices[p] > 0)
00254 {
00255 ivPosPatches[p].erase(ivPosPatches[p].end() - ivWarpIndices[p], ivPosPatches[p].end());
00256 ivWarpIndices[p] = 0;
00257 }
00258 }
00259
00260 void NNClassifier::addObject(const NNPatch& patch)
00261 {
00262 ivPosPatches.push_back(std::vector<NNPatch>());
00263 ivPosPatches[ivPosPatches.size() - 1].push_back(patch);
00264 ivWarpIndices.push_back(0);
00265
00266 for(int i = ivNegPatches.size() - 1; i >= 0; i--)
00267 {
00268 double ncc = crossCorr(ivNegPatches[i].patch.data(), patch.patch.data(),
00269 ivNegPatches[i].norm2 * patch.norm2);
00270 if(ncc > 0.8){
00271 ivNegPatches.erase(ivNegPatches.begin() + i);
00272 #if DEBUG
00273 std::cout << "removed negative patch " << i << " (ncc = " << ncc << ")" << std::endl;
00274 #endif
00275 }
00276 }
00277 }
00278
00284 bool NNClassifier::trainNN(const NNPatch& patch, int objId, bool positive, bool tmp)
00285 {
00286 double conf = getConf(patch, positive ? objId : -1, false);
00287 if(positive)
00288 {
00289 if(conf < 0.75)
00290 {
00291 ivPosPatches[objId].push_back(patch);
00292 if(tmp)
00293 ivWarpIndices[objId]++;
00294 return true;
00295 }
00296 }
00297 else if(conf < 0.85)
00298 {
00299 ivNegPatches.push_back(patch);
00300 return true;
00301 }
00302 return false;
00303 }
00304
00308 double NNClassifier::getConf(const NNPatch& patch, int objId, bool conservative) const
00309 {
00310 if(ivUseColor)
00311 {
00312 double conf = getConf(patch.patch.data(), patch.norm2, objId, conservative);
00313 if(objId < 0 || conf < 0.58 || patch.histogram == NULL || ivPosPatches[objId][0].histogram == NULL)
00314 return conf;
00315 double colorCons = cmpHistograms(ivPosPatches[objId][0].histogram, patch.histogram);
00316
00317
00318 if(colorCons < 0.75)
00319 return conf * 0.8;
00320 return conf + (1-conf) * 0.4;
00321 }
00322 return getConf(patch.patch.data(), patch.norm2, objId, conservative);
00323 }
00324
00326 double NNClassifier::getConf(NNPatch& patch, int objId, bool conservative,
00327 const ObjectBox& bbox, const unsigned char * rgb, int w, int h) const
00328 {
00329 if(ivUseColor)
00330 {
00331 double conf = getConf(patch.patch.data(), patch.norm2, objId, conservative);
00332 if(objId < 0 || conf < 0.58 || ivPosPatches[objId][0].histogram == NULL)
00333 return conf;
00334 else{
00335 if(patch.histogram == NULL && rgb != NULL)
00336 patch.histogram = Histogram::getInstance()->getColorDistribution(rgb, w, h, bbox);
00337 if(patch.histogram != NULL)
00338 {
00339 double colorCons = cmpHistograms(ivPosPatches[objId][0].histogram, patch.histogram);
00340
00341
00342 if(colorCons < 0.75)
00343 return conf * 0.8;
00344 return conf + (1-conf) * 0.4;
00345 }
00346 return conf;
00347 }
00348 }
00349 return getConf(patch.patch.data(), patch.norm2, objId, conservative);
00350 }
00351
00352 double NNClassifier::getConf(const float* patch, float norm2, int objId, bool conservative) const
00353 {
00354
00355 double posNCC = 0;
00356 if (objId >= 0)
00357 {
00358 int nPos = ivPosPatches[objId].size();
00359 double* posNCCs = new double[nPos];
00360 #pragma omp parallel for
00361 for (int i = 0; i < nPos; i++)
00362 {
00363 posNCCs[i] = crossCorr(ivPosPatches[objId][i].patch.data(), patch, (ivPosPatches[objId][i].norm2 * norm2));
00364 if (!ivAllowFastChange && conservative && i > nPos/2)
00365 posNCCs[i] *= 1.0 - 0.05 * (i-nPos/2) / (double)nPos;
00366 }
00367 for (int i = 0; i < nPos; i++)
00368 if (posNCCs[i] > posNCC)
00369 posNCC = posNCCs[i];
00370 delete[] posNCCs;
00371 }
00372
00373 double negNCC = 0;
00374 int nNeg = ivNegPatches.size();
00375 if (nNeg)
00376 {
00377 double* negNCCs = new double[nNeg];
00378 #pragma omp parallel for
00379 for (int i = 0; i < nNeg; i++)
00380 negNCCs[i] = crossCorr(ivNegPatches[i].patch.data(), patch, (ivNegPatches[i].norm2 * norm2));
00381 for (int i = 0; i < nNeg; i++)
00382 if (negNCCs[i] > negNCC)
00383 negNCC = negNCCs[i];
00384 delete[] negNCCs;
00385 }else
00386 negNCC = 0.3;
00387
00388 if(objId < 0)
00389 return negNCC;
00390 return (1 - negNCC) / (2 - negNCC - posNCC);
00391 }
00392
00393 double NNClassifier::crossCorr(const float* patchA, const float* patchB, float denom) const
00394 {
00395 double sumDiff = 0;
00396 for (int i = 0; i < ivPatchSize*ivPatchSize; ++i)
00397 sumDiff += patchA[i] * patchB[i];
00398 if (denom <= 0)
00399 return sumDiff;
00400 return (sumDiff/sqrt(denom) + 1) / 2.0;
00401 }
00402
00403 #define INVBINS (1.0/NUM_BINS)
00404 double NNClassifier::cmpHistograms(const float* h1, const float* h2) const
00405 {
00406 double corr = 0;
00407 double norm1 = 0;
00408 double norm2 = 0;
00409 for (int i = 0; i < NUM_BINS; ++i)
00410 {
00411 corr += (h1[i]-INVBINS) * (h2[i]-INVBINS);
00412 norm1 += (h1[i]-INVBINS) * (h1[i]-INVBINS);
00413 norm2 += (h2[i]-INVBINS) * (h2[i]-INVBINS);
00414 }
00415 return (corr / sqrt(norm1*norm2) + 1) / 2.0;
00416 }
00417
00418 void NNClassifier::saveToStream(std::ofstream & outputStream) const
00419 {
00420 outputStream.write((char*)&ivWidth, sizeof(int));
00421 outputStream.write((char*)&ivHeight, sizeof(int));
00422 outputStream.write((char*)&ivPatchSize, sizeof(int));
00423 outputStream.write((char*)&ivUseColor, sizeof(bool));
00424 outputStream.write((char*)&ivAllowFastChange, sizeof(bool));
00425
00426 int nNeg = ivNegPatches.size();
00427 outputStream.write((char*)&nNeg, sizeof(int));
00428 for(std::vector<NNPatch>::const_iterator it = ivNegPatches.begin(); it != ivNegPatches.end(); ++it)
00429 it->saveToStream(outputStream);
00430
00431 int nObs = ivPosPatches.size();
00432 outputStream.write((char*)&nObs, sizeof(int));
00433 for(std::vector<std::vector<NNPatch> >::const_iterator oit = ivPosPatches.begin(); oit != ivPosPatches.end(); ++oit)
00434 {
00435 int nPos = oit->size();
00436 outputStream.write((char*)&nPos, sizeof(int));
00437 for(std::vector<NNPatch>::const_iterator it = oit->begin(); it != oit->end(); ++it)
00438 it->saveToStream(outputStream);
00439 }
00440 }
00441
00442 }
00443
00444 #endif //NNCLASSIFIER_H