CImgList<uint8_t>* ph_getKeyFramesFromVideo(const char *filename){ long N = GetNumberVideoFrames(filename); if (N < 0){ return NULL; } float frames_per_sec = 0.5*fps(filename); if (frames_per_sec < 0){ return NULL; } int step = (int)(frames_per_sec + ROUNDING_FACTOR(frames_per_sec)); long nbframes = (long)(N/step); float *dist = (float*)malloc((nbframes)*sizeof(float)); if (!dist){ return NULL; } CImg<float> prev(64,1,1,1,0); VFInfo st_info; st_info.filename = filename; st_info.nb_retrieval = 100; st_info.step = step; st_info.pixelformat = 0; st_info.pFormatCtx = NULL; st_info.width = -1; st_info.height = -1; CImgList<uint8_t> *pframelist = new CImgList<uint8_t>(); if (!pframelist){ return NULL; } int nbread = 0; int k=0; do { nbread = NextFrames(&st_info, pframelist); if (nbread < 0){ delete pframelist; free(dist); return NULL; } unsigned int i = 0; while ((i < pframelist->size()) && (k < nbframes)){ CImg<uint8_t> current = pframelist->at(i++); CImg<float> hist = current.get_histogram(64,0,255); float d = 0.0; dist[k] = 0.0; cimg_forX(hist,X){ d = hist(X) - prev(X); d = (d>=0) ? d : -d; dist[k] += d; prev(X) = hist(X); } k++; } pframelist->clear(); } while ((nbread >= st_info.nb_retrieval)&&(k < nbframes));
int main(int argc, char *argv[]) { //@ Leer filtro, aplicar filtro, convolve con filtro de distinta medida. Filtra segun un umbral const char* _input = cimg_option("-i", "../images/hubble.tif", "Input Image File"); const char* _filter = cimg_option("-m", "filtro_ej3.txt", "Input filter File"); const unsigned int _lado = cimg_option("-l", 5, "Input filter File"); const unsigned int _umbral = cimg_option("-u", 150, "Input filter File"); CImg<unsigned char> img(_input), output, output_grises(img.width(), img.height(), 1 , 1 , 0), img_binaria(img.width(), img.height(), 1 , 1 , 0); //Creamos el filtro de promediado utils::genArchivoMascara(_filter, _lado, _lado); CImg<double> filtro = utils::get_filtro(_filter); //Convolucionamos output = img.get_convolve(filtro); //Binarizamos la imagen a partir del umbral definido cimg_forXY(output, x , y) { if (output(x,y) > _umbral ) { img_binaria(x,y) = 255; output_grises(x,y) = img(x,y); } } //Dibujamos CImgList<double> lista; lista.assign(img, output, img_binaria, output_grises ); lista.display(); }
void inciso3() { unsigned int w = 256; unsigned int h = 256; CImg<double> linea_vertical = lineaVertical(w,h,w/2).get_normalize(0,255); CImg<double> linea_horizontal = lineaHorizontal(w,h,h/2).get_normalize(0,255); CImg<double> cuadrado = rectCentrado(w,h,w/40,h/4).get_normalize(0,255); CImg<double> rectangulo = rectCentrado(w,h,w/2,h/10).get_normalize(0,255); CImg<double> circulo = circuloCentrado(w,0).get_normalize(0,255); CImgList<double> lista; lista.assign(linea_vertical,linea_horizontal,cuadrado,rectangulo,circulo); lista.display(); //CImgList<double> f_linea_vertical = linea_vertical.get_FFT(); //CImgList<double> f_linea_horizontal = linea_horizontal.get_FFT(); //CImgList<double> f_cuadrado = cuadrado.get_FFT(); //CImgList<double> f_rectangulo = rectangulo.get_FFT(); //CImgList<double> f_circulo = circulo.get_FFT(); CImg<double> fm_linea_vertical = magn_tdf(linea_vertical, true); CImg<double> fm_linea_horizontal = magn_tdf(linea_horizontal, true); CImg<double> fm_cuadrado = magn_tdf(cuadrado, true); CImg<double> fm_rectangulo = magn_tdf(rectangulo, true); CImg<double> fm_circulo = magn_tdf(circulo, true); CImgList<double> lista_fft; lista_fft.assign(fm_linea_vertical, fm_linea_horizontal, fm_cuadrado, fm_rectangulo, fm_circulo); lista_fft.display(); }
int main() { //Reading the image const CImg<double> img = CImg<double>("marilyn1.png").resize(256,256).save("original.png"); //Applying fourier transform. Referenced it frm CImg.h. //Returns list in 0 and 1 column. We assummed the values in 0 column are magnitude and 1 column are phase CImgList<double> F = img.get_FFT(); //FFT Shift. Referenced from CImg.h cimglist_apply(F,shift)(img.width()/2,img.height()/2,0,0,2); complex<double> H[256][256]; //Complex double array for saving Gaussian mask double D0,D; double B[65536]; //65536 is the total number pixels available in the image double S[65536]; //Calculating the gaussian mask. Magnitude and the Phase values are saved in seperate arrays. //Referenced from Online source. The mask is for low pass filter. int i = 0; for ( int u = 0; u < img.width() ; u++){ for ( int v = 0; v < img.height() ; v++){ D0 = 15; D = sqrt(pow((double)u - ((double)img.width()/2),2) + pow((double)v - ((double)img.height()/2),2)); H[u][v] = exp(complex<double>(0.0,-(double)((double)pow(D, 2)/ (double)(2* pow(D0,2))))); B[i] = std::abs(H[u][v]); S[i] = std::arg(H[u][v]); i++; } } printf("%d",i); //Multiplying the Magnitude of Gaussian Mask with Magnitude of FFT result for (int z=0; z< i; z++) { F[0][z] = (F[0][z]*B[i]); } //Taking Inverse FFT of the Result CImgList<double> FT = F.get_FFT(true); const CImg<double> mag = ((FT[0].get_pow(2) + FT[1].get_pow(2)).sqrt() + 1).log().normalize(0,255); CImgList<double> visu(img,mag); mag.save("fftimage.png"); }
int main( int argc, char **argv ) { const char *filename = cimg_option( "-f", "../../imagenes/estanbul.tif", "ruta archivo imagen" ); int umbral = cimg_option( "-u", 127, "umbral" ); CImgDisplay disp, disp2, disp3, disp4, disp5, disp6, disp7, disp8; CImg<double> img ( filename ), gx, gy, gxy, gyx; img.channel(0); img.display(disp); gx = img.get_convolve( masks::sobel_gx() ); gy = img.get_convolve( masks::sobel_gy() ); gxy = img.get_convolve( masks::sobel_gxy() ); gyx = img.get_convolve( masks::sobel_gyx() ); CImgList<double> list ( gx, gy, gxy ,gyx ); list.display(disp3); disp3.set_title("deteccion de bordes: sobel gx - gy - gxy - gyx"); (gx+gy+gxy+gyx).normalize(0,255).display(disp4); disp4.set_title("deteccion de bordes: sobel gx + gy + gxy + gyx"); CImgList<double> list2 ( gx.get_normalize(0,255).get_threshold( umbral ), gy.get_normalize(0,255).get_threshold( umbral ), gxy.get_normalize(0,255).get_threshold( umbral ), gyx.get_normalize(0,255).get_threshold( umbral ) ); list2.display(disp7); disp7.set_title("sobel umbral: gx - gy - gxy - gyx"); CImgList<double> list3 ( masks::sobel_gx().resize(100,100), masks::sobel_gy().resize(100,100), masks::sobel_gxy().resize(100,100), masks::sobel_gyx().resize(100,100) ); list3.display(disp8); disp8.set_title("masks sobel: gx - gy - gxy - gyx"); while ( (!disp.is_closed() && !disp.is_keyQ()) ) { disp.wait_all(); } return 0; }
void insert_fiber(const CImg<T>& fiber, const CImg<te>& eigen, const CImg<tc>& palette, const int xm, const int ym, const int zm, const float vx, const float vy, const float vz, CImgList<tp>& points, CImgList<tf>& primitives, CImgList<tc>& colors) { const int N0 = points.size(); float x0 = fiber(0,0), y0 = fiber(0,1), z0 = fiber(0,2), fa0 = eigen.linear_atXYZ(x0,y0,z0,12); points.insert(CImg<>::vector(vx*(x0 -xm),vy*(y0 - ym),vz*(z0 - zm))); for (int l = 1; l<fiber.width(); ++l) { float x1 = fiber(l,0), y1 = fiber(l,1), z1 = fiber(l,2), fa1 = eigen.linear_atXYZ(x1,y1,z1,12); points.insert(CImg<tp>::vector(vx*(x1 - xm),vy*(y1 - ym),vz*(z1 - zm))); primitives.insert(CImg<tf>::vector(N0 + l - 1,N0 + l)); const unsigned char icol = (unsigned char)(fa0*255), r = palette(icol,0), g = palette(icol,1), b = palette(icol,2); colors.insert(CImg<unsigned char>::vector(r,g,b)); x0 = x1; y0 = y1; z0 = z1; fa0 = fa1; } }
void insert_ellipsoid(const CImg<t>& tensor,const float X,const float Y,const float Z,const float tfact, const float vx, const float vy, const float vz, CImgList<tp>& points, CImgList<tf>& faces, CImgList<tc>& colors, const unsigned int res1 = 20, const unsigned int res2 = 20) { // Compute eigen elements const float l1 = tensor[0], l2 = tensor[1], l3 = tensor[2], fa = get_FA(l1,l2,l3); CImg<> vec = CImg<>::matrix(tensor[3],tensor[6],tensor[9], tensor[4],tensor[7],tensor[10], tensor[5],tensor[8],tensor[11]); const int r = (int)cimg::min(30+1.5f*cimg::abs(255*fa*tensor[3]),255.0f), g = (int)cimg::min(30+1.5f*cimg::abs(255*fa*tensor[4]),255.0f), b = (int)cimg::min(30+1.5f*cimg::abs(255*fa*tensor[5]),255.0f); // Define mesh points const unsigned int N0 = points.size; for (unsigned int v=1; v<res2; v++) for (unsigned int u=0; u<res1; u++) { const float alpha = (float)(u*2*cimg::valuePI/res1), beta = (float)(-cimg::valuePI/2 + v*cimg::valuePI/res2), x = (float)(tfact*l1*std::cos(beta)*std::cos(alpha)), y = (float)(tfact*l2*std::cos(beta)*std::sin(alpha)), z = (float)(tfact*l3*std::sin(beta)); points.insert((CImg<tp>::vector(X,Y,Z)+vec*CImg<tp>::vector(x,y,z)).mul(CImg<tp>::vector(vx,vy,vz))); } const unsigned int N1 = points.size; points.insert((CImg<tp>::vector(X,Y,Z)+vec*CImg<tp>::vector(0,0,-l3*tfact))); points.insert((CImg<tp>::vector(X,Y,Z)+vec*CImg<tp>::vector(0,0,l3*tfact))); points[points.size-2](0)*=vx; points[points.size-2](1)*=vy; points[points.size-2](2)*=vz; points[points.size-1](0)*=vx; points[points.size-1](1)*=vy; points[points.size-1](2)*=vz; // Define mesh triangles for (unsigned int vv=0; vv<res2-2; vv++) for (unsigned int uu=0; uu<res1; uu++) { const int nv = (vv+1)%(res2-1), nu = (uu+1)%res1; faces.insert(CImg<tf>::vector(N0+res1*vv+nu,N0+res1*nv+uu,N0+res1*vv+uu)); faces.insert(CImg<tf>::vector(N0+res1*vv+nu,N0+res1*nv+nu,N0+res1*nv+uu)); colors.insert(CImg<tc>::vector(r,g,b)); colors.insert(CImg<tc>::vector(r,g,b)); } for (unsigned int uu=0; uu<res1; uu++) { const int nu = (uu+1)%res1; faces.insert(CImg<tf>::vector(N0+nu,N0+uu,N1)); faces.insert(CImg<tf>::vector(N0+res1*(res2-2)+nu, N1+1,N0+res1*(res2-2)+uu)); colors.insert(CImg<tc>::vector(r,g,b)); colors.insert(CImg<tc>::vector(r,g,b)); } }
void inciso4() { unsigned int w = 512; unsigned int h = 512; CImg<double> linea = lineaVertical(w,h,w/2).get_normalize(0,255); CImg<double> rotada = linea.get_rotate(20); CImg<double> c_linea = linea.get_crop(w/4,h/4,3*w/4, 3*h/4); CImg<double> c_rotada = rotada.get_crop(w/4+100,h/4,3*w/4+100, 3*h/4); CImgList<double> lista; lista.assign(linea,rotada,c_linea,c_rotada); lista.display(); CImg<double> fm_linea = magn_tdf(c_linea, true); CImg<double> fm_rotada = magn_tdf(c_rotada, true); CImgList<double> lista_fft; lista_fft.assign(fm_linea, fm_rotada); lista_fft.display(); }
int main(int argc, char *argv[]) { //@ Leer filtro, aplicar filtro, convolve con filtro de distinta medida const char* _input = cimg_option("-i", "../images/cameraman.tif", "Input Image File"); const char* _filter = cimg_option("-m", "filtro_examen_m1.txt", "Input filter File"); const char* _filter2 = cimg_option("-s", "filtro_examen_m2.txt", "Input filter File"); CImg<double> img(_input), m1, m2; CImg<double> filtro = get_filtro(_filter); CImg<double> filtro2 = get_filtro(_filter2); m1 = img.get_convolve(filtro); m2 = m1.get_convolve(filtro2); CImgList<unsigned char> lista; lista.assign(img, m1, m2); lista.display(); }
//@ Aplica el operador derivada segun el parametro opcion //0: Gradiente de Roberts //1: Gradiente de Prewitt //2: Gradiente de Sobel //3: Laplaciano de 4 vecinos //4: Laplaciano de 8 vecinos //5: LoG, Laplaciano del Gaussiano //Devuelve una lista con todos los resultados de aplicar todas las mascaras del operador en particular CImgList<double> aplicarDerivada(CImg<double> img, unsigned int opcion = 0) { CImgList<double> derivada; if (opcion == 0) derivada = operadorRoberts(); if (opcion == 1) derivada = operadorPrewitt(); if (opcion == 2) derivada = operadorSobel(); if (opcion == 3) derivada = operadorLaplaciano4(); if (opcion == 4) derivada = operadorLaplaciano8(); if (opcion == 5) derivada = operadorLoG(); CImgList<double> resultados; unsigned int cantidad = derivada.size(); for (unsigned int i = 0; i < cantidad; i++) { resultados.push_back(img.get_convolve(derivada[i])); } return resultados; }
int main(int argc, char *argv[]) { if ( !argv[1] ){ printf( "%s: Convoluciona la imagen con un kernel de 3x3.\n", argv[0] ); printf( "uso: %s <archivo_imagen>\n", argv[0] ); return 1; } CImg<double> kernel ( 3,3,1,1,1); kernel(0,0)=0; kernel(1,0)=1; kernel(2,0)=2; kernel(0,1)=1; kernel(1,1)=2; kernel(2,1)=1; kernel(0,2)=2; kernel(1,2)=1; kernel(2,2)=0; CImg<double> imagen( argv[1] ); CImgList<double> result ( imagen.get_normalize(0,255), kernel.get_normalize(0,255), imagen.get_convolve( kernel ).get_normalize(0,255) ); result.display(); return 0; }
//@ Toma una lista de imagenes y le aplica el umbral especificado a cada imagen CImgList<bool> umbralizarLista(CImgList<double> l_img, double umbral) { CImgList<bool> ret_val; //Recorre la lista for (unsigned int i = 0; i < l_img.size(); i++) { //Temporal a pushear CImg<bool> tempy(l_img[i].width(), l_img[i].height(), l_img[i].depth(), l_img[i].spectrum(), false); //Recorre la imagen cimg_forXY(l_img[i],x,y) { if (fabs(l_img[i](x,y)) > umbral) { tempy(x,y) = true; } } ret_val.push_back(tempy); } return ret_val; }
int main(int argc, char *argv[]) { //@ Compara el resultado de ecualizar una imagen a partir de cada canal RGB y la intensidad de HSI const char* _input = cimg_option("-i", "../images/futbol.jpg", "Input Image File"); //Declaramos imagenes a trabajar CImg<double> input(_input), output(input.width(), input.height(), input.depth(), 3 , 0) ; (input.get_RGBtoHSI().get_channel(0), input.get_RGBtoHSI().get_channel(1)).display(); CImg<double> recorte = input.get_crop(132,105,203,230); // recorte.display(); // CImg<unsigned char> histograma_r = recorte.get_channel(0).get_histogram(256, 0, 255); // CImg<unsigned char> histograma_g = recorte.get_channel(1).get_histogram(256, 0, 255); // CImg<unsigned char> histograma_b = recorte.get_channel(2).get_histogram(256, 0, 255); // histograma_r.display_graph("",3); // histograma_g.display_graph("",3); // histograma_b.display_graph("",3); CImg<bool> mascara_binaria(input.width(), input.height()); CImg<double> c1 = input.get_channel(0); CImg<double> c2 = input.get_channel(1); CImg<double> c3 = input.get_channel(2); cimg_forXY(input, x , y) { if (dentro_circulo(c1(x,y), 40, 20) && //rojo dentro_circulo(c2(x,y), 85, 10) && //verde dentro_circulo(c3(x,y), 150, 105)) { //azul mascara_binaria(x,y) = true; output(x,y,0,0) = input(x,y,0,0); output(x,y,0,1) = input(x,y,0,1); output(x,y,0,2) = input(x,y,0,2); } else { mascara_binaria(x,y) = false; } } // //Display! CImgList<double> lista; lista.assign(input, mascara_binaria.normalize(0,255) , output ); lista.display(); // CImg<double> output_RGB(_input); // CImg<double> output_HSI(_input); // CImg<double> filtro = get_filtro(_filtro); // //Temporales necesarios // CImg<double> c1, c2, c3; // //Ecualización de la RGB // //Obtenemos los canales // c1 = output_RGB.get_channel(0); // c2 = output_RGB.get_channel(1); // c3 = output_RGB.get_channel(2); // //Los ecualizamos // c1.convolve(filtro); // c2.convolve(filtro); // c3.convolve(filtro); // //Recomponemos la imágen // c1.append(c2, 'c'); // c1.append(c3, 'c'); // output_RGB = c1; // //Ecualizamos la imagen HSI // output_HSI.RGBtoHSI(); // //Obtenemos los canales // c1 = output_HSI.get_channel(0); // c2 = output_HSI.get_channel(1); // c3 = output_HSI.get_channel(2); // //Ecualizo el canal de Intensidad solamente // c3.convolve(filtro); // //Recomponemos la imágen // c1.append(c2, 'c'); // c1.append(c3, 'c'); // output_HSI = c1; // output_HSI.HSItoRGB(); // return 0; }
// Main procedure //---------------- int main(int argc,char **argv) { // Read command line arguments. cimg_usage("Render an image as a surface"); const char *file_i = cimg_option("-i",cimg_imagepath "logo.bmp","Input image"); const char *file_o = cimg_option("-o",(char*)0,"Output 3D object"); const float sigma = cimg_option("-smooth",1.0f,"Amount of image smoothing"); const float ratioz = cimg_option("-z",0.25f,"Aspect ratio along z-axis"); const unsigned int di = cimg_option("-di",10,"Step for isophote skipping"); // Load 2D image file. std::fprintf(stderr,"\n- Load file '%s'",cimg::basename(file_i)); std::fflush(stderr); const CImg<unsigned char> img = CImg<>(file_i).blur(sigma).resize(-100,-100,1,3), norm = img.get_norm().normalize(0,255); // Compute surface with triangles. std::fprintf(stderr,"\n- Create image surface"); std::fflush(stderr); CImgList<unsigned int> primitives; CImgList<unsigned char> colors; const CImg<> points = img.get_elevation3d(primitives,colors,norm*-ratioz); // Compute image isophotes. std::fprintf(stderr,"\n- Compute image isophotes"); std::fflush(stderr); CImgList<unsigned int> isoprimitives; CImgList<unsigned char> isocolors; CImg<> isopoints; for (unsigned int i = 0; i<255; i+=di) { CImgList<> prims; const CImg<> pts = norm.get_isoline3d(prims,(float)i); isopoints.append_object3d(isoprimitives,pts,prims); } cimglist_for(isoprimitives,l) { const unsigned int i0 = isoprimitives(l,0); const float x0 = isopoints(i0,0), y0 = isopoints(i0,1); const unsigned char r = (unsigned char)img.linear_atXY(x0,y0,0), g = (unsigned char)img.linear_atXY(x0,y0,1), b = (unsigned char)img.linear_atXY(x0,y0,2); isocolors.insert(CImg<unsigned char>::vector(r,g,b)); } cimg_forX(isopoints,ll) isopoints(ll,2) = -ratioz*norm.linear_atXY(isopoints(ll,0),isopoints(ll,1)); // Save object if necessary if (file_o) { std::fprintf(stderr,"\n- Save 3d object as '%s'",cimg::basename(file_o)); std::fflush(stderr); points.save_off(primitives,colors,file_o); } // Enter event loop std::fprintf(stderr, "\n- Enter interactive loop.\n\n" "Reminder : \n" " + Use mouse to rotate and zoom object\n" " + key 'F' : Toggle fullscreen\n" " + key 'Q' or 'ESC' : Quit\n" " + Any other key : Change rendering type\n\n"); std::fflush(stderr); const char *const title = "Image viewed as a surface"; CImgDisplay disp(800,600,title,0); unsigned int rtype = 2; CImg<float> pose = CImg<float>::identity_matrix(4); while (!disp.is_closed()) { const unsigned char white[3]={ 255, 255, 255 }; CImg<unsigned char> visu(disp.width(),disp.height(),1,3,0); visu.draw_text(10,10,"%s",white,0,1,24, rtype==0?"Points":(rtype==1?"Lines":(rtype==2?"Faces":(rtype==3?"Flat-shaded faces": (rtype==4?"Gouraud-shaded faces":(rtype==5?"Phong-shaded faces":"Isophotes")))))); static bool first_time = true; if (rtype==6) visu.display_object3d(disp,isopoints,isoprimitives,isocolors,first_time,1,-1,true, 500.0f,0.0f,0.0f,-5000.0f,0.0f,0.0f,true,pose.data()); else visu.display_object3d(disp,points,primitives,colors,first_time,rtype,-1,true, 500.0f,0.0f,0.0f,-5000.0f,0.0f,0.0f,true,pose.data()); first_time = false; switch (disp.key()) { case 0: break; case cimg::keyBACKSPACE: rtype = (7 + rtype - 1)%7; break; case cimg::keyQ: case cimg::keyESC: disp.close(); break; case cimg::keyF: if (disp.is_fullscreen()) disp.resize(800,600); else disp.resize(disp.screen_width(),disp.screen_height()); disp.toggle_fullscreen(); break; default: rtype = (rtype + 1)%7; break; } } return 0; }
//' Display image list using CImg library //' //' @param imlist a list of cimg objects //' @export // [[Rcpp::export]] void display_list(List imlist) { CImgList<double> L = sharedCImgList(imlist); L.display(); return; }
// Main procedure //---------------- int main (int argc, char **argv) { cimg_usage("Compute the skeleton of a shape, using Hamilton-Jacobi equations"); // Read command line arguments cimg_help("Input/Output options\n" "--------------------"); const char* file_i = cimg_option("-i",cimg_imagepath "milla.bmp","Input (black&white) image"); const int median = cimg_option("-median",0,"Apply median filter"); const bool invert = cimg_option("-inv",false,"Invert image values"); const char* file_o = cimg_option("-o",(char*)0,"Output skeleton image"); const bool display = cimg_option("-visu",true,"Display results"); cimg_help("Skeleton computation parameters\n" "-------------------------------"); const float thresh = cimg_option("-t",-0.3f,"Threshold"); const bool curve = cimg_option("-curve",false,"Create medial curve"); cimg_help("Torsello correction parameters\n" "------------------------------"); const bool correction = cimg_option("-corr",false,"Torsello correction"); const float dlt1 = 2; const float dlt2 = cimg_option("-dlt",1.0f,"Discrete step"); // Load the image (forcing it to be scalar with 2 values { 0,1 }). CImg<unsigned int> image0(file_i), image = image0.get_norm().quantize(2).normalize(0.0f,1.0f); if (median) image.blur_median(median); if (invert) (image-=1)*=-1; if (display) (image0.get_normalize(0,255),image.get_normalize(0,255)).display("Input image - Binary image"); // Compute distance map. CImgList<float> visu; CImg<float> distance = image.get_distance(0); if (display) visu.insert(distance); // Compute the gradient of the distance function, and the flux (divergence) of the gradient field. const CImgList<float> grad = distance.get_gradient("xyz"); CImg<float> flux = image.get_flux(grad,1,1); if (display) visu.insert(flux); // Use the Torsello correction of the flux if necessary. if (correction) { CImg<float> logdensity = image.get_logdensity(distance,grad,flux,dlt1), nflux = image.get_corrected_flux(logdensity,grad,flux,dlt2); if (display) visu.insert(logdensity).insert(nflux); flux = nflux; } if (visu) { cimglist_apply(visu,normalize)(0,255); visu.display(visu.size()==2?"Distance function - Flux":"Distance function - Flux - Log-density - Corrected flux"); } // Compute the skeleton const CImg<unsigned int> skel = image.get_skeleton(flux,distance,curve,thresh); if (display) { (image0.resize(-100,-100,1,3)*=0.7f).get_shared_channel(1)|=skel*255.0; image0.draw_image(0,0,0,0,image*255.0,0.5f).display("Image + Skeleton"); } // Save output image if necessary. if (file_o) skel.save(file_o); return 0; }
CImg<> get_fibertrack(CImg<T>& eigen, const int X0, const int Y0, const int Z0, const float lmax=100, const float dl=0.1f, const float FAmin=0.7f, const float cmin=0.5f) { #define align_eigen(i,j,k) \ { T &u = eigen(i,j,k,3), &v = eigen(i,j,k,4), &w = eigen(i,j,k,5); \ if (u*cu + v*cv + w*cw<0) { u=-u; v=-v; w=-w; }} CImgList<> resf; // Forward tracking float normU = 0, normpU = 0, l = 0, X = (float)X0, Y = (float)Y0, Z = (float)Z0; T pu = eigen(X0,Y0,Z0,3), pv = eigen(X0,Y0,Z0,4), pw = eigen(X0,Y0,Z0,5); normpU = (float)std::sqrt(pu*pu + pv*pv + pw*pw); bool stopflag = false; while (!stopflag) { if (X<0 || X>eigen.width() - 1 || Y<0 || Y>eigen.height() - 1 || Z<0 || Z>eigen.depth() - 1 || eigen((int)X,(int)Y,(int)Z,12)<FAmin || l>lmax) stopflag = true; else { resf.insert(CImg<>::vector(X,Y,Z)); const int cx = (int)X, px = (cx - 1<0)?0:cx - 1, nx = (cx + 1>=eigen.width())?eigen.width() - 1:cx + 1, cy = (int)Y, py = (cy - 1<0)?0:cy - 1, ny = (cy + 1>=eigen.height())?eigen.height() - 1:cy + 1, cz = (int)Z, pz = (cz - 1<0)?0:cz - 1, nz = (cz + 1>=eigen.depth())?eigen.depth() - 1:cz + 1; const T cu = eigen(cx,cy,cz,3), cv = eigen(cx,cy,cz,4), cw = eigen(cx,cy,cz,5); align_eigen(px,py,pz); align_eigen(cx,py,pz); align_eigen(nx,py,pz); align_eigen(px,cy,pz); align_eigen(cx,cy,pz); align_eigen(nx,cy,pz); align_eigen(px,ny,pz); align_eigen(cx,ny,pz); align_eigen(nx,ny,pz); align_eigen(px,py,cz); align_eigen(cx,py,cz); align_eigen(nx,py,cz); align_eigen(px,cy,cz); align_eigen(nx,cy,cz); align_eigen(px,ny,cz); align_eigen(cx,ny,cz); align_eigen(nx,ny,cz); align_eigen(px,py,nz); align_eigen(cx,py,nz); align_eigen(nx,py,nz); align_eigen(px,cy,nz); align_eigen(cx,cy,nz); align_eigen(nx,cy,nz); align_eigen(px,ny,nz); align_eigen(cx,ny,nz); align_eigen(nx,ny,nz); const T u0 = 0.5f*dl*eigen.linear_atXYZ(X,Y,Z,3), v0 = 0.5f*dl*eigen.linear_atXYZ(X,Y,Z,4), w0 = 0.5f*dl*eigen.linear_atXYZ(X,Y,Z,5), u1 = 0.5f*dl*eigen.linear_atXYZ(X + u0,Y + v0,Z + w0,3), v1 = 0.5f*dl*eigen.linear_atXYZ(X + u0,Y + v0,Z + w0,4), w1 = 0.5f*dl*eigen.linear_atXYZ(X + u0,Y + v0,Z + w0,5), u2 = 0.5f*dl*eigen.linear_atXYZ(X + u1,Y + v1,Z + w1,3), v2 = 0.5f*dl*eigen.linear_atXYZ(X + u1,Y + v1,Z + w1,4), w2 = 0.5f*dl*eigen.linear_atXYZ(X + u1,Y + v1,Z + w1,5), u3 = 0.5f*dl*eigen.linear_atXYZ(X + u2,Y + v2,Z + w2,3), v3 = 0.5f*dl*eigen.linear_atXYZ(X + u2,Y + v2,Z + w2,4), w3 = 0.5f*dl*eigen.linear_atXYZ(X + u2,Y + v2,Z + w2,5); T u = u0/3 + 2*u1/3 + 2*u2/3 + u3/3, v = v0/3 + 2*v1/3 + 2*v2/3 + v3/3, w = w0/3 + 2*w1/3 + 2*w2/3 + w3/3; if (u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; } normU = (float)std::sqrt(u*u + v*v + w*w); const float scal = (u*pu + v*pv + w*pw)/(normU*normpU); if (scal<cmin) stopflag=true; X+=(pu=u); Y+=(pv=v); Z+=(pw=w); normpU = normU; l+=dl; } } // Backward tracking l = dl; X = (float)X0; Y = (float)Y0; Z = (float)Z0; pu = eigen(X0,Y0,Z0,3); pv = eigen(X0,Y0,Z0,4); pw = eigen(X0,Y0,Z0,5); normpU = (float)std::sqrt(pu*pu + pv*pv + pw*pw); stopflag = false; while (!stopflag) { if (X<0 || X>eigen.width() - 1 || Y<0 || Y>eigen.height() - 1 || Z<0 || Z>eigen.depth() - 1 || eigen((int)X,(int)Y,(int)Z,12)<FAmin || l>lmax) stopflag = true; else { const int cx = (int)X, px = (cx - 1<0)?0:cx - 1, nx = (cx + 1>=eigen.width())?eigen.width() - 1:cx + 1, cy = (int)Y, py = (cy - 1<0)?0:cy - 1, ny = (cy + 1>=eigen.height())?eigen.height() - 1:cy + 1, cz = (int)Z, pz = (cz - 1<0)?0:cz - 1, nz = (cz + 1>=eigen.depth())?eigen.depth() - 1:cz + 1; const T cu = eigen(cx,cy,cz,3), cv = eigen(cx,cy,cz,4), cw = eigen(cx,cy,cz,5); align_eigen(px,py,pz); align_eigen(cx,py,pz); align_eigen(nx,py,pz); align_eigen(px,cy,pz); align_eigen(cx,cy,pz); align_eigen(nx,cy,pz); align_eigen(px,ny,pz); align_eigen(cx,ny,pz); align_eigen(nx,ny,pz); align_eigen(px,py,cz); align_eigen(cx,py,cz); align_eigen(nx,py,cz); align_eigen(px,cy,cz); align_eigen(nx,cy,cz); align_eigen(px,ny,cz); align_eigen(cx,ny,cz); align_eigen(nx,ny,cz); align_eigen(px,py,nz); align_eigen(cx,py,nz); align_eigen(nx,py,nz); align_eigen(px,cy,nz); align_eigen(cx,cy,nz); align_eigen(nx,cy,nz); align_eigen(px,ny,nz); align_eigen(cx,ny,nz); align_eigen(nx,ny,nz); const T u0 = 0.5f*dl*eigen.linear_atXYZ(X,Y,Z,3), v0 = 0.5f*dl*eigen.linear_atXYZ(X,Y,Z,4), w0 = 0.5f*dl*eigen.linear_atXYZ(X,Y,Z,5), u1 = 0.5f*dl*eigen.linear_atXYZ(X + u0,Y + v0,Z + w0,3), v1 = 0.5f*dl*eigen.linear_atXYZ(X + u0,Y + v0,Z + w0,4), w1 = 0.5f*dl*eigen.linear_atXYZ(X + u0,Y + v0,Z + w0,5), u2 = 0.5f*dl*eigen.linear_atXYZ(X + u1,Y + v1,Z + w1,3), v2 = 0.5f*dl*eigen.linear_atXYZ(X + u1,Y + v1,Z + w1,4), w2 = 0.5f*dl*eigen.linear_atXYZ(X + u1,Y + v1,Z + w1,5), u3 = 0.5f*dl*eigen.linear_atXYZ(X + u2,Y + v2,Z + w2,3), v3 = 0.5f*dl*eigen.linear_atXYZ(X + u2,Y + v2,Z + w2,4), w3 = 0.5f*dl*eigen.linear_atXYZ(X + u2,Y + v2,Z + w2,5); T u = u0/3 + 2*u1/3 + 2*u2/3 + u3/3, v = v0/3 + 2*v1/3 + 2*v2/3 + v3/3, w = w0/3 + 2*w1/3 + 2*w2/3 + w3/3; if (u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; } normU = (float)std::sqrt(u*u + v*v + w*w); const float scal = (u*pu + v*pv + w*pw)/(normU*normpU); if (scal<cmin) stopflag=true; X-=(pu=u); Y-=(pv=v); Z-=(pw=w); normpU=normU; l+=dl; resf.insert(CImg<>::vector(X,Y,Z),0); } } return resf>'x'; }
// Main procedure //---------------- int main(int argc,char **argv) { // Read and init data //-------------------- cimg_usage("A viewer of Diffusion-Tensor MRI volumes."); const char *file_i = cimg_option("-i",(char*)0,"Input : Filename of tensor field (volume wxhxdx6)"); const char* vsize = cimg_option("-vsize","1x1x1","Input : Voxel aspect"); const bool normalize = cimg_option("-normalize",true,"Input : Enable tensor normalization"); const char *file_f = cimg_option("-f",(char*)0,"Input : Input fibers\n"); const float dl = cimg_option("-dl",0.5f,"Fiber computation : Integration step"); const float famin = cimg_option("-famin",0.3f,"Fiber computation : Fractional Anisotropy threshold"); const float cmin = cimg_option("-cmin",0.2f,"Fiber computation : Curvature threshold"); const float lmin = cimg_option("-lmin",10.0f,"Fiber computation : Minimum length\n"); const float lmax = cimg_option("-lmax",1000.0f,"Fiber computation : Maximum length\n"); const float tfact = cimg_option("-tfact",1.2f,"Display : Tensor size factor"); const char *bgcolor = cimg_option("-bg","0,0,0","Display : Background color"); unsigned int bgr = 0, bgg = 0, bgb = 0; std::sscanf(bgcolor,"%u%*c%u%*c%u",&bgr,&bgg,&bgb); CImg<> tensors; if (file_i) { std::fprintf(stderr,"\n- Loading tensors '%s'",cimg::basename(file_i)); tensors.load(file_i); } else { // Create a synthetic tensor field here std::fprintf(stderr,"\n- No input files : Creating a synthetic tensor field"); tensors.assign(32,32,32,6); cimg_forXYZ(tensors,x,y,z) { const float u = x - tensors.width()/2.0f, v = y - tensors.height()/2.0f, w = z - tensors.depth()/2.0f, norm = (float)std::sqrt(1e-5f + u*u + v*v + w*w), nu = u/norm, nv = v/norm, nw = w/norm; const CImg<> dir1 = CImg<>::vector(nu,nv,nw), dir2 = CImg<>::vector(-nv,nu,nw), dir3 = CImg<>::vector(nw*(nv - nu),-nw*(nu + nv),nu*nu + nv*nv); tensors.set_tensor_at(2.0*dir1*dir1.get_transpose() + 1.0*dir2*dir2.get_transpose() + 0.7*dir3*dir3.get_transpose(), x,y,z); } } float voxw = 1, voxh = 1, voxd = 1; std::sscanf(vsize,"%f%*c%f%*c%f",&voxw,&voxh,&voxd); std::fprintf(stderr," : %ux%ux%u image, voxsize=%gx%gx%g.", tensors.width(),tensors.height(),tensors.depth(), voxw,voxh,voxd); CImgList<> fibers; if (file_f) { std::fprintf(stderr,"\n- Loading fibers '%s'.",cimg::basename(file_f)); fibers.load(file_f); } const CImg<unsigned char> fiber_palette = CImg<>(2,1,1,3).fill(200,255,0,255,0,200).RGBtoHSV().resize(256,1,1,3,3).HSVtoRGB(); // Compute eigen elements //------------------------ std::fprintf(stderr,"\n- Compute eigen elements."); CImg<unsigned char> coloredFA(tensors.width(),tensors.height(),tensors.depth(),3); CImg<> eigen(tensors.width(),tensors.height(),tensors.depth(),13); CImg<> val,vec; float eigmax = 0; cimg_forXYZ(tensors,x,y,z) { tensors.get_tensor_at(x,y,z).symmetric_eigen(val,vec); eigen(x,y,z,0) = val[0]; eigen(x,y,z,1) = val[1]; eigen(x,y,z,2) = val[2]; if (val[0]<0) val[0] = 0; if (val[1]<0) val[1] = 0; if (val[2]<0) val[2] = 0; if (val[0]>eigmax) eigmax = val[0]; eigen(x,y,z,3) = vec(0,0); eigen(x,y,z,4) = vec(0,1); eigen(x,y,z,5) = vec(0,2); eigen(x,y,z,6) = vec(1,0); eigen(x,y,z,7) = vec(1,1); eigen(x,y,z,8) = vec(1,2); eigen(x,y,z,9) = vec(2,0); eigen(x,y,z,10) = vec(2,1); eigen(x,y,z,11) = vec(2,2); const float fa = get_FA(val[0],val[1],val[2]); eigen(x,y,z,12) = fa; const int r = (int)cimg::min(255.0f,1.5f*cimg::abs(255*fa*vec(0,0))), g = (int)cimg::min(255.0f,1.5f*cimg::abs(255*fa*vec(0,1))), b = (int)cimg::min(255.0f,1.5f*cimg::abs(255*fa*vec(0,2))); coloredFA(x,y,z,0) = (unsigned char)r; coloredFA(x,y,z,1) = (unsigned char)g; coloredFA(x,y,z,2) = (unsigned char)b; }
void display3D(CImg<T> image, const char *file_o,const float ratioz,const unsigned int di, const char *file_pose_i,const char *file_pose_o, unsigned int rtype,bool color_type) { std::cout<<"display image as 3D surface"<<std::flush; const CImg<unsigned char> norm=image.get_norm().normalize(0,255); // Compute surface with triangles. std::fprintf(stderr,"\n- Create image surface"); std::fflush(stderr); CImgList<unsigned int> primitives; ////image colors CImgList<unsigned char> colors; const CImg<> points = image.get_elevation3d(primitives,colors,norm*-ratioz); ////constant colors CImgList<unsigned char> colors2; colors2=colors; cimglist_for(colors2,l) colors2(l).fill(255);//white // Compute image isophotes. std::fprintf(stderr,"\n- Compute image isophotes"); std::fflush(stderr); CImgList<unsigned int> isoprimitives; ////image colors CImgList<unsigned char> isocolors; CImg<> isopoints; for (unsigned int i = 0; i<255; i+=di) { CImgList<> prims; const CImg<> pts = norm.get_isoline3d(prims,(float)i); isopoints.append_object3d(isoprimitives,pts,prims); } cimglist_for(isoprimitives,l) { const unsigned int i0 = isoprimitives(l,0); const float x0 = isopoints(i0,0), y0 = isopoints(i0,1); const unsigned char r = (unsigned char)image.linear_atXY(x0,y0,0), g = (unsigned char)image.linear_atXY(x0,y0,1), b = (unsigned char)image.linear_atXY(x0,y0,2); isocolors.insert(CImg<unsigned char>::vector(r,g,b)); } cimg_forX(isopoints,l) isopoints(l,2) = -ratioz*norm.linear_atXY(isopoints(l,0),isopoints(l,1)); ////constant colors CImgList<unsigned char> isocolors2; isocolors2=isocolors; cimglist_for(isocolors2,l) isocolors2(l).fill(255);//white // Save object if necessary if (file_o) { std::fprintf(stderr,"\n- Save 3d object as '%s'",cimg::basename(file_o)); std::fflush(stderr); points.save_off(primitives,colors,file_o); } //display GUI information std::fprintf(stderr, "\n- Enter interactive loop.\n\n" "GUI reminder: \n" " + Use mouse to rotate and zoom object\n" " + key 'F' : toggle Fullscreen\n" " + key 'Q' or 'ESC' : Quit (i.e. exit)\n" " load or save file:\n" " + key 'S' : Save displayed image (i.e. 3D view)\n" " + key 'O' : save pOse (i.e. 3D view parameters)\n" " + key 'R' : Read pose (i.e. 3D view parameters)\n" " render type:\n" " + key 'C' : color render (image or constant)\n" " + key 'T' : poinTs render\n" " + key 'L' : Lines render\n" " + key 'A' : fAces render\n" " + key 'H' : flat-sHaded faces render\n" " + key 'G' : Gouraud-shaded faces render\n" " + key 'P' : Phong-shaded faces render\n" " + key 'I' : Isophotes render\n" " + key 'BackSpace' : change rendering type (i.e. decrement type)\n" " + Any other key : change rendering type (i.e. increment type)\n\n" ); std::fflush(stderr); const char *const title = "Image viewed as a surface"; CImgDisplay disp(800,600,title,0); CImg<float> pose=CImg<float>::identity_matrix(4); //load pose if set if(file_pose_i) { std::cerr<<"- read pose from file \""<<file_pose_i<<"\"\n"<<std::flush; pose.load(file_pose_i); } //pose.print("pose");std::cerr<<std::flush; //GUI loop while (!disp.is_closed()) { const unsigned char text_color[3]= {123,234,234}; CImg<unsigned char> visu(disp.width(),disp.height(),1,3,0); visu.draw_text(10,10,"%s",text_color,0,1,24, rtype==0?"Points (0,T)":(rtype==1?"Lines (1,L)":(rtype==2?"Faces (2,A)":(rtype==3?"Flat-shaded faces (3,H)": (rtype==4?"Gouraud-shaded faces (4,G)":(rtype==5?"Phong-shaded faces (5,P)":"Isophotes (6,I)")))))); static bool first_time=(file_pose_i)?false:true; if (rtype==6) visu.display_object3d(disp,isopoints,isoprimitives,(color_type)?isocolors:isocolors2,first_time,1,-1,true, 500.0f,0.0f,0.0f,-5000.0f,0.0f,0.0f,true,pose.data()); else visu.display_object3d(disp,points,primitives,(color_type)?colors:colors2,first_time,rtype,-1,true, 500.0f,0.0f,0.0f,-5000.0f,0.0f,0.0f,true,pose.data()); first_time=false; //pose.print("pose");std::cerr<<std::flush; switch (disp.key()) { case 0: break; case cimg::keyBACKSPACE: rtype=(7+rtype-1)%7; break; case cimg::keyQ: case cimg::keyESC: disp.close(); break; //fullscreen display or not case cimg::keyF: if (disp.is_fullscreen()) disp.resize(800,600); else disp.resize(disp.screen_width(),disp.screen_height()); disp.toggle_fullscreen(); break; //save display case cimg::keyS: { std::string file_tmp="CImg_surface3D.png"; std::cerr<<"saving display as image in \""<<file_tmp<<"\".\n"<<std::flush; CImg<unsigned char> tmp; disp.snapshot(tmp); tmp.save(file_tmp.c_str()); } break; //save pose case cimg::keyO: { std::cerr<<"saving pose in file \""<<file_pose_o<<"\".\n"<<std::flush; pose.save(file_pose_o); } break; //load pose case cimg::keyR: { std::cerr<<"loading pose from file \""<<file_pose_i<<"\".\n"<<std::flush; pose.load(file_pose_i); } break; //display type case cimg::keyC: color_type=(color_type)?false:true; break;//color type case cimg::keyT: rtype=0; break;//poinTs case cimg::keyL: rtype=1; break;//Lines case cimg::keyA: rtype=2; break;//fAces case cimg::keyH: rtype=3; break;//flat-sHaded faces case cimg::keyG: rtype=4; break;//Gouraud-shaded faces case cimg::keyP: rtype=5; break;//Phong-shaded faces case cimg::keyI: rtype=6; break;//Isophotes default: rtype = (rtype+1)%7; break; }//Key switch }//GUI loop }