iSpike  2.1
Spike conversion library for robotics
D:/Home/Programs/iSpike/src/VisualDataReducer/LogPolarVisualDataReducer.cpp
Go to the documentation of this file.
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 
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Defines