$search
00001 00002 #include <blort/TomGine/tgImageProcessor.h> 00003 #include <blort/TomGine/tgError.h> 00004 #include <stdexcept> 00005 00006 using namespace TomGine; 00007 00008 tgImageProcessor::tgImageProcessor( const char *gauss_frag_file, 00009 const char *sobel_frag_file, 00010 const char *thinning_frag_file, 00011 const char *spreading_frag_file, 00012 unsigned img_width, unsigned img_height, 00013 int avg_resolution) 00014 { 00015 m_shadeGauss = new tgShader(NULL, gauss_frag_file, NULL); 00016 m_shadeSobel = new tgShader(NULL, sobel_frag_file, NULL); 00017 m_shadeThinning = new tgShader(NULL, thinning_frag_file, NULL); 00018 m_shadeSpreading = new tgShader(NULL, spreading_frag_file, NULL); 00019 00020 init(img_width, img_height); 00021 initShader((float)img_width, (float)img_height); 00022 initFBO(avg_resolution); 00023 tgCheckError("[tgImageProcessor::tgImageProcessor()]"); 00024 } 00025 00026 tgImageProcessor::~tgImageProcessor(){ 00027 if(glIsFramebuffer(fbo)) glDeleteFramebuffers(1, &fbo); 00028 if(glIsTexture(fbo_tex)) glDeleteTextures(1,&fbo_tex); 00029 if(glIsTexture(fbo_tex_depth)) glDeleteTextures(1,&fbo_tex_depth); 00030 00031 if(m_shadeGauss) delete(m_shadeGauss); 00032 if(m_shadeSobel) delete(m_shadeSobel); 00033 if(m_shadeThinning) delete(m_shadeThinning); 00034 if(m_shadeSpreading) delete(m_shadeSpreading); 00035 } 00036 00037 // Main initialisation function 00038 void tgImageProcessor::init(unsigned width, unsigned height){ 00039 00040 m_width = width; 00041 m_height = height; 00042 00043 // Initialise camera 00044 m_cam_ortho.Set( 0.0f, 0.0f, 1.0f, 00045 0.0f, 0.0f, 0.0f, 00046 0.0f, 1.0f, 0.0f, 00047 45.0f, m_width, m_height, 00048 0.1f, 10.0f, 00049 GL_ORTHO); 00050 } 00051 00052 // Load and compile shaders and set parameters 00053 void tgImageProcessor::initShader(float w, float h){ 00054 float sq2 = 1.0f/sqrt(2.0f); 00055 00056 // offsets of neighbouring pixels in texture coordinates 00057 GLfloat offX[9] = { -1.0f/w, 0.0, 1.0f/w, 00058 -1.0f/w, 0.0, 1.0f/w, 00059 -1.0f/w, 0.0, 1.0f/w }; 00060 GLfloat offY[9] = { 1.0f/h, 1.0f/h, 1.0f/h, 00061 0.0, 0.0, 0.0, 00062 -1.0f/h,-1.0f/h, -1.0f/h }; 00063 00064 // distance of neighbouring pixels 00065 GLfloat dist[9] = { sq2, 1.0f, sq2, 00066 1.0f, 0.0f, 1.0f, 00067 sq2, 1.0f, sq2 }; 00068 GLfloat kernel[25] = { 2, 4, 5, 4, 2, 00069 4, 9, 12, 9, 4, 00070 5, 12, 15, 12, 5, 00071 4, 9, 12, 9, 4, 00072 2, 4, 5, 4, 2 }; 00073 float hi = 10.0f/22; // = sqrt((3+10+3)^2 + (3+10+3)^2) = 22.6 00074 float lo = 3.0f/22; 00075 GLfloat sobelX[9] = { -lo, 0.0f, lo, 00076 -hi, 0.0f, hi, 00077 -lo, 0.0f, lo }; 00078 // dont modify structure of sobelY -> division in sobel.frag 00079 GLfloat sobelY[9] = { lo, hi, lo, 00080 0.0f, 0.0f, 0.0f, 00081 -lo, -hi, -lo }; 00082 00083 // Gauss shader 00084 m_shadeGauss->bind(); 00085 m_shadeGauss->setUniform( "kernel", 25, kernel ); 00086 m_shadeGauss->setUniform( "width", w); 00087 m_shadeGauss->setUniform( "height", h); 00088 m_shadeGauss->unbind(); 00089 00090 // Sobel shader 00091 m_shadeSobel->bind(); 00092 m_shadeSobel->setUniform( "frame", 0 ); 00093 m_shadeSobel->setUniform( "mask", 1); 00094 m_shadeSobel->setUniform( "binary", false); 00095 m_shadeSobel->setUniform( "mOffsetX", mat3(offX), GL_FALSE ); 00096 m_shadeSobel->setUniform( "mOffsetY", mat3(offY), GL_FALSE ); 00097 m_shadeSobel->setUniform( "mSobelX", mat3(sobelX), GL_FALSE ); 00098 m_shadeSobel->setUniform( "mSobelY", mat3(sobelY), GL_FALSE ); 00099 m_shadeSobel->setUniform( "fThreshold", 0.1f ); 00100 m_shadeSobel->unbind(); 00101 00102 // Thinning shader 00103 m_shadeThinning->bind(); 00104 m_shadeThinning->setUniform( "frame", 0 ); 00105 m_shadeThinning->setUniform( "mask", 1); 00106 m_shadeThinning->setUniform( "mOffsetX", mat3(offX), GL_FALSE ); 00107 m_shadeThinning->setUniform( "mOffsetY", mat3(offY), GL_FALSE ); 00108 m_shadeThinning->setUniform( "fThreshold", 0.1f ); 00109 m_shadeThinning->unbind(); 00110 00111 // Spreading shader 00112 m_shadeSpreading->bind(); 00113 m_shadeSpreading->setUniform( "mOffsetX", mat3(offX), GL_FALSE ); 00114 m_shadeSpreading->setUniform( "mOffsetY", mat3(offY), GL_FALSE ); 00115 m_shadeSpreading->setUniform( "mDistance", mat3(dist), GL_FALSE ); 00116 m_shadeSpreading->setUniform( "fThreshold", 0.1f ); 00117 m_shadeSpreading->setUniform( "fDistScale", 0.5f ); 00118 m_shadeSpreading->unbind(); 00119 } 00120 00121 void tgImageProcessor::initFBO(int res) 00122 { 00123 if(res < 16) 00124 printf("[tgImageProcessor::avgInit] Warning: resolution too low\n"); // doesn't make sense computing low number of sums on GPU 00125 00126 bool res_valid = false; 00127 for(unsigned i=4; i<13; i++) 00128 if(res == pow(2,i)) 00129 res_valid = true; 00130 00131 if(!res_valid) 00132 printf("[tgImageProcessor::avgInit] Warning: resolution must be power of 2 and in the range of 16 to 4048\n"); 00133 00134 fbo_res = res; 00135 00136 glGenTextures(1, &fbo_tex_depth); 00137 glBindTexture(GL_TEXTURE_2D, fbo_tex_depth); 00138 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); 00139 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 00140 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 00141 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // for attaching to fbo texture must be mipmap complete 00142 // glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, fbo_res, fbo_res, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL); 00143 00144 // tgTexture (=memory) for fbo 00145 glGenTextures(1, &fbo_tex); 00146 glBindTexture(GL_TEXTURE_2D, fbo_tex); 00147 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); 00148 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 00149 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 00150 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // for attaching to fbo texture must be mipmap complete 00151 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, fbo_res, fbo_res, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); 00152 00153 00154 // fbo = framebuffer object 00155 glGenFramebuffers(1,&fbo); 00156 glBindFramebuffer(GL_FRAMEBUFFER, fbo); 00157 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo_tex, 0); 00158 // glFramebuffertgTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, fbo_tex_depth, 0); 00159 00160 m_cam_ortho_fbo.Set( 0.0f, 0.0f, 1.0f, 00161 0.0f, 0.0f, 0.0f, 00162 0.0f, 1.0f, 0.0f, 00163 45.0f, fbo_res, fbo_res, 00164 0.0f, 1.0f, 00165 GL_ORTHO); 00166 00167 glBindFramebuffer(GL_FRAMEBUFFER, 0); 00168 00169 fbo_stage = ilog2(fbo_res); 00170 m_avg_init = true; 00171 00172 if( tgCheckFBError(GL_FRAMEBUFFER, "tgImageProcessor::avgInit()")!=GL_FRAMEBUFFER_COMPLETE || 00173 tgCheckError("tgImageProcessor::avgInit()")!=GL_NO_ERROR) 00174 { 00175 std::string errmsg = std::string("[tgImageProcessor::initFBO()] Error generating frame buffer objects"); 00176 throw std::runtime_error(errmsg.c_str()); 00177 } 00178 } 00179 00180 void tgImageProcessor::drawQuad(float w, float h){ 00181 glBegin(GL_QUADS); 00182 glTexCoord2f(0.0f,0.0f); glVertex3f( 0, 0, 0.0f); 00183 glTexCoord2f(1.0f,0.0f); glVertex3f( w, 0, 0.0f); 00184 glTexCoord2f(1.0f,1.0f); glVertex3f( w, h, 0.0f); 00185 glTexCoord2f(0.0f,1.0f); glVertex3f( 0, h, 0.0f); 00186 glEnd(); 00187 } 00188 00189 // Display list for flipping image upside down 00190 void tgImageProcessor::drawQuadUpsideDown(float w, float h){ 00191 glBegin(GL_QUADS); 00192 glTexCoord2f(0.0f,1.0f); glVertex3f( 0, 0, 0.0f); 00193 glTexCoord2f(1.0f,1.0f); glVertex3f( w, 0, 0.0f); 00194 glTexCoord2f(1.0f,0.0f); glVertex3f( w, h, 0.0f); 00195 glTexCoord2f(0.0f,0.0f); glVertex3f( 0, h, 0.0f); 00196 glEnd(); 00197 } 00198 00199 // Set functions 00200 void tgImageProcessor::setCamOrtho(){ 00201 m_cam_ortho.Activate(); 00202 } 00203 00204 // *** Image Processing functions *** 00205 void tgImageProcessor::flipUpsideDown(const tgTexture& source, tgTexture& result){ 00206 int w = source.GetWidth(); 00207 int h = source.GetHeight(); 00208 m_cam_ortho.Activate(); 00209 glEnable(GL_TEXTURE_2D); 00210 source.Bind(); 00211 drawQuadUpsideDown(w, h); 00212 result.CopyTexImage2D(w, h); 00213 glDisable(GL_TEXTURE_2D); 00214 } 00215 00216 void tgImageProcessor::copy(const tgTexture& source, tgTexture& result){ 00217 int w = source.GetWidth(); 00218 int h = source.GetHeight(); 00219 m_cam_ortho.Activate(); 00220 glEnable(GL_TEXTURE_2D); 00221 source.Bind(); 00222 drawQuad(w,h); 00223 result.CopyTexImage2D(w, h); 00224 glDisable(GL_TEXTURE_2D); 00225 } 00226 00227 void tgImageProcessor::gauss(const tgTexture& source, tgTexture& result){ 00228 int w = source.GetWidth(); 00229 int h = source.GetHeight(); 00230 m_cam_ortho.Activate(); 00231 m_shadeGauss->bind(); 00232 glEnable(GL_TEXTURE_2D); 00233 source.Bind(); 00234 drawQuad(w,h); 00235 result.CopyTexImage2D(w, h); 00236 glDisable(GL_TEXTURE_2D); 00237 m_shadeGauss->unbind(); 00238 } 00239 00240 void tgImageProcessor::sobel(const tgTexture& source, tgTexture& result, float threshold, bool normalise, bool binary){ 00241 int w = source.GetWidth(); 00242 int h = source.GetHeight(); 00243 m_cam_ortho.Activate(); 00244 m_shadeSobel->bind(); 00245 m_shadeSobel->setUniform( "fThreshold", threshold ); 00246 m_shadeSobel->setUniform( "norm", normalise); 00247 m_shadeSobel->setUniform( "binary", binary); 00248 glEnable(GL_TEXTURE_2D); 00249 source.Bind(); 00250 drawQuad(w,h); 00251 result.CopyTexImage2D(w, h); 00252 glDisable(GL_TEXTURE_2D); 00253 m_shadeSobel->unbind(); 00254 } 00255 00256 void tgImageProcessor::sobel(const tgTexture& source, tgTexture& result, tgTexture& mask, float threshold, bool normalise, bool binary){ 00257 int w = source.GetWidth(); 00258 int h = source.GetHeight(); 00259 m_cam_ortho.Activate(); 00260 m_shadeSobel->bind(); 00261 m_shadeSobel->setUniform( "fThreshold", threshold ); 00262 m_shadeSobel->setUniform( "norm", normalise); 00263 m_shadeSobel->setUniform( "masked", true); 00264 m_shadeSobel->setUniform( "binary", binary); 00265 glEnable(GL_TEXTURE_2D); 00266 source.Bind(0); 00267 mask.Bind(1); 00268 drawQuad(w,h); 00269 result.CopyTexImage2D(w, h); 00270 glDisable(GL_TEXTURE_2D); 00271 m_shadeSobel->unbind(); 00272 m_shadeSobel->setUniform( "masked", false); 00273 } 00274 00275 void tgImageProcessor::thinning(const tgTexture& source, tgTexture& result){ 00276 int w = source.GetWidth(); 00277 int h = source.GetHeight(); 00278 m_cam_ortho.Activate(); 00279 m_shadeThinning->bind(); 00280 glEnable(GL_TEXTURE_2D); 00281 source.Bind(); 00282 drawQuad(w,h); 00283 result.CopyTexImage2D(w, h); 00284 glDisable(GL_TEXTURE_2D); 00285 m_shadeThinning->unbind(); 00286 } 00287 00288 void tgImageProcessor::thinning(const tgTexture& source, tgTexture& result, tgTexture& mask){ 00289 int w = source.GetWidth(); 00290 int h = source.GetHeight(); 00291 m_cam_ortho.Activate(); 00292 m_shadeThinning->bind(); 00293 m_shadeThinning->setUniform( "masked", true); 00294 glEnable(GL_TEXTURE_2D); 00295 source.Bind(0); 00296 mask.Bind(1); 00297 drawQuad(w,h); 00298 result.CopyTexImage2D(w, h); 00299 glDisable(GL_TEXTURE_2D); 00300 m_shadeThinning->setUniform( "masked", false); 00301 m_shadeThinning->unbind(); 00302 } 00303 00304 void tgImageProcessor::spreading(const tgTexture& source, tgTexture& result){ 00305 int w = source.GetWidth(); 00306 int h = source.GetHeight(); 00307 m_cam_ortho.Activate(); 00308 m_shadeSpreading->bind(); 00309 glEnable(GL_TEXTURE_2D); 00310 source.Bind(); 00311 drawQuad(w,h); 00312 result.CopyTexImage2D(w, h); 00313 glDisable(GL_TEXTURE_2D); 00314 m_shadeSpreading->unbind(); 00315 } 00316 00317 void tgImageProcessor::render(const tgTexture& tex){ 00318 m_cam_ortho.Activate(); 00319 glEnable(GL_TEXTURE_2D); 00320 tex.Bind(); 00321 drawQuad(tex.GetWidth(), tex.GetHeight()); 00322 glDisable(GL_TEXTURE_2D); 00323 } 00324 00325 void tgImageProcessor::avgActivate(){ 00326 if(m_avg_init){ 00327 glBindFramebuffer(GL_FRAMEBUFFER, fbo); 00328 tgCheckError("tgImageProcessor::avgActivate() A"); 00329 glClearColor(0,0,0,0); 00330 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 00331 tgCheckError("tgImageProcessor::avgActivate() B"); 00332 } 00333 } 00334 00335 void tgImageProcessor::avgGet(float *avg, int lvl){ 00336 if(m_avg_init){ 00337 if(lvl==0){ 00338 // for some reason glGetTexImage doesen't get the value of the very last mipmap stage 00339 // float tmp[16]; 00340 // glBindtgTexture(GL_TEXTURE_2D, fbo_tex); 00341 // glGenerateMipmap(GL_TEXTURE_2D); 00342 // glGetTexImage(GL_TEXTURE_2D, fbo_stage-1, GL_RGBA, GL_FLOAT, tmp); 00343 // avg[0] = 0; 00344 // for(unsigned i=0; i<4; i++) 00345 // avg[0] += 0.25 * tmp[i]; 00346 printf("[tgImageProcessor::avgGet] Warning: not implemented\n"); 00347 }else{ 00348 glBindTexture(GL_TEXTURE_2D, fbo_tex); 00349 // glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16,0, 0, fbo_res,fbo_res,0); 00350 glGenerateMipmap(GL_TEXTURE_2D); 00351 glGetTexImage(GL_TEXTURE_2D, fbo_stage-lvl, GL_RED, GL_FLOAT, avg); 00352 tgCheckError("tgImageProcessor::avgGet()"); 00353 } 00354 } 00355 } 00356 00357 void tgImageProcessor::avgDeactivate(){ 00358 if(m_avg_init){ 00359 glBindFramebuffer(GL_FRAMEBUFFER, 0); 00360 } 00361 } 00362 00363 00364 00365