iSpike
2.1
Spike conversion library for robotics
|
00001 #include <iSpike/VisualDataReducer/LogPolarVisualDataReducer.hpp> 00002 #include <iSpike/Bitmap.hpp> 00003 #include <iSpike/Reader/VisualReader.hpp> 00004 #include <iSpike/Common.hpp> 00005 #include <iSpike/ISpikeException.hpp> 00006 using namespace ispike; 00007 00008 #include <fstream> 00009 #include <iostream> 00010 #include <sstream> 00011 using namespace std; 00012 00013 #include <boost/math/special_functions/round.hpp> 00014 00015 //Pi 00016 #define PI 3.141592653589793238 00017 00019 //#define DEBUG_IMAGES 00020 //#define DEBUG_COORDINATES 00021 //#define DEBUG 00022 00023 00025 LogPolarVisualDataReducer::LogPolarVisualDataReducer() : 00026 inputWidth(0), 00027 inputHeight(0), 00028 outputWidth(0), 00029 outputHeight(0), 00030 foveaRadius(0.0), 00031 initialized(false) 00032 { 00033 ; 00034 } 00035 00036 00037 00038 /*--------------------------------------------------------------------*/ 00039 /*--------- PUBLIC METHODS -------*/ 00040 /*--------------------------------------------------------------------*/ 00041 00043 Bitmap& LogPolarVisualDataReducer::getReducedImage(){ 00044 return *reducedImage; 00045 } 00046 00047 00049 void LogPolarVisualDataReducer::setBitmap(Bitmap& bitmap){ 00050 if(bitmap.isEmpty()) { 00051 LOG(LOG_DEBUG)<<"LogPolarVisualDataReducer: Empty bitmap"; 00052 return; 00053 } 00054 00055 //Prepare for first use if necessary 00056 if(!isInitialized()){ 00057 initialize(bitmap); 00058 } 00059 00060 //Check bitmap is correct size 00061 if(bitmap.getWidth() != inputWidth || bitmap.getHeight() != inputHeight) { 00062 throw ISpikeException("LogPolarVisualDataReducer: Incoming image size has changed."); 00063 } 00064 00065 //Calculate the log polar foveated image 00066 calculateReducedImage(bitmap); 00067 } 00068 00069 00071 void LogPolarVisualDataReducer::setOutputHeight(unsigned outputHeight){ 00072 if(isInitialized()) { 00073 throw ISpikeException("LogPolarVisualDataReducer: Output height cannot be set after class has been initialized."); 00074 } 00075 this->outputHeight = outputHeight; 00076 } 00077 00078 00080 void LogPolarVisualDataReducer::setOutputWidth(unsigned outputWidth){ 00081 if(isInitialized()) { 00082 throw ISpikeException("LogPolarVisualDataReducer: Output width cannot be set after class has been initialized."); 00083 } 00084 this->outputWidth = outputWidth; 00085 } 00086 00087 00089 void LogPolarVisualDataReducer::setFoveaRadius(double foveaRadius){ 00090 if(isInitialized()) 00091 throw ISpikeException("LogPolarVisualDataReducer: Fovea radius cannot be set after class has been initialized."); 00092 this->foveaRadius = foveaRadius; 00093 } 00094 00095 00096 /*--------------------------------------------------------------------*/ 00097 /*--------- PRIVATE METHODS -------*/ 00098 /*--------------------------------------------------------------------*/ 00099 00101 void LogPolarVisualDataReducer::calculateReducedImage(Bitmap& bitmap){ 00102 if(bitmap.getDepth() != reducedImage->getDepth()) { 00103 throw ISpikeException("Depth in input image and reduced image do not match"); 00104 } 00105 int tmpDepth = bitmap.getDepth(); 00106 00107 //References to arrays inside both images 00108 unsigned char* inputImageArray = bitmap.getContents(); 00109 unsigned char* reducedImageArray = reducedImage->getContents(); 00110 00111 #ifdef DEBUG 00112 LOG(LOG_DEBUG)<<"Input image size: "<<bitmap.size()<<"; reduced image size: "<<reducedImage->size(); 00113 #endif//DEBUG 00114 00115 //Copy the pixels across from the input to the output maps 00116 for(vector<PolarCartCoords>::iterator iter = coordinatesVector.begin(); iter != coordinatesVector.end(); ++iter){ 00117 for(int d=0; d<tmpDepth; ++d){ 00118 //Copy pixel data into reduced image array 00119 //First coordinate is the polar(r, theta) in the output bitmap; second coordinate is the Cartesian(x,y) in the input bitmap 00120 if(tmpDepth == 0){ 00121 reducedImageArray[ outputWidth * (iter->theta) + (iter->radius)] = 00122 inputImageArray[ inputWidth * (iter->y) + (iter->x) ]; 00123 } 00124 else{ 00125 //LOG(LOG_DEBUG)<<"Input image index: "<<(inputWidth * (iter->y) * tmpDepth + (iter->x) * tmpDepth + d)<<"; reducedImageIndex: "<<( outputWidth * (iter->theta) * tmpDepth + (iter->radius) * tmpDepth + d ); 00126 reducedImageArray[ outputWidth * (iter->theta) * tmpDepth + (iter->radius) * tmpDepth + d ] = 00127 inputImageArray[ inputWidth * (iter->y) * tmpDepth + (iter->x) * tmpDepth + d ]; 00128 } 00129 } 00130 } 00131 00132 //Output reduced image if required 00133 #ifdef DEBUG_IMAGES 00134 Common::savePPMImage("inputImage.ppm", &bitmap); 00135 Common::savePPMImage("logPolar.ppm", reducedImage.get()); 00136 #endif//DEBUG_IMAGES 00137 } 00138 00139 00141 void LogPolarVisualDataReducer::initialize(Bitmap& bitmap){ 00142 if(outputWidth == 0 || outputHeight == 0) { 00143 throw ISpikeException("LogPolarVisualDataReducer: Cannot initialize with zero width and/or height."); 00144 } 00145 00146 if(foveaRadius > outputWidth) { 00147 throw ISpikeException("LogPolarVisualDataReducer: Fovea radius must be less than or equal to output width."); 00148 } 00149 00150 if(bitmap.getWidth() < outputWidth || bitmap.getHeight() < outputWidth) { 00151 throw ISpikeException("LogPolarVisualDataReducer: Incoming image must be greater in size than output."); 00152 } 00153 00154 //Store input width and height 00155 inputWidth = bitmap.getWidth(); 00156 inputHeight = bitmap.getHeight(); 00157 if(foveaRadius > inputWidth/2 || foveaRadius > inputHeight/2) { 00158 throw ISpikeException("LogPolarVisualDataReducer: Fovea radius must be less than or equal to half of input width or height."); 00159 } 00160 00161 //Initialize map for converting between polar and Cartesian 00162 initialisePolarToCartesianVector(); 00163 00164 //Create reduced image 00165 reducedImage.reset(new Bitmap(outputWidth, outputHeight, bitmap.getDepth())); 00166 00167 initialized = true; 00168 } 00169 00170 00174 void LogPolarVisualDataReducer::initialisePolarToCartesianVector(){ 00175 coordinatesVector.clear(); 00176 #ifdef DEBUG_COORDINATES 00177 ofstream fileStream; 00178 fileStream.open("LogPolarVisualDataReducer.log", fstream::out); 00179 #endif//DEBUG_COORDINATES 00180 00181 //Map between a location on the log polar map and an angle on input map 00182 double angleResolution = 360.0 / outputHeight; 00183 00184 //Get appropriate base for logarithm 00185 double inputRadius = inputWidth/2.0; 00186 if(inputWidth>inputHeight) { 00187 inputRadius = inputHeight/2.0;//Minimum - can only sample a circular pattern in input 00188 } 00189 00190 //Radius lengths outside foveated area 00191 double outputLogRadius = outputWidth - foveaRadius; 00192 double inputLogRadius = inputRadius - foveaRadius; 00193 00194 //Find the base such that the maximum value will lie inside the inputRadius 00195 double expBase = pow(10.0, log10(inputLogRadius) / outputLogRadius ); 00196 00197 #ifdef DEBUG 00198 LOG(LOG_DEBUG)<<"OutputLogRadius: "<<outputLogRadius<<"; InputLogRadius: "<<inputLogRadius<<"; ExpBase: "<<expBase; 00199 #endif//DEBUG 00200 00201 for(unsigned r=0; r<outputWidth; ++r){ 00202 for(unsigned theta=0; theta<outputHeight; ++theta){ 00203 if(r <= foveaRadius){ 00204 pair<int, int> tmpPair = getInputCartesianCoordinate(r, theta*angleResolution); 00205 coordinatesVector.push_back(PolarCartCoords(r, theta, tmpPair.first, tmpPair.second)); 00206 #ifdef DEBUG_COORDINATES 00207 fileStream<<"Polar("<<r<<", "<<theta<<") -> Cart("<<tmpPair.first<<", "<<tmpPair.second<<")"<<endl; 00208 #endif//DEBUG_COORDINATES 00209 } else { 00210 pair<int, int> tmpPair = getInputCartesianCoordinate(foveaRadius + pow(expBase, r-foveaRadius), theta*angleResolution); 00211 coordinatesVector.push_back(PolarCartCoords(r, theta, tmpPair.first, tmpPair.second)); 00212 #ifdef DEBUG_COORDINATES 00213 fileStream<<"Polar("<<r<<", "<<theta<<") -> Cart("<<tmpPair.first<<", "<<tmpPair.second<<")"<<endl; 00214 #endif//DEBUG_COORDINATES 00215 } 00216 } 00217 } 00218 #ifdef DEBUG_COORDINATES 00219 fileStream.close(); 00220 #endif//DEBUG_COORDINATES 00221 } 00222 00223 00227 pair<int, int> LogPolarVisualDataReducer::getInputCartesianCoordinate(double radius, double theta_deg){ 00228 double theta_rads = (theta_deg / 360.0) * 2.0 * PI; 00229 int tmpY = boost::math::iround(double(inputHeight/2) + radius * sin(theta_rads)); 00230 if(tmpY >= int(inputHeight)) { 00231 throw ISpikeException("LogPolarVisualDataReducer: Y out of range: ", tmpY); 00232 } 00233 int tmpX = boost::math::iround(double(inputWidth/2) + radius * cos(theta_rads)); 00234 if(tmpX >= int(inputWidth)) { 00235 throw ISpikeException("LogPolarVisualDataReducer: X out of range: ", tmpX); 00236 } 00237 return make_pair(tmpX, tmpY); 00238 } 00239 00240