PTdecode/CImg-1.3.0/examples/radon_transform.cpp

Wed, 05 Aug 2009 15:00:54 +0100

author
Philip Pemberton <philpem@philpem.me.uk>
date
Wed, 05 Aug 2009 15:00:54 +0100
changeset 12
96e1df9bd27c
parent 5
1204ebf9340d
permissions
-rwxr-xr-x

small changes to hexdump code to stop a gcc warning on platforms where sizeof(int) != sizeof(int*) e.g. x86_64

philpem@5 1 /*
philpem@5 2 #
philpem@5 3 # File : radon_transform.cpp
philpem@5 4 # ( C++ source file )
philpem@5 5 #
philpem@5 6 # Description : An implementation of the Radon Transform.
philpem@5 7 # This file is a part of the CImg Library project.
philpem@5 8 # ( http://cimg.sourceforge.net )
philpem@5 9 #
philpem@5 10 # Copyright : David G. Starkweather
philpem@5 11 # ( starkdg@sourceforge.net - starkweatherd@cox.net )
philpem@5 12 #
philpem@5 13 # License : CeCILL v2.0
philpem@5 14 # ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html )
philpem@5 15 #
philpem@5 16 # This software is governed by the CeCILL license under French law and
philpem@5 17 # abiding by the rules of distribution of free software. You can use,
philpem@5 18 # modify and/ or redistribute the software under the terms of the CeCILL
philpem@5 19 # license as circulated by CEA, CNRS and INRIA at the following URL
philpem@5 20 # "http://www.cecill.info".
philpem@5 21 #
philpem@5 22 # As a counterpart to the access to the source code and rights to copy,
philpem@5 23 # modify and redistribute granted by the license, users are provided only
philpem@5 24 # with a limited warranty and the software's author, the holder of the
philpem@5 25 # economic rights, and the successive licensors have only limited
philpem@5 26 # liability.
philpem@5 27 #
philpem@5 28 # In this respect, the user's attention is drawn to the risks associated
philpem@5 29 # with loading, using, modifying and/or developing or reproducing the
philpem@5 30 # software by the user in light of its specific status of free software,
philpem@5 31 # that may mean that it is complicated to manipulate, and that also
philpem@5 32 # therefore means that it is reserved for developers and experienced
philpem@5 33 # professionals having in-depth computer knowledge. Users are therefore
philpem@5 34 # encouraged to load and test the software's suitability as regards their
philpem@5 35 # requirements in conditions enabling the security of their systems and/or
philpem@5 36 # data to be ensured and, more generally, to use and operate it in the
philpem@5 37 # same conditions as regards security.
philpem@5 38 #
philpem@5 39 # The fact that you are presently reading this means that you have had
philpem@5 40 # knowledge of the CeCILL license and that you accept its terms.
philpem@5 41 #
philpem@5 42 */
philpem@5 43
philpem@5 44 #define ROUNDING_FACTOR(x) (((x) >= 0) ? 0.5 : -0.5)
philpem@5 45 #include <cmath>
philpem@5 46 #include "CImg.h"
philpem@5 47 using namespace cimg_library;
philpem@5 48
philpem@5 49 // The lines below are necessary when using a non-standard compiler as visualcpp6.
philpem@5 50 #ifdef cimg_use_visualcpp6
philpem@5 51 #define std
philpem@5 52 #endif
philpem@5 53 #ifdef min
philpem@5 54 #undef min
philpem@5 55 #undef max
philpem@5 56 #endif
philpem@5 57
philpem@5 58 #ifndef cimg_imagepath
philpem@5 59 #define cimg_imagepath "img/"
philpem@5 60 #endif
philpem@5 61
philpem@5 62 CImg<double> GaussianKernel(double rho);
philpem@5 63 CImg<float> ApplyGaussian(CImg<unsigned char> im,double rho);
philpem@5 64 CImg<unsigned char> RGBtoGrayScale(CImg<unsigned char> &im);
philpem@5 65 int GetAngle(int dy,int dx);
philpem@5 66 CImg<unsigned char> CannyEdges(CImg<float> im, double T1, double T2,bool doHysteresis);
philpem@5 67 CImg<> RadonTransform(CImg<unsigned char> im,int N);
philpem@5 68
philpem@5 69 int main(int argc,char **argv) {
philpem@5 70 cimg_usage("Illustration of the Radon Transform");
philpem@5 71
philpem@5 72 const char *file = cimg_option("-f",cimg_imagepath "parrot_original.ppm","path and file name");
philpem@5 73 const double sigma = cimg_option("-r",1.0,"blur coefficient for gaussian low pass filter (lpf)"),
philpem@5 74 thresh1 = cimg_option("-t1",0.50,"lower threshold for canny edge detector"),
philpem@5 75 thresh2 = cimg_option("-t2",1.25,"upper threshold for canny edge detector");;
philpem@5 76 const int N = cimg_option("-n",64,"number of angles to consider in the Radon transform - should be a power of 2");
philpem@5 77
philpem@5 78 //color to draw lines
philpem@5 79 const unsigned char green[] = {0,255,0};
philpem@5 80 CImg<unsigned char> src(file);
philpem@5 81
philpem@5 82 int rhomax = (int)std::sqrt((double)(src.dimx()*src.dimx() + src.dimy()*src.dimy()))/2;
philpem@5 83
philpem@5 84 if (cimg::dialog(cimg::basename(argv[0]),
philpem@5 85 "Instructions:\n"
philpem@5 86 "Click on space bar or Enter key to display Radon transform of given image\n"
philpem@5 87 "Click on anywhere in the transform window to display a \n"
philpem@5 88 "corresponding green line in the original image\n",
philpem@5 89 "Start", "Quit",0,0,0,0,
philpem@5 90 src.get_resize(100,100,1,3),true)) std::exit(0);
philpem@5 91
philpem@5 92 //retrieve a grayscale from the image
philpem@5 93 CImg<unsigned char> grayScaleIm;
philpem@5 94 if ((src.dimv() == 3) && (src.dimx() > 0) && (src.dimy() > 0) && (src.dimz() == 1))
philpem@5 95 grayScaleIm = (CImg<unsigned char>)src.get_pointwise_norm(0).quantize(255,false);
philpem@5 96 else if ((src.dimv() == 1)&&(src.dimx() > 0) && (src.dimy() > 0) && (src.dimz() == 1))
philpem@5 97 grayScaleIm = src;
philpem@5 98 else { // image in wrong format
philpem@5 99 if (cimg::dialog(cimg::basename("wrong file format"),
philpem@5 100 "Incorrect file format\n","OK",0,0,0,0,0,
philpem@5 101 src.get_resize(100,100,1,3),true)) std::exit(0);
philpem@5 102 }
philpem@5 103
philpem@5 104 //blur the image with a Gaussian lpf to remove spurious edges (e.g. noise)
philpem@5 105 CImg<float> blurredIm = ApplyGaussian(grayScaleIm,sigma);
philpem@5 106
philpem@5 107 //use canny edge detection algorithm to get edge map of the image
philpem@5 108 //- the threshold values are used to perform hysteresis in the edge detection process
philpem@5 109 CImg<unsigned char> cannyEdgeMap = CannyEdges(blurredIm,thresh1,thresh2,false);
philpem@5 110 CImg<unsigned char> radonImage = *(new CImg<unsigned char>(500,400,1,1,0));
philpem@5 111
philpem@5 112 //display the two windows
philpem@5 113 CImgDisplay dispImage(src,"original image");
philpem@5 114 dispImage.move(CImgDisplay::screen_dimx()/8,CImgDisplay::screen_dimy()/8);
philpem@5 115 CImgDisplay dispRadon(radonImage,"Radon Transform");
philpem@5 116 dispRadon.move(CImgDisplay::screen_dimx()/4,CImgDisplay::screen_dimy()/4);
philpem@5 117 CImgDisplay dispCanny(cannyEdgeMap,"canny edges");
philpem@5 118 //start main display loop
philpem@5 119 while (!dispImage.is_closed && !dispRadon.is_closed &&
philpem@5 120 !dispImage.is_keyQ && !dispRadon.is_keyQ &&
philpem@5 121 !dispImage.is_keyESC && !dispRadon.is_keyESC){
philpem@5 122
philpem@5 123 CImgDisplay::wait(dispImage,dispRadon);
philpem@5 124
philpem@5 125 if (dispImage.is_keySPACE || dispRadon.is_keySPACE)
philpem@5 126 {
philpem@5 127 radonImage = (CImg<unsigned char>)RadonTransform(cannyEdgeMap,N).quantize(255,false).resize(500,400);
philpem@5 128 radonImage.display(dispRadon);
philpem@5 129 }
philpem@5 130
philpem@5 131 //when clicking on dispRadon window, draw line in original image window
philpem@5 132 if (dispRadon.button)
philpem@5 133 {
philpem@5 134 const double rho = dispRadon.mouse_y*rhomax/dispRadon.dimy(),
philpem@5 135 theta = (dispRadon.mouse_x*N/dispRadon.dimx())*2*cimg::valuePI/N,
philpem@5 136 x = src.dimx()/2 + rho*std::cos(theta),
philpem@5 137 y = src.dimy()/2 + rho*std::sin(theta);
philpem@5 138 const int x0 = (int)(x + 1000*std::cos(theta + cimg::valuePI/2)),
philpem@5 139 y0 = (int)(y + 1000*std::sin(theta + cimg::valuePI/2)),
philpem@5 140 x1 = (int)(x - 1000*std::cos(theta + cimg::valuePI/2)),
philpem@5 141 y1 = (int)(y - 1000*std::sin(theta + cimg::valuePI/2));
philpem@5 142 src.draw_line(x0,y0,x1,y1,green,1.0f,0xF0F0F0F0).display(dispImage);
philpem@5 143 }
philpem@5 144 }
philpem@5 145 return 0;
philpem@5 146 }
philpem@5 147 /**
philpem@5 148 * PURPOSE: create a 5x5 gaussian kernel matrix
philpem@5 149 * PARAM rho - gaussiam equation parameter (default = 1.0)
philpem@5 150 * RETURN CImg<double> the gaussian kernel
philpem@5 151 **/
philpem@5 152
philpem@5 153 CImg<double> GaussianKernel(double sigma = 1.0)
philpem@5 154 {
philpem@5 155 CImg<double> resultIm(5,5,1,1,0);
philpem@5 156 int midX = 3, midY = 3;
philpem@5 157 cimg_forXY(resultIm,X,Y)
philpem@5 158 {
philpem@5 159 resultIm(X,Y) = std::ceil(256.0*(std::exp(-(midX*midX + midY*midY)/(2*sigma*sigma)))/(2*cimg::valuePI*sigma*sigma));
philpem@5 160 }
philpem@5 161 return resultIm;
philpem@5 162 }
philpem@5 163 /*
philpem@5 164 * PURPOSE: convolve a given image with the gaussian kernel
philpem@5 165 * PARAM CImg<unsigned char> im - image to be convolved upon
philpem@5 166 * PARAM double sigma - gaussian equation parameter
philpem@5 167 * RETURN CImg<float> image resulting from the convolution
philpem@5 168 * */
philpem@5 169 CImg<float> ApplyGaussian(CImg<unsigned char> im,double sigma)
philpem@5 170 {
philpem@5 171 CImg<float> smoothIm(im.dimx(),im.dimy(),1,1,0);
philpem@5 172
philpem@5 173 //make gaussian kernel
philpem@5 174 CImg<float> gk = GaussianKernel(sigma);
philpem@5 175 //apply gaussian
philpem@5 176
philpem@5 177 CImg_5x5(I,int);
philpem@5 178 cimg_for5x5(im,X,Y,0,0,I)
philpem@5 179 {
philpem@5 180 float sum = 0;
philpem@5 181 sum += gk(0,0)*Ibb + gk(0,1)*Ibp + gk(0,2)*Ibc + gk(0,3)*Ibn + gk(0,4)*Iba;
philpem@5 182 sum += gk(1,0)*Ipb + gk(1,1)*Ipp + gk(1,2)*Ipc + gk(1,3)*Ipn + gk(1,4)*Ipa;
philpem@5 183 sum += gk(2,0)*Icb + gk(2,1)*Icp + gk(2,2)*Icc + gk(2,3)*Icn + gk(2,4)*Ica;
philpem@5 184 sum += gk(3,0)*Inb + gk(3,1)*Inp + gk(3,2)*Inc + gk(3,3)*Inn + gk(3,4)*Ina;
philpem@5 185 sum += gk(4,0)*Iab + gk(4,1)*Iap + gk(4,2)*Iac + gk(4,3)*Ian + gk(4,4)*Iaa;
philpem@5 186 smoothIm(X,Y)= sum/256;
philpem@5 187 }
philpem@5 188
philpem@5 189 return smoothIm;
philpem@5 190 }
philpem@5 191 /**
philpem@5 192 * PURPOSE: convert a given rgb image to a MxNX1 single vector grayscale image
philpem@5 193 * PARAM: CImg<unsigned char> im - rgb image to convert
philpem@5 194 * RETURN: CImg<unsigned char> grayscale image with MxNx1x1 dimensions
philpem@5 195 **/
philpem@5 196
philpem@5 197 CImg<unsigned char> RGBtoGrayScale(CImg<unsigned char> &im)
philpem@5 198 {
philpem@5 199 CImg<unsigned char> grayImage(im.dimx(),im.dimy(),im.dimz(),1,0);
philpem@5 200 if (im.dimv() == 3)
philpem@5 201 { cimg_forXYZ(im,X,Y,Z)
philpem@5 202 {
philpem@5 203 grayImage(X,Y,Z,0) = (unsigned char)(0.299*im(X,Y,Z,0) + 0.587*im(X,Y,Z,1) + 0.114*im(X,Y,Z,2));
philpem@5 204 }
philpem@5 205 }
philpem@5 206 grayImage.quantize(255,false);
philpem@5 207 return grayImage;
philpem@5 208 }
philpem@5 209 /**
philpem@5 210 * PURPOSE: aux. function used by CannyEdges to quantize an angle theta given by gradients, dx and dy
philpem@5 211 * into 0 - 7
philpem@5 212 * PARAM: dx,dy - gradient magnitudes
philpem@5 213 * RETURN int value between 0 and 7
philpem@5 214 **/
philpem@5 215 int GetAngle(int dy,int dx)
philpem@5 216 {
philpem@5 217 double angle = cimg::abs(std::atan2((double)dy,(double)dx));
philpem@5 218 if ((angle >= -cimg::valuePI/8)&&(angle <= cimg::valuePI/8))//-pi/8 to pi/8 => 0
philpem@5 219 return 0;
philpem@5 220 else if ((angle >= cimg::valuePI/8)&&(angle <= 3*cimg::valuePI/8))//pi/8 to 3pi/8 => pi/4
philpem@5 221 return 1;
philpem@5 222 else if ((angle > 3*cimg::valuePI/8)&&(angle <= 5*cimg::valuePI/8))//3pi/8 to 5pi/8 => pi/2
philpem@5 223 return 2;
philpem@5 224 else if ((angle > 5*cimg::valuePI/8)&&(angle <= 7*cimg::valuePI/8))//5pi/8 to 7pi/8 => 3pi/4
philpem@5 225 return 3;
philpem@5 226 else if (((angle > 7*cimg::valuePI/8) && (angle <= cimg::valuePI)) || ((angle <= -7*cimg::valuePI/8)&&(angle >= -cimg::valuePI))) //-7pi/8 to -pi OR 7pi/8 to pi => pi
philpem@5 227 return 4;
philpem@5 228 else return 0;
philpem@5 229 }
philpem@5 230 /**
philpem@5 231 * PURPOSE: create an edge map of the given image with hysteresis using thresholds T1 and T2
philpem@5 232 * PARAMS: CImg<float> im the image to perform edge detection on
philpem@5 233 * T1 lower threshold
philpem@5 234 * T2 upper threshold
philpem@5 235 * RETURN CImg<unsigned char> edge map
philpem@5 236 **/
philpem@5 237 CImg<unsigned char> CannyEdges(CImg<float> im, double T1, double T2, bool doHysteresis=false)
philpem@5 238 {
philpem@5 239 CImg<unsigned char> edges(im);
philpem@5 240 CImg<float> secDerivs(im);
philpem@5 241 secDerivs.fill(0);
philpem@5 242 edges.fill(0);
philpem@5 243 CImgList<float> gradients = im.get_gradient("xy",1);
philpem@5 244 int image_width = im.dimx();
philpem@5 245 int image_height = im.dimy();
philpem@5 246
philpem@5 247 { cimg_forXY(im,X,Y)
philpem@5 248 {
philpem@5 249 double Gr = std::sqrt(std::pow((double)gradients[0](X,Y),2.0) + std::pow((double)gradients[1](X,Y),2.0));
philpem@5 250 double theta = GetAngle(Y,X);
philpem@5 251 //if Gradient magnitude is positive and X,Y within the image
philpem@5 252 //take the 2nd deriv in the appropriate direction
philpem@5 253 if ((Gr > 0)&&(X < image_width-1)&&(Y < image_height - 1))
philpem@5 254 {
philpem@5 255 if (theta == 0)
philpem@5 256 secDerivs(X,Y) = im(X+2,Y) - 2*im(X+1,Y) + im(X,Y);
philpem@5 257 else if (theta == 1)
philpem@5 258 secDerivs(X,Y) = im(X+2,Y+2) - 2*im(X+1,Y+1) + im(X,Y);
philpem@5 259 else if (theta == 2)
philpem@5 260 secDerivs(X,Y) = im(X,Y+2) - 2*im(X,Y+1) + im(X,Y);
philpem@5 261 else if (theta == 3)
philpem@5 262 secDerivs(X,Y) = im(X+2,Y+2) - 2*im(X+1,Y+1) + im(X,Y);
philpem@5 263 else if (theta == 4)
philpem@5 264 secDerivs(X,Y) = im(X+2,Y) - 2*im(X+1,Y) + im(X,Y);
philpem@5 265 }
philpem@5 266 }}
philpem@5 267 //for each 2nd deriv that crosses a zero point and magnitude passes the upper threshold.
philpem@5 268 //Perform hysteresis in the direction of the gradient, rechecking the gradient
philpem@5 269 //angle for each pixel that meets the threshold requirement. Stop checking when
philpem@5 270 //the lower threshold is not reached.
philpem@5 271 CImg_5x5(I,float);
philpem@5 272 cimg_for5x5(secDerivs,X,Y,0,0,I)
philpem@5 273 {
philpem@5 274 if ( (Ipp*Ibb < 0)||
philpem@5 275 (Ipc*Ibc < 0)||
philpem@5 276 (Icp*Icb < 0) )
philpem@5 277 {
philpem@5 278 double Gr = std::sqrt(std::pow((double)gradients[0](X,Y),2.0) + std::pow((double)gradients[1](X,Y),2.0));
philpem@5 279 int dir = GetAngle(Y,X);
philpem@5 280 int Xt=X, Yt=Y, delta_x = 0, delta_y=0;
philpem@5 281 double GRt = Gr;
philpem@5 282 if (Gr >= T2)
philpem@5 283 edges(X,Y) = 255;
philpem@5 284 //work along the gradient in one direction
philpem@5 285 if (doHysteresis)
philpem@5 286 {
philpem@5 287 while ((Xt > 0) && (Xt < image_width-1) && (Yt > 0) && (Yt < image_height-1))
philpem@5 288 {
philpem@5 289 switch (dir){
philpem@5 290 case 0:delta_x=0;delta_y=1;break;
philpem@5 291 case 1:delta_x=1;delta_y=1;break;
philpem@5 292 case 2:delta_x=1;delta_y=0;break;
philpem@5 293 case 3:delta_x=1;delta_y=-1;break;
philpem@5 294 case 4:delta_x=0;delta_y=1;break;
philpem@5 295 }
philpem@5 296 Xt += delta_x;
philpem@5 297 Yt += delta_y;
philpem@5 298 GRt = std::sqrt(std::pow((double)gradients[0](Xt,Yt),2.0) + std::pow((double)gradients[1](Xt,Yt),2.0));
philpem@5 299 dir = GetAngle(Yt,Xt);
philpem@5 300 if (GRt >= T1)
philpem@5 301 edges(Xt,Yt) = 255;
philpem@5 302 }
philpem@5 303 //work along gradient in other direction
philpem@5 304 Xt = X; Yt = Y;
philpem@5 305 while ((Xt > 0) && (Xt < image_width-1) && (Yt > 0) && (Yt < image_height-1))
philpem@5 306 {
philpem@5 307 switch (dir){
philpem@5 308 case 0:delta_x=0;delta_y=1;break;
philpem@5 309 case 1:delta_x=1;delta_y=1;break;
philpem@5 310 case 2:delta_x=1;delta_y=0;break;
philpem@5 311 case 3:delta_x=1;delta_y=-1;break;
philpem@5 312 case 4:delta_x=0;delta_y=1;break;
philpem@5 313 }
philpem@5 314 Xt -= delta_x;
philpem@5 315 Yt -= delta_y;
philpem@5 316 GRt = std::sqrt(std::pow((double)gradients[0](Xt,Yt),2.0) + std::pow((double)gradients[1](Xt,Yt),2.0));
philpem@5 317 dir = GetAngle(Yt,Xt);
philpem@5 318 if (GRt >= T1)
philpem@5 319 edges(Xt,Yt) = 255;
philpem@5 320 }
philpem@5 321 }
philpem@5 322 }
philpem@5 323 }
philpem@5 324 return edges;
philpem@5 325 }
philpem@5 326 /**
philpem@5 327 * PURPOSE: perform radon transform of given image
philpem@5 328 * PARAM: CImg<unsigned char> im - image to detect lines
philpem@5 329 * int N - number of angles to consider (should be a power of 2)
philpem@5 330 * (the values of N will be spread over 0 to 2PI)
philpem@5 331 * RETURN CImg<unsigned char> - transform of given image of size, N x D
philpem@5 332 * D = rhomax = sqrt(dimx*dimx + dimy*dimy)/2
philpem@5 333 **/
philpem@5 334 CImg<> RadonTransform(CImg<unsigned char> im,int N)
philpem@5 335 {
philpem@5 336 int image_width = im.dimx();
philpem@5 337 int image_height = im.dimy();
philpem@5 338
philpem@5 339 //calc offsets to center the image
philpem@5 340 float xofftemp = image_width/2.0f - 1;
philpem@5 341 float yofftemp = image_height/2.0f - 1;
philpem@5 342 int xoffset = (int)std::floor(xofftemp + ROUNDING_FACTOR(xofftemp));
philpem@5 343 int yoffset = (int)std::floor(yofftemp + ROUNDING_FACTOR(yofftemp));
philpem@5 344 float dtemp = (float)std::sqrt((double)(xoffset*xoffset + yoffset*yoffset));
philpem@5 345 int D = (int)std::floor(dtemp + ROUNDING_FACTOR(dtemp));
philpem@5 346
philpem@5 347 CImg<> imRadon(N,D,1,1,0);
philpem@5 348
philpem@5 349 //for each angle k to consider
philpem@5 350 for (int k= 0 ; k < N; k++)
philpem@5 351 {
philpem@5 352 //only consider from PI/8 to 3PI/8 and 5PI/8 to 7PI/8
philpem@5 353 //to avoid computational complexity of a steep angle
philpem@5 354 if (k == 0){k = N/8;continue;}
philpem@5 355 else if (k == (3*N/8 + 1)){ k = 5*N/8;continue;}
philpem@5 356 else if (k == 7*N/8 + 1){k = N; continue;}
philpem@5 357
philpem@5 358 //for each rho length, determine linear equation and sum the line
philpem@5 359 //sum is to sum the values along the line at angle k2pi/N
philpem@5 360 //sum2 is to sum the values along the line at angle k2pi/N + N/4
philpem@5 361 //The sum2 is performed merely by swapping the x,y axis as if the image were rotated 90 degrees.
philpem@5 362 for (int d=0; d < D; d++){
philpem@5 363 double theta = 2*k*cimg::valuePI/N;//calculate actual theta
philpem@5 364 double alpha = std::tan(theta+cimg::valuePI/2);//calculate the slope
philpem@5 365 double beta_temp = -alpha*d*std::cos(theta) + d*std::sin(theta);//y-axis intercept for the line
philpem@5 366 int beta = (int)std::floor(beta_temp + ROUNDING_FACTOR(beta_temp));
philpem@5 367 //for each value of m along x-axis, calculate y
philpem@5 368 //if the x,y location is within the boundary for the respective image orientations, add to the sum
philpem@5 369 unsigned int sum1 = 0,
philpem@5 370 sum2 = 0;
philpem@5 371 int M = (image_width >= image_height) ? image_width : image_height;
philpem@5 372 for (int m=0;m < M; m++)
philpem@5 373 {
philpem@5 374 //interpolate in-between values using nearest-neighbor approximation
philpem@5 375 //using m,n as x,y indices into image
philpem@5 376 double n_temp = alpha*(m-xoffset) + beta;
philpem@5 377 int n = (int)std::floor(n_temp + ROUNDING_FACTOR(n_temp));
philpem@5 378 if ((m < image_width) && (n + yoffset >= 0) && (n + yoffset < image_height))
philpem@5 379 {
philpem@5 380 sum1 += im(m, n + yoffset);
philpem@5 381 }
philpem@5 382 n_temp = alpha*(m-yoffset) + beta;
philpem@5 383 n = (int)std::floor(n_temp + ROUNDING_FACTOR(n_temp));
philpem@5 384 if ((m < image_height)&&(n + xoffset >= 0)&&(n + xoffset < image_width))
philpem@5 385 {
philpem@5 386 sum2 += im(-(n + xoffset) + image_width - 1, m);
philpem@5 387 }
philpem@5 388 }
philpem@5 389 //assign the sums into the result matrix
philpem@5 390 imRadon(k,d) = (float)sum1;
philpem@5 391 //assign sum2 to angle position for theta+PI/4
philpem@5 392 imRadon(((k + N/4)%N),d) = (float)sum2;
philpem@5 393 }
philpem@5 394 }
philpem@5 395 return imRadon;
philpem@5 396 }
philpem@5 397 /* references:
philpem@5 398 * 1. See Peter Toft's thesis on the Radon transform: http://petertoft.dk/PhD/index.html
philpem@5 399 * While I changed his basic algorithm, the main idea is still the same and provides an excellent explanation.
philpem@5 400 *
philpem@5 401 * */
philpem@5 402