/************************************************************************** *** *** PROGRAM: invert_cut.cc *** *** Program developed for Project 1 CSE 585 *** *** This program can degrade and restore images using various *** methods. It was written for the second part of project 1. *** *** Author: Nils Krahnstoever *** Date of last revision: 3/3/99 *** **************************************************************************/ /************************************************************************** *** *** INCLUDES *** **************************************************************************/ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <math.h> // Numerical Recipes Include Files #define NRANSI #include "nr.h" #include "nrutil.h" #include "complex.h" // Personal classes for image and matrix handling #include <matrix.h> #include <matrixtools.h> #include <image.h> #include <imagetools.h> // Personal classes for converting images to matrices and vice versa #include <converttools.h> /************************************************************************** *** *** GLOBAL VARIABLES *** **************************************************************************/ // Global variables storing the images of differnet stages of the // restoration. Matrix<float> img_re(256,256); // working matrix Matrix<float> img_im(256,256); Matrix<float> ori_re(256,256); // blurred noise free image Matrix<float> ori_im(256,256); Matrix<float> sys_re(256,256); // system response Matrix<float> sys_im(256,256); Matrix<float> res_re(256,256); // working matrix (often for results) Matrix<float> res_im(256,256); Matrix<float> res_abs(256,256); // absolute results Matrix<float> lap_re(256,256); // laplacian Matrix<float> lap_im(256,256); Matrix<float> noise_fft(256,256); // fft of noise // Global variables storing the input arguments. int mode; // restoration mode float value; // restoration parameter int flag; // general flags int len; // length of motion blurring or -1 if defocus float dir; // direction of motion blurring or defocus sigma float sigma; // noise sigma float from,to; // repeat restoration varying <value> from <from> to <to>... int steps; // ... in <steps> steps /************************************************************************** *** *** FUNCTIONS *** **************************************************************************/ /************************************************************************** *** *** gaussnoise *** *** returns a gaussian distributed random variable of mean <mean> *** and standard deviation <var> *** **************************************************************************/ float gaussnoise(float mean, float var) { static int first=1; static long seed; if(first) { first=0; seed=(long)&seed; if(seed>0) seed=-seed; ran2(&seed); } return(mean+var*gasdev(&seed)); } /************************************************************************** *** *** add_noise *** *** add gaussian distributed noise of mean 0 and stdev of *** <sigma> (global) to the matrix <mat> *** **************************************************************************/ void add_noise(Matrix<float> &mat) { static int fl=0; int rs=mat.rows(); int cs=mat.cols(); int r; int c; static Matrix<float> re(256,256); static Matrix<float> im(256,256); static Image *imgptr; for(r=0;r<rs;r++) { for(c=0;c<cs;c++) { re[r][c]=gaussnoise(0,sigma); mat[r][c]+=re[r][c]; } } if(fl==0) // the first time this function is called { // create an FFT of the noise fl=1; im.zero(); fft(re,im,0); // fft Cabs(noise_fft,im,re); // abs lnp1(noise_fft); // ln( . + 1 ) => ln(|image|+1) if(flag & 1) // save image of fft { imgptr=range_matrix2image(noise_fft); imgptr->saveimg("noise_fft.pgm"); delete imgptr; } } } /************************************************************************** *** *** create_defocus *** *** create a system response function of a blurring process *** caused by a gaussian with sigma of <dir> (global) *** **************************************************************************/ void create_defocus(Matrix<float> &re,Matrix<float> &im) { int w=256,h=256,x,y,x0,y0; float p=acos(-1),sigma,u,v,ua; double norm; Matrix<float> abs(h,w); Matrix<float> re2(h,w),im2(h,w); Image *img; re.zero(); im.zero(); fprintf(stderr,"Creating response (defocus)\n"); sigma=2*p*p*dir*dir; // correct sigma in frequency space norm=0.0; for(x=0;x<w;x++) { for(y=0;y<h;y++) { x0=((x+127)%256-127); // origin is at [0][0] y0=((y+127)%256-127); u=float(x0)/w; v=float(y0)/h; ua=u*u+v*v; re[y][x]= exp(-ua*sigma); norm+=re[y][x]; im[y][x]=0.0; } } // output various images (for debugging) if(flag & 1) { Cabs(abs,re,im); lnp1(abs); img=range_matrix2image(abs); //img=matrix2image(abs); img->saveimg("response.pgm"); delete img; re2=re; im2=im; fft(re2,im2,1); Cabs(abs,re2,im2); abs.scale(1.0/256/256); stat(abs,stderr); //img=matrix2image(abs); img=range_matrix2image(abs); img->saveimg("response_fft.pgm"); delete img; } } /************************************************************************** *** *** create_motion *** *** create a system response function of a blurring process *** caused by motion of length <len> pixel in direction <dir> *** *** two methods are used: *** *** if bit 3 of <flag> is set the response function is calculated *** analytically *** *** if bit 3 of <flag> is zero, the convolution mask is created directly *** and the response function obtained by FFTing the mask *** *** Throughout this project bit 3 of <flag> was set! **************************************************************************/ void create_motion(Matrix<float> &re,Matrix<float> &im) { int w=256,h=256,x,y,x0,y0; int t; float p=acos(-1),dx,dy,u,v,ua,t2,si; Matrix<float> abs(h,w); Image *img; re.zero(); im.zero(); fprintf(stderr,"Creating response (motion)\n"); if(flag & 8) { dx=cos(dir)*p; dy=-sin(dir)*p; for(x=0;x<w;x++) { for(y=0;y<h;y++) { x0=((x+127)%256-127); y0=((y+127)%256-127); u=float(x0)/w; v=float(y0)/h; ua=(dx*u+dy*v)*len; if(fabs(ua)<1e-15) { si=1.0; } else { si=sin(ua)/ua; } re[y][x]=si*cos(-ua); im[y][x]=si*sin(-ua); } } } else { for(t=0;t<len;t++) { t2=t-len/2; x0=256+(int)(t2*cos(dir)); y0=256+(int)(-t2*sin(dir)); re[y0%256][x0%256]=1.0/len; } fft(re,im,0); } // save image (debugging) if(flag & 1) { Cabs(abs,re,im); lnp1(abs); img=range_matrix2image(abs); img->saveimg("response.pgm"); delete img; } } /************************************************************************** *** *** create_response *** *** create a system response function of a blurring process *** **************************************************************************/ void create_response(Matrix<float> &re,Matrix<float> &im) { if(len==-1) { create_defocus(re,im); } else { create_motion(re,im); } } /************************************************************************** *** *** degrade_image *** *** degrade <img> with system response function <sys> *** **************************************************************************/ Matrix<float> *degrade_image(Matrix<float> &img,Matrix<float> &sys_re,Matrix<float> &sys_im) { int r,c; Matrix<float> *mat=new Matrix<float>(256,256); Matrix<float> img_im(256,256); Matrix<float> img_re(256,256); fcomplex a,b,s; img_re=img; img_im.zero(); fprintf(stderr,"norm(image)=%g ",sum(img_re)); // <img> -> FFT domain fft(img_re,img_im,0); // FFT(<img>)*<sys> r=img_re.rows(); c=img_re.cols(); for(r=0;r<256;r++) { for(c=0;c<256;c++) { a.r=img_re[r][c]; a.i=img_im[r][c]; b.r=sys_re[r][c]; b.i=sys_im[r][c]; s=Cmul(a,b); img_re[r][c]=s.r; img_im[r][c]=s.i; } } // FFT(<blurred image>) -> spatial domain fft(img_re,img_im,1); // make real Cabs(*mat,img_re,img_im); // fix scaling mat->scale(1.0/256/256); // did the norm change much? (debugging) fprintf(stderr,"norm(deg)=%g\n",sum(*mat)); // return degraded image return(mat); } /************************************************************************** *** *** laplacian *** *** if mode==4 (constr. least square) we need a laplacian in freq. space *** this function creates one *** **************************************************************************/ void laplacian(Matrix<float> &lap_re,Matrix<float> &lap_im) { Matrix<float> lap_abs(256,256); Image *imgptr; lap_re.zero(); lap_im.zero(); // 0 -1 0 // -1 4 -1 // 0 -1 0 lap_re[0][0]=0; lap_re[0][1]=-1; lap_re[0][2]=0; lap_re[1][0]=-1; lap_re[1][1]=4; lap_re[1][2]=-1; lap_re[2][0]=0; lap_re[2][1]=-1; lap_re[2][2]=0; // FFT fft(lap_re,lap_im,0); // save (debugging) if(flag & 1) { Cabs(lap_abs,lap_re,lap_im); lnp1(lap_abs); imgptr=range_matrix2image(lap_abs); imgptr->saveimg("laplacian.pgm"); delete imgptr; } } /************************************************************************** *** *** invert *** *** invert image <img> with respect to system response <sys> *** *** <mode> (global variable) denotes the method used *** **************************************************************************/ Matrix<float> *invert(Matrix<float> &img_re,Matrix<float> &img_im, Matrix<float> &sys_re,Matrix<float> &sys_im) { fcomplex a,b,s; float dx,d,p,v,n,f; int r,c; Matrix<float> res_re(256,256); // result Matrix<float> res_im(256,256); Matrix<float> *res; // result to be returned res=new Matrix<float>(256,256); // create matrix if(mode==2) v=value*value; // mode2 cutoff frequency^2 for(r=0;r<256;r++) // for each pixel { for(c=0;c<256;c++) { a.r=img_re[r][c]; // a -> G(u,v) a.i=img_im[r][c]; b.r=sys_re[r][c]; // b -> H(u,v) b.i=sys_im[r][c]; // G/H and the variouse flavor of this are stored in s switch(mode) { case 1: // invert ignoring values less than <value> { if(Cabs(b)<value) { s.r=a.r; s.i=a.i; } else s=Cdiv(a,b); break; } case 2: // invert with frequency cut-off { dx=((r+127)%256-127); dx*=dx; d=((c+127)%256-127); d*=d; d+=dx; if(d>v || Cabs(b)<0.05) // still ignore small values { s.r=a.r; s.i=a.i; } else s=Cdiv(a,b); break; } case 3: // add constant (Wiener with constant) { d=Cabs(b); d=1/(d*d+value); b.r*=d; b.i*=-d; s=Cmul(a,b); break; } case 4: // constr. least square using // laplacian <lap> { d=Cabs(b); s.r=lap_re[r][c]; s.i=lap_im[r][c]; p=Cabs(s); d=1/(d*d+value*p*p); b.r*=d; b.i*=-d; s=Cmul(a,b); break; } case 6: // power Spectrum Filter { d=Cabs(b); n=exp(noise_fft[r][c])-1; // noise est. // original image (quite unrealistic) f=ori_re[r][c]*ori_re[r][c]+ori_im[r][c]*ori_im[r][c]; d=sqrt(f/(d*d*f+value*n*n)); s.r=a.r*d; s.i=a.i*d; break; } case 7: // parametric Wiener Filter { d=Cabs(b); n=exp(noise_fft[r][c])-1; // noise est. // original image (quite unrealistic) f=ori_re[r][c]*ori_re[r][c]+ori_im[r][c]*ori_im[r][c]; d=1/(d*d+value*n*n/f); b.r=b.r*d; b.i=-b.i*d; s=Cmul(a,b); break; } default: // just divide s=Cdiv(a,b); } res_re[r][c]=s.r; res_im[r][c]=s.i; } } // FFT back fft(res_re,res_im,1); // make real Cabs(*res,res_re,res_im); // rescale res->scale(1.0/256/256); // return result return res; } /************************************************************************** *** *** rms_diff *** *** calculates the root mean square of difference between the original *** image and the <mat> *** **************************************************************************/ float rms_diff(Image &img,Matrix<float> &mat) { float rms=0.0,d; int i,x,y; i=0; for(y=0;y<256;y++) { for(x=0;x<256;x++) { d=mat[y][x]-(float)img.data(i); i++; d=d*d; rms+=d; } } rms=sqrt(rms/256/256); return rms; } /************************************************************************** *** *** var_diff *** *** calculates the variance of the differences *** this measure makes it possible to assure that a high rms error is *** not caused by a systematic offset of pixel intensity values in the result *** **************************************************************************/ float var_diff(Image &img,Matrix<float> &mat) { float mean=0.0,var=0.0,d; int i,x,y; i=0; for(y=0;y<256;y++) { for(x=0;x<256;x++) { d=(mat[y][x])-(float)img.data(i); i++; mean+=d; } } mean=mean/256/256; i=0; for(y=0;y<256;y++) { for(x=0;x<256;x++) { d=mean-((mat[y][x])-(float)img.data(i)); i++; d*=d; var+=d; } } var=var/256/256; return sqrt(var); } /************************************************************************** *** *** MAIN *** **************************************************************************/ void main(int argc,char *argv[]) { float nsq; Image img(argv[1]); Image *imgptr; Matrix<float> *res=NULL; Matrix<float> *mat; Matrix<float> orig(256,256); float phi,diff; int step=0,noise,nopt; char name[80]; int vary_noise=0; // 11 arguments !!! if (argc!=11) { fprintf(stderr,"usage: invert_cut <image [PGM]> <result PGM> <mode> <value> <length> <dir/sigma> <flag> <noise ampl.> <to> <nval>\n"); fprintf(stderr,"flag : 1 - save respone, laplacian etc...\n"); fprintf(stderr," 2 - optimize least square gamma (mode 4)\n"); fprintf(stderr," 4 - don't create motion image <image [PGM]> is already blurred\n"); fprintf(stderr," 8 - calculate motion response directly (rather than using FFT)\n"); fprintf(stderr," 16 - save animation\n"); fprintf(stderr,"modes: 0 - don't treat zeros\n"); fprintf(stderr," 1 - ignore values less than <value>\n"); fprintf(stderr," 2 - ignore freq greater <value>\n"); fprintf(stderr," 3 - Wiener filtering with constant <value>\n"); fprintf(stderr," 4 - constr. least squared with gamma of <value>\n"); fprintf(stderr," 5 - only add noise\n"); fprintf(stderr," 6 - power spectrum filter\n"); fprintf(stderr," 7 - Wiener Filter\n"); fprintf(stderr,"<len> = -1 => add defocus image\n"); fprintf(stderr,"<noise ampl.> <= 0 => no noise\n"); exit(1); } printf("----\n"); // get input variables mode=atoi(argv[3]); value=atof(argv[4]); from=value; len=atoi(argv[5]); dir=atof(argv[6]); to=atof(argv[9]); steps=atoi(argv[10]); if(len!=-1) dir*=2.0*acos(-1)/360.0; // deg to rad if(argc>=9) sigma=atof(argv[8]); flag=0; if(argc>=8) flag=atoi(argv[7]); // create response function of blurring process create_response(sys_re,sys_im); // convert input image to matrix format mat=image2matrix(img); // print statistics about image stat(*mat,stderr); // obtain fft of image and store in ori_re, ori_im ori_re=*mat; ori_im.zero(); fft(ori_re,ori_im,0); // save resulting fft if desired if(flag & 1) { Cabs(res_abs,ori_re,ori_im); lnp1(res_abs); imgptr=range_matrix2image(res_abs); imgptr->saveimg("orig_fft.pgm"); delete imgptr; } // optain blurred version of image <img> if bit 2 not set // by multiplying with the response function img_re=*mat; if(!(flag & 4)) { fprintf(stderr,"Degrading image!\n"); if(mode!=5) { delete mat; mat=degrade_image(img_re,sys_re,sys_im); // degrade } } else { fprintf(stderr,"Not degrading image!\n"); } if(flag & 1) { Matrix<float> m(256,256); m=*mat; imgptr=matrix2image(m); imgptr->saveimg("motion.pgm"); // save degraded image if(sigma>0) add_noise(m); // add noise delete(imgptr); imgptr=matrix2image(m); imgptr->saveimg("noise.pgm"); // save degraded noisy image delete(imgptr); } orig=*mat; // keep blurred (noise free) image // calc fft of blurred image img_re=*mat; img_im.zero(); fft(img_re,img_im,0); if(flag & 1) { Cabs(res_abs,img_re,img_im); lnp1(res_abs); imgptr=range_matrix2image(res_abs); imgptr->saveimg("image_fft.pgm"); // save fft of blurred image delete imgptr; } // calculate laplacian operator if(mode==4) { laplacian(lap_re,lap_im); } if(sigma<0) vary_noise=1; // repeat for varying noise levels for(noise=0;noise<6;noise++) // different noise levels (not used in proj.) { // NOISE CONTROL if(vary_noise==1) { if(noise==0) sigma=0; else sigma=pow(2.0,(float)noise); // set noise level } else { noise=7; // don't loop anymore } // VARY PARAMETERS OF INVERSION for(step=0;step<steps;step++) // change parameter of invert { // SET <VALUE> if(steps<2) value=from; else value=from+(to-from)*step/(steps-1); // OPTIMIZE OF DESIRED nopt=0;diff=1e10; while(abs(diff)>10 && nopt<10) { nopt++; img_re=orig; // the original blurred image img_im.zero(); nsq=(256-1)*(256-1)*sigma*sigma; if(sigma>0) add_noise(img_re); // add noise fft(img_re,img_im,0); // get FFT if(res!=NULL) delete res; // delete old result res=invert(img_re,img_im,sys_re,sys_im); // invert if(mode==4 && (flag & 2)) // constr. least square? { delete mat; mat=degrade_image(*res,sys_re,sys_im); // degrade phi=normofdiff(*mat,orig); // compare diff=(phi-nsq)/256/256; // calc diff fprintf(stderr,"OPT %d: diff=%g phi=%g nsq=%g\n",nopt,diff,phi,nsq); } diff=0; } // output some quantitative results printf("method=%s, par=%2d, mode=%d, value=%6.4f, noise=%02d, rms error=%8.2f, variance of diff=%8.2f\n",len < 0 ? "g" : "m",len < 0 ? (int) dir : len,mode,value,(int)sigma,rms_diff(img,*res),var_diff(img,*res)); // save restored image (parameters part of the name) if(flag & 16) { sprintf(name,"%s_par%03d_mod%d_val%5.4f_noi%02d.pgm" ,len < 0 ? "g" : "m" ,len < 0 ? (int) dir : len ,mode,value,(int)sigma); imgptr=mean_matrix2image(*res); //imgptr=matrix2image(*res); imgptr->saveimg(name); delete imgptr; } } } // output some statistics stat(*res,stderr); // transfer to PGM imgptr=mean_matrix2image(*res); // save final result imgptr->saveimg(argv[2]); // delete old images and matrices delete imgptr; delete res; }