main.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. /****************************************************************************
  2. *
  3. * MODULE: r.out.mpeg
  4. * AUTHOR(S): Bill Brown, CERL (original contributor)
  5. * Brad Douglas <rez touchofmadness.com>, Markus Neteler <neteler itc.it>,
  6. * Glynn Clements <glynn gclements.plus.com>, Hamish Bowman <hamish_b yahoo.com>,
  7. * Jan-Oliver Wagner <jan intevation.de>, Paul Kelly <paul-grass stjohnspoint.co.uk>
  8. * PURPOSE: combines a series of GRASS raster maps into a single MPEG-1
  9. * COPYRIGHT: (C) 1999-2006, 2011 by the GRASS Development Team
  10. *
  11. * This program is free software under the GNU General Public
  12. * License (>=v2). Read the file COPYING that comes with GRASS
  13. * for details.
  14. *
  15. *****************************************************************************/
  16. /* Written by Bill Brown, USACERL (brown@zorro.cecer.army.mil)
  17. * May, 1994
  18. *
  19. * This code is in the public domain. Specifically, we give to the public
  20. * domain all rights for future licensing of the source code, all resale
  21. * rights, and all publishing rights.
  22. *
  23. * We ask, but do not require, that the following message be included in
  24. * all derived works:
  25. * "Portions developed at the US Army Construction Engineering
  26. * Research Laboratories, Champaign, Illinois."
  27. *
  28. * USACERL GIVES NO WARRANTY, EXPRESSED OR IMPLIED,
  29. * FOR THE SOFTWARE AND/OR DOCUMENTATION PROVIDED, INCLUDING, WITHOUT
  30. * LIMITATION, WARRANTY OF MERCHANTABILITY AND WARRANTY OF FITNESS FOR A
  31. * PARTICULAR PURPOSE.
  32. */
  33. #include <stdio.h>
  34. #include <stdlib.h>
  35. #include <string.h>
  36. #include <math.h>
  37. #include <unistd.h>
  38. #include <grass/gis.h>
  39. #include <grass/raster.h>
  40. #include <grass/spawn.h>
  41. #include <grass/glocale.h>
  42. #include "rom_proto.h"
  43. #define MAXIMAGES 400
  44. #define DEF_MAX 500
  45. #define DEF_MIN 200
  46. #define MAXVIEWS 4
  47. #define BORDER_W 2
  48. /* global variables */
  49. int nrows, ncols, numviews, quality;
  50. char *vfiles[MAXVIEWS][MAXIMAGES];
  51. char outfile[GPATH_MAX];
  52. const char *encoder;
  53. float vscale, scale; /* resampling scale factors */
  54. int irows, icols, vrows, vcols;
  55. int frames;
  56. /* function prototypes */
  57. static int load_files(void);
  58. static int use_r_out(void);
  59. static char **gee_wildfiles(const char *wildarg, const char *element, int *num);
  60. static void parse_command(struct Option **viewopts,
  61. char *vfiles[MAXVIEWS][MAXIMAGES],
  62. int *numviews, int *numframes);
  63. static int check_encoder(const char *encoder)
  64. {
  65. int status, prev;
  66. prev = G_suppress_warnings(1);
  67. status = G_spawn_ex(
  68. encoder, encoder,
  69. SF_REDIRECT_FILE, SF_STDERR, SF_MODE_OUT, G_DEV_NULL,
  70. NULL);
  71. G_suppress_warnings(prev);
  72. return status >= 0 && status != 127;
  73. }
  74. int main(int argc, char **argv)
  75. {
  76. struct GModule *module;
  77. struct Option *viewopts[MAXVIEWS], *out, *qual;
  78. struct Flag *conv;
  79. int i;
  80. int *sdimp, longdim, r_out;
  81. G_gisinit(argv[0]);
  82. module = G_define_module();
  83. G_add_keyword(_("raster"));
  84. G_add_keyword(_("export"));
  85. G_add_keyword(_("animation"));
  86. module->description =
  87. _("Converts raster map series to MPEG movie.");
  88. for (i = 0; i < MAXVIEWS; i++) {
  89. char *buf = NULL;
  90. viewopts[i] = G_define_standard_option(G_OPT_R_INPUTS);
  91. G_asprintf(&buf, "view%d", i + 1);
  92. viewopts[i]->key = G_store(buf);
  93. viewopts[i]->required = (i ? NO : YES);
  94. G_asprintf(&buf, _("Name of input raster map(s) for view no.%d"), i + 1);
  95. viewopts[i]->description = G_store(buf);
  96. viewopts[i]->guisection = _("Views");
  97. G_free(buf);
  98. }
  99. out = G_define_standard_option(G_OPT_F_OUTPUT);
  100. qual = G_define_option();
  101. qual->key = "quality";
  102. qual->type = TYPE_INTEGER;
  103. qual->required = NO;
  104. qual->multiple = NO;
  105. qual->answer = "3";
  106. qual->options = "1-5";
  107. qual->description =
  108. _("Quality factor (1 = highest quality, lowest compression)");
  109. qual->guisection = _("Settings");
  110. conv = G_define_flag();
  111. conv->key = 'c';
  112. conv->label = _("Convert on the fly, uses less disk space");
  113. conv->description = _("Requires r.out.ppm with stdout option");
  114. if (G_parser(argc, argv))
  115. exit(EXIT_FAILURE);
  116. parse_command(viewopts, vfiles, &numviews, &frames);
  117. r_out = 0;
  118. if (conv->answer)
  119. r_out = 1;
  120. quality = 3;
  121. if (qual->answer != NULL)
  122. sscanf(qual->answer, "%d", &quality);
  123. if (quality > 5 || quality < 1)
  124. quality = 3;
  125. /* find a working encoder */
  126. if (check_encoder("ppmtompeg"))
  127. encoder = "ppmtompeg";
  128. else if (check_encoder("mpeg_encode"))
  129. encoder = "mpeg_encode";
  130. else
  131. G_fatal_error(_("Either mpeg_encode or ppmtompeg must be installed"));
  132. G_debug(1, "encoder = [%s]", encoder);
  133. vrows = Rast_window_rows();
  134. vcols = Rast_window_cols();
  135. nrows = vrows;
  136. ncols = vcols;
  137. /* short dimension */
  138. sdimp = nrows > ncols ? &ncols : &nrows;
  139. /* these proportions should work fine for 1 or 4 views, but for
  140. 2 views, want to double the narrow dim & for 3 views triple it */
  141. if (numviews == 2)
  142. *sdimp *= 2;
  143. else if (numviews == 3)
  144. *sdimp *= 3;
  145. longdim = nrows > ncols ? nrows : ncols;
  146. scale = 1.0;
  147. { /* find animation image size */
  148. int max, min;
  149. char *p;
  150. max = DEF_MAX;
  151. min = DEF_MIN;
  152. if ((p = getenv("GMPEG_SIZE")))
  153. max = min = atoi(p);
  154. if (longdim > max) /* scale down */
  155. scale = (float)max / longdim;
  156. else if (longdim < min) /* scale up */
  157. scale = (float)min / longdim;
  158. }
  159. /* TODO: align image size to 16 pixel width & height */
  160. vscale = scale;
  161. if (numviews == 4)
  162. vscale = scale / 2.;
  163. nrows *= scale;
  164. ncols *= scale;
  165. /* now nrows & ncols are the size of the combined - views image */
  166. vrows *= vscale;
  167. vcols *= vscale;
  168. /* now vrows & vcols are the size for each sub-image */
  169. /* add to nrows & ncols for borders */
  170. /* irows, icols used for vert/horizontal determination in loop below */
  171. irows = nrows;
  172. icols = ncols;
  173. nrows += (1 + (nrows / vrows)) * BORDER_W;
  174. ncols += (1 + (ncols / vcols)) * BORDER_W;
  175. if (numviews == 1 && r_out)
  176. use_r_out();
  177. else
  178. load_files();
  179. return (EXIT_SUCCESS);
  180. }
  181. static int load_files(void)
  182. {
  183. void *voidc;
  184. int rtype;
  185. register int i, rowoff, row, col, vxoff, vyoff, offset;
  186. int cnt, fd, size, tsiz, coff;
  187. int vnum;
  188. int y_rows, y_cols;
  189. char *pr, *pg, *pb;
  190. unsigned char *tr, *tg, *tb, *tset;
  191. char *mpfilename, *name;
  192. char *yfiles[MAXIMAGES];
  193. struct Colors colors;
  194. int ret;
  195. size = nrows * ncols;
  196. pr = G_malloc(size);
  197. pg = G_malloc(size);
  198. pb = G_malloc(size);
  199. tsiz = Rast_window_cols();
  200. tr = (unsigned char *)G_malloc(tsiz);
  201. tg = (unsigned char *)G_malloc(tsiz);
  202. tb = (unsigned char *)G_malloc(tsiz);
  203. tset = (unsigned char *)G_malloc(tsiz);
  204. for (cnt = 0; cnt < frames; cnt++) {
  205. if (cnt > MAXIMAGES) {
  206. cnt--;
  207. break;
  208. }
  209. for (i = 0; i < size; i++)
  210. pr[i] = pg[i] = pb[i] = 0;
  211. for (vnum = 0; vnum < numviews; vnum++) {
  212. if (icols == vcols) {
  213. vxoff = BORDER_W;
  214. vyoff = (irows == vrows) ? BORDER_W :
  215. BORDER_W + vnum * (BORDER_W + vrows);
  216. }
  217. else if (irows == vrows) {
  218. vxoff = (icols == vcols) ? BORDER_W :
  219. BORDER_W + vnum * (BORDER_W + vcols);
  220. vyoff = BORDER_W;
  221. }
  222. else { /* 4 views */
  223. /* assumes we want:
  224. view1 view2
  225. view3 view4
  226. */
  227. vxoff = vnum % 2 ? BORDER_W : vcols + 2 * BORDER_W;
  228. vyoff = vnum > 1 ? vrows + 2 * BORDER_W : BORDER_W;
  229. }
  230. name = vfiles[vnum][cnt];
  231. G_message(_("Reading raster map <%s>..."), name);
  232. fd = Rast_open_old(name, "");
  233. if (Rast_read_colors(name, "", &colors) < 0)
  234. G_fatal_error(_("Unable to read color table for <%s>"), name);
  235. rtype = Rast_get_map_type(fd);
  236. voidc = Rast_allocate_buf(rtype);
  237. for (row = 0; row < vrows; row++) {
  238. Rast_get_row(fd, voidc, (int)(row / vscale), rtype);
  239. rowoff = (vyoff + row) * ncols;
  240. Rast_lookup_colors(voidc, tr, tg, tb,
  241. tset, tsiz, &colors, rtype);
  242. for (col = 0; col < vcols; col++) {
  243. coff = (int)(col / vscale);
  244. offset = rowoff + col + vxoff;
  245. if (!tset[coff])
  246. pr[offset] = pg[offset] = pb[offset] = (char)255;
  247. else {
  248. pr[offset] = (char)tr[coff];
  249. pg[offset] = (char)tg[coff];
  250. pb[offset] = (char)tb[coff];
  251. }
  252. }
  253. }
  254. Rast_close(fd);
  255. }
  256. yfiles[cnt] = G_tempfile();
  257. #ifdef USE_PPM
  258. write_ppm(pr, pg, pb, nrows, ncols, &y_rows, &y_cols, yfiles[cnt]);
  259. #else
  260. write_ycc(pr, pg, pb, nrows, ncols, &y_rows, &y_cols, yfiles[cnt]);
  261. #endif
  262. }
  263. mpfilename = G_tempfile();
  264. write_params(mpfilename, yfiles, outfile, cnt, quality, y_rows, y_cols, 0);
  265. if (G_verbose() <= G_verbose_min())
  266. ret = G_spawn(encoder, encoder, mpfilename,
  267. SF_REDIRECT_FILE, SF_STDOUT, SF_MODE_OUT, G_DEV_NULL,
  268. SF_REDIRECT_FILE, SF_STDERR, SF_MODE_OUT, G_DEV_NULL,
  269. NULL);
  270. else
  271. ret = G_spawn(encoder, encoder, mpfilename, NULL);
  272. if (ret != 0)
  273. G_warning(_("mpeg_encode ERROR"));
  274. clean_files(mpfilename, yfiles, cnt);
  275. G_free(voidc);
  276. G_free(tset);
  277. G_free(tr);
  278. G_free(tg);
  279. G_free(tb);
  280. G_free(pr);
  281. G_free(pg);
  282. G_free(pb);
  283. return (cnt);
  284. }
  285. static int use_r_out(void)
  286. {
  287. char *mpfilename;
  288. int ret;
  289. mpfilename = G_tempfile();
  290. write_params(mpfilename, vfiles[0], outfile, frames, quality, 0, 0, 1);
  291. if (G_verbose() <= G_verbose_min())
  292. ret = G_spawn(encoder, encoder, mpfilename,
  293. SF_REDIRECT_FILE, SF_STDOUT, SF_MODE_OUT, G_DEV_NULL,
  294. SF_REDIRECT_FILE, SF_STDERR, SF_MODE_OUT, G_DEV_NULL,
  295. NULL);
  296. else
  297. ret = G_spawn(encoder, encoder, mpfilename, NULL);
  298. if (ret != 0)
  299. G_warning(_("mpeg_encode ERROR"));
  300. clean_files(mpfilename, NULL, 0);
  301. return (1);
  302. }
  303. /* ###################################################### */
  304. static void mlist(const char *element, const char *wildarg, const char *outfile)
  305. {
  306. int n;
  307. const char *mapset;
  308. for (n = 0; (mapset = G_get_mapset_name(n)); n++) {
  309. char type_arg[GNAME_MAX];
  310. char pattern_arg[GNAME_MAX];
  311. char mapset_arg[GMAPSET_MAX];
  312. if (strcmp(mapset, ".") == 0)
  313. mapset = G_mapset();
  314. sprintf(type_arg, "type=%s", element);
  315. sprintf(pattern_arg, "pattern=%s", wildarg);
  316. sprintf(mapset_arg, "mapset=%s", mapset);
  317. G_spawn_ex("g.list", "g.list",
  318. type_arg, pattern_arg, mapset_arg,
  319. SF_REDIRECT_FILE, SF_STDOUT, SF_MODE_APPEND, outfile,
  320. NULL);
  321. }
  322. }
  323. static char **parse(const char *filename, int *num)
  324. {
  325. char buf[GNAME_MAX];
  326. char **files = NULL;
  327. int max_files = 0;
  328. int num_files = 0;
  329. FILE *fp;
  330. fp = fopen(filename, "r");
  331. if (!fp)
  332. G_fatal_error(_("Error reading wildcard"));
  333. while (fgets(buf, sizeof(buf), fp)) {
  334. char *p = strchr(buf, '\n');
  335. if (p)
  336. *p = '\0';
  337. if (!*buf)
  338. continue;
  339. if (num_files >= max_files) {
  340. max_files += 50;
  341. files = (char **) G_realloc((void *) files,
  342. max_files * sizeof(char *));
  343. }
  344. files[num_files++] = G_store(buf);
  345. }
  346. fclose(fp);
  347. *num = num_files;
  348. return files;
  349. }
  350. static char **gee_wildfiles(const char *wildarg, const char *element, int *num)
  351. {
  352. char *tfile;
  353. char **files;
  354. tfile = G_tempfile();
  355. mlist(element, wildarg, tfile);
  356. files = parse(tfile, num);
  357. remove(tfile);
  358. G_free(tfile);
  359. return files;
  360. }
  361. /********************************************************************/
  362. static void parse_command(struct Option **viewopts,
  363. char *vfiles[MAXVIEWS][MAXIMAGES],
  364. int *numviews, int *numframes)
  365. {
  366. int i, j, k;
  367. *numviews = *numframes = 0;
  368. for (i = 0; i < MAXVIEWS; i++) {
  369. if (viewopts[i]->answers) {
  370. int numi, wildnum;
  371. (*numviews)++;
  372. for (j = 0, numi = 0; viewopts[i]->answers[j]; j++) {
  373. if ((NULL != strchr(viewopts[i]->answers[j], '*')) ||
  374. (NULL != strchr(viewopts[i]->answers[j], '?')) ||
  375. (NULL != strchr(viewopts[i]->answers[j], '['))) {
  376. char **wildfiles = gee_wildfiles(viewopts[i]->answers[j],
  377. "rast", &wildnum);
  378. for (k = 0; k < wildnum; k++)
  379. vfiles[i][numi++] = wildfiles[k];
  380. }
  381. else
  382. vfiles[i][numi++] = G_store(viewopts[i]->answers[j]);
  383. }
  384. /* keep track of smallest number of frames */
  385. *numframes =
  386. *numframes ? *numframes > numi ? numi : *numframes : numi;
  387. }
  388. }
  389. }