Fri, 25 Sep 2009 10:50:44 +0100
added dots-per-inch to status readback
philpem@5 | 1 | /* |
philpem@5 | 2 | # |
philpem@5 | 3 | # File : wavelet_atrous.cpp |
philpem@5 | 4 | # ( C++ source file ) |
philpem@5 | 5 | # |
philpem@5 | 6 | # Description : Performs a 2D or 3D 'a trous' wavelet transform |
philpem@5 | 7 | # (using a cubic spline) on an image or a video sequence. |
philpem@5 | 8 | # This file is a part of the CImg Library project. |
philpem@5 | 9 | # ( http://cimg.sourceforge.net ) |
philpem@5 | 10 | # |
philpem@5 | 11 | # Author : Renaud Peteri |
philpem@5 | 12 | # ( Renaud.Peteri(at)mines-paris.org ) |
philpem@5 | 13 | # |
philpem@5 | 14 | # Institution : CWI, Amsterdam |
philpem@5 | 15 | # |
philpem@5 | 16 | # Date : February 2005 |
philpem@5 | 17 | # |
philpem@5 | 18 | # References : Starck, J.-L., Murtagh, F. and Bijaoui, A., |
philpem@5 | 19 | # Image Processing and Data Analysis: The Multiscale Approach, |
philpem@5 | 20 | # Cambridge University Press, 1998. |
philpem@5 | 21 | # (Hardback and softback, ISBN 0-521-59084-1 and 0-521-59914-8.) |
philpem@5 | 22 | # |
philpem@5 | 23 | # License : CeCILL v2.0 |
philpem@5 | 24 | # ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html ) |
philpem@5 | 25 | # |
philpem@5 | 26 | # This software is governed by the CeCILL license under French law and |
philpem@5 | 27 | # abiding by the rules of distribution of free software. You can use, |
philpem@5 | 28 | # modify and/ or redistribute the software under the terms of the CeCILL |
philpem@5 | 29 | # license as circulated by CEA, CNRS and INRIA at the following URL |
philpem@5 | 30 | # "http://www.cecill.info". |
philpem@5 | 31 | # |
philpem@5 | 32 | # As a counterpart to the access to the source code and rights to copy, |
philpem@5 | 33 | # modify and redistribute granted by the license, users are provided only |
philpem@5 | 34 | # with a limited warranty and the software's author, the holder of the |
philpem@5 | 35 | # economic rights, and the successive licensors have only limited |
philpem@5 | 36 | # liability. |
philpem@5 | 37 | # |
philpem@5 | 38 | # In this respect, the user's attention is drawn to the risks associated |
philpem@5 | 39 | # with loading, using, modifying and/or developing or reproducing the |
philpem@5 | 40 | # software by the user in light of its specific status of free software, |
philpem@5 | 41 | # that may mean that it is complicated to manipulate, and that also |
philpem@5 | 42 | # therefore means that it is reserved for developers and experienced |
philpem@5 | 43 | # professionals having in-depth computer knowledge. Users are therefore |
philpem@5 | 44 | # encouraged to load and test the software's suitability as regards their |
philpem@5 | 45 | # requirements in conditions enabling the security of their systems and/or |
philpem@5 | 46 | # data to be ensured and, more generally, to use and operate it in the |
philpem@5 | 47 | # same conditions as regards security. |
philpem@5 | 48 | # |
philpem@5 | 49 | # The fact that you are presently reading this means that you have had |
philpem@5 | 50 | # knowledge of the CeCILL license and that you accept its terms. |
philpem@5 | 51 | # |
philpem@5 | 52 | */ |
philpem@5 | 53 | |
philpem@5 | 54 | #include "CImg.h" |
philpem@5 | 55 | using namespace cimg_library; |
philpem@5 | 56 | |
philpem@5 | 57 | // The lines below are necessary when using a non-standard compiler as visualcpp6. |
philpem@5 | 58 | #ifdef cimg_use_visualcpp6 |
philpem@5 | 59 | #define std |
philpem@5 | 60 | #endif |
philpem@5 | 61 | #ifdef min |
philpem@5 | 62 | #undef min |
philpem@5 | 63 | #undef max |
philpem@5 | 64 | #endif |
philpem@5 | 65 | |
philpem@5 | 66 | #ifndef cimg_imagepath |
philpem@5 | 67 | #define cimg_imagepath "img/" |
philpem@5 | 68 | #endif |
philpem@5 | 69 | |
philpem@5 | 70 | CImg<float> mask_x(const unsigned char scale) { |
philpem@5 | 71 | unsigned char d1 = (unsigned char)std::pow(2.0,(double)(scale-1)); |
philpem@5 | 72 | unsigned char d2 = (unsigned char)std::pow(2.0,(double)(scale)); |
philpem@5 | 73 | unsigned char cx = (unsigned char)std::pow(2.0,(double)(scale)); |
philpem@5 | 74 | unsigned char res = (unsigned char)std::pow(2.0,(double)scale); |
philpem@5 | 75 | CImg<float> m(2*res +1,1,1);m.fill(0); |
philpem@5 | 76 | m(cx) = 6.0; |
philpem@5 | 77 | m(cx-d1) = m(cx+d1) =4.0; |
philpem@5 | 78 | m(cx-d2) = m(cx+d2) =1.0; |
philpem@5 | 79 | m /= 16.0; |
philpem@5 | 80 | return m; |
philpem@5 | 81 | } |
philpem@5 | 82 | |
philpem@5 | 83 | CImg<float> mask_y(const unsigned char scale) { |
philpem@5 | 84 | unsigned char d1 = (unsigned char)std::pow(2.0,(double)(scale-1)); |
philpem@5 | 85 | unsigned char d2 = (unsigned char)std::pow(2.0,(double)(scale)); |
philpem@5 | 86 | unsigned char cy = (unsigned char)std::pow(2.0,(double)(scale)); |
philpem@5 | 87 | unsigned char res = (unsigned char)std::pow(2.0,(double)scale); |
philpem@5 | 88 | CImg<float> m(1,2*res +1);m.fill(0); |
philpem@5 | 89 | m(0,cy) = 6.0; |
philpem@5 | 90 | m(0,cy-d1) = m(0,cy+d1) =4.0; |
philpem@5 | 91 | m(0,cy-d2) = m(0,cy+d2) =1.0; |
philpem@5 | 92 | m /= 16.0; |
philpem@5 | 93 | return m; |
philpem@5 | 94 | } |
philpem@5 | 95 | |
philpem@5 | 96 | CImg<float> mask_t(const unsigned char scale) { |
philpem@5 | 97 | unsigned char d1 = (unsigned char)std::pow(2.0,(double)(scale-1)); |
philpem@5 | 98 | unsigned char d2 = (unsigned char)std::pow(2.0,(double)(scale)); |
philpem@5 | 99 | unsigned char ct = (unsigned char)std::pow(2.0,(double)(scale)); |
philpem@5 | 100 | unsigned char res = (unsigned char)std::pow(2.0,(double)scale); |
philpem@5 | 101 | CImg<float> m(1,1,2*res +1);m.fill(0); |
philpem@5 | 102 | m(0,0,ct) = 6.0; |
philpem@5 | 103 | m(0,0,ct-d1) = m(0,0,ct+d1) =4.0; |
philpem@5 | 104 | m(0,0,ct-d2) = m(0,0,ct+d2) =1.0; |
philpem@5 | 105 | m /= 16.0; |
philpem@5 | 106 | return m; |
philpem@5 | 107 | } |
philpem@5 | 108 | |
philpem@5 | 109 | /*------------------ |
philpem@5 | 110 | Main procedure |
philpem@5 | 111 | ----------------*/ |
philpem@5 | 112 | int main(int argc,char **argv) { |
philpem@5 | 113 | |
philpem@5 | 114 | cimg_usage("Perform an 'a trous' wavelet transform (using a cubic spline) on an image or on a video sequence.\n" |
philpem@5 | 115 | "This wavelet transform is undecimated and produces 2 images/videos at each scale. For an example of\n" |
philpem@5 | 116 | "decomposition on a video, try -i img/trees.inr (sequence from the MIT).\n" |
philpem@5 | 117 | "\t(Type -h for help)"); |
philpem@5 | 118 | |
philpem@5 | 119 | // Read command line parameters |
philpem@5 | 120 | const char |
philpem@5 | 121 | *name_i = cimg_option("-i",cimg_imagepath "lena.pgm","Input image or video"), |
philpem@5 | 122 | *name_o = cimg_option("-o","","Name of the multiscale analysis output"), |
philpem@5 | 123 | *axe_dec = cimg_option("-axe",(char*)NULL,"Perform the multiscale decomposition in just one direction ('x', 'y' or 't')"); |
philpem@5 | 124 | const unsigned int |
philpem@5 | 125 | s = cimg_option("-s",3,"Scale of decomposition"); |
philpem@5 | 126 | |
philpem@5 | 127 | const bool help = cimg_option("-h",false,"Display Help"); |
philpem@5 | 128 | if(help) exit(0); |
philpem@5 | 129 | |
philpem@5 | 130 | // Initialize Image Data |
philpem@5 | 131 | std::fprintf(stderr," - Load image sequence '%s'...\n",cimg::basename(name_i)); |
philpem@5 | 132 | const CImg<float> texture_in(name_i); |
philpem@5 | 133 | CImg<float> mask_conv_x, mask_conv_y, mask_conv_t; |
philpem@5 | 134 | CImgList<float> res(s, texture_in.dimx(),texture_in.dimy(),texture_in.dimz()); |
philpem@5 | 135 | CImgList<float> wav(s,texture_in.dimx(), texture_in.dimy(), texture_in.dimz()); |
philpem@5 | 136 | cimglist_for(res,l) { res(l).fill(0.0); wav(l).fill(0.0);} |
philpem@5 | 137 | unsigned int i; |
philpem@5 | 138 | |
philpem@5 | 139 | if (!axe_dec){ |
philpem@5 | 140 | // Perform the multiscale decomposition in all directions |
philpem@5 | 141 | for(i=0;i<s;i++){ |
philpem@5 | 142 | std::fprintf(stderr," - Performing scale %u ...\n",i+1); |
philpem@5 | 143 | if(i==0){ res(i) = texture_in;} else { res(i) = res(i-1);} |
philpem@5 | 144 | mask_conv_x = mask_x(i+1); |
philpem@5 | 145 | res(i) = res(i).get_convolve(mask_conv_x); |
philpem@5 | 146 | mask_conv_y = mask_y(i+1); |
philpem@5 | 147 | res(i) = res(i).get_convolve(mask_conv_y); |
philpem@5 | 148 | mask_conv_t = mask_t(i+1); |
philpem@5 | 149 | res(i) = res(i).get_convolve(mask_conv_t); |
philpem@5 | 150 | if(i==0){wav(i) = texture_in - res(i);} // res(0) and wav(0) are the 1st scale of decompostion |
philpem@5 | 151 | else {wav(i) = res(i-1) - res(i);} |
philpem@5 | 152 | } } |
philpem@5 | 153 | |
philpem@5 | 154 | if (axe_dec) { |
philpem@5 | 155 | // Perform the multiscale decomposition in just one direction |
philpem@5 | 156 | char c; |
philpem@5 | 157 | c = cimg::uncase(axe_dec[0]); |
philpem@5 | 158 | fprintf(stderr," - Decompose the image along axe '%c'\n",c); fflush(stdout); |
philpem@5 | 159 | |
philpem@5 | 160 | switch(c) { |
philpem@5 | 161 | case 'x': { |
philpem@5 | 162 | for(i=0;i<s;i++) { |
philpem@5 | 163 | std::fprintf(stderr," - Performing scale %u ...\n",i+1); |
philpem@5 | 164 | if(i==0){ res(i) = texture_in;} else { res(i) = res(i-1);} |
philpem@5 | 165 | mask_conv_x = mask_x(i+1); |
philpem@5 | 166 | res(i) = res(i).get_convolve(mask_conv_x); |
philpem@5 | 167 | if(i==0){wav(i) = texture_in - res(i);} |
philpem@5 | 168 | else {wav(i) = res(i-1) - res(i);}}} |
philpem@5 | 169 | break; |
philpem@5 | 170 | |
philpem@5 | 171 | case 'y': { |
philpem@5 | 172 | for(i=0;i<s;i++) { |
philpem@5 | 173 | std::fprintf(stderr," - Performing scale %u ...\n",i+1); |
philpem@5 | 174 | if(i==0){ res(i) = texture_in;} else { res(i) = res(i-1);} |
philpem@5 | 175 | mask_conv_y = mask_y(i+1); |
philpem@5 | 176 | res(i) = res(i).get_convolve(mask_conv_y); |
philpem@5 | 177 | if(i==0){wav(i) = texture_in - res(i);} |
philpem@5 | 178 | else {wav(i) = res(i-1) - res(i);}}} |
philpem@5 | 179 | break; |
philpem@5 | 180 | |
philpem@5 | 181 | case 't': { |
philpem@5 | 182 | for(i=0;i<s;i++) { |
philpem@5 | 183 | std::fprintf(stderr," - Performing scale %u ...\n",i+1); |
philpem@5 | 184 | if(i==0){ res(i) = texture_in;} else { res(i) = res(i-1);} |
philpem@5 | 185 | mask_conv_t = mask_t(i+1); |
philpem@5 | 186 | res(i) = res(i).get_convolve(mask_conv_t); |
philpem@5 | 187 | if(i==0){wav(i) = texture_in - res(i);} |
philpem@5 | 188 | else {wav(i) = res(i-1) - res(i);}}} |
philpem@5 | 189 | break; |
philpem@5 | 190 | |
philpem@5 | 191 | default: throw CImgException("Error, unknow decompostion axe '%c', try 'x', 'y' or 't'",c); |
philpem@5 | 192 | } |
philpem@5 | 193 | fputc('\n',stderr); |
philpem@5 | 194 | } |
philpem@5 | 195 | |
philpem@5 | 196 | if (*name_o){ |
philpem@5 | 197 | // Save the Multi-Scale Analysis |
philpem@5 | 198 | std::fprintf(stderr," - Saving of all output sequences : %s in the msa/ directory... \n",cimg::basename(name_o)); |
philpem@5 | 199 | int count = 1; // res0 = original image |
philpem@5 | 200 | char filename[256] = "", filename_wav[256] = ""; |
philpem@5 | 201 | char STmp[3] = ""; |
philpem@5 | 202 | system("mkdir msa"); |
philpem@5 | 203 | for(i=0;i<s;i++){ |
philpem@5 | 204 | strcpy( filename, "msa/res" ); |
philpem@5 | 205 | strcpy( filename_wav, "msa/wav" ); |
philpem@5 | 206 | if( count < 10 ) |
philpem@5 | 207 | { strcat( filename, "0" );strcat( filename_wav, "0" );} |
philpem@5 | 208 | sprintf( STmp, "%d_", count ); |
philpem@5 | 209 | strcat( filename, STmp ); strcat( filename_wav, STmp ); |
philpem@5 | 210 | strcat( filename,name_o);strcat( filename_wav,name_o); |
philpem@5 | 211 | res(i).save(filename); |
philpem@5 | 212 | wav(i).save(filename_wav); |
philpem@5 | 213 | count++; |
philpem@5 | 214 | } |
philpem@5 | 215 | } |
philpem@5 | 216 | |
philpem@5 | 217 | // Result visualization |
philpem@5 | 218 | const float value = 255; |
philpem@5 | 219 | for(i=0;i<s;i++) { |
philpem@5 | 220 | res[i].normalize(0,255).draw_text(2,2,"Scale %d",&value,0,1,11,i); |
philpem@5 | 221 | wav[i].normalize(0,255).draw_text(2,2,"Scale %d",&value,0,1,11,i); |
philpem@5 | 222 | } |
philpem@5 | 223 | |
philpem@5 | 224 | CImgDisplay disp(res,"Approximations levels by increasing scale",0); |
philpem@5 | 225 | CImgDisplay disp2(wav,"Wavelet coefficients by increasing scale",0); |
philpem@5 | 226 | while ( !disp.is_closed && !disp.is_keyQ && !disp.is_keyESC && |
philpem@5 | 227 | !disp2.is_closed && !disp2.is_keyQ && !disp2.is_keyESC ) { |
philpem@5 | 228 | if (disp.is_resized) disp.resize().display(res); |
philpem@5 | 229 | if (disp2.is_resized) disp2.resize().display(wav); |
philpem@5 | 230 | CImgDisplay::wait(disp,disp2); |
philpem@5 | 231 | } |
philpem@5 | 232 | |
philpem@5 | 233 | return 0; |
philpem@5 | 234 | } |