/* Panorama_Tools - Generate, Edit and Convert Panoramic Images Copyright (C) 1998,1999 - Helmut Dersch der@fh-furtwangen.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /*------------------------------------------------------------*/ #include "filter.h" #include "fftn.h" #include "f2c.h" static void fconvolution ( TrformStr *TrPtr, Image *psf ); static void makePSF ( pt_int32 width, pt_int32 height, Image *image, double *re, double *im, int color, int direction ); static void makeDoubleDiffImage ( Image *src, Image *fimage, double *re, double *im, int color ); static int makeDoubleImage ( Image *image, double *re, double *im, int color, double pgamma ); static void makeUcharImage ( Image *image, double *re, int color ); // static void makeGaussPSF ( Image *im, double s ); static void windowFunction ( double *im, pt_int32 width, pt_int32 height, double frame); // static void invWindowFunction ( double *im, int width, int height, double frame); static void fresize ( TrformStr *TrPtr ); void fourier ( TrformStr *TrPtr, cPrefs *cP ) { Image psf, nff; psf.data = nff.data = NULL; switch( cP->fourier_mode ) { case _faddBlurr: if( readPSD( &psf, &(cP->psf), 1) != 0 ) { PrintError("Error reading Point Spread Image"); TrPtr->success = 0; return; } fconvolution( TrPtr, &psf ); break; case _fremoveBlurr: if( readPSD( &psf, &(cP->psf), 1) != 0 ) { PrintError("Error reading Point Spread Image"); TrPtr->success = 0; return; } if( cP->fourier_nf == _nf_internal ) { memcpy( &nff, TrPtr->src, sizeof( Image )); nff.data = (unsigned char**) mymalloc( (size_t)nff.dataSize ); if( nff.data == NULL ) { PrintError("Not enough memory"); TrPtr->success = 0; goto _fourier_exit; } noisefilter( &nff, TrPtr->src ); } else { if( readPSD( &nff, &(cP->nff), 1) != 0 ) { PrintError("Error reading Filtered Image"); TrPtr->success = 0; goto _fourier_exit; } if( !HaveEqualSize( &nff, TrPtr->src ) ) { PrintError("Filtered Image and Source must have equal Size and Pixel Size"); TrPtr->success = 0; goto _fourier_exit; } } // Now we have a noise filtered image in nff fwiener( TrPtr, &nff, &psf, cP->filterfactor, cP->fourier_frame ); myfree( (void**) nff.data ); break; case _fresize: { if(cP->width <= 0 && cP->height <= 0) { PrintError("Parameter error: New image dimensions"); TrPtr->success = 0; goto _fourier_exit; } fresize( TrPtr ); } break; default:PrintError("Unknown Error"); TrPtr->success = 0; return; break; } _fourier_exit: if( psf.data ) myfree( (void**)psf.data ); } #define BX 1 // 2 #define MSUM 2.6 //4.4 void noisefilter( Image *dest, Image *src ) { register int x,y,cy,cx,i,k; int fc, bpp; register unsigned char *s, *d; double r,g,b, fi; #if 0 double bl[5][5] = { { 0.0, 0.1, 0.2, 0.1, 0.0}, { 0.1, 0.2, 0.5, 0.2, 0.1}, { 0.2, 0.5, 1.0, 0.5, 0.2}, { 0.1, 0.2, 0.5, 0.2, 0.1}, { 0.0, 0.1, 0.2, 0.1, 0.0}}; #endif double bl[3][3] = { { 0.1, 0.3, 0.1}, { 0.3, 1.0, 0.3}, { 0.1, 0.3, 0.1}}; d = *(dest->data); s = *(src->data); if( src->bitsPerPixel == 32 ) { fc = 1; bpp = 4; } else { fc = 0; bpp = 3; } memcpy( d, s, (size_t)(dest->dataSize)); for(y=BX; yheight-BX; y++) { for(x=BX; xwidth-BX;x++) { cy = y*src->bytesPerLine + x*bpp + fc; r = b = g = 0.0; for(i=-BX; i<=BX; i++) { for(k=-BX; k<=BX; k++) { cx = cy + k*bpp + i*src->bytesPerLine; fi = bl[i+BX][k+BX]; r+=(double)s[cx] * fi; g+=(double)s[cx+1] * fi; b+=(double)s[cx+2] * fi; } } r/= MSUM ; g/= MSUM; b/= MSUM; DBL_TO_UC( d[cy], r ); DBL_TO_UC( d[cy+1], g ); DBL_TO_UC( d[cy+2], b ); } } return; } #define UPDATE_PROGRESS_CONVOLUTION prog += delta; sprintf( percent, "%d", prog ); \ if( ! Progress( _setProgress, percent ) ) \ { \ TrPtr->success = 0; goto _fconvolution_exit; \ } static void fconvolution( TrformStr *TrPtr, Image *psf ) { int dims[2], i, k, dim, prog=0, delta = 100/15; double **Re = NULL, **Im = NULL, **PRe = NULL, **PIm = NULL; char percent[25]; dims[0] = TrPtr->src->width; dims[1] = TrPtr->src->height; dim = TrPtr->src->width * TrPtr->src->height; Progress( _initProgress, "Convolution Filter" ); Re = (double**)mymalloc( dim * sizeof( double ) ); Im = (double**)mymalloc( dim * sizeof( double ) ); PRe = (double**)mymalloc( dim * sizeof( double ) ); PIm = (double**)mymalloc( dim * sizeof( double ) ); if( Re == NULL || Im == NULL || PRe == NULL || PIm == NULL) { PrintError("Not enough memory"); TrPtr->success = 0; goto _fconvolution_exit; } for( i=0; i<3; i++ ) { register double x,y,a,b; UPDATE_PROGRESS_CONVOLUTION makePSF( TrPtr->src->width, TrPtr->src->height, psf, *PRe, *PIm, i, 1 ); fftn (2, dims, *PRe, *PIm, 1, 1.0 ); UPDATE_PROGRESS_CONVOLUTION if( makeDoubleImage ( TrPtr->src, *Re, *Im, i, TrPtr->gamma ) != 0 ) { PrintError("Could not make Real-version of image"); TrPtr->success = 0; goto _fconvolution_exit; } fftn (2, dims, *Re, *Im, 1, 1.0 ); // forward transform; don't scale UPDATE_PROGRESS_CONVOLUTION // Multiply with psf for( k= 0; kdest, *Re, i ); } TrPtr->success = 1; _fconvolution_exit: Progress( _disposeProgress, percent ); fft_free(); if( Re != NULL ) myfree( (void**)Re ); if( Im != NULL ) myfree( (void**)Im ); if( PRe != NULL ) myfree( (void**)PRe ); if( PIm != NULL ) myfree( (void**)PIm ); } #define UPDATE_PROGRESS_WIENER prog += delta; sprintf( percent, "%d", prog ); \ if( ! Progress( _setProgress, percent ) ) \ { \ TrPtr->success = 0; goto _fwiener_exit; \ } void fwiener( TrformStr *TrPtr, Image *nf, Image *psf, double gamma , double frame) { int dims[2], i, k, dim, prog=0, delta = 100/27; double **d1 = NULL, **d2 = NULL, **d3 = NULL, **d4 = NULL; // double arrays char percent[25]; dims[0] = TrPtr->src->width; dims[1] = TrPtr->src->height; dim = TrPtr->src->width * TrPtr->src->height; Progress( _initProgress, "Wiener Filter" ); d1 = (double**)mymalloc( dim * sizeof( double ) ); d2 = (double**)mymalloc( dim * sizeof( double ) ); d3 = (double**)mymalloc( dim * sizeof( double ) ); d4 = (double**)mymalloc( dim * sizeof( double ) ); if( d1 == NULL || d2 == NULL || d3 == NULL || d4 == NULL ) { PrintError("Not enough memory"); TrPtr->success = 0; goto _fwiener_exit; } for( i=0; i<3; i++ ) { register double x,y,si,sn,rden,a,b; UPDATE_PROGRESS_WIENER if( makeDoubleImage ( nf, *d1, *d2, i, TrPtr->gamma ) != 0 ) { PrintError("Could not make Real-Version of image"); TrPtr->success = 0; goto _fwiener_exit; } fftn (2, dims, *d1, *d2, 1, 1.0 ); // Noise filtered image UPDATE_PROGRESS_WIENER makeDoubleDiffImage ( TrPtr->src, nf, *d3, *d4, i ); fftn (2, dims, *d3, *d4, 1, 1.0 ); // Noise in image UPDATE_PROGRESS_WIENER for( k= 0; ksrc->width, TrPtr->src->height, psf, *d3, *d4, i, -1 ); fftn (2, dims, *d3, *d4, 1, 1.0 ); // H(w1,w2) UPDATE_PROGRESS_WIENER for( k= 0; ksrc, *d3, *d4, i, TrPtr->gamma ) != 0 ) { PrintError("Could not make Real-Version of image"); TrPtr->success = 0; goto _fwiener_exit; } if( frame > 0.0) windowFunction( *d3, TrPtr->src->width, TrPtr->src->height, frame ); fftn (2, dims, *d3, *d4, 1, 1.0 ); // v(w1,w2) UPDATE_PROGRESS_WIENER for( k= 0; ksrc->width, TrPtr->src->height, 25.0); UPDATE_PROGRESS_WIENER makeUcharImage ( TrPtr->dest, *d1, i ); } TrPtr->success = 1; _fwiener_exit: Progress( _disposeProgress, percent ); fft_free(); if( d1 != NULL ) myfree( (void**)d1 ); if( d2 != NULL ) myfree( (void**)d2 ); if( d3 != NULL ) myfree( (void**)d3 ); if( d4 != NULL ) myfree( (void**)d4 ); } // Create double point spread function from colour channel in image. // Move point from center to (x|y) = (0|0) // Mirror image (both x & y) if backXform (direction = -1) static void makePSF( pt_int32 width, pt_int32 height, Image *image, double *re, double *im, int color, int direction ) { pt_int32 w, h, w2, h2, dim = width*height, cb, bpp, bpl, yw, cy; register pt_int32 i,x,y; register unsigned char *data = *(image->data); register double scale, *r; w = (width < image->width ? width/2 : image->width /2 ); h = (height< image->height ? height/2: image->height/2 ); w2 = image->width/2; h2 = image->height/2; bpl=image->bytesPerLine; if( image->bitsPerPixel == 32 ) { cb = 1 + color; bpp = 4; } else { cb = 0 + color; bpp = 3; } for(i=0; idata); int cy, dy, cl = color, bpp = image->bitsPerPixel/8; if( SetUpGamma( pgamma, 1 ) != 0 ) return -1; if( bpp == 4 ) cl++; for( y=0; yheight; y++) { cy = y * image->bytesPerLine + cl; dy = y * image->width; for( x=0; xwidth; x++) { re[ dy + x ] = glu.DeGamma[ (int) (data[ cy + bpp * x ]) ]; im[ dy + x ] = 0.0; } } return 0; } // Subtract image - (noise filtered image) // allow negative values static void makeDoubleDiffImage ( Image *src, Image *fimage, double *re, double *im, int color ) { register int x,y; register unsigned char *data1 = *(src->data), *data2 = *(fimage->data); int cy, dy, cl = color, bpp = src->bitsPerPixel/8, cf; if( bpp == 4 ) cl++; for( y=0; yheight; y++) { cy = y * src->bytesPerLine + cl; dy = y * src->width; for( x=0; xwidth; x++) { cf = cy + bpp * x; re[ dy + x ] = (double) data1[ cf ] - (double) data2[ cf ]; im[ dy + x ] = 0.0; } } } // Create unsigned char version of image data // color = 0,1,2 static void makeUcharImage( Image *image, double *re, int color ) { register int x,y; register unsigned char *data = *(image->data); int cy, dy, cl = color, bpp = image->bitsPerPixel/8; double maxval = 0.0, scale = 1.0; if( bpp == 4 ) cl++; for( y=0; yheight; y++) { cy = y * image->bytesPerLine + cl; dy = y * image->width; for( x=0; xwidth; x++) { if( re[ dy + x ] > maxval) maxval = re[ dy + x ]; } } if(maxval > (double)glu.ChannelSize || maxval < (double)glu.ChannelSize/3.0 ) scale = (double)glu.ChannelSize / maxval; for( y=0; yheight; y++) { cy = y * image->bytesPerLine + cl; dy = y * image->width; for( x=0; xwidth; x++) { data[ cy + bpp * x ] = (unsigned char)(gamma_correct( re[ dy + x ] * scale )); } } // Dangerous, but should be ok if( glu.DeGamma ) free( glu.DeGamma ); glu.DeGamma = NULL; if( glu.Gamma ) free( glu.Gamma ); glu.Gamma = NULL; } /* static void makeGaussPSF( Image *im, double s ) { register int x,y,cy,bpp,fc; int w2 = im->width/2, h2 = im->height/2; unsigned char pix, *data = *(im->data), *d; double xw,yw,sw = s*s; if( im->bitsPerPixel == 32 ) { fc = 1; bpp = 4; } else { fc = 0; bpp = 3; } for(y=0; yheight; y++) { cy = y*im->bytesPerLine + fc; for(x=0; xwidth; x++) { d = &(data[cy+x*bpp]); xw = x-w2; yw = y-h2; pix = 255.0 * exp( -(yw*yw + xw*xw)/sw ); *d++ = pix; *d++ = pix; *d = pix; } } } */ // Mask image with window function exp( -frame/x ) #define MEDIUMGRAY 127.0 static void windowFunction( double *im, pt_int32 width, pt_int32 height, double frame) { double *wf; register double z; pt_int32 w2 = width/2, h2 = height/2,dx,dy,cy,i,x,y; pt_int32 dl = (width < height ? width : height) / 2 + 1; wf = (double*)malloc( dl * sizeof( double ) ); if( wf == NULL ) { PrintError("Not enough memory to apply windowfunction. Trying without..."); return; } wf[0] = 0.0; for(i=1; isuccess = 0; goto _antialias_exit; \ } static void fresize( TrformStr *TrPtr ) { int dims[2], dest_dims[2], i, dim, prog=0, delta = 100/12; double **Re = NULL, **Im = NULL; char percent[25]; double *re,*im; // unsigned char *ch; // int x,y,dy,sy,x1,x2,y1,y2,rx,ry; int x,y,dy,sy,x1,y1,rx,ry; dims[0] = TrPtr->src->width; dims[1] = TrPtr->src->height; dim = max( TrPtr->src->width * TrPtr->src->height, TrPtr->dest->width * TrPtr->dest->height ); dest_dims[1] = TrPtr->dest->height; dest_dims[0] = TrPtr->dest->width; ry = (TrPtr->src->height - TrPtr->dest->height); rx = (TrPtr->src->width - TrPtr->dest->width); x1 = min( TrPtr->dest->width/2, TrPtr->src->width/2) ; y1 = min( TrPtr->dest->height/2, TrPtr->src->height/2) ; Progress( _initProgress, "Resize Filter" ); Re = (double**)mymalloc( dim * sizeof( double ) ); Im = (double**)mymalloc( dim * sizeof( double ) ); if( Re == NULL || Im == NULL ) { PrintError("Not enough memory"); TrPtr->success = 0; goto _antialias_exit; } re = *Re; im = *Im; for( i=0; i<3; i++ ) { UPDATE_PROGRESS_ANTIALIAS if( makeDoubleImage ( TrPtr->src, *Re, *Im, i, TrPtr->gamma ) != 0 ) { PrintError("Could not make Real-version of image"); TrPtr->success = 0; goto _antialias_exit; } fftn (2, dims, *Re, *Im, 1, -1.0 ); // forward transform; don't scale UPDATE_PROGRESS_ANTIALIAS if( TrPtr->dest->width < TrPtr->src->width ) { // Cut frame if decimating for( y=0; ysrc->width; dy = y * TrPtr->dest->width; for(x=0; xdest->width; x++) { if(x=x1) { re[dy+x] = re[sy+x+rx];im[dy+x] = im[sy+x+rx]; } } } for( y=y1; ydest->height; y++ ) { sy = (y+ry) * TrPtr->src->width; dy = y * TrPtr->dest->width; for(x=0; xdest->width; x++) { if(x=x1) { re[dy+x] = re[sy+x+rx];im[dy+x] = im[sy+x+rx]; } } } } else { // Pad if enlarging for( y=TrPtr->dest->height-1;y>=y1; y-- ) { sy = (y+ry) * TrPtr->src->width; dy = y * TrPtr->dest->width; for(x=TrPtr->dest->width-1;x>=0; x--) { if(x=x1) { re[dy+x] = re[sy+x+rx];im[dy+x] = im[sy+x+rx]; } else { re[dy+x] = 0.0;im[dy+x] = 0.0; } } } for( y=y1-1;y>=0;y--) { sy = y * TrPtr->src->width; dy = y * TrPtr->dest->width; for(x=TrPtr->dest->width-1;x>=0; x--) { if(x=x1) { re[dy+x] = re[sy+x+rx];im[dy+x] = im[sy+x+rx]; } else { re[dy+x] = 0.0;im[dy+x] = 0.0; } } } } UPDATE_PROGRESS_ANTIALIAS fftn (2, dest_dims, *Re, *Im, -1, 1.0 ); // backward transform; scale by dim; UPDATE_PROGRESS_ANTIALIAS makeUcharImage ( TrPtr->dest, *Re, i ); } TrPtr->success = 1; _antialias_exit: Progress( _disposeProgress, percent ); fft_free(); if( Re != NULL ) myfree( (void**)Re ); if( Im != NULL ) myfree( (void**)Im ); }