00001
00008 #include "kernelfactory.h"
00009 #include "cmdparam.h"
00010
00011
00012
00013 double KernelFactory::blur_radius=0;
00014 double KernelFactory::blur_radius_surround_multiplier=1.6;
00015 double KernelFactory::blur_scale=1.0;
00016 double KernelFactory::blur_range_multiplier=1.0;
00017 int KernelFactory::blur_type=Blur_CircularAverage;
00018 char KernelFactory::default_kernel_filename[CMD_MAX_LINE_LENGTH] = "kernel.pgm";
00019
00020
00021
00022 void KernelFactory::register_params_and_commands( void )
00023 {
00024 PARAM_II(PARAM_INT, blur_type,&blur_type,0,KernelFactory::max_blur_type,
00025 "Type of blurring, smoothing, or (in general) convolution to use on the\n"
00026 "inputs to be presented, when blur_radius>0. Available types include\n"
00027 "the following (prefixed with 'Blur_'):\n"
00028 " CircularAverage,SquareAverage,Gaussian,DoG,LoG,GoD,DoGGoD");
00029 params_define_default_expr("blur_type","Blur_CircularAverage");
00030
00031 PARAM_LL(PARAM_DOUBLE,blur_radius,&blur_radius,0,
00032 "Default blurring or smoothing radius for the input, in retinal receptors.\n"
00033 "If zero, no blurring function is applied. This parameter determines the\n"
00034 "nominal radius, but the various blur_type options each produce a function\n"
00035 "with a different actual width. For instance, a Gaussian with sigma blur_radius\n"
00036 "is much narrower than a uniform circle of radius blur_radius, yet wider\n"
00037 "than the Laplacian of a Gaussian. See also blur_type and\n"
00038 "blur_range_multiplier.");
00039
00040 PARAM_LL(PARAM_DOUBLE,blur_radius_surround_multiplier,&blur_radius_surround_multiplier,0,
00041 "Ratio of surround radius to blur_radius. Defaults to 1.6, which makes the\n"
00042 "DoG approximate the Laplacian derivative of a Gaussian (see Blur_LoG,\n"
00043 "\\cite{marr:prslb80,marr:vision}), albeit at a different spatial scale.");
00044
00045 PARAM_LL(PARAM_DOUBLE,blur_range_multiplier,&blur_range_multiplier,0,
00046 "Each non-constant blur_type (i.e., those other than Blur_SquareAverage\n"
00047 "and Blur_CircularAverage) has a default maximum circular radius at which\n"
00048 "to evaluate the blurring function (i.e., clipping). The default should\n"
00049 "usually be sufficient, but it can be increased for greater accuracy (or\n"
00050 "decreased for speed) by making this parameter higher or lower than 1.0,\n"
00051 "respectively. Since clipping is done circularly, the shape of an isotropic\n"
00052 "kernel should remain approximately isotropic even when the multiplier is set\n"
00053 "far enough below 1.0 that heavy clipping occurs.");
00054
00055 PARAM_LL(PARAM_DOUBLE,blur_scale,&blur_scale,0,
00056 "Intensity scale for the blurring kernel, if any. This is primarily useful\n"
00057 "for blurring types which change the input strength dramatically, such as\n"
00058 "Blur_DoG or a Blur_Gaussian with a large radius. This scaling factor is\n"
00059 "used only when blur_radius>0, so it can help make the input strength\n"
00060 "independent of whether blurring is in use.");
00061
00062 CONST_I(Blur_CircularAverage,Blur_CircularAverage,False,
00063 "Blur type (see command input_define_convolution) with parameters:\n"
00064 " [<radius> [<scale>]]\n\n"
00065
00066 "Replaces each input pixel with the given scale times the arithmetic mean\n"
00067 "of the pixels in the surrounding circular region with the specified radius.\n\n"
00068
00069 "For a very small radius, the approximation to a circle is poor (e.g.,\n"
00070 "it is square when radius=1). That property can make this otherwise\n"
00071 "isotropic filter anisotropic. This type of blurring doesn't work\n"
00072 "particularly well with patterns that have sharp edges, since the\n"
00073 "blurring pattern also has a sharp edge. However, this type of blurring\n"
00074 "is much faster than ones like Blur_Gaussian, since a smaller convolution\n"
00075 "kernel is needed, and a flat one can sometimes be simpler to reason about.");
00076
00077 CONST_I(Blur_SquareAverage,Blur_SquareAverage,False,
00078 "Blur type (see command input_define_convolution) with parameters:\n"
00079 " [<radius> [<scale>]]\n\n"
00080
00081 "Replaces each input pixel with the given scale times the arithmetic mean\n"
00082 "of the pixels in the surrounding square region with the specified integer\n"
00083 "radius.\n\n"
00084
00085 "This type of blurring is not isotropic, i.e. it blurs differently\n"
00086 "for different directions, so it should usually be avoided.");
00087
00088 CONST_I(Blur_Gaussian,Blur_Gaussian,False,
00089 "Blur type (see command input_define_convolution) with parameters:\n"
00090 " [<radius> [<scale> [<radius_range_scale>]]]\n\n"
00091
00092 "Replaces each input pixel with the convolution of a Gaussian (with the\n"
00093 "specified radius) with the input image surrounding that pixel. The\n"
00094 "Gaussian is normalized to have a total sum of 1.0 by default, but if a\n"
00095 "different scale is specified, the normalized Gaussian is multiplied by\n"
00096 "it.\n\n"
00097
00098 "The function is continuous but is only evaluated up to a finite distance from\n"
00099 "its center in all directions. If desired, a scale for this maximum radius may\n"
00100 "be supplied, which is multiplied by the function's radius to get the actual\n"
00101 "range. By default, a range is used which ensures that the function is nearly\n"
00102 "zero without having a kernel that is too large, which would slow down the\n"
00103 "program. The blur_range_multiplier can be used to increase or decrease this\n"
00104 "default automatically.\n\n"
00105
00106 "This type of blurring is effective at removing detail at a spatial scale below\n"
00107 "the radius of the Gaussian, without introducing edge effects like\n"
00108 "Blur_CircularAverage. (It is essentially a low-pass filter with a cutoff\n"
00109 "frequency determined by the blur_radius.)");
00110
00111 CONST_I(Blur_LoG,Blur_LoG,False,
00112 "Blur type (see command input_define_convolution) with parameters:\n"
00113 " [<radius> [<scale> [<radius_range_scale>]]]\n\n"
00114
00115 "Replaces each input pixel with the convolution of the Laplacian derivative\n"
00116 "of a Gaussian (with radius blur_radius and height 1.0) with the input image\n"
00117 "surrounding that pixel. The scale is currently arbitrary, but it can be\n"
00118 "adjusted with the scale parameter. See Blur_Gaussian for a description of\n"
00119 "the radius_range_scale parameter.\n\n"
00120
00121 "This type of blurring is nearly identical in shape to a Blur_DoG, and\n"
00122 "\\cite{marr:vision} has proposed that a DoG is approximating this\n"
00123 "function. However, the height and spatial scales differ substantially\n"
00124 "from a DoG, partially because the DoG can easily be normalized to\n"
00125 "sum to zero, so it may be difficult to use this blurring type (though\n"
00126 "no less computationally efficient).");
00127
00128
00129 CONST_I(Blur_DoG,Blur_DoG,False,
00130 "Blur type (see command input_define_convolution) with parameters:\n"
00131 " [<radius> [<surround_radius> [<scale> [<surround_scale> [<radius_range_scale>]]]]\n\n"
00132
00133 "Replaces each input pixel with the convolution of a Difference of Gaussians\n"
00134 "(a Mexican-hat whose center Gaussian sums to the given scale and has the given\n"
00135 "radius and whose surround (negative) Gaussian sums to the given surround_scale\n"
00136 "and has the given surround_radius) with the input image surrounding that pixel.\n"
00137 "The surround_radius defaults to be blur_radius*blur_radius_surround_multiplier.\n\n"
00138
00139 "See Blur_Gaussian for a description of the radius_range_scale parameter; the\n"
00140 "surround radius is used as a base since it is presumably larger than the other.\n\n"
00141
00142 "This type of blurring serves to detect edges and similar structures at a scale\n"
00143 "somewhat smaller than the radius of the center Gaussian, and resembles\n"
00144 "the processing done by a population of RF-size-matched retinal ganglion cells.\n"
00145 "(It is basically a band-pass spatial filter.)");
00146
00147 CONST_I(Blur_GoD,Blur_GoD,False,
00148 "Blur type (see command input_define_convolution) with parameters:\n"
00149 " [<radius> [<surround_radius> [<scale> [<surround_scale> [<radius_range_scale>]]]]\n\n"
00150
00151 "This blur_type is the same as Blur_DoG except that the surround Gaussian\n"
00152 "is taken as positive while the center is negative (OFF-center). Given that\n"
00153 "the retina itself is always positive, this filter serves to highlight changes\n"
00154 "of the opposite sign as Blur_DoG (e.g. a dark area surrounded by light).");
00155
00156 CONST_I(Blur_DoGGoD,Blur_DoGGoD,False,
00157 "Blur type (see command input_define_convolution) with parameters:\n"
00158 " [<radius> [<surround_radius> [<scale> [<surround_scale> [<radius_range_scale>]]]]\n\n"
00159
00160 "When this blur_type is used, even-numbered eyes (starting at zero) use\n"
00161 "Blur_DoG while the rest use Blur_GoD. Assuming every pair of eyes\n"
00162 "has identical input patterns, the pair thus approximates a full set of\n"
00163 "ON-center and OFF-center inputs. Presumably, num_eyes should be 2 or 4\n"
00164 "for this blurring type to be meaningful.");
00165
00166
00167 CONST_I(Blur_PGM,Blur_PGM,False,
00168 "Blur type (see command input_define_convolution) with parameters:\n"
00169 " [<filename> [<offset> [<scale> [<normalize>]]]]\n\n"
00170
00171 "The given PGM image file (\"kernel.pgm\" by default) is read and\n"
00172 "used as a convolution kernel. The value range specified in the file\n"
00173 "is scaled to a range from 0 to 1.0, then the offset (-0.5 by default)\n"
00174 "is added. Next, if normalization is enabled (on by default), the\n"
00175 "matrix is normalized to have a sum of the given scale; otherwise\n"
00176 "the matrix is simply multiplied by the given scale.");
00177 }
00178
00179
00180
00201 KernelFactory::KernelMatrix KernelFactory::create( int index, double wss, StringArgs arglist )
00202 {
00203 KernelMatrix kernel;
00204 const int type = arglist.next(blur_type);
00205 string errors;
00206
00207
00208 switch (type) {
00209
00210 case Blur_PGM: {
00211 const string filename = arglist.next(string(default_kernel_filename));
00212 const double offset = arglist.next(-0.5);
00213 const double scale = arglist.next(blur_scale);
00214 const int normalize = arglist.next(1);
00215 KernelMatrix new_kernel;
00216
00217 if (!pgm_read(filename, new_kernel)) {
00218 ipc_notify(IPC_ONE,IPC_ERROR, "Can't read file: %s", filename.c_str());
00219 break;
00220 }
00221
00222 if (new_kernel.ncols()%2 != 1 || new_kernel.nrows()%2 != 1){
00223 ipc_notify(IPC_ONE,IPC_ERROR,
00224 "The convolution matrix must have an odd number of columns and rows (%d,%d)",
00225 new_kernel.ncols(),new_kernel.nrows());
00226 break;
00227 }
00228 new_kernel += offset;
00229
00230 if (!normalize)
00231 new_kernel *= scale;
00232 else {
00233 const double kernelsum = mat::sum(new_kernel);
00234 if (kernelsum==0)
00235 ipc_notify(IPC_ONE,IPC_ERROR,"Can't normalize blurring kernel since it sums to zero");
00236 else new_kernel *= fabs(scale/kernelsum);
00237 }
00238
00239 kernel = new_kernel;
00240 break;
00241 }
00242
00243
00244 case Blur_SquareAverage: case Blur_CircularAverage: case Blur_Gaussian: case Blur_LoG: {
00245
00246 const double rad = arglist.next(blur_radius*wss);
00247 if (rad==0) break;
00248 const double scale = arglist.next(blur_scale);
00249
00250 switch (type) {
00251 case Blur_SquareAverage: kernel = create(RadialFunction::Constant, rad,(int)rad,errors,false); break;
00252 case Blur_CircularAverage: kernel = create(RadialFunction::Constant, rad,(int)rad,errors); break;
00253 case Blur_Gaussian: {
00254 const double rscale = arglist.next(4.7*blur_range_multiplier);
00255 kernel = create(RadialFunction::Gaussian, rad, (int)ROUND(rad*rscale),errors); break;
00256 }
00257 case Blur_LoG: {
00258 const double rscale = arglist.next(3.2*blur_range_multiplier);
00259 kernel = create(RadialFunction::LoG, rad, (int)ROUND(rad*rscale),errors,true,false); break;
00260 }
00261 }
00262 kernel *= scale;
00263 break;
00264 }
00265
00266 case Blur_DoG: case Blur_GoD: case Blur_DoGGoD: {
00267
00268 const double scalesign = ( type == Blur_DoG ? 1.0 :
00269 ( type == Blur_GoD ? -1.0 :
00270 (type == Blur_DoGGoD ? 1.0-2*(index%2) :
00271 1.0)));
00272
00273 const double rad = arglist.next((blur_radius)*wss);
00274 if (rad==0) break;
00275
00276 const double srad = arglist.next((blur_radius_surround_multiplier*rad/wss)*wss);
00277 const double scale = arglist.next(blur_scale);
00278 const double sscale = arglist.next(scale);
00279 const double range_scale = arglist.next(4.7*blur_range_multiplier);
00280 const int maxrad = int(ROUND(srad*range_scale));
00281
00282 #ifdef USE_MTL_MATRIX
00283 kernel = create(RadialFunction::Gaussian, rad, maxrad,errors);
00284 mtl::scale(kernel,scale*scalesign);
00285 mtl::add( mtl::scaled(create(RadialFunction::Gaussian, srad, maxrad,errors), -1*sscale*scalesign), kernel);
00286 #else
00287 kernel = create(RadialFunction::Gaussian, rad, maxrad,errors) * scale*scalesign -
00288 create(RadialFunction::Gaussian, srad, maxrad,errors) * sscale*scalesign;
00289 #endif
00290 break;
00291 }
00292
00293 default: ipc_notify(IPC_ONE,IPC_ERROR,"Unknown blur_type %d",type); break;
00294 }
00295
00296 if (errors!="")
00297 ipc_notify(IPC_ONE,IPC_ERROR,errors.c_str());
00298
00299 return kernel;
00300 }
00301