00001 /*!@file Features/JunctionHOG.C */ 00002 00003 00004 // //////////////////////////////////////////////////////////////////// // 00005 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005 // 00006 // by the University of Southern California (USC) and the iLab at USC. // 00007 // See http://iLab.usc.edu for information about this project. // 00008 // //////////////////////////////////////////////////////////////////// // 00009 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00010 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00011 // in Visual Environments, and Applications'' by Christof Koch and // 00012 // Laurent Itti, California Institute of Technology, 2001 (patent // 00013 // pending; application number 09/912,225 filed July 23, 2001; see // 00014 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00015 // //////////////////////////////////////////////////////////////////// // 00016 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00017 // // 00018 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00019 // redistribute it and/or modify it under the terms of the GNU General // 00020 // Public License as published by the Free Software Foundation; either // 00021 // version 2 of the License, or (at your option) any later version. // 00022 // // 00023 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00024 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00025 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00026 // PURPOSE. See the GNU General Public License for more details. // 00027 // // 00028 // You should have received a copy of the GNU General Public License // 00029 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00030 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00031 // Boston, MA 02111-1307 USA. // 00032 // //////////////////////////////////////////////////////////////////// // 00033 // 00034 // Primary maintainer for this file: Daniel Parks <danielfp@usc.edu> 00035 // $HeadURL$ 00036 // $Id$ 00037 // 00038 00039 #include "Features/JunctionHOG.H" 00040 #include "SIFT/FeatureVector.H" 00041 00042 JunctionHOG::JunctionHOG() : 00043 HistogramOfGradients(), 00044 itsNeighborDistance(0.0) 00045 { 00046 00047 } 00048 00049 JunctionHOG::JunctionHOG(bool normalize, Dims cellDims, bool fixedDims, int numOrientations, bool signedOrient, int numContinuityBins, float neighborDistance) : 00050 HistogramOfGradients(normalize,cellDims,fixedDims,numOrientations,signedOrient), 00051 itsContinuityBins(numContinuityBins), 00052 itsNeighborDistance(neighborDistance) 00053 { 00054 if(neighborDistance > 0) 00055 { 00056 for(int t=0;t<8;t++) 00057 { 00058 float ang=float(t)/8.0*M_PI; 00059 initializeNeighbors(itsRelevantNeighbors[t],itsPerpAngles[t],itsParallelAngles[t],ang); 00060 } 00061 } 00062 } 00063 00064 JunctionHOG::~JunctionHOG() 00065 { 00066 } 00067 00068 00069 std::vector<float> JunctionHOG::createHistogramFromGradient(const Image<float>& gradmag, const Image<float>& gradang) 00070 { 00071 std::vector<float> ret = HistogramOfGradients::createHistogramFromGradient(gradmag,gradang); 00072 std::vector<float> tmp = calculateJunctionHistogram(gradmag,gradang); 00073 ret.insert(ret.end(),tmp.begin(),tmp.end()); 00074 return ret; 00075 } 00076 00077 00078 void JunctionHOG::initializeNeighbors(std::vector<Point2D<int> >& neighbors, std::vector<float>& perpAngles, std::vector<float>& parallelAngles, float angle) 00079 { 00080 // NOTE: This makes the same assumptions about an image that Rectangle.H makes, namely that (0,0) is the top left of the image 00081 00082 // Remember atan2 has coords inverted: atan2(y,x) 00083 00084 // Left center point 00085 float lcprot=atan2(0,-1)+angle; 00086 Point2D<int> lcp=Point2D<int>(round(cos(lcprot)*itsNeighborDistance),round(sin(lcprot)*itsNeighborDistance)); 00087 float lca=0+angle; 00088 lca=(lca>M_PI) ? lca-M_PI : lca; 00089 float lcaPerp=(lca>M_PI/2.0) ? lca-M_PI/2.0 : lca+M_PI/2.0; 00090 neighbors.push_back(lcp); parallelAngles.push_back(lca); perpAngles.push_back(lcaPerp); 00091 // Left upper point 00092 float luprot=atan2(-1,-1)+angle; 00093 Point2D<int> lup=Point2D<int>(round(cos(luprot)*itsNeighborDistance),round(sin(luprot)*itsNeighborDistance)); 00094 float lua=M_PI/4.0+angle; 00095 lua=(lua>M_PI) ? lua-M_PI : lua; 00096 float luaPerp=(lua>M_PI/2.0) ? lua-M_PI/2.0 : lua+M_PI/2.0; 00097 neighbors.push_back(lup); parallelAngles.push_back(lua); perpAngles.push_back(luaPerp); 00098 // Left lower point 00099 float llprot=atan2(1,-1)+angle; 00100 Point2D<int> llp = Point2D<int>(round(cos(llprot)*itsNeighborDistance),round(sin(llprot)*itsNeighborDistance)); 00101 float lla=M_PI-M_PI/4.0+angle; 00102 lla=(lla>M_PI) ? lla-M_PI : lla; 00103 float llaPerp=(lla>M_PI/2.0) ? lla-M_PI/2.0 : lla+M_PI/2.0; 00104 neighbors.push_back(llp); parallelAngles.push_back(lla); perpAngles.push_back(llaPerp); 00105 00106 // Right center point 00107 float rcprot=atan2(0,1)+angle; 00108 Point2D<int> rcp=Point2D<int>(round(cos(rcprot)*itsNeighborDistance),round(sin(rcprot)*itsNeighborDistance)); 00109 float rca=0+angle; 00110 rca=(rca>M_PI) ? rca-M_PI : rca; 00111 float rcaPerp=(rca>M_PI/2.0) ? rca-M_PI/2.0 : rca+M_PI/2.0; 00112 neighbors.push_back(rcp); parallelAngles.push_back(rca); perpAngles.push_back(rcaPerp); 00113 // Right upper point 00114 float ruprot=atan2(-1,1)+angle; 00115 Point2D<int> rup=Point2D<int>(round(cos(ruprot)*itsNeighborDistance),round(sin(ruprot)*itsNeighborDistance)); 00116 float rua=M_PI/4.0+angle; 00117 rua=(rua>M_PI) ? rua-M_PI : rua; 00118 float ruaPerp=(rua>M_PI/2.0) ? rua-M_PI/2.0 : rua+M_PI/2.0; 00119 neighbors.push_back(rup); parallelAngles.push_back(rua); perpAngles.push_back(ruaPerp); 00120 // Right lower point 00121 float rlprot=atan2(1,1)+angle; 00122 Point2D<int> rlp = Point2D<int>(round(cos(rlprot)*itsNeighborDistance),round(sin(rlprot)*itsNeighborDistance)); 00123 float rla=M_PI-M_PI/4.0+angle; 00124 rla=(rla>M_PI) ? rla-M_PI : rla; 00125 float rlaPerp=(rla>M_PI/2.0) ? rla-M_PI/2.0 : rla+M_PI/2.0; 00126 neighbors.push_back(rlp); parallelAngles.push_back(rla); perpAngles.push_back(rlaPerp); 00127 00128 } 00129 00130 00131 std::vector<float> JunctionHOG::calculateJunctionHistogram(Image<float> gradmag, Image<float> gradang) 00132 { 00133 // Determine the number of cells, based on whether we have fixed dims or not 00134 Dims cells; 00135 if(itsFixedDims) 00136 cells = itsCellDims; 00137 else 00138 cells = Dims(int(round(float(gradmag.getWidth())/itsCellDims.w())),int(round(float(gradmag.getHeight())/itsCellDims.h()))); 00139 00140 const int w=gradmag.getWidth(), h=gradmag.getHeight(); 00141 00142 // Create a SIFT-like feature vector to soft bin the continuity values 00143 FeatureVector cntfv = FeatureVector(cells.w(),cells.h(),itsContinuityBins,true); 00144 Image<float>::const_iterator gmg = gradmag.begin(), gang = gradang.begin(); 00145 00146 // loop over rows: 00147 for (int i = 0; i < w; i ++) 00148 { 00149 // loop over columns: 00150 for (int j = 0; j < h; j ++) 00151 { 00152 // Check for line continuity using unsigned angles into bins [0-7] 00153 int oriBin=floor(((*gang < 0) ? *gang + M_PI : *gang)/M_PI*8); 00154 float continuityStrength=0; 00155 float continuityParallel=0; 00156 int validNeighbors = 0; 00157 // Go through all relevant neighbors and accumulate continuity votes 00158 for(size_t n=0;n<itsRelevantNeighbors[oriBin].size();n++) 00159 { 00160 Point2D<int> neigh = itsRelevantNeighbors[oriBin][n]; 00161 if(!gradmag.coordsOk(i+neigh.i,j+neigh.j)) 00162 { 00163 // Ignore this neighbor, it is outside of image bounds 00164 continue; 00165 } 00166 int offset=neigh.i*h+neigh.j; 00167 // Convert neighbor orientation to unsigned orientation 00168 float unsignedNeighAng=(gang[offset] < 0) ? gang[offset] + M_PI : gang[offset]; 00169 // Vote strength of neighbor 00170 continuityStrength += gmg[offset]; // gradmag.getValInterp(neigh); 00171 // Add parallel support for continuity 00172 float ctrParallel=itsParallelAngles[oriBin][n]; 00173 //LINFO("offset %d ctrParallel %f, uNO %f",offset,ctrParallel,unsignedNeighAng); 00174 continuityParallel += cos(fabs(ctrParallel-unsignedNeighAng)); 00175 // Perpendicular support is negative parallel support 00176 float ctrPerp=itsPerpAngles[oriBin][n]; 00177 continuityParallel -= cos(fabs(ctrPerp-unsignedNeighAng)); 00178 ASSERT(!isnan(continuityParallel) && !isnan(continuityStrength)); 00179 validNeighbors++; 00180 } 00181 // Normalize by the number of contributing neighbors 00182 if(itsNormalize && validNeighbors>0) 00183 { 00184 continuityParallel/=float(validNeighbors); 00185 continuityStrength/=float(validNeighbors); 00186 } 00187 float continuityAng = atan2(continuityParallel,continuityStrength); 00188 float continuityMag = sqrt(continuityParallel*continuityParallel+continuityStrength*continuityStrength); 00189 00190 // Get bin fractions 00191 const float xf = float(i)/float(w)*cells.w(); 00192 const float yf = float(j)/float(h)*cells.h(); 00193 // Add Orientation to bin (always unsigned orientation) 00194 addToBin(xf,yf,continuityAng,continuityMag,true,cntfv); 00195 gmg++; 00196 gang++; 00197 } 00198 } 00199 // Return the continuity histogram 00200 00201 std::vector<float> hist; 00202 if(itsNormalize) 00203 hist = normalizeFeatureVector(cntfv); 00204 else 00205 hist = cntfv.getFeatureVector(); 00206 LINFO("Calculated Junction Histogram of size %Zu",hist.size()); 00207 return hist; 00208 } 00209