You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

main.c 9.4 KiB

21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
19 years ago
21 years ago
21 years ago
21 years ago
21 years ago
4 years ago
4 years ago
21 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. /*
  2. * This file is part of Aptdec.
  3. * Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019-2020
  4. *
  5. * Aptdec is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. *
  18. */
  19. #include <stdlib.h>
  20. #include <stdio.h>
  21. #include <string.h>
  22. #ifndef _MSC_VER
  23. #include <libgen.h>
  24. #else
  25. #include <windows.h>
  26. #endif
  27. #include <math.h>
  28. #include <sndfile.h>
  29. #include <errno.h>
  30. #include <time.h>
  31. #include "libs/argparse.h"
  32. #include "common.h"
  33. #include "apt.h"
  34. #include "pngio.h"
  35. #include "image.h"
  36. #include "color.h"
  37. // Audio file
  38. static SNDFILE *audioFile;
  39. // Number of channels in audio file
  40. int channels = 1;
  41. // Function declarations
  42. static int initsnd(char *filename);
  43. int getsamples(void *context, float *samples, int nb);
  44. static int processAudio(char *filename, options_t *opts);
  45. #ifdef _MSC_VER
  46. // Functions not supported by MSVC
  47. static char *dirname(char *path)
  48. {
  49. static char dir[MAX_PATH];
  50. _splitpath(path, NULL, dir, NULL, NULL);
  51. return dir;
  52. }
  53. static char *basename(char *path)
  54. {
  55. static char base[MAX_PATH];
  56. _splitpath(path, NULL, NULL, base, NULL);
  57. return base;
  58. }
  59. #endif
  60. int main(int argc, const char **argv) {
  61. options_t opts = { "r", "", 19, "", ".", 0, "", "", 1.0, 0 };
  62. static const char *const usages[] = {
  63. "aptdec [options] [[--] sources]",
  64. "aptdec [sources]",
  65. NULL,
  66. };
  67. struct argparse_option options[] = {
  68. OPT_HELP(),
  69. OPT_GROUP("Image options"),
  70. OPT_STRING('i', "image", &opts.type, "set output image type (see the README for a list)", NULL, 0, 0),
  71. OPT_STRING('e', "effect", &opts.effects, "add an effect (see the README for a list)", NULL, 0, 0),
  72. OPT_FLOAT('g', "gamma", &opts.gamma, "gamma adjustment (1.0 = off)", NULL, 0, 0),
  73. OPT_GROUP("Satellite options"),
  74. OPT_INTEGER('s', "satellite", &opts.satnum, "satellite ID, must be between 15 and 19", NULL, 0, 0),
  75. OPT_GROUP("Paths"),
  76. OPT_STRING('p', "palette", &opts.palette, "path to a palette", NULL, 0, 0),
  77. OPT_STRING('m', "map", &opts.map, "path to a WXtoImg map", NULL, 0, 0),
  78. OPT_STRING('o', "filename", &opts.filename, "filename of the output image", NULL, 0, 0),
  79. OPT_STRING('d', "output", &opts.path, "output directory (must exist first)", NULL, 0, 0),
  80. OPT_GROUP("Misc"),
  81. OPT_BOOLEAN('r', "realtime", &opts.realtime, "decode in realtime", NULL, 0, 0),
  82. OPT_INTEGER('k', "map-offset", &opts.mapOffset, "Map offset (in px, default 0)", NULL, 0, 0),
  83. OPT_END(),
  84. };
  85. struct argparse argparse;
  86. argparse_init(&argparse, options, usages, 0);
  87. argparse_describe(&argparse, "\nA lightweight FOSS NOAA APT satellite imagery decoder.", "\nSee `README.md` for a full description of command line arguments and `LICENSE` for licensing conditions.");
  88. argc = argparse_parse(&argparse, argc, argv);
  89. if(argc == 0){
  90. argparse_usage(&argparse);
  91. }
  92. // Actually decode the files
  93. for (int i = 0; i < argc; i++) {
  94. char *filename = strdup(argv[i]);
  95. processAudio(filename, &opts);
  96. }
  97. return 0;
  98. }
  99. static int processAudio(char *filename, options_t *opts){
  100. // Image info struct
  101. apt_image_t img;
  102. // Mapping between wedge value and channel ID
  103. static struct {
  104. char *id[7];
  105. char *name[7];
  106. } ch = {
  107. { "?", "1", "2", "3A", "4", "5", "3B" },
  108. { "unknown", "visble", "near-infrared", "near-infrared", "thermal-infrared", "thermal-infrared", "mid-infrared" }
  109. };
  110. // Buffer for image channel
  111. char desc[60];
  112. // Parse file path
  113. char path[256], extension[32];
  114. strcpy(path, filename);
  115. strcpy(path, dirname(path));
  116. sscanf(basename(filename), "%255[^.].%31s", img.name, extension);
  117. if(opts->realtime){
  118. // Set output filename to current time when in realtime mode
  119. time_t t;
  120. time(&t);
  121. strncpy(img.name, ctime(&t), 24);
  122. // Init a row writer
  123. initWriter(opts, &img, APT_IMG_WIDTH, APT_MAX_HEIGHT, "Unprocessed realtime image", "r");
  124. }
  125. if(strcmp(extension, "png") == 0){
  126. // Read PNG into image buffer
  127. printf("Reading %s\n", filename);
  128. if(readRawImage(filename, img.prow, &img.nrow) == 0){
  129. exit(EPERM);
  130. }
  131. }else{
  132. // Attempt to open the audio file
  133. if (initsnd(filename) == 0)
  134. exit(EPERM);
  135. // Build image
  136. // TODO: multithreading, would require some sort of input buffer
  137. for (img.nrow = 0; img.nrow < APT_MAX_HEIGHT; img.nrow++) {
  138. // Allocate memory for this row
  139. img.prow[img.nrow] = (float *) malloc(sizeof(float) * APT_PROW_WIDTH);
  140. // Write into memory and break the loop when there are no more samples to read
  141. if (apt_getpixelrow(img.prow[img.nrow], img.nrow, &img.zenith, (img.nrow == 0), getsamples, NULL) == 0)
  142. break;
  143. if(opts->realtime) pushRow(img.prow[img.nrow], APT_IMG_WIDTH);
  144. fprintf(stderr, "Row: %d\r", img.nrow);
  145. fflush(stderr);
  146. }
  147. // Close stream
  148. sf_close(audioFile);
  149. }
  150. if(opts->realtime) closeWriter();
  151. printf("Total rows: %d\n", img.nrow);
  152. // Fallback for detecting the zenith
  153. // TODO: encode metadata in raw images
  154. if(opts->map != NULL && opts->map[0] != '\0' && img.zenith == 0){
  155. fprintf(stderr, "Guessing zenith in image, map will most likely be misaligned.\n");
  156. img.zenith = img.nrow / 2;
  157. }
  158. // Calibrate
  159. img.chA = apt_calibrate(img.prow, img.nrow, APT_CHA_OFFSET, APT_CH_WIDTH);
  160. img.chB = apt_calibrate(img.prow, img.nrow, APT_CHB_OFFSET, APT_CH_WIDTH);
  161. printf("Channel A: %s (%s)\n", ch.id[img.chA], ch.name[img.chA]);
  162. printf("Channel B: %s (%s)\n", ch.id[img.chB], ch.name[img.chB]);
  163. // Crop noise from start and end of image
  164. if(CONTAINS(opts->effects, Crop_Noise)){
  165. img.zenith -= apt_cropNoise(&img);
  166. }
  167. // Denoise
  168. if(CONTAINS(opts->effects, Denoise)){
  169. apt_denoise(img.prow, img.nrow, APT_CHA_OFFSET, APT_CH_WIDTH);
  170. apt_denoise(img.prow, img.nrow, APT_CHB_OFFSET, APT_CH_WIDTH);
  171. }
  172. // Flip, for northbound passes
  173. if(CONTAINS(opts->effects, Flip_Image)){
  174. apt_flipImage(&img, APT_CH_WIDTH, APT_CHA_OFFSET);
  175. apt_flipImage(&img, APT_CH_WIDTH, APT_CHB_OFFSET);
  176. }
  177. // Temperature
  178. if (CONTAINS(opts->type, Temperature) && img.chB >= 4) {
  179. // Create another buffer as to not modify the orignal
  180. apt_image_t tmpimg = img;
  181. for(int i = 0; i < img.nrow; i++){
  182. tmpimg.prow[i] = (float *) malloc(sizeof(float) * APT_PROW_WIDTH);
  183. memcpy(tmpimg.prow[i], img.prow[i], sizeof(float) * APT_PROW_WIDTH);
  184. }
  185. // Perform temperature calibration
  186. apt_temperature(opts->satnum, &tmpimg, APT_CHB_OFFSET, APT_CH_WIDTH);
  187. ImageOut(opts, &tmpimg, APT_CHB_OFFSET, APT_CH_WIDTH, "Temperature", Temperature, (char *)apt_TempPalette);
  188. }
  189. // MCIR
  190. if (CONTAINS(opts->type, MCIR))
  191. ImageOut(opts, &img, APT_CHA_OFFSET, APT_CH_WIDTH, "MCIR", MCIR, NULL);
  192. // Linear equalise
  193. if(CONTAINS(opts->effects, Linear_Equalise)){
  194. apt_linearEnhance(img.prow, img.nrow, APT_CHA_OFFSET, APT_CH_WIDTH);
  195. apt_linearEnhance(img.prow, img.nrow, APT_CHB_OFFSET, APT_CH_WIDTH);
  196. }
  197. // Histogram equalise
  198. if(CONTAINS(opts->effects, Histogram_Equalise)){
  199. apt_histogramEqualise(img.prow, img.nrow, APT_CHA_OFFSET, APT_CH_WIDTH);
  200. apt_histogramEqualise(img.prow, img.nrow, APT_CHB_OFFSET, APT_CH_WIDTH);
  201. }
  202. // Raw image
  203. if (CONTAINS(opts->type, Raw_Image)) {
  204. sprintf(desc, "%s (%s) & %s (%s)", ch.id[img.chA], ch.name[img.chA], ch.id[img.chB], ch.name[img.chB]);
  205. ImageOut(opts, &img, 0, APT_IMG_WIDTH, desc, Raw_Image, NULL);
  206. }
  207. // Palette image
  208. if (CONTAINS(opts->type, Palleted)) {
  209. img.palette = opts->palette;
  210. strcpy(desc, "Palette composite");
  211. ImageOut(opts, &img, APT_CHA_OFFSET, APT_CH_WIDTH, desc, Palleted, NULL);
  212. }
  213. // Channel A
  214. if (CONTAINS(opts->type, Channel_A)) {
  215. sprintf(desc, "%s (%s)", ch.id[img.chA], ch.name[img.chA]);
  216. ImageOut(opts, &img, APT_CHA_OFFSET, APT_CH_WIDTH, desc, Channel_A, NULL);
  217. }
  218. // Channel B
  219. if (CONTAINS(opts->type, Channel_B)) {
  220. sprintf(desc, "%s (%s)", ch.id[img.chB], ch.name[img.chB]);
  221. ImageOut(opts, &img, APT_CHB_OFFSET, APT_CH_WIDTH, desc, Channel_B, NULL);
  222. }
  223. return 1;
  224. }
  225. static int initsnd(char *filename) {
  226. SF_INFO infwav;
  227. int res;
  228. // Open audio file
  229. infwav.format = 0;
  230. audioFile = sf_open(filename, SFM_READ, &infwav);
  231. if (audioFile == NULL) {
  232. fprintf(stderr, "Could not open %s\n", filename);
  233. return 0;
  234. }
  235. res = apt_init(infwav.samplerate);
  236. printf("Input file: %s\n", filename);
  237. if(res < 0) {
  238. fprintf(stderr, "Input sample rate too low: %d\n", infwav.samplerate);
  239. return 0;
  240. }else if(res > 0) {
  241. fprintf(stderr, "Input sample rate too high: %d\n", infwav.samplerate);
  242. return 0;
  243. }
  244. printf("Input sample rate: %d\n", infwav.samplerate);
  245. channels = infwav.channels;
  246. return 1;
  247. }
  248. // Read samples from the audio file
  249. int getsamples(void *context, float *samples, int nb) {
  250. (void) context;
  251. if(channels == 1){
  252. return (int)sf_read_float(audioFile, samples, nb);
  253. }else{
  254. /* Multi channel audio is encoded such as:
  255. * Ch1,Ch2,Ch1,Ch2,Ch1,Ch2
  256. */
  257. float *buf = malloc(sizeof(float) * nb * channels); // Something like BLKIN*2 could also be used
  258. int samplesRead = (int)sf_read_float(audioFile, buf, nb * channels);
  259. for(int i = 0; i < nb; i++) samples[i] = buf[i * channels];
  260. free(buf);
  261. return samplesRead / channels;
  262. }
  263. }