/* LiquidRescaling Library DEMO program * Copyright (C) 2007-2009 Carlo Baldassi (the "Author") <carlobaldassi@gmail.com>. * All Rights Reserved. * * 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; version 3 dated June, 2007. * 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, see <http://www.gnu.org/licenses/> */ #include <pngwriter.h> #include <lqr.h> #include <getopt.h> #include "liquidrescale.h" using namespace std; LqrCarver *carver; gchar *infile = NULL; gchar *outfile = NULL; gchar *pres_infile = NULL; gchar *pres_outfile = NULL; gchar *disc_infile = NULL; gchar *disc_outfile = NULL; gchar *vmap_infile = NULL; gchar *vmap_outfile = NULL; gchar *rigmask_infile = NULL; gchar *rigmask_outfile = NULL; gchar *energy_w_outfile = NULL; gchar *energy_h_outfile = NULL; gint new_width = 0; gint new_height = 0; gfloat rigidity = 0; gint max_step = 1; gint pres_strength = 1000; gint disc_strength = 1000; LqrResizeOrder res_order = LQR_RES_ORDER_HOR; gint side_switch_frequency = 0; gfloat enl_step = 1.5; gchar *energy_function = NULL; gfloat new_width_p = 0; gfloat new_height_p = 0; gboolean quiet = FALSE; clock_t clock_start, clock_now; /*** MAIN ***/ int main(int argc, char **argv) {/*{{{*/ /*** init glib multithreading ***/ /* (currently only used for * cancelling) */ if (!g_thread_supported()) { g_thread_init(NULL); } /*** read the command line ***/ TRAP(parse_command_line(argc, argv)); /*** open input and output files ***/ pngwriter png(1, 1, 0, outfile); png.readfromfile(infile); /*** old and new size ***/ gint old_width = png.getwidth(); gint old_height = png.getheight(); if (new_width_p) { new_width = (gint) (new_width_p * old_width / 100); } if (new_height_p) { new_height = (gint) (new_height_p * old_height / 100); } new_width = (new_width ? new_width : old_width); new_height = (new_height ? new_height : old_height); if (new_width < 2) { cerr << "The width should be greater than 2" << endl; exit(1); } if (new_height < 2) { cerr << "The height should be greater than 2" << endl; exit(1); } /*** print some information on screen ***/ if (!quiet) { cout << "resizing " << infile << " [" << old_width << "x" << old_height << "] into " << outfile << " [" << new_width << "x" << new_height << "]" << endl << endl << flush; } if (vmap_infile) { info_msg("will read VMap from", vmap_infile); } if (vmap_outfile) { info_msg("will write VMap to", vmap_outfile); } if ((new_height != old_height) && (new_width != old_width) && (vmap_outfile)) { cerr << "Warning: only the last computed visibility map will be written" << endl; } /*** read and check the feature masks ***/ pngwriter png_pmask; pngwriter png_dmask; if (pres_infile) { info_msg("will read preservation mask from", pres_infile); if (pres_outfile) { info_msg("will write preservation mask to", pres_outfile); png_pmask.pngwriter_rename(pres_outfile); } png_pmask.readfromfile(pres_infile); if (pres_outfile) { if (png_pmask.getwidth() != old_width) { cerr << "Fatal error: preservation mask width does not match input file width" << endl; cerr << "cannot honour the --pres-out-file option" << endl; exit(1); } if (png_pmask.getheight() != old_height) { cerr << "Fatal error: preservation mask height does not match input file height" << endl; cerr << "cannot honour the --pres-out-file option" << endl; exit(1); } } } if (disc_infile) { info_msg("will read discard mask from", disc_infile); if (disc_outfile) { info_msg("will write discrad mask to", disc_outfile); png_dmask.pngwriter_rename(disc_outfile); } png_dmask.readfromfile(disc_infile); if (disc_outfile) { if (png_dmask.getwidth() != old_width) { cerr << "Fatal error: discard mask width does not match input file width" << endl; cerr << "cannot honour the --disc-out-file option" << endl; exit(1); } if (png_dmask.getheight() != old_height) { cerr << "Fatal error: discard mask height does not match input file height" << endl; cerr << "cannot honour the --disc-out-file option" << endl; exit(1); } } } /*** read and check the rigidity mask ***/ pngwriter png_rigmask; if (rigmask_infile) { info_msg("will read rigidity mask from", rigmask_infile); if (rigmask_outfile) { info_msg("will write rigidity mask to", rigmask_outfile); png_pmask.pngwriter_rename(rigmask_outfile); } png_rigmask.readfromfile(rigmask_infile); if (rigmask_outfile) { if (png_rigmask.getwidth() != old_width) { cerr << "Fatal error: rigidity mask width does not match input file width" << endl; cerr << "cannot honour the --rigmask-out-file option" << endl; exit(1); } if (png_rigmask.getheight() != old_height) { cerr << "Fatal error: rigidity mask height does not match input file height" << endl; cerr << "cannot honour the --rigmask-out-file option" << endl; exit(1); } } } if (energy_w_outfile) { info_msg("will write horizontal energy to", energy_w_outfile); } if (energy_h_outfile) { info_msg("will write vertical energy to", energy_h_outfile); } /* convert the images into rgb buffers to use them with the library */ guchar *rgb_buffer; guchar *rgb_pres_buffer = NULL; guchar *rgb_disc_buffer = NULL; guchar *rgb_rigmask_buffer = NULL; TRAP_N(rgb_buffer = rgb_buffer_from_image(&png)); if (pres_infile) { TRAP_N(rgb_pres_buffer = rgb_buffer_from_image(&png_pmask)); } if (disc_infile) { TRAP_N(rgb_disc_buffer = rgb_buffer_from_image(&png_dmask)); } if (rigmask_infile) { TRAP_N(rgb_rigmask_buffer = rgb_buffer_from_image(&png_rigmask)); } if (!quiet) { cout << endl; } /**** (I) GENERATE THE MULTISIZE REPRESENTATION ****/ /* (I.1) swallow the buffer in a (minimal) LqrCarver object * (arguments are width, height and number of colour channels) */ TRAP_N(carver = lqr_carver_new(rgb_buffer, old_width, old_height, 3)); /* (I.2) if we have to attach other images, we have to do so * immediatley after the carver construction (we might * initialize the carver first, but not load a visibility map)*/ LqrCarver *pres_carver; if (pres_outfile) { TRAP_N(pres_carver = lqr_carver_new(rgb_pres_buffer, old_width, old_height, 3)); TRAP(lqr_carver_attach(carver, pres_carver)); /* note : this way the rgb_pres_buffer is lost, and we will have * to read it again in order to use it as a bias (this could * be avoided but the code would be harder to read) */ } LqrCarver *disc_carver; if (disc_outfile) { TRAP_N(disc_carver = lqr_carver_new(rgb_disc_buffer, old_width, old_height, 3)); TRAP(lqr_carver_attach(carver, disc_carver)); } LqrCarver *rigmask_carver; if (rigmask_outfile) { TRAP_N(rigmask_carver = lqr_carver_new(rgb_rigmask_buffer, old_width, old_height, 3)); TRAP(lqr_carver_attach(carver, rigmask_carver)); } /* (I.3) next step depends on whether we have a pre-computed * map to use or not*/ if (!vmap_infile) { /* (I.3a.1) initialize the carver (with default values), * so that we can do the resizing */ TRAP(lqr_carver_init(carver, max_step, rigidity)); /* (I.3a.2) add the bias (positive to preserve, negative * to discard) and the rigidity mask*/ if (pres_infile) { TRAP_N(rgb_pres_buffer = rgb_buffer_from_image(&png_pmask)); TRAP(lqr_carver_bias_add_rgb(carver, rgb_pres_buffer, pres_strength, 3)); } if (disc_infile) { TRAP_N(rgb_disc_buffer = rgb_buffer_from_image(&png_dmask)); TRAP(lqr_carver_bias_add_rgb(carver, rgb_disc_buffer, -disc_strength, 3)); } if (rigmask_infile) { TRAP_N(rgb_rigmask_buffer = rgb_buffer_from_image(&png_rigmask)); TRAP(lqr_carver_rigmask_add_rgb(carver, rgb_rigmask_buffer, 3)); } /* (I.3a.3) set the energy function */ TRAP(set_energy(carver, energy_function)); /* (I.3a.4) output the energy function */ if (energy_w_outfile) { TRAP(write_energy(carver, energy_w_outfile, 0)); } if (energy_h_outfile) { TRAP(write_energy(carver, energy_h_outfile, 0)); } /* (I.3b.5) set the side switch frequency */ lqr_carver_set_side_switch_frequency(carver, side_switch_frequency); /* (I.3b.6) set the enlargement step */ TRAP(lqr_carver_set_enl_step(carver, enl_step)); } else { /* (I.3b.1) read a visibility map from a file */ LqrVMap *vmap; vmap = load_vmap_from_file(vmap_infile); if (vmap == NULL) { cerr << "Unable to load vmap, aborting" << endl; exit(1); } /* (I.3b.2) load it in the carver */ TRAP(lqr_vmap_load(carver, vmap)); } /* (I.4) if we want to access the visibility maps, we have * to set it in the carver before rescaling occurs */ if (vmap_outfile) { lqr_carver_set_dump_vmaps(carver); } /**** (II) SET UP THE PROGRESS INDICATOR ****/ if (!quiet) { /* (II.1) generate a progress with default values */ LqrProgress *progress; TRAP_N(progress = lqr_progress_new()); /* (II.2) set up with custom commands */ init_progress(progress); /* (II.3) attach the progress to out multisize image */ lqr_carver_set_progress(carver, progress); } /**** (III) LIQUID RESCALE ****/ /* (III.1) set the rescaling order */ lqr_carver_set_resize_order(carver, res_order); /* (III.2) catch the INT signal */ signal(SIGINT, cancel_handler); /* (III.3) invoke the rescaling function * this step could be reiterated at wish */ TRAP(lqr_carver_resize(carver, new_width, new_height)); /**** (IV) SAVE THE VISIBILITY MAP ****/ if (vmap_outfile) { LqrVMap *vmap; TRAP_N(vmap = lqr_vmap_dump(carver)); TRAP(save_vmap_to_file(vmap, vmap_outfile)); } #if 0 /* alternative way */ LqrVMapList *vmap_list = lqr_vmap_list_start(carver); if (vmap_outfile) { TRAP(save_vmap_to_file(lqr_vmap_list_current(vmap_list), vmap_outfile)); lqr_vmap_list_next(vmap_list); } #endif /**** (V) READOUT THE MULTISIZE IMAGE ****/ /* (V.1) readout the main image */ TRAP(write_carver_to_image(carver, &png)); /* (V.2) readout the atteched images */ LqrCarverList *carver_list = lqr_carver_list_start(carver); if (pres_outfile) { TRAP(write_carver_to_image(lqr_carver_list_current(carver_list), &png_pmask)); lqr_carver_list_next(carver_list); } if (disc_outfile) { TRAP(write_carver_to_image(lqr_carver_list_current(carver_list), &png_dmask)); lqr_carver_list_next(carver_list); } if (rigmask_outfile) { TRAP(write_carver_to_image(lqr_carver_list_current(carver_list), &png_rigmask)); } /**** (VI) DESTROY THE CARVER OBJECT ****/ lqr_carver_destroy(carver); /*** close files (write the images on disk) ***/ png.close(); if (pres_outfile) { png_pmask.close(); } if (disc_outfile) { png_dmask.close(); } if (rigmask_outfile) { png_rigmask.close(); } return 0; }/*}}}*/ /*** PARSE COMMAND LINE ***/ LqrRetVal parse_command_line(int argc, char **argv) {/*{{{*/ int i; int c; struct option lopts[] = { {"file", required_argument, NULL, 'f'}, {"out-file", required_argument, NULL, 'o'}, {"width", required_argument, NULL, 'w'}, {"height", required_argument, NULL, 'h'}, {"rigidity", required_argument, NULL, 'r'}, {"max-step", required_argument, NULL, 's'}, {"pres-file", required_argument, NULL, 'p'}, {"pres-out-file", required_argument, NULL, 'P'}, {"pres-strength", required_argument, NULL, 'z'}, {"disc-file", required_argument, NULL, 'd'}, {"disc-out-file", required_argument, NULL, 'D'}, {"disc-strength", required_argument, NULL, 'x'}, {"rigmask-file", required_argument, NULL, 'k'}, {"rigmask-out-file", required_argument, NULL, 'K'}, {"vmap-out-file", required_argument, NULL, 'v'}, {"vmap-in-file", required_argument, NULL, 'V'}, {"vertical-first", no_argument, NULL, 't'}, {"side-switch-frequency", required_argument, NULL, 'n'}, {"enl-step", required_argument, NULL, 'e'}, {"energy-function", required_argument, NULL, 'E'}, {"energy-w-out-file", required_argument, NULL, 'W'}, {"energy-h-out-file", required_argument, NULL, 'H'}, {"quiet", no_argument, NULL, 'q'}, {"help", no_argument, NULL, '#'}, {NULL, 0, NULL, 0} }; while ((c = getopt_long(argc, argv, "f:,o:,w:,h:,r:,s:,p:,P:,z:,d:,D:,x:,k:,K:,v:,V:,t,n:,e:,E:,W:,H:,q", lopts, &i)) != EOF) { switch (c) { case 'f': infile = optarg; break; case 'o': outfile = optarg; break; case 'w': if (optarg[strlen(optarg) - 1] == '%') { new_width_p = atof(optarg); } else { new_width = atoi(optarg); new_width_p = 0; } break; case 'h': if (optarg[strlen(optarg) - 1] == '%') { new_height_p = atof(optarg); } else { new_height = atoi(optarg); new_height_p = 0; } break; case 'r': rigidity = atof(optarg); break; case 's': max_step = atoi(optarg); break; case 'p': pres_infile = optarg; break; case 'P': pres_outfile = optarg; break; case 'z': pres_strength = atoi(optarg); break; case 'd': disc_infile = optarg; break; case 'D': disc_outfile = optarg; break; case 'x': disc_strength = atoi(optarg); break; case 'k': rigmask_infile = optarg; break; case 'K': rigmask_outfile = optarg; break; case 'v': vmap_infile = optarg; break; case 'V': vmap_outfile = optarg; break; case 't': res_order = LQR_RES_ORDER_VERT; break; case 'n': side_switch_frequency = atoi(optarg); break; case 'e': enl_step = atof(optarg); break; case 'E': energy_function = optarg; break; case 'W': energy_w_outfile = optarg; break; case 'H': energy_h_outfile = optarg; break; case 'q': quiet = 1; break; case '#': help(argv[0]); exit(0); default: cerr << "Error parsing command line. Use " << argv[0] << " --help for usage instructions." << endl; return LQR_ERROR; } } if (!infile) { cerr << "Input file missing." << endl; return LQR_ERROR; } if (!outfile) { cerr << "Output file missing." << endl; return LQR_ERROR; } if (!new_width && !new_height && !new_width_p && !new_height_p) { cerr << "At least one of --width or --height has to be specified (and be different from 0)." << endl; return LQR_ERROR; } if (pres_outfile && !pres_infile) { cerr << "Option --pres-out-file can't be used without --pres-in-file." << endl; return LQR_ERROR; } if (disc_outfile && !disc_infile) { cerr << "Option --disc-out-file can't be used without --disc-in-file." << endl; return LQR_ERROR; } if (rigmask_outfile && !rigmask_infile) { cerr << "Option --rigmask-out-file can't be used without --rigmask-in-file." << endl; return LQR_ERROR; } if (pres_strength < 0) { cerr << "Preservation strength cannot be negative." << endl; return LQR_ERROR; } if (disc_strength < 0) { cerr << "Discard strength cannot be negative." << endl; return LQR_ERROR; } if (rigidity < 0) { cerr << "Rigidity cannot be negative." << endl; return LQR_ERROR; } if (max_step < 0) { cerr << "Max step cannot be negative." << endl; return LQR_ERROR; } if ((enl_step <= 1) || (enl_step > 2)) { cerr << "Enlargement step must be greater than 1 and not greater than 2." << endl; return LQR_ERROR; } return LQR_OK; }/*}}}*/ void help(char *command) {/*{{{*/ cout << "Usage: " << command << " -f <file> -o <out-file> [ -w <width> | -h <height> ] [ ... ]" << endl; cout << " Options:" << endl; cout << " -f <file> or --file <file>" << endl; cout << " Specifies input file. Must be in PNG format, in RGB without alpha channel" << endl; cout << " -o <out-file> or --out-file <out-file>" << endl; cout << " Specifies the output file." << endl; cout << " -w <width> or --width <width>" << endl; cout << " The new width. It must be greater than 2." << endl; cout << " If it is 0, or it is not given, the width is unchanged." << endl; cout << " If it is followed by a %, it is interpreted as a percentage with" << endl; cout << " respect to the original width (and needs not to be an integer)." << endl; cout << " -h <height> or --height <height>" << endl; cout << " Same as -w for the height." << endl; cout << " -r <rigidity> or --rigidity < rigidity>" << endl; cout << " Seams rigidity. Any non-negative value is allowed. Defaults to 0." << endl; cout << " -s <max-step> or --max-step <max-step>" << endl; cout << " Maximum seam transversal step. Default value is 1." << endl; cout << " -p <pres-file> or --pres-file <pres-file>" << endl; cout << " File to be used as a mask for features preservation. It must be in the" << endl; cout << " same format as the input file." << endl; cout << " -P <pres-out-file> or --pres-out-file <pres-out-file>" << endl; cout << " If specified, the preservation mask will be rescaled along with the" << endl; cout << " input file, and the output will be written in the specified file." << endl; cout << " The size of the preservation mask has to match that of the original" << endl; cout << " image." << endl; cout << " -z <pres-strength> or --pres-strength <pres-strength>" << endl; cout << " Preservation mask strength. Any integer non-negative value is allowed." << endl; cout << " Defaults to 1000." << endl; cout << " -d <disc-file> or --disc-file <disc-file>" << endl; cout << " File to be used as a mask for features discard. It must be in the" << endl; cout << " same format as the input file." << endl; cout << " -D <disc-out-file> or --disc-out-file <disc-out-file>" << endl; cout << " Same as -P for the discard mask." << endl; cout << " -x <disc-strength> or --disc-strength <disc-strength>" << endl; cout << " Same as -z for the discard mask." << endl; cout << " -v <vmap-file> or --vmap-file <vmap-file>" << endl; cout << " Reads the visibility map from the specified file. The rescaling will fail" << endl; cout << " if it is asked to go beyond the depth of the map." << endl; cout << " -V <vmap-out-file> or --vmap-out-file <vmap-out-file>" << endl; cout << " Writes the visibility map in the specified file. Currently, only the first one." << endl; cout << " -t or --vertical-first" << endl; cout << " Rescale vertically first (instead of horizontally)." << endl; cout << " -n <frequency> or --side-switch-frequency <frequency>" << endl; cout << " Set the number of switches of the side choice for each size modification." << endl; cout << " -e <step> or --enl-step <step>" << endl; cout << " Set the maximum enlargement in a single step." << endl; cout << " It must be greater than 1 and not greater than 2 (default = 1.5)" << endl; cout << " -E <energy-function> or --energy-function <energy-function>" << endl; cout << " Possible values are: xabs, sumabs, norm, xsobel, sobel" << endl; cout << " -W <energy-w-out-file> or --energy-w-out-file <energy-w-out-file>" << endl; cout << " Writes the energy map for horizontal scalings in the specified file" << endl; cout << " -H <energy-h-out-file> or --energy-h-out-file <energy-h-out-file>" << endl; cout << " Writes the energy map for vertical scalings in the specified file" << endl; cout << " -q or --quiet" << endl; cout << " Quiet mode." << endl; cout << " --help" << endl; cout << " This help." << endl; }/*}}}*/ /*** AUXILIARY I/O FUNCTIONS ***/ /* convert the image in the right format */ guchar * rgb_buffer_from_image(pngwriter *png) {/*{{{*/ gint x, y, k, channels; gint w, h; guchar *buffer; /* get info from the image */ w = png->getwidth(); h = png->getheight(); channels = 3; // we assume an RGB image here /* allocate memory to store w * h * channels unsigned chars */ buffer = g_try_new(guchar, channels * w * h); g_assert(buffer != NULL); /* start iteration (always y first, then x, then colours) */ for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { for (k = 0; k < channels; k++) { /* read the image channel k at position x,y */ buffer[(y * w + x) * channels + k] = (guchar) (png->dread(x + 1, y + 1, k + 1) * 255); /* note : the x+1,y+1,k+1 on the right side are * specific the pngwriter library */ } } } return buffer; }/*}}}*/ /* readout the multizie image */ LqrRetVal write_carver_to_image(LqrCarver *r, pngwriter *png) {/*{{{*/ gint x, y; guchar *rgb; gdouble red, green, blue; gint w, h; /* make sure the image is RGB */ LQR_CATCH_F(lqr_carver_get_channels(r) == 3); /* resize the image canvas as needed to * fit for the new size */ w = lqr_carver_get_width(r); h = lqr_carver_get_height(r); png->resize(w, h); /* initialize image reading */ lqr_carver_scan_reset(r); /* readout (no need to init rgb) */ while (lqr_carver_scan(r, &x, &y, &rgb)) { /* convert the output into doubles */ red = (gdouble) rgb[0] / 255; green = (gdouble) rgb[1] / 255; blue = (gdouble) rgb[2] / 255; /* plot (pngwriter's coordinates start from 1,1) */ png->plot(x + 1, y + 1, red, green, blue); } return LQR_OK; }/*}}}*/ /*** ENERGY FUNCTIONS ***/ /* define custom energy function: sobelx */ gfloat sobelx(gint x, gint y, gint w, gint h, LqrReadingWindow *rw, gpointer extra_data) {/*{{{*/ gint i, j; gdouble e = 0; gdouble k[3][3] = { {0.125, 0.25, 0.125}, {0, 0, 0}, {-0.125, -0.25, -0.125} }; for (i = -1; i <= 1; i++) { for (j = -1; j <= 1; j++) { e += k[i + 1][j + 1] * lqr_rwindow_read(rw, i, j, 0); } } return (gfloat) fabs(e); }/*}}}*/ /* define custom energy function: sobel */ gfloat sobel(gint x, gint y, gint w, gint h, LqrReadingWindow *rw, gpointer extra_data) {/*{{{*/ gint i, j; gdouble ex = 0; gdouble ey = 0; gdouble k[3][3] = { {0.125, 0.25, 0.125}, {0, 0, 0}, {-0.125, -0.25, -0.125} }; for (i = -1; i <= 1; i++) { for (j = -1; j <= 1; j++) { ex += k[i + 1][j + 1] * lqr_rwindow_read(rw, i, j, 0); ey += k[j + 1][i + 1] * lqr_rwindow_read(rw, i, j, 0); } } return (gfloat) (sqrt(ex * ex + ey * ey)); }/*}}}*/ /* set the energy function */ LqrRetVal set_energy(LqrCarver *carver, gchar *energy_function) {/*{{{*/ if (energy_function == NULL) { return LQR_OK; } else if (g_strcmp0(energy_function, "xabs") == 0) { LQR_CATCH(lqr_carver_set_energy_function_builtin(carver, LQR_EF_GRAD_XABS)); } else if (g_strcmp0(energy_function, "sumabs") == 0) { LQR_CATCH(lqr_carver_set_energy_function_builtin(carver, LQR_EF_GRAD_SUMABS)); } else if (g_strcmp0(energy_function, "norm") == 0) { LQR_CATCH(lqr_carver_set_energy_function_builtin(carver, LQR_EF_GRAD_NORM)); } else if (g_strcmp0(energy_function, "sobelx") == 0) { LQR_CATCH(lqr_carver_set_energy_function(carver, sobelx, 1, LQR_ER_BRIGHTNESS, NULL)); } else if (g_strcmp0(energy_function, "sobel") == 0) { LQR_CATCH(lqr_carver_set_energy_function(carver, sobel, 1, LQR_ER_BRIGHTNESS, NULL)); } else { cerr << "Unknown energy function: " << energy_function << endl; exit(1); } return LQR_OK; }/*}}}*/ /* write out the energy */ LqrRetVal write_energy(LqrCarver *carver, gchar *energy_outfile, gint orientation) {/*{{{*/ gfloat *nrg_buffer; pngwriter png_nrg; png_nrg.pngwriter_rename(energy_outfile); gint x, y; gfloat en; gdouble red, green, blue; gint w = lqr_carver_get_width(carver); gint h = lqr_carver_get_height(carver); png_nrg.resize(w, h); LQR_CATCH_MEM(nrg_buffer = g_try_new0(gfloat, w * h)); LQR_CATCH(lqr_carver_get_energy(carver, nrg_buffer, orientation)); for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { en = nrg_buffer[y * w + x]; red = (gdouble) en; green = (gdouble) en; blue = (gdouble) en; /* plot (pngwriter's coordinates start from 1,1) */ png_nrg.plot(x + 1, y + 1, red, green, blue); } } g_free(nrg_buffer); png_nrg.close(); return LQR_OK; }/*}}}*/ /*** PROGRESS INDICATOR ***/ /* set up the progress functions */ LqrRetVal my_progress_init(const gchar *message) {/*{{{*/ printf("%s -------------------- 0.00%% (00:00.00)", message); fflush(stdout); clock_start = clock(); return LQR_OK; }/*}}}*/ LqrRetVal my_progress_update(gdouble percentage) {/*{{{*/ gint i; gfloat p; for (i = 0; i < 38; i++) { printf("\b"); } for (i = 0; i < 20 * percentage; i++) { printf("*"); } for (; i < 20; i++) { printf("-"); } p = 100 * percentage; printf(" %s%.2f%%", p < 10 ? " " : "", p); gfloat t; clock_now = clock(); t = (gfloat) (clock_now - clock_start) / CLOCKS_PER_SEC; i = (gint) t; gint min, sec, cent; min = i / 60; sec = i % 60; cent = (gint) ((t - i) * 100); printf(" (%.2i:%.2i.%.2i)", min, sec, cent); fflush(stdout); return LQR_OK; }/*}}}*/ LqrRetVal my_progress_end(const gchar *message) {/*{{{*/ gint i; for (i = 0; i < 38; i++) { printf("\b"); } printf("******************** %s", message); gfloat t; clock_now = clock(); t = (gfloat) (clock_now - clock_start) / CLOCKS_PER_SEC; i = (gint) t; gint min, sec, cent; min = i / 60; sec = i % 60; cent = (gint) ((t - i) * 100); printf(" (%.2i:%.2i.%.2i)\n", min, sec, cent); fflush(stdout); return LQR_OK; }/*}}}*/ /* setup the progress machinery */ void init_progress(LqrProgress * progress) {/*{{{*/ lqr_progress_set_init(progress, my_progress_init); lqr_progress_set_update(progress, my_progress_update); lqr_progress_set_end(progress, my_progress_end); lqr_progress_set_init_width_message(progress, "Resizing width :"); lqr_progress_set_init_height_message(progress, "Resizing height :"); lqr_progress_set_end_width_message(progress, "done"); lqr_progress_set_end_height_message(progress, "done"); lqr_progress_set_update_step(progress, 0.01); }/*}}}*/ /*** VISIBILTY MAPS ***/ /* convert a visibility map in binary and stores it in a file */ LqrRetVal save_vmap_to_file(LqrVMap *vmap, gchar *name) {/*{{{*/ FILE *sink; gint *buffer; gint width, height, depth, orientation; gint y, x; gint32 vs; buffer = lqr_vmap_get_data(vmap); width = lqr_vmap_get_width(vmap); height = lqr_vmap_get_height(vmap); depth = lqr_vmap_get_depth(vmap); orientation = lqr_vmap_get_orientation(vmap); /* open file */ if ((sink = fopen(name, "wb")) == NULL) { cerr << "error opening outfile : " << name << endl; return LQR_ERROR; } /* VMAP : filtype */ fprintf(sink, "VMAP["); /* HEAD : the header */ fprintf(sink, "HEAD["); fprintf(sink, "[width=%i]", width); fprintf(sink, "[height=%i]", height); fprintf(sink, "[orientation=%i]", orientation); fprintf(sink, "[depth=%i]", depth); fprintf(sink, "[comment=()]"); /* close HEAD */ fprintf(sink, "]"); /* BODY : the data */ fprintf(sink, "BODY["); /* vmap is a buffer of gint's */ for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { vs = buffer[y * width + x]; fprintf(sink, "%c%c%c%c", (vs >> 24) & 0xFF, ((vs >> 16) & 0xFF), ((vs >> 8) & 0xFF), vs & 0xFF); } } /* close BODY */ fprintf(sink, "]"); /* close VMAP */ fprintf(sink, "]"); /* close file */ fclose(sink); return LQR_OK; }/*}}}*/ /* reads a binary file and converts it into a visibility map */ LqrVMap * load_vmap_from_file(gchar *name) {/*{{{*/ FILE *input; gint x, y, z0; gint w, h, depth, orientation; gint c, i; guchar i1, i2, i3, i4; gint32 vs; gboolean w_ok, h_ok, d_ok, o_ok; gchar read_buffer[RBS]; gchar read_tag[RBS]; gint *buffer; LqrVMap *vmap; /* flags */ w_ok = h_ok = d_ok = o_ok = FALSE; w = h = depth = orientation = 0; /* open file */ CHECK_OR_N(((input = fopen(name, "rb")) != NULL), "can't open vmap file"); /* read filetype */ CHECK_OR_N(fscanf(input, "VMAP[") != EOF, "not a VMAP file"); /* read the header */ CHECK_OR_N(fscanf(input, "HEAD[") != EOF, "missing vmap header"); /* scan the header */ while (1) { CHECK_OR_N((c = getc(input)) != EOF, "corrupted vmap header"); if (c == ']') { /* the header has ended */ break; } ungetc(c, input); /* start reading a tag */ CHECK_OR_N(fscanf(input, "[") != EOF, "corrupted vmap header"); /* start reading the tag name */ i = 0; while (((c = getc(input)) != EOF) && (i < RBS)) { if (c == '=') { /* the tag name has ended */ break; } read_tag[i++] = c; } CHECK_OR_N(i < RBS, "vmap tag name too long"); read_tag[i] = '\0'; /* start reading the tag content */ i = 0; while (((c = getc(input)) != EOF) && (i < RBS)) { if (c == ']') { /* the tag has ended */ break; } read_buffer[i++] = c; } CHECK_OR_N(i < RBS, "tag content too long"); read_buffer[i] = '\0'; /* set the corresponding variable and the flag */ if (strncmp(read_tag, "width", RBS) == 0) { w = atoi(read_buffer); w_ok = TRUE; } else if (strncmp(read_tag, "height", RBS) == 0) { h = atoi(read_buffer); h_ok = TRUE; } else if (strncmp(read_tag, "depth", RBS) == 0) { depth = atoi(read_buffer); d_ok = TRUE; } else if (strncmp(read_tag, "orientation", RBS) == 0) { orientation = atoi(read_buffer); o_ok = TRUE; } else if (strncmp(read_tag, "comment", RBS) == 0) { /* discard comments */ } else { cerr << "warning : unknown tag : " << read_tag << endl; } } /* check if all the needed quantities are there */ CHECK_OR_N(w_ok && h_ok && d_ok && o_ok, "missing vmap tags"); /* start reading the data */ CHECK_OR_N(fscanf(input, "BODY[") != EOF, "missing vmap body"); /* allocate memory for the vmap buffer */ buffer = g_try_new0(gint, w * h); if (buffer == NULL) { return NULL; } /* scan the data */ y = x = 0; while ((y < h) && ((c = getc(input)) != EOF)) { ungetc(c, input); /* read a 32 bit integer */ CHECK_OR_N(fscanf(input, "%c%c%c%c", &i1, &i2, &i3, &i4) == 4, "vmap data corrupted"); vs = i4 + (i3 << 8) + (i2 << 16) + (i1 << 24); /* set it in the buffer */ z0 = y * w + x; buffer[z0] = vs; /* update the coordinates */ x++; if (x == w) { x = 0; y++; } } /* test if the amount of data read is correct */ CHECK_OR_N((x == 0) && (y == h), "vmap data corrupted"); /* end reading the data */ CHECK_OR_N(fscanf(input, "]") != EOF, "unterminated vmap body"); /* end reading the vmap file */ CHECK_OR_N(fscanf(input, "]") != EOF, "unterminated vmap file"); /* create the vmap object with all the data aquired */ vmap = lqr_vmap_new(buffer, w, h, depth, orientation); if (vmap == NULL) { return NULL; } return vmap; }/*}}}*/ /*** EXTRA ***/ void info_msg(const gchar *msg, const gchar *name) {/*{{{*/ if (!quiet) { cout << " + " << msg << " " << name << endl << flush; } }/*}}}*/ void cancel_handler(int signum) {/*{{{*/ printf("\n"); fflush(stdout); /* We must cancel the computation * from a different thread */ g_thread_create(cancel_thread, (gpointer) carver, FALSE, NULL); }/*}}}*/ gpointer cancel_thread(gpointer data) {/*{{{*/ lqr_carver_cancel((LqrCarver *) data); return NULL; }/*}}}*/