iSpike  2.1
Spike conversion library for robotics
D:/Home/Programs/iSpike/src/VisualFilter/DOGVisualFilter.cpp
Go to the documentation of this file.
00001 #include <iSpike/VisualFilter/DOGVisualFilter.hpp>
00002 #include <iSpike/ISpikeException.hpp>
00003 #include <iSpike/Bitmap.hpp>
00004 #include <iSpike/Common.hpp>
00005 #include <iSpike/Log/Log.hpp>
00006 using namespace ispike;
00007 
00008 #include <boost/math/constants/constants.hpp>
00009 #include <boost/math/special_functions/round.hpp>
00010 
00011 
00013 #define MAX_PIXEL_VALUE 255
00014 
00016 //#define DEBUG_IMAGES
00017 
00018 
00023 DOGVisualFilter::DOGVisualFilter(LogPolarVisualDataReducer* reducer) :
00024         reducer(reducer),
00025         positiveSigma(-1.0),
00026         negativeSigma(-1.0),
00027         positiveFactor(-1.0),
00028         negativeFactor(-1.0),
00029         opponencyTypeID(-1),
00030         normalize(false),
00031         initialized(false),
00032         outputBitmap(NULL),
00033         redBitmap(NULL),
00034         greenBitmap(NULL),
00035         blueBitmap(NULL),
00036         yellowBitmap(NULL),
00037         greyBitmap1(NULL),
00038         greyBitmap2(NULL),
00039         positiveBlurredBitmap(NULL),
00040         negativeBlurredBitmap(NULL)
00041 {
00042         ;
00043 }
00044 
00045 
00046 DOGVisualFilter::~DOGVisualFilter(){
00047         if(isInitialized()){
00048                 delete redBitmap;
00049                 delete greenBitmap;
00050                 delete blueBitmap;
00051                 delete yellowBitmap;
00052                 delete greyBitmap1;
00053                 delete greyBitmap2;
00054                 delete positiveBlurredBitmap;
00055                 delete negativeBlurredBitmap;
00056                 delete outputBitmap;
00057         }
00058 }
00059 
00060 
00061 /*--------------------------------------------------------------------*/
00062 /*---------                 PUBLIC METHODS                     -------*/
00063 /*--------------------------------------------------------------------*/
00064 
00066 Bitmap& DOGVisualFilter::getBitmap(){
00067         return *outputBitmap;
00068 }
00069 
00070 
00072 void DOGVisualFilter::setOpponencyTypeID(int opponencyTypeID){
00073         if(isInitialized())
00074                 throw ISpikeException("DOGVisualFilter: Opponency type ID cannot be set after class has been initialized.");
00075         this->opponencyTypeID = opponencyTypeID;
00076 }
00077 
00078 
00087 void DOGVisualFilter::update(){
00088         //Get log polar bitmap and check it is ok
00089         Bitmap& reducedImage = reducer->getReducedImage();
00090         if(reducedImage.isEmpty())      {
00091                 LOG(LOG_DEBUG)<<"DOGVisualFilter: Empty bitmap";
00092                 return;
00093         }
00094 
00095         //Create bitmaps if they have not been created already
00096         if(!isInitialized()){
00097                 initialize(reducedImage.getWidth(), reducedImage.getHeight());
00098         }
00099 
00100         //Calculate opponency map
00101         if(opponencyTypeID == Common::redVsGreen){
00102                 extractRedChannel(reducedImage);
00103                 extractGreenChannel(reducedImage);
00104                 calculateOpponency(*redBitmap, *greenBitmap);
00105         }
00106         else if(opponencyTypeID == Common::greenVsRed){
00107                 extractRedChannel(reducedImage);
00108                 extractGreenChannel(reducedImage);
00109                 calculateOpponency(*greenBitmap, *redBitmap);
00110         }
00111         else if(opponencyTypeID == Common::blueVsYellow){
00112                 extractRedChannel(reducedImage);
00113                 extractGreenChannel(reducedImage);
00114                 extractBlueChannel(reducedImage);
00115                 extractYellowChannel();
00116                 calculateOpponency(*blueBitmap, *yellowBitmap);
00117         }
00118         else if(opponencyTypeID == Common::greyVsGrey){
00119                 extractGreyChannel(reducedImage, *greyBitmap1, false);
00120                 calculateOpponency(*greyBitmap1, *greyBitmap1);
00121         }
00122         else if(opponencyTypeID == Common::motionSensitive || opponencyTypeID == Common::motionSensitiveLog){
00123                 if(useGreyBitmap1){
00124                         if(opponencyTypeID == Common::motionSensitiveLog)
00125                                 extractGreyChannel(reducedImage, *greyBitmap1, true);
00126                         else
00127                                 extractGreyChannel(reducedImage, *greyBitmap1, false);
00128                         subtractImages(*greyBitmap1, *greyBitmap2, 1.0, 1.0, *outputBitmap);
00129                         useGreyBitmap1 = false;
00130                 }
00131                 else{
00132                         if(opponencyTypeID == Common::motionSensitiveLog)
00133                                 extractGreyChannel(reducedImage, *greyBitmap2, true);
00134                         else
00135                                 extractGreyChannel(reducedImage, *greyBitmap2, false);
00136                         subtractImages(*greyBitmap2, *greyBitmap1, 1.0, 1.0, *outputBitmap);
00137                         useGreyBitmap1 = true;
00138                 }
00139         }
00140         else
00141                 throw ISpikeException("Opponency type ID not recognized: ", opponencyTypeID);
00142 
00143         //Output debug image if required
00144         #ifdef DEBUG_IMAGES
00145                 if(opponencyTypeID == Common::redVsGreen)
00146                         Common::savePPMImage("Red+Green-.ppm", outputBitmap);
00147                 else if(opponencyTypeID == Common::greenVsRed)
00148                         Common::savePPMImage("Green+Red-.ppm", outputBitmap);
00149                 else if(opponencyTypeID == Common::blueVsYellow)
00150                         Common::savePPMImage("Blue+Yellow-.ppm", outputBitmap);
00151                 else if (opponencyTypeID == Common::greyVsGrey)
00152                         Common::savePPMImage("Grey+Grey-.ppm", outputBitmap);
00153                 else if (opponencyTypeID == Common::motionSensitive)
00154                         Common::savePPMImage("Motion.ppm", outputBitmap);
00155                 else if (opponencyTypeID == Common::motionSensitiveLog)
00156                         Common::savePPMImage("MotionLog.ppm", outputBitmap);
00157         #endif//DEBUG_IMAGES
00158 }
00159 
00160 
00161 /*--------------------------------------------------------------------*/
00162 /*---------                 PRIVATE METHODS                    -------*/
00163 /*--------------------------------------------------------------------*/
00164 
00166 void DOGVisualFilter::calculateOpponency(Bitmap& bitmap1, Bitmap& bitmap2){
00167         //Blur the positive image
00168         gaussianBlur(bitmap1, *positiveBlurredBitmap, positiveSigma);
00169         #ifdef DEBUG_IMAGES
00170                 Common::savePPMImage("positiveBlur.ppm", positiveBlurredBitmap);
00171         #endif//DEBUG_IMAGES
00172 
00173         //Blur the negative image
00174         gaussianBlur(bitmap2, *negativeBlurredBitmap, negativeSigma);
00175         #ifdef DEBUG_IMAGES
00176                 Common::savePPMImage("negativeBlur.ppm", negativeBlurredBitmap);
00177         #endif//DEBUG_IMAGES
00178 
00179         //Subtract the negative from the positive image to get the opponency map
00180         subtractImages(*positiveBlurredBitmap, *positiveBlurredBitmap, *outputBitmap);
00181 
00182         //Normalize opponency map if required
00183         if(normalize)
00184                 normalizeImage(*outputBitmap);
00185 }
00186 
00187 
00189 void DOGVisualFilter::gaussianBlur(Bitmap& inputBitmap, Bitmap& resultBitmap, double sigma){
00190         if(inputBitmap.getWidth() != resultBitmap.getWidth() || inputBitmap.getHeight() != resultBitmap.getHeight())
00191                 throw ISpikeException("DOGVisualFilter: Mismatch between input and results dimensions.");
00192         int width = inputBitmap.getWidth();
00193         int height = inputBitmap.getHeight();
00194 
00195         //Pointers to arrays in bitmaps
00196         unsigned char* inputContents = inputBitmap.getContents();
00197         unsigned char* resultContents = resultBitmap.getContents();
00198 
00199         //Create kernel
00200         double* kernel = new double[(int)ceil(6 * sigma) + 1];
00201         for(int i = 0; i < ceil( 6 * sigma ) + 1; i++){
00202                 int x = i - int(ceil( 3 * sigma ));
00203                 double k = exp( - ( (x * x) / ( 2 * sigma * sigma ) ) );
00204                 kernel[i] = k / sqrt( 2 * boost::math::constants::pi<double>() * sigma * sigma );
00205         }
00206 
00207         //Iterate horizontally
00208         for(int y = 0; y < height; y++){
00209                 for(int x = 0; x < width; x++){
00210                         double colour_value = 0;
00211                         unsigned char current_colour, *sourcePtr;
00212 
00213                         //iterate over the kernel
00214                         for(int i = 0; i < ceil( 6 * sigma ) + 1; i++){
00215                                 int kernel_x = i - int(ceil( 3 * sigma ));
00216                                 if(x + kernel_x < 0 )
00217                                         continue;
00218                                 else if(x + kernel_x > width)
00219                                         continue;
00220                                 sourcePtr = inputContents + ( y * width ) + x;
00221                                 current_colour = (unsigned char) *(sourcePtr + kernel_x);
00222                                 colour_value += current_colour * kernel[i];
00223                         }
00224                         resultContents[y * width + x] = (int)floor( colour_value + 0.5 );
00225                 }
00226         }
00227 
00228         //Iterate vertically
00229         for(int x = 0; x < width; x++){
00230                 for(int y = 0; y < height; y++){
00231                         double colour_value = 0;
00232                         unsigned char current_colour, *sourcePtr;
00233 
00234                         //iterate over the kernel
00235                         for(int i = 0; i < ceil( 6 * sigma ) + 1; i++){
00236                                 int kernel_y = i - int(ceil( 3 * sigma ));
00237                                 if(y + kernel_y <= 0 )
00238                                         continue;
00239                                 else if(y + kernel_y >= height)
00240                                         continue;
00241                                 sourcePtr = inputContents + ( y * width ) + x;
00242                                 current_colour = (unsigned char) *(sourcePtr + (kernel_y * width));
00243                                 colour_value += current_colour * kernel[i];
00244                         }
00245                         resultContents[y * width + x] += (int)floor( colour_value + 0.5 );
00246                 }
00247         }
00248 
00249         //Clean up kernel memory
00250         free(kernel);
00251 }
00252 
00253 
00256 void DOGVisualFilter::extractRedChannel(Bitmap& image){
00257         //Check image dimensions match
00258         if(image.getWidth() != redBitmap->getWidth() || image.getHeight() != redBitmap->getHeight())
00259                 throw ISpikeException("DOGVisualFilter: Red image and incoming reduced image have different dimensions");
00260 
00261         //Check incoming image has depth 3
00262         if(image.getDepth() !=3)
00263                 throw ISpikeException ("DOGVisualFilter: expecting full colour image for red extraction.");
00264 
00265         //Avoid multiple function calls
00266         int imageSize = redBitmap->size();
00267         unsigned char* redContents = redBitmap->getContents();
00268         unsigned char* imageContents = image.getContents();
00269 
00270         //Copy the red pixels
00271         for(int pixel = 0; pixel < imageSize; ++pixel) {
00272                 redContents[pixel] = imageContents[pixel * 3];
00273         }
00274 
00275         //Output debug image if required
00276         #ifdef DEBUG_IMAGES
00277                 Common::savePPMImage("red.ppm", redBitmap);
00278         #endif//DEBUG_IMAGES
00279 }
00280 
00281 
00284 void DOGVisualFilter::extractGreenChannel(Bitmap& image){
00285         //Check image dimensions match
00286         if(image.getWidth() != greenBitmap->getWidth() || image.getHeight() != greenBitmap->getHeight())
00287                 throw ISpikeException("DOGVisualFilter: Green image and incoming reduced image have different dimensions");
00288 
00289         //Avoid multiple function calls
00290         int imageSize = greenBitmap->size();
00291         unsigned char* greenContents = greenBitmap->getContents();
00292         unsigned char* imageContents = image.getContents();
00293 
00294         //Copy the green pixels
00295         for(int pixel = 0; pixel < imageSize; ++pixel) {
00296                 greenContents[pixel] = imageContents[pixel * 3 + 1];
00297         }
00298 
00299         //Output debug image if required
00300         #ifdef DEBUG_IMAGES
00301                 Common::savePPMImage("green.ppm", greenBitmap);
00302         #endif//DEBUG_IMAGES
00303 }
00304 
00305 
00308 void DOGVisualFilter::extractBlueChannel(Bitmap& image){
00309         //Check image dimensions match
00310         if(image.getWidth() != blueBitmap->getWidth() || image.getHeight() != blueBitmap->getHeight())
00311                 throw ISpikeException("DOGVisualFilter: Blue image and incoming reduced image have different dimensions");
00312 
00313         //Avoid multiple function calls
00314         int imageSize = blueBitmap->size();
00315         unsigned char* blueContents = blueBitmap->getContents();
00316         unsigned char* imageContents = image.getContents();
00317 
00318         //Copy the blue pixels
00319         for(int pixel = 0; pixel < imageSize; ++pixel) {
00320                 blueContents[pixel] = imageContents[pixel * 3 + 2];
00321         }
00322 
00323         //Output debug image if required
00324         #ifdef DEBUG_IMAGES
00325                 Common::savePPMImage("blue.ppm", blueBitmap);
00326         #endif//DEBUG_IMAGES
00327 }
00328 
00329 
00331 void DOGVisualFilter::extractYellowChannel(){
00332         //Avoid multiple function calls
00333         int imageSize = yellowBitmap->size();
00334         unsigned char* redContents = redBitmap->getContents();
00335         unsigned char* greenContents = greenBitmap->getContents();
00336         unsigned char* yellowContents = yellowBitmap->getContents();
00337         int tmpNum;
00338 
00339         //Add green and red to create the yellow
00340         for(int pixel=0; pixel < imageSize; ++pixel){
00341                 tmpNum = redContents[pixel] + greenContents[pixel];
00342                 yellowContents[pixel] = (unsigned char)(tmpNum/2);
00343         }
00344 
00345         //Output debug image if required
00346         #ifdef DEBUG_IMAGES
00347                 Common::savePPMImage("yellow.ppm", yellowBitmap);
00348         #endif//DEBUG_IMAGES
00349 }
00350 
00351 
00355 void DOGVisualFilter::extractGreyChannel(Bitmap& inputBitmap, Bitmap& newBitmap, bool logInput){
00356         //Check image dimensions match
00357         if(inputBitmap.getWidth() != newBitmap.getWidth() || inputBitmap.getHeight() != newBitmap.getHeight())
00358                 throw ISpikeException("DOGVisualFilter: Grey image and incoming reduced image have different dimensions");
00359 
00360         //Avoid multiple function calls
00361         int imageSize = newBitmap.size();//Size of the destination map, which should have depth of 1
00362         unsigned char* inContents = inputBitmap.getContents();
00363         unsigned char* newContents = newBitmap.getContents();
00364 
00365         //Take the average of the red, green and blue pixels
00366         double tmpSum, tmpRes;//Avoid overrun of unsigned char
00367         if(logInput){
00368                 for(int pixel = 0; pixel < imageSize; ++pixel) {
00369                         tmpSum =  inContents[pixel * 3];
00370                         tmpSum += inContents[pixel * 3 + 1];
00371                         tmpSum += inContents[pixel * 3 + 2];
00372 
00373                         //Calculate the log if tmpSum is > 0
00374                         if(tmpSum != 0.0)
00375                                 tmpRes = log(tmpSum/3.0);
00376                         if(tmpRes < 0.0)
00377                                 tmpRes = 0.0;
00378                         newContents[pixel] = (unsigned char)rint(tmpRes);
00379                 }
00380         }
00381         else{
00382                 for(int pixel = 0; pixel < imageSize; ++pixel) {
00383                         tmpSum =  inContents[pixel * 3];
00384                         tmpSum += inContents[pixel * 3 + 1];
00385                         tmpSum += inContents[pixel * 3 + 2];
00386                         newContents[pixel] = (unsigned char)rint(tmpSum/3.0);
00387                 }
00388         }
00389 
00390         //Output debug image if required
00391         #ifdef DEBUG_IMAGES
00392                 Common::savePPMImage("grey.ppm", &newBitmap);
00393         #endif//DEBUG_IMAGES
00394 }
00395 
00396 
00398 void DOGVisualFilter::initialize(int width, int height){
00399         //Check variables have been set
00400         if(positiveSigma<0 || negativeSigma<0 || positiveFactor <0 || negativeFactor<0 || opponencyTypeID<0)
00401                 throw ISpikeException("DOGVisualFilter: Some or all of the parameters have not been set.");
00402 
00403         redBitmap = new Bitmap(width, height, 1);
00404         greenBitmap = new Bitmap(width, height, 1);
00405         blueBitmap = new Bitmap(width, height, 1);
00406         yellowBitmap = new Bitmap(width, height, 1);
00407         greyBitmap1 = new Bitmap(width, height, 1, 0);
00408         greyBitmap2 = new Bitmap(width, height, 1, 0);
00409         positiveBlurredBitmap = new Bitmap(width, height, 1);
00410         negativeBlurredBitmap = new Bitmap(width, height, 1);
00411         outputBitmap = new Bitmap(width, height, 1);
00412         useGreyBitmap1 = true;
00413         initialized = true;
00414 }
00415 
00416 
00418 void DOGVisualFilter::normalizeImage(Bitmap& image){
00419         //References to buffer etc.
00420         int imageSize = image.size();
00421         unsigned char* imageContents = image.getContents();
00422 
00423         //Find the maximum value in the bitmap
00424         unsigned char max = 0;
00425         for(int i=0; i<imageSize; ++i){
00426                 if(imageContents[i] > max)
00427                         max = imageContents[i];
00428         }
00429 
00430         //Do nothing if the image is already normalized
00431         if(max == MAX_PIXEL_VALUE)
00432                 return;
00433 
00434         //Normalize the image
00435         double factor = MAX_PIXEL_VALUE / (double)max;
00436         for(int i=0; i<imageSize; ++i)
00437                 imageContents[i] = (unsigned char)floor(imageContents[i] * factor);
00438 }
00439 
00440 
00442 void DOGVisualFilter::subtractImages(Bitmap& firstImage, Bitmap& secondImage, Bitmap& result){
00443         if(firstImage.size() != secondImage.size() || firstImage.size() != result.size())
00444                 throw ISpikeException("Subtraction only works between images of the same size. The result must be the same size as well.");
00445 
00446         //Get references to buffers etc.
00447         int imageSize = firstImage.size();
00448         unsigned char* firstImageContents = firstImage.getContents();
00449         unsigned char* secondImageContents = secondImage.getContents();
00450         unsigned char* resultContents = result.getContents();
00451 
00452         for(int i=0; i<imageSize; ++i){
00453                 double val = double(firstImageContents[i])*positiveFactor - double(secondImageContents[i])*negativeFactor;
00454                 if(val < 0.0)
00455                         val = 0.0;
00456                 resultContents[i] = boost::math::round<unsigned char>(val);
00457         }
00458 }
00459 
00460 
00462 void DOGVisualFilter::subtractImages(Bitmap& firstImage, Bitmap& secondImage, double positiveFactor, double negativeFactor,  Bitmap& result){
00463         if(firstImage.size() != secondImage.size() || firstImage.size() != result.size())
00464                 throw ISpikeException("Subtraction only works between images of the same size. The result must be the same size as well.");
00465 
00466         //Get references to buffers etc.
00467         int imageSize = firstImage.size();
00468         unsigned char* firstImageContents = firstImage.getContents();
00469         unsigned char* secondImageContents = secondImage.getContents();
00470         unsigned char* resultContents = result.getContents();
00471 
00472         for(int i=0; i<imageSize; ++i){
00473                 double val = double(firstImageContents[i])*positiveFactor - double(secondImageContents[i])*negativeFactor;
00474                 if(val < 0.0)
00475                         val = 0.0;
00476                 resultContents[i] = boost::math::round<unsigned char>(val);
00477         }
00478 }
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Defines