iSpike
2.1
Spike conversion library for robotics
|
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 }