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,int(KernelFactory::max_blur_type),
00025 "Type of blurring, smoothing, or (in general) convolution to use on the\n"
00026 "inputs to be presented, when layers_pereye>1. 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, the identity 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.\n\n"
00039
00040 "Compatibility note: in previous versions, setting blur_radius to zero\n"
00041 "disabled retinal ganglion cell processing, while setting it to a nonzero\n"
00042 "value enabled it. Such behavior is now controlled by the layers_pereye\n"
00043 "parameter instead, and no longer depends on blur_radius.");
00044
00045 PARAM_LL(PARAM_DOUBLE,blur_radius_surround_multiplier,&blur_radius_surround_multiplier,0,
00046 "Ratio of surround radius to blur_radius. Defaults to 1.6, which makes the\n"
00047 "DoG approximate the Laplacian derivative of a Gaussian (see Blur_LoG,\n"
00048 "\\cite{marr:prslb80,marr:vision}), albeit at a different spatial scale.");
00049
00050 PARAM_LL(PARAM_DOUBLE,blur_range_multiplier,&blur_range_multiplier,0,
00051 "Each non-constant blur_type (i.e., those other than Blur_SquareAverage\n"
00052 "and Blur_CircularAverage) has a default maximum circular radius at which\n"
00053 "to evaluate the blurring function (i.e., clipping). The default should\n"
00054 "usually be sufficient, but it can be increased for greater accuracy (or\n"
00055 "decreased for speed) by making this parameter higher or lower than 1.0,\n"
00056 "respectively. Since clipping is done circularly, the shape of an isotropic\n"
00057 "kernel should remain approximately isotropic even when the multiplier is set\n"
00058 "far enough below 1.0 that heavy clipping occurs.");
00059
00060 PARAM_LL(PARAM_DOUBLE,blur_scale,&blur_scale,0,
00061 "Intensity scale for the blurring kernel, if any. This is primarily useful\n"
00062 "for blurring types which change the input strength dramatically, such as\n"
00063 "Blur_DoG or a Blur_Gaussian with a large radius. This scaling factor is\n"
00064 "used only when layers_pereye>1, so it can help make the input strength\n"
00065 "independent of whether blurring is in use. At present it is the\n"
00066 "equivalent of gammaaff for a FixedWtRegion, and should probably\n"
00067 "be phased out in favor of gammaaff for simplicity.");
00068
00069 CONST_I(Blur_CircularAverage,int(Blur_CircularAverage),False,
00070 "Blur type (see command input_define_convolution) with parameters:\n"
00071 " [<radius> [<scale>]]\n\n"
00072
00073 "Replaces each input pixel with the given scale times the arithmetic mean\n"
00074 "of the pixels in the surrounding circular region with the specified radius.\n\n"
00075
00076 "For a very small radius, the approximation to a circle is poor (e.g.,\n"
00077 "it is square when radius=1). That property can make this otherwise\n"
00078 "isotropic filter anisotropic. This type of blurring doesn't work\n"
00079 "particularly well with patterns that have sharp edges, since the\n"
00080 "blurring pattern also has a sharp edge. However, this type of blurring\n"
00081 "is much faster than ones like Blur_Gaussian, since a smaller convolution\n"
00082 "kernel is needed, and a flat one can sometimes be simpler to reason about.");
00083
00084 CONST_I(Blur_CircularRandom,int(Blur_CircularRandom),False,
00085 "Blur type (see command input_define_convolution) with parameters:\n"
00086 " [<radius> [<scale>]]\n\n"
00087
00088 "Replaces each input pixel with the convolution of a circular region (with\n"
00089 "the specified radius) of random values with the input image surrounding\n"
00090 "that pixel. The values are normalized to have a total sum of 1.0 by\n"
00091 "default, but if a different scale is specified, the normalized sum\n"
00092 "is then multiplied by it.\n\n"
00093
00094 "This type of blurring is a default against which some more effective\n"
00095 "variety may be compared.");
00096
00097 CONST_I(Blur_SquareAverage,int(Blur_SquareAverage),False,
00098 "Blur type (see command input_define_convolution) with parameters:\n"
00099 " [<radius> [<scale>]]\n\n"
00100
00101 "Replaces each input pixel with the given scale times the arithmetic mean\n"
00102 "of the pixels in the surrounding square region with the specified integer\n"
00103 "radius.\n\n"
00104
00105 "This type of blurring is not isotropic, i.e. it blurs differently\n"
00106 "for different directions, so it should usually be avoided.");
00107
00108 CONST_I(Blur_SquareRandom,int(Blur_SquareRandom),False,
00109 "Blur type (see command input_define_convolution) with parameters:\n"
00110 " [<radius> [<scale>]]\n\n"
00111
00112 "Replaces each input pixel with the convolution of a square region (with\n"
00113 "the specified radius) of random values with the input image surrounding\n"
00114 "that pixel. The values are normalized to have a total sum of 1.0 by\n"
00115 "default, but if a different scale is specified, the normalized sum\n"
00116 "is then multiplied by it.\n\n"
00117
00118 "This type of blurring is a default against which some more effective\n"
00119 "variety may be compared.");
00120
00121 CONST_I(Blur_Gaussian,int(Blur_Gaussian),False,
00122 "Blur type (see command input_define_convolution) with parameters:\n"
00123 " [<radius> [<scale> [<radius_range_scale>]]]\n\n"
00124
00125 "Replaces each input pixel with the convolution of a Gaussian (with the\n"
00126 "specified radius) with the input image surrounding that pixel. The\n"
00127 "Gaussian is normalized to have a total sum of 1.0 by default, but if a\n"
00128 "different scale is specified, the normalized Gaussian is multiplied by\n"
00129 "it.\n\n"
00130
00131 "The function is continuous but is only evaluated up to a finite distance from\n"
00132 "its center in all directions. If desired, a scale for this maximum radius may\n"
00133 "be supplied, which is multiplied by the function's radius to get the actual\n"
00134 "range. By default, a range is used which ensures that the function is nearly\n"
00135 "zero without having a kernel that is too large, which would slow down the\n"
00136 "program. The blur_range_multiplier can be used to increase or decrease this\n"
00137 "default automatically.\n\n"
00138
00139 "This type of blurring is effective at removing detail at a spatial scale below\n"
00140 "the radius of the Gaussian, without introducing edge effects like\n"
00141 "Blur_CircularAverage. (It is essentially a low-pass filter with a cutoff\n"
00142 "frequency determined by the blur_radius.)");
00143
00144 CONST_I(Blur_LoG,int(Blur_LoG),False,
00145 "Blur type (see command input_define_convolution) with parameters:\n"
00146 " [<radius> [<scale> [<radius_range_scale>]]]\n\n"
00147
00148 "Replaces each input pixel with the convolution of the Laplacian derivative\n"
00149 "of a Gaussian (with radius blur_radius and height 1.0) with the input image\n"
00150 "surrounding that pixel. The scale is currently arbitrary, but it can be\n"
00151 "adjusted with the scale parameter. See Blur_Gaussian for a description of\n"
00152 "the radius_range_scale parameter.\n\n"
00153
00154 "This type of blurring is nearly identical in shape to a Blur_DoG, and\n"
00155 "\\cite{marr:vision} has proposed that a DoG is approximating this\n"
00156 "function. However, the height and spatial scales differ substantially\n"
00157 "from a DoG, partially because the DoG can easily be normalized to\n"
00158 "sum to zero, so it may be difficult to use this blurring type (though\n"
00159 "no less computationally efficient).");
00160
00161
00162 CONST_I(Blur_DoG,int(Blur_DoG),False,
00163 "Blur type (see command input_define_convolution) with parameters:\n"
00164 " [<radius> [<surround_radius> [<scale> [<surround_scale> [<radius_range_scale>]]]]\n\n"
00165
00166 "Replaces each input pixel with the convolution of a Difference of Gaussians\n"
00167 "(a Mexican-hat whose center Gaussian sums to the given scale and has the given\n"
00168 "radius and whose surround (negative) Gaussian sums to the given surround_scale\n"
00169 "and has the given surround_radius) with the input image surrounding that pixel.\n"
00170 "The surround_radius defaults to be blur_radius*blur_radius_surround_multiplier.\n\n"
00171
00172 "See Blur_Gaussian for a description of the radius_range_scale parameter; the\n"
00173 "surround radius is used as a base since it is presumably larger than the other.\n\n"
00174
00175 "This type of blurring serves to detect edges and similar structures at a scale\n"
00176 "somewhat smaller than the radius of the center Gaussian, and resembles\n"
00177 "the processing done by a population of RF-size-matched retinal ganglion cells.\n"
00178 "(It is basically a band-pass spatial filter.)");
00179
00180 CONST_I(Blur_GoD,int(Blur_GoD),False,
00181 "Blur type (see command input_define_convolution) with parameters:\n"
00182 " [<radius> [<surround_radius> [<scale> [<surround_scale> [<radius_range_scale>]]]]\n\n"
00183
00184 "This blur_type is the same as Blur_DoG except that the surround Gaussian\n"
00185 "is taken as positive while the center is negative (OFF-center). Given that\n"
00186 "the retina itself is always positive, this filter serves to highlight changes\n"
00187 "of the opposite sign as Blur_DoG (e.g. a dark area surrounded by light).");
00188
00189 CONST_I(Blur_DoGGoD,int(Blur_DoGGoD),False,
00190 "Blur type (see command input_define_convolution) with parameters:\n"
00191 " [<radius> [<surround_radius> [<scale> [<surround_scale> [<radius_range_scale>]]]]\n\n"
00192
00193 "When this blur_type is used, even-numbered eyes (starting at zero) use\n"
00194 "Blur_DoG while the rest use Blur_GoD. Assuming every pair of eyes\n"
00195 "has identical input patterns, the pair thus approximates a full set of\n"
00196 "ON-center and OFF-center inputs. Presumably, num_eyes should be 2 or 4\n"
00197 "for this blurring type to be meaningful.");
00198
00199 CONST_I(Blur_PGM,int(Blur_PGM),False,
00200 "Blur type (see command input_define_convolution) with parameters:\n"
00201 " [<filename> [<offset> [<scale> [<normalize>]]]]\n\n"
00202
00203 "The given PGM image file (\"kernel.pgm\" by default) is read and\n"
00204 "used as a convolution kernel. The value range specified in the file\n"
00205 "is scaled to a range from 0 to 1.0, then the offset (-0.5 by default)\n"
00206 "is added. Next, if normalization is enabled (on by default), the\n"
00207 "matrix is normalized to have a sum of the given scale; otherwise\n"
00208 "the matrix is simply multiplied by the given scale.");
00209 }
00210
00211
00212
00234 KernelFactory::KernelMatrix KernelFactory::create( int index, double wss, StringArgs arglist )
00235 {
00236 KernelMatrix kernel;
00237 const int type = arglist.next(blur_type);
00238 string errors;
00239
00240
00241 switch (type) {
00242
00243 case Blur_PGM: {
00244 const string filename = arglist.next(string(default_kernel_filename));
00245 const double offset = arglist.next(-0.5);
00246 const double scale = arglist.next(blur_scale);
00247 const int normalize = arglist.next(1);
00248 KernelMatrix new_kernel;
00249
00250 if (!pgm_read(filename, new_kernel)) {
00251 ipc_notify(IPC_ONE,IPC_ERROR, "Can't read file: %s", filename.c_str());
00252 break;
00253 }
00254
00255 if (new_kernel.ncols()%2 != 1 || new_kernel.nrows()%2 != 1){
00256 ipc_notify(IPC_ONE,IPC_ERROR,
00257 "The convolution matrix must have an odd number of columns and rows (%d,%d)",
00258 new_kernel.ncols(),new_kernel.nrows());
00259 break;
00260 }
00261 new_kernel += offset;
00262
00263 if (!normalize)
00264 new_kernel *= scale;
00265 else {
00266 const double kernelsum = mat::sum(new_kernel);
00267 if (kernelsum==0)
00268 ipc_notify(IPC_ONE,IPC_ERROR,"Can't normalize blurring kernel since it sums to zero");
00269 else new_kernel *= fabs(scale/kernelsum);
00270 }
00271
00272 kernel = new_kernel;
00273 break;
00274 }
00275
00276
00277 case Blur_SquareAverage: case Blur_CircularAverage: case Blur_Gaussian: case Blur_LoG: {
00278
00279 const double rad = arglist.next(blur_radius*wss);
00280 const double scale = arglist.next(blur_scale);
00281
00282 switch (type) {
00283 case Blur_SquareAverage: kernel = create(RadialFunction::Constant, rad,rad-0.5,errors,false); break;
00284 case Blur_CircularAverage: kernel = create(RadialFunction::Constant, rad,rad-0.5,errors); break;
00285 case Blur_Gaussian: {
00286 const double rscale = arglist.next(4.7*blur_range_multiplier);
00287 kernel = create(RadialFunction::Gaussian, rad, rad*rscale,errors); break;
00288 }
00289 case Blur_LoG: {
00290 const double rscale = arglist.next(3.2*blur_range_multiplier);
00291 kernel = create(RadialFunction::LoG, rad, rad*rscale,errors,true,false); break;
00292 }
00293 }
00294 kernel *= scale;
00295 break;
00296 }
00297
00298 case Blur_DoG: case Blur_GoD: case Blur_DoGGoD: {
00299
00300 const double scalesign = ( type == Blur_DoG ? 1.0 :
00301 ( type == Blur_GoD ? -1.0 :
00302 (type == Blur_DoGGoD ? 1.0-2*(index%2) :
00303 1.0)));
00304
00305 const double rad = arglist.next((blur_radius)*wss);
00306
00307 const double srad = arglist.next((blur_radius_surround_multiplier*rad/wss)*wss);
00308 const double scale = arglist.next(blur_scale);
00309 const double sscale = arglist.next(scale);
00310 const double range_scale = arglist.next(4.7*blur_range_multiplier);
00311 const double maxrad = srad*range_scale;
00312
00313 #ifdef USE_MTL_MATRIX
00314 kernel = create(RadialFunction::Gaussian, rad, maxrad,errors);
00315 mtl::scale(kernel,scale*scalesign);
00316 mtl::add( mtl::scaled(create(RadialFunction::Gaussian, srad, maxrad,errors), -1*sscale*scalesign), kernel);
00317 #else
00318 kernel = create(RadialFunction::Gaussian, rad, maxrad,errors) * scale*scalesign -
00319 create(RadialFunction::Gaussian, srad, maxrad,errors) * sscale*scalesign;
00320 #endif
00321 break;
00322 }
00323
00324 case Blur_CircularRandom: case Blur_SquareRandom: {
00325 const double rad = arglist.next(blur_radius*wss);
00326 const double scale = arglist.next(blur_scale);
00327
00328 switch (type) {
00329 case Blur_SquareRandom: kernel = create(RadialFunction::Random, rad,rad-0.5,errors,false); break;
00330 case Blur_CircularRandom: kernel = create(RadialFunction::Random, rad,rad-0.5,errors); break;
00331 }
00332 kernel *= scale;
00333 break;
00334 }
00335
00336 default: ipc_notify(IPC_ONE,IPC_ERROR,"Unknown blur_type %d",type); break;
00337 }
00338
00339 if (errors!="")
00340 ipc_notify(IPC_ONE,IPC_ERROR,errors.c_str());
00341
00342 return kernel;
00343 }
00344