Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 
 
 

320 rader
8.6 KiB

  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. #include <getopt.h>
  23. #include <libgen.h>
  24. #include <math.h>
  25. #include <sndfile.h>
  26. #include <errno.h>
  27. #include "messages.h"
  28. #include "offsets.h"
  29. #define FAILURE 0
  30. #define SUCCESS 1
  31. typedef struct {
  32. char *type; // Output image type
  33. char *effects;
  34. int satnum; // The satellite number
  35. char *map; // Path to a map file
  36. char *path; // Output directory
  37. } options_t;
  38. typedef struct {
  39. float *prow[3000]; // Row buffers
  40. int nrow; // Number of rows
  41. int chA, chB; // ID of each channel
  42. char name[256]; // Stripped filename
  43. } image_t;
  44. // DSP
  45. extern int getpixelrow(float *pixelv, int nrow, int *zenith);
  46. extern int init_dsp(double F);
  47. // I/O
  48. extern int readfcconf(char *file);
  49. extern int readRawImage(char *filename, float **prow, int *nrow);
  50. extern int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, char *chid, char *palette);
  51. // Image functions
  52. extern int calibrate(float **prow, int nrow, int offset, int width);
  53. extern void histogramEqualise(float **prow, int nrow, int offset, int width);
  54. extern void temperature(options_t *opts, image_t *img, int offset, int width);
  55. extern int Ngvi(float **prow, int nrow);
  56. extern void denoise(float **prow, int nrow, int offset, int width);
  57. extern void distrib(options_t *opts, image_t *img, char *chid);
  58. // Palettes
  59. extern char GviPalette[256*3];
  60. extern char TempPalette[256*3];
  61. // Row where the satellite is closest to the observer
  62. int zenith = 0;
  63. // Audio file
  64. static SNDFILE *audioFile;
  65. static int initsnd(char *filename);
  66. int getsample(float *sample, int nb);
  67. static int processAudio(char *filename, options_t *opts);
  68. static void usage(void);
  69. int main(int argc, char **argv) {
  70. fprintf(stderr, VERSION"\n");
  71. if(argc == 1)
  72. usage();
  73. // Check if there are actually any input files
  74. if(optind == argc){
  75. fprintf(stderr, "No input files provided.\n");
  76. usage();
  77. }
  78. options_t opts = { "r", "", 19, "", "." };
  79. // Parse arguments
  80. int opt;
  81. while ((opt = getopt(argc, argv, "c:m:d:i:s:e:")) != EOF) {
  82. switch (opt) {
  83. case 'd':
  84. opts.path = optarg;
  85. break;
  86. case 'c':
  87. readfcconf(optarg);
  88. break;
  89. case 'm':
  90. opts.map = optarg;
  91. break;
  92. case 'i':
  93. opts.type = optarg;
  94. break;
  95. case 's':
  96. opts.satnum = atoi(optarg);
  97. if(opts.satnum < 15 || opts.satnum > 19){
  98. fprintf(stderr, "Invalid satellite number, it must be the range 15-19\n");
  99. exit(EPERM);
  100. }
  101. break;
  102. case 'e':
  103. opts.effects = optarg;
  104. break;
  105. default:
  106. usage();
  107. }
  108. }
  109. // Process the files
  110. for (; optind < argc; optind++) {
  111. processAudio(argv[optind], &opts);
  112. }
  113. exit(0);
  114. }
  115. static int processAudio(char *filename, options_t *opts){
  116. // Image info struct
  117. image_t img;
  118. // Mapping between wedge value and channel ID
  119. static struct {
  120. char *id[7];
  121. char *name[7];
  122. } ch = {
  123. { "?", "1", "2", "3A", "4", "5", "3B" },
  124. { "unknown", "visble", "near-infrared", "mid-infrared", "thermal-infrared", "thermal-infrared", "mid-infrared" }
  125. };
  126. // Buffer for image channel
  127. char desc[60];
  128. // Parse file path
  129. char path[256], extension[32];
  130. strcpy(path, filename);
  131. strcpy(path, dirname(path));
  132. sscanf(basename(filename), "%[^.].%s", img.name, extension);
  133. if(strcmp(extension, "png") == 0){
  134. // Read PNG into image buffer
  135. printf("Reading %s", filename);
  136. if(readRawImage(filename, img.prow, &img.nrow) == 0){
  137. fprintf(stderr, "Skipping %s; see above.\n", img.name);
  138. return FAILURE;
  139. }
  140. }else{
  141. // Attempt to open the audio file
  142. if (initsnd(filename) == 0)
  143. exit(EPERM);
  144. // Build image
  145. for (img.nrow = 0; img.nrow < 3000; img.nrow++) {
  146. // Allocate memory for this row
  147. img.prow[img.nrow] = (float *) malloc(sizeof(float) * 2150);
  148. // Write into memory and break the loop when there are no more samples to read
  149. if (getpixelrow(img.prow[img.nrow], img.nrow, &zenith) == 0)
  150. break;
  151. fprintf(stderr, "Row: %d\r", img.nrow);
  152. fflush(stderr);
  153. }
  154. // Close stream
  155. sf_close(audioFile);
  156. }
  157. printf("\nTotal rows: %d\n", img.nrow);
  158. // Fallback for detecting the zenith
  159. // TODO: encode zenith in raw images
  160. if(opts->map != NULL && opts->map[0] != '\0' && zenith == 0){
  161. fprintf(stderr, "Guessing zenith in image, map will most likely be misaligned.\n");
  162. zenith = img.nrow / 2;
  163. }
  164. // Calibrate
  165. img.chA = calibrate(img.prow, img.nrow, CHA_OFFSET, CH_WIDTH);
  166. img.chB = calibrate(img.prow, img.nrow, CHB_OFFSET, CH_WIDTH);
  167. printf("Channel A: %s (%s)\n", ch.id[img.chA], ch.name[img.chA]);
  168. printf("Channel B: %s (%s)\n", ch.id[img.chB], ch.name[img.chB]);
  169. // Denoise
  170. if(CONTAINS(opts->effects, 'd')){
  171. denoise(img.prow, img.nrow, CHA_OFFSET, CH_WIDTH);
  172. denoise(img.prow, img.nrow, CHB_OFFSET, CH_WIDTH);
  173. }
  174. // Temperature
  175. if (CONTAINS(opts->type, 't') && img.chB >= 4) {
  176. temperature(opts, &img, CHB_OFFSET, CH_WIDTH);
  177. ImageOut(opts, &img, CHB_OFFSET, CH_WIDTH, "Temperature", "t", (char *)TempPalette);
  178. }
  179. // False color image
  180. if(CONTAINS(opts->type, 'c')){
  181. if (img.chA == 2 && img.chB >= 4) { // Normal false color
  182. // TODO: use real MSA
  183. // TODO: provide more than just "natural" color images
  184. ImageOut(opts, &img, 0, CH_WIDTH, "False Color", "c", NULL);
  185. } else if (img.chB == 2) { // GVI (global vegetation index) false color
  186. Ngvi(img.prow, img.nrow);
  187. ImageOut(opts, &img, CHB_OFFSET, CH_WIDTH, "GVI False Color", "c", (char *)GviPalette);
  188. } else {
  189. fprintf(stderr, "Skipping False Color generation; lacking required channels.\n");
  190. }
  191. }
  192. // MCIR
  193. if (CONTAINS(opts->type, 'm'))
  194. ImageOut(opts, &img, 0, IMG_WIDTH, "MCIR", "m", NULL);
  195. // Histogram equalise
  196. if(CONTAINS(opts->effects, 'h')){
  197. histogramEqualise(img.prow, img.nrow, CHA_OFFSET, CH_WIDTH);
  198. histogramEqualise(img.prow, img.nrow, CHB_OFFSET, CH_WIDTH);
  199. }
  200. // Raw image
  201. if (CONTAINS(opts->type, 'r')) {
  202. sprintf(desc, "%s (%s) & %s (%s)", ch.id[img.chA], ch.name[img.chA], ch.id[img.chB], ch.name[img.chB]);
  203. ImageOut(opts, &img, 0, IMG_WIDTH, desc, "r", NULL);
  204. }
  205. // Channel A
  206. if (CONTAINS(opts->type, 'a')) {
  207. sprintf(desc, "%s (%s)", ch.id[img.chA], ch.name[img.chA]);
  208. ImageOut(opts, &img, CHA_OFFSET, CH_WIDTH, desc, ch.id[img.chA], NULL);
  209. }
  210. // Channel B
  211. if (CONTAINS(opts->type, 'b')) {
  212. sprintf(desc, "%s (%s)", ch.id[img.chB], ch.name[img.chB]);
  213. ImageOut(opts, &img, CHB_OFFSET, CH_WIDTH, desc, ch.id[img.chA], NULL);
  214. }
  215. // Distribution image
  216. if (CONTAINS(opts->type, 'd'))
  217. distrib(opts, &img, "d");
  218. return SUCCESS;
  219. }
  220. static int initsnd(char *filename) {
  221. SF_INFO infwav;
  222. int res;
  223. // Open audio file
  224. infwav.format = 0;
  225. audioFile = sf_open(filename, SFM_READ, &infwav);
  226. if (audioFile == NULL) {
  227. fprintf(stderr, ERR_FILE_READ, filename);
  228. return FAILURE;
  229. }
  230. res = init_dsp(infwav.samplerate);
  231. printf("Input file: %s\n", filename);
  232. if(res < 0) {
  233. fprintf(stderr, "Input sample rate too low: %d\n", infwav.samplerate);
  234. return FAILURE;
  235. }else if(res > 0) {
  236. fprintf(stderr, "Input sample rate too high: %d\n", infwav.samplerate);
  237. return FAILURE;
  238. }
  239. printf("Input sample rate: %d\n", infwav.samplerate);
  240. // TODO: accept stereo audio
  241. if (infwav.channels != 1) {
  242. fprintf(stderr, "Too many channels in input file: %d\n", infwav.channels);
  243. return FAILURE;
  244. }
  245. return SUCCESS;
  246. }
  247. // Read samples from the wave file
  248. int getsample(float *sample, int nb) {
  249. return sf_read_float(audioFile, sample, nb);
  250. }
  251. static void usage(void) {
  252. fprintf(stderr,
  253. "Aptdec [options] audio files ...\n"
  254. "Options:\n"
  255. " -e [t|h] Effects\n"
  256. " t: Crop telemetry\n"
  257. " h: Histogram equalise\n"
  258. " d: Denoise\n"
  259. " p: Precipitation\n"
  260. " -i [r|a|b|c|t] Output image\n"
  261. " r: Raw\n"
  262. " a: Channel A\n"
  263. " b: Channel B\n"
  264. " c: False color\n"
  265. " t: Temperature\n"
  266. " m: MCIR\n"
  267. " -d <dir> Image destination directory.\n"
  268. " -s [15-19] Satellite number\n"
  269. " -c <file> False color config file\n"
  270. " -m <file> Map file\n");
  271. exit(EINVAL);
  272. }