Browse Source

Fix temperature calibration & lots of small changes

Add linear equalise
Add image flip for southbound passes
Completly remove old falseclor in favour of a much simplier method
tags/v1.8.0
Xerbo 4 years ago
parent
commit
9738b05653
13 changed files with 366 additions and 487 deletions
  1. +8
    -7
      Makefile
  2. +10
    -12
      README.md
  3. +46
    -80
      color.c
  4. +26
    -6
      common.h
  5. +17
    -17
      dsp.c
  6. +0
    -10
      falsecolor.conf
  7. +0
    -105
      fcolor.c
  8. +2
    -2
      filter.c
  9. +1
    -2
      filter.h
  10. +165
    -130
      image.c
  11. +55
    -61
      main.c
  12. +0
    -5
      offsets.h
  13. +36
    -50
      pngio.c

+ 8
- 7
Makefile View File

@@ -2,18 +2,19 @@ CC = gcc
BIN = /usr/bin BIN = /usr/bin
INCLUDES = -I. INCLUDES = -I.
CFLAGS = -O3 -DNDEBUG -Wall -Wextra $(INCLUDES) CFLAGS = -O3 -DNDEBUG -Wall -Wextra $(INCLUDES)
OBJS = main.o image.o dsp.o filter.o reg.o fcolor.o pngio.o median.o color.o
OBJS = main.o image.o dsp.o filter.o reg.o pngio.o median.o color.o


aptdec: $(OBJS) aptdec: $(OBJS)
$(CC) -o $@ $(OBJS) -lm -lsndfile -lpng $(CC) -o $@ $(OBJS) -lm -lsndfile -lpng


reg.o: reg.c
color.o: color.c color.o: color.c
main.o: main.c offsets.h messages.h
dsp.o: dsp.c filtercoeff.h filter.h
filter.o: filter.c filter.h
image.o: image.c offsets.h messages.h offsets.h
fcolor.o: fcolor.c offsets.h
pngio.o: pngio.c offsets.h messages.h
main.o: main.c
media.o: median.c
dsp.o: dsp.c
filter.o: filter.c
image.o: image.c
pngio.o: pngio.c


clean: clean:
rm -f *.o aptdec rm -f *.o aptdec


+ 10
- 12
README.md View File

@@ -24,7 +24,7 @@ Aptdec uses `libsndfile` to read the input audio, so any format supported by `li
## Compilation ## Compilation


Aptdec is portable since it is written in standard C. Aptdec is portable since it is written in standard C.
It has successfully compiled and ran on Debian with both `gcc` and `clang` and will most likely work on any Unix platform.
It has successfully compiled and ran on Debian with both `gcc`, `clang` and `tcc` and will most likely work on any Unix platform.
Just edit the Makefile and run `make` (no configure script as of right now). Just edit the Makefile and run `make` (no configure script as of right now).


Aptdec uses `libsndfile`, `libpng` and `libm`. Aptdec uses `libsndfile`, `libpng` and `libm`.
@@ -65,18 +65,14 @@ Satellite number
For temperature calibration For temperature calibration
Default: "19" Default: "19"


-e [t|h|d|p]
-e [r|a|b|c|t|m]
Effects Effects
Histogram equalise (h), Crop Telemetry (t), Denoise (d) or Precipitation (p)
Histogram equalise (h), Crop Telemetry (t), Denoise (d), Precipitation (p) or Linear equalise (l)
Defaults: off Defaults: off


-m <file> -m <file>
Map file generated by wxmap Map file generated by wxmap


-c <file>
Use configuration file for false color generation
Default: Internal defaults

-r -r
Realtime decode. When decoding in realtime it is highly recommended to choose a plain raw image. Realtime decode. When decoding in realtime it is highly recommended to choose a plain raw image.
``` ```
@@ -88,17 +84,19 @@ Generated images are outputted in PNG and are 24 bit RGB for all image types apa
Image names are `audiofile-x.png`, where `x` is: Image names are `audiofile-x.png`, where `x` is:


- `r` for raw images - `r` for raw images
- Sensor ID (1, 2, 3A, 3B, 4, 5) for channel A|B images
- `c` for false color.
- Sensor ID (`1`, `2`, `3A`, `3B`, `4`, `5`) for channel A|B images
- `c` for false color
- `t` for temperature calibrated images - `t` for temperature calibrated images
- `m` for MCIR images - `m` for MCIR images


Currently there are 4 available effects:
Currently there are 6 available effects:


- `t` for crop telemetry, off by default, only has effects on raw images - `t` for crop telemetry, off by default, only has effects on raw images
- `h` for histogram equalise, stretch the colors in the image to black and white - `h` for histogram equalise, stretch the colors in the image to black and white
- `d` for a median denoise filter - `d` for a median denoise filter
- `p` for a precipitation overlay - `p` for a precipitation overlay
- `f` to flip the image (for southbound passes)
- `l` to linearly equalise the image (recommended for falsecolor images)


## Examples ## Examples


@@ -108,7 +106,7 @@ This will process all `.wav` files in the current directory, generate calibrated


`aptdec -e dh -i b audio.wav` `aptdec -e dh -i b audio.wav`


Decode `audio.wav` with denoise and histogram equalisation and save it into the current directory.
Decode `audio.wav` with denoise and histogram equalization and save it into the current directory.


## Realtime decoding ## Realtime decoding


@@ -120,7 +118,7 @@ aptdec /tmp/aptaudio
sox -t pulseaudio alsa_output.pci-0000_00_1b.0.analog-stereo.monitor -c 1 -t wav /tmp/aptaudio sox -t pulseaudio alsa_output.pci-0000_00_1b.0.analog-stereo.monitor -c 1 -t wav /tmp/aptaudio
``` ```


Perform a realtime decode with the audio being played out of`alsa_output.pci-0000_00_1b.0.analog`.
Perform a realtime decode with the audio being played out of `alsa_output.pci-0000_00_1b.0.analog`.


## Further reading ## Further reading




+ 46
- 80
color.c View File

@@ -17,11 +17,9 @@
* *
*/ */


#include "offsets.h"
#include "common.h"


typedef struct {
float r, g, b;
} rgb_t;
#define MCOMPOSITE(m1, a1, m2, a2) (m1*a1 + m2*a2*(1-a1))


rgb_t applyPalette(char *palette, int val){ rgb_t applyPalette(char *palette, int val){
return (rgb_t){ return (rgb_t){
@@ -32,86 +30,54 @@ rgb_t applyPalette(char *palette, int val){
} }


rgb_t RGBcomposite(rgb_t top, float top_a, rgb_t bottom, float bottom_a){ rgb_t RGBcomposite(rgb_t top, float top_a, rgb_t bottom, float bottom_a){
rgb_t composite;
composite.r = MCOMPOSITE(top.r, top_a, bottom.r, bottom_a);
composite.g = MCOMPOSITE(top.g, top_a, bottom.g, bottom_a);
composite.b = MCOMPOSITE(top.b, top_a, bottom.b, bottom_a);
return composite;
return (rgb_t){
MCOMPOSITE(top.r, top_a, bottom.r, bottom_a),
MCOMPOSITE(top.g, top_a, bottom.g, bottom_a),
MCOMPOSITE(top.b, top_a, bottom.b, bottom_a)
};
} }


char GviPalette[256*3] = {
"\230t\17\233x\22\236{\27\241\200\33\244\203\37\247\210#\252\214'\255\220"
",\260\2240\264\2305\267\2358\272\240=\274\245A\300\251E\303\255I\306\262"
"M\311\266Q\314\272V\317\276Z\322\302^\325\306b\330\312g\334\317k\337\323"
"o\342\330s\344\333w\350\337{\352\344\200\356\350\203\361\354\207\364\360"
"\213\367\364\220\372\370\224\376\376\230\376\375\230\376\372\227\376\366"
"\224\376\362\222\376\357\220\376\352\216\376\347\213\376\343\211\376\340"
"\207\376\334\205\376\330\202\376\325\200\376\321\177\376\315|\376\312z\376"
"\306y\376\302v\376\277t\376\273q\376\267o\376\263m\376\260k\376\254h\376"
"\250f\376\245d\376\241b\376\235_\376\232^\376\226[\376\223Y\376\217W\376"
"\213U\376\207R\376\203Q\376\200N\376}L\376zJ\376vG\376rE\376nB\376k@\376"
"g>\376d<\376`:\376\\7\376X6\376T3\376Q0\376M/\376J-\376F*\376C(\376>%\376"
";$\3767!\3763\37\3760\35\376,\32\376(\31\376$\26\376!\24\376\35\22\376\32"
"\20\376\26\15\376\22\13\376\16\11\376\12\6\376\7\4\376\0\0\373\2\0\366\3"
"\0\362\5\0\355\7\0\350\11\0\343\13\0\336\15\0\332\17\0\325\21\0\320\22\0"
"\313\24\0\307\26\0\303\27\0\275\32\0\270\33\0\264\35\0\257\37\0\253!\0\246"
"#\0\241%\0\234&\0\227)\0\223+\0\216,\0\212.\0\2050\0\2002\0}4\0w6\0s7\0n"
"9\0i;\0e>\0`?\0[A\0VC\0QE\0MG\0HH\0CK\0?M\0:N\0""5P\0""0R\0,S\0'V\0\"X\0"
"\36Z\0\31\\\0\23^\0\17_\0\12a\0\3b\0\6e\2\12i\5\17n\11\23r\14\30v\17\34{"
"\23\40\200\26$\203\31)\207\35.\215\40""1\221$6\225':\232*?\236.C\2431G\250"
"4K\2548O\260;U\265>Y\272A\\\276Eb\303Ie\307Lj\313On\320Ss\324Vw\331Y{\336"
"]\200\342`\203\347d\207\353g\213\357j\217\364n\223\371q\230\376t\222\375"
"{\207\374\205}\373\216r\371\230g\371\241\\\370\254Q\367\265F\365\300;\364"
"\3110\363\323%\362\334\32\361\347\17\357\361\3\356\371\4\353\370\3\347\365"
"\4\342\361\4\335\357\4\331\354\4\323\351\3\317\346\3\313\343\4\306\340\3"
"\301\335\3\275\332\3\270\327\3\263\324\2\257\322\3\252\316\3\245\314\3\241"
"\311\2\234\306\2\230\303\2\223\300\3\216\275\3\211\273\2\205\267\2\200\265"
"\2|\262\2w\257\2s\254\2n\251\2j\246\2e\243\2`\240\2[\235\2W\232\1S\230\2"
"M\225\1I\221\2E\217\1@\214\1;\211\1""7\206\1""1\203\1-\200\0(~\1${\0\37y"
"\0\33u\0\25r\0\21p\0\14l\0\7j\0\3"
};
// The "I totally didn't just steal this from WXtoImg" palette
char TempPalette[256*3] = { char TempPalette[256*3] = {
"\376\376\376\376\376\376\375\375\376\374\375\376\374\375\375\374\373\375"
"\373\373\375\372\373\375\372\373\374\372\372\374\371\372\374\371\371\375"
"\370\371\374\367\370\375\367\370\374\367\367\374\366\367\373\366\366\373"
"\365\366\373\364\366\373\364\365\374\363\365\373\363\364\373\363\364\373"
"\362\363\372\361\363\372\361\362\372\361\361\372\360\361\372\357\361\372"
"\357\361\372\356\360\372\355\357\371\355\357\371\355\356\372\354\356\371"
"\354\355\371\354\355\371\353\355\371\353\354\370\352\353\370\351\353\370"
"\351\353\370\350\352\370\350\351\370\347\351\370\347\351\370\346\350\367"
"\346\350\367\346\350\367\345\346\367\344\346\367\344\346\367\344\345\366"
"\342\346\366\343\344\366\342\345\366\341\344\366\341\343\365\340\343\366"
"\337\342\365\337\341\366\336\341\365\337\341\365\336\340\365\335\340\365"
"\335\337\364\334\337\364\334\336\364\334\336\364\333\336\364\333\335\364"
"\332\334\364\331\334\363\331\334\364\330\333\363\330\333\363\327\332\363"
"\326\332\363\327\331\362\326\331\362\325\331\362\325\330\362\324\330\363"
"\323\327\362\323\327\362\322\326\362\322\326\362\322\325\361\321\325\361"
"\321\325\361\320\323\361\320\323\361\317\323\361\317\322\360\316\322\361"
"\316\322\360\315\321\360\314\321\360\314\320\360\313\320\360\313\317\360"
"\312\317\357\311\317\357\312\316\357\311\316\357\310\315\356\310\314\356"
"\307\314\356\307\313\357\306\313\357\306\313\356\305\312\356\305\312\355"
"\305\311\356\304\311\356\303\311\355\302\310\355\302\307\355\302\307\355"
"\301\306\355\301\306\355\300\305\354\300\305\354\277\305\354\277\304\354"
"\276\304\354\276\304\354\276\302\353\275\303\354\274\302\353\274\301\353"
"\274\301\353\273\301\353\272\300\353\272\277\353\271\277\352\271\276\352"
"\270\276\352\270\276\352\267\275\351\267\274\352\266\275\352\265\273\352"
"\265\273\351\264\273\351\265\272\351\263\272\351\263\272\351\263\271\350"
"\263\271\351\262\271\350\261\267\350\260\267\350\260\266\350\257\267\350"
"\256\270\350\255\272\350\254\273\351\252\275\351\251\275\351\247\300\351"
"\247\301\351\244\303\352\243\306\352\242\310\353\240\312\352\237\315\352"
"\236\317\352\234\322\353\233\324\353\232\330\353\230\332\354\227\336\354"
"\225\341\354\224\345\354\223\350\354\222\354\355\220\355\353\217\355\347"
"\216\355\344\214\355\337\212\356\334\211\356\330\210\357\324\207\356\317"
"\205\357\313\204\357\307\202\360\302\200\357\275\200\360\270\177\360\264"
"~\360\256|\361\251z\360\244y\361\237x\361\230v\362\223u\362\215s\362\207"
"r\362\200p\362|o\362un\362np\363ls\363kw\363i|\364g\200\363f\204\363d\210"
"\364b\215\364a\222\364`\227\365^\234\365]\241\365\\\246\365Z\254\366Y\261"
"\365W\267\366U\275\366T\303\366S\311\367Q\317\367O\325\367N\334\370L\343"
"\370J\352\370I\360\370G\367\371F\370\362E\371\354C\370\345A\371\336@\371"
"\326?\372\316=\372\307;\372\277:\372\2679\373\2577\373\2475\373\2373\373"
"\2261\373\2161\373\206.\373}-\374u,\375k*\374b(\375Y'\375O%\375E#\375;\""
"\3762\40\376(\37\376\35"
"\x45\x0\x8f\x46\x0\x91\x47\x0\x92\x48\x0\x94\x49\x0\x96\x4a\x0\x98\x4b\x0\x9b\x4d\x0\x9d"
"\x4e\x0\xa0\x50\x0\xa2\x51\x0\xa5\x52\x0\xa7\x54\x0\xaa\x56\x0\xae\x57\x0\xb1"
"\x58\x0\xb4\x5a\x0\xb7\x5c\x0\xba\x5e\x0\xbd\x5f\x0\xc0\x61\x0\xc4\x64\x0\xc8"
"\x66\x0\xcb\x68\x0\xce\x69\x0\xd1\x68\x0\xd4\x65\x0\xd7\x63\x0\xda\x61\x0\xdd"
"\x5d\x0\xe1\x5b\x0\xe4\x59\x0\xe6\x56\x0\xe9\x53\x0\xeb\x50\x0\xee\x4d\x0\xf0"
"\x49\x0\xf3\x47\x0\xfc\x43\x0\xfa\x31\x0\xbf\x20\x0\x89\x20\x0\x92\x1e\x0\x95"
"\x1b\x0\x97\x19\x0\x9a\x17\x0\x9c\x15\x0\x9e\x12\x0\xa0\xf\x0\xa3\xf\x2\xa5"
"\xe\x6\xa8\xe\xa\xab\xe\xd\xad\xe\x11\xb1\xd\x15\xb4\xd\x18\xb7\xd\x1c\xba"
"\xb\x21\xbd\xa\x25\xc0\xa\x29\xc3\x9\x2d\xc6\x8\x33\xca\x7\x36\xcd\x7\x3b\xd0"
"\x7\x41\xd3\x5\x45\xd6\x4\x4b\xd9\x4\x50\xdc\x3\x55\xde\x2\x5d\xe2\x1\x61\xe5"
"\x0\x66\xe7\x0\x6c\xea\x0\x72\xec\x0\x78\xee\x0\x7d\xf0\x0\x82\xf3\x0\x8d\xfc"
"\x0\x90\xfa\x0\x71\xbf\x0\x54\x89\x0\x5c\x91\x0\x61\x94\x0\x64\x96\x0\x68\x97"
"\x0\x6d\x99\x0\x71\x9b\x0\x75\x9d\x0\x79\x9f\x0\x7e\xa0\x0\x82\xa2\x0\x87\xa4"
"\x0\x8c\xa6\x0\x92\xaa\x0\x96\xac\x0\x9c\xae\x0\xa2\xb1\x0\xa6\xb3\x0\xaa\xb5"
"\x0\xad\xb7\x0\xb1\xba\x0\xb6\xbe\x0\xba\xc0\x0\xbe\xc2\x0\xc2\xc5\x0\xc6\xc6"
"\x0\xca\xc9\x0\xcc\xca\x0\xcf\xcb\x0\xd2\xcc\x0\xd4\xcc\x0\xd6\xcc\x0\xd9\xcb"
"\x0\xdb\xcb\x0\xde\xcb\x0\xe0\xcb\x0\xe2\xcc\x0\xea\xd2\x0\xea\xcf\x0\xb9\xa4"
"\x0\x8e\x7a\x1\x94\x7c\x4\x97\x79\x7\x99\x75\x9\x9b\x71\xd\x9d\x6b\x10\x9f\x67"
"\x12\xa1\x63\x15\xa3\x5f\x17\xa5\x59\x1a\xa8\x55\x1d\xaa\x50\x20\xac\x4b\x24\xaf\x45"
"\x28\xb2\x41\x2b\xb5\x3b\x2e\xb8\x35\x31\xba\x30\x34\xbd\x2b\x39\xbf\x24\x3f\xc1\x17"
"\x49\xc5\x8\x4f\xc8\x1\x4f\xca\x0\x4e\xcd\x0\x4e\xcf\x0\x4f\xd2\x0\x54\xd5\x0"
"\x5d\xd8\x0\x68\xdb\x0\x6e\xdd\x0\x74\xdf\x0\x7a\xe2\x0\x7f\xe4\x0\x85\xe7\x0"
"\x8b\xe9\x0\x8f\xeb\x0\x9b\xf3\x0\x9e\xf2\x0\x7e\xbb\x0\x60\x8a\x0\x68\x92\x0"
"\x6d\x95\x0\x71\x96\x0\x75\x98\x0\x7b\x9a\x0\x7f\x9d\x0\x83\x9f\x0\x87\xa1\x0"
"\x8c\xa2\x0\x8f\xa5\x0\x92\xa7\x0\x96\xa9\x0\x9a\xad\x0\x9d\xb0\x0\xa1\xb2\x0"
"\xa5\xb5\x0\xa9\xb7\x0\xad\xba\x0\xb2\xbd\x0\xb6\xbf\x0\xbb\xc3\x0\xbf\xc6\x0"
"\xc3\xc8\x0\xc8\xcb\x0\xcc\xce\x0\xd0\xd1\x0\xd3\xd2\x0\xd5\xd4\x0\xd9\xd4\x0"
"\xdc\xd4\x0\xde\xd5\x0\xe1\xd5\x0\xe3\xd5\x0\xe6\xd4\x0\xe8\xd1\x0\xea\xce\x0"
"\xf2\xcf\x0\xf2\xca\x0\xbb\x99\x0\x8a\x6e\x0\x92\x72\x0\x95\x72\x0\x97\x71\x0"
"\x9a\x70\x0\x9c\x6e\x0\x9e\x6d\x0\xa0\x6b\x0\xa3\x6a\x0\xa5\x68\x0\xa8\x67\x0"
"\xab\x66\x0\xae\x65\x0\xb2\x63\x0\xb4\x61\x0\xb7\x5f\x0\xba\x5d\x0\xbd\x5c\x0"
"\xc0\x59\x0\xc3\x57\x0\xc6\x54\x0\xca\x50\x0\xcd\x4d\x0\xd0\x4a\x0\xd3\x47\x0"
"\xd6\x43\x0\xd9\x40\x0\xdc\x3d\x0\xde\x39\x0\xe2\x33\x0\xe5\x2f\x0\xe7\x2c\x0"
"\xea\x28\x0\xec\x23\x0\xef\x1f\x0\xf1\x1a\x0\xf3\x14\x0\xfb\xf\x0\xfa\xd\x0"
"\xc1\x5\x0\x8e\x0\x0\x97\x0\x0\x9b\x0\x0\x9e\x0\x0\xa1\x0\x0\xa5\x0\x0"
"\xa9\x0\x0\xad\x0\x0\xb1\x0\x0\xb6\x0\x0\xba\x0\x0\xbd\x0\x0\xc2\x0\x0"
"\xc8\x0\x0\xcc\x0\x0\xcc\x0\x0"
}; };

char PrecipPalette[256*3] = { char PrecipPalette[256*3] = {
"\xe0\x98\x8\xec\x84\x10\xf5\x70\x1b\xfc\x5c\x29\xff\x49\x38\xff\x37\x4a" "\xe0\x98\x8\xec\x84\x10\xf5\x70\x1b\xfc\x5c\x29\xff\x49\x38\xff\x37\x4a"
"\xfb\x28\x5d\xf5\x1a\x71\xeb\xf\x85\xdf\x8\x99\xd0\x3\xad\xc0\x1\xbf" "\xfb\x28\x5d\xf5\x1a\x71\xeb\xf\x85\xdf\x8\x99\xd0\x3\xad\xc0\x1\xbf"


messages.h → common.h View File

@@ -17,9 +17,29 @@
* *
*/ */


#define ERR_FILE_WRITE "Could not open %s for writing\n"
#define ERR_FILE_READ "Could not open %s for reading\n"
#define ERR_PNG_WRITE "Could not create a PNG write struct\n"
#define ERR_PNG_INFO "Could not create a PNG info struct\n"
#define ERR_TELE_ROW "Telemetry decoding error, not enough rows.\n"
#define VERSION "Aptdec; copyright (c) 2004-2009 Thierry Leconte F4DWV, Xerbo (xerbo@protonmail.com) 2019-2020"
// Constants
#define VERSION "Aptdec; (c) 2004-2009 Thierry Leconte F4DWV, Xerbo (xerbo@protonmail.com) 2019-2020"
#define MAX_HEIGHT 3000

// Useful macros
#define CLIP(v, lo, hi) (v > hi ? hi : (v > lo ? v : lo))
#define CONTAINS(str, char) (strchr(str, (int) char) != NULL)

// Typedefs
typedef struct {
float r, g, b;
} rgb_t;
typedef struct {
float *prow[MAX_HEIGHT]; // Row buffers
int nrow; // Number of rows
int chA, chB; // ID of each channel
char name[256]; // Stripped filename
} image_t;
typedef struct {
char *type; // Output image type
char *effects; // Effects on the image
int satnum; // The satellite number
char *map; // Path to a map file
char *path; // Output directory
int realtime; // Realtime decoding
} options_t;

+ 17
- 17
dsp.c View File

@@ -53,8 +53,8 @@ static double K1, K2;


// Check the sample rate and calculate some constants // Check the sample rate and calculate some constants
int init_dsp(double F) { int init_dsp(double F) {
if(F > Fi) return(1);
if(F < Fp) return(-1);
if(F > Fi) return 1;
if(F < Fp) return -1;
Fe = F; Fe = F;


K1 = DFc / Fe; K1 = DFc / Fe;
@@ -62,7 +62,7 @@ int init_dsp(double F) {
// Number of samples per cycle // Number of samples per cycle
FreqOsc = Fc / Fe; FreqOsc = Fc / Fe;


return(0);
return 0;
} }


/* Fast phase estimator /* Fast phase estimator
@@ -72,7 +72,7 @@ static inline double Phase(double I, double Q) {
double angle, r; double angle, r;
int s; int s;


if(I == 0.0 && Q == 0.0) return(0.0);
if(I == 0.0 && Q == 0.0) return 0.0;


if (Q < 0) { if (Q < 0) {
s = -1; s = -1;
@@ -90,9 +90,9 @@ static inline double Phase(double I, double Q) {
} }


if(s > 0){ if(s > 0){
return(angle);
return angle;
}else{ }else{
return(-angle);
return -angle;
} }
} }


@@ -129,7 +129,7 @@ static double pll(double I, double Q) {
if (FreqOsc < ((Fc - DFc) / Fe)) if (FreqOsc < ((Fc - DFc) / Fe))
FreqOsc = (Fc - DFc) / Fe; FreqOsc = (Fc - DFc) / Fe;


return(Ip);
return Ip;
} }


// Convert samples into pixels // Convert samples into pixels
@@ -154,7 +154,7 @@ static int getamp(double *ampbuff, int count) {
// Make sure there is enough samples to continue // Make sure there is enough samples to continue
if (nin < IQFilterLen * 2 + 2) if (nin < IQFilterLen * 2 + 2)
return(n);
return n;
} }


// Process read samples into a brightness value // Process read samples into a brightness value
@@ -166,7 +166,7 @@ static int getamp(double *ampbuff, int count) {
nin--; nin--;
} }


return(count);
return count;
} }


// Sub-pixel offsetting + FIR compensation // Sub-pixel offsetting + FIR compensation
@@ -192,7 +192,7 @@ int getpixelv(float *pvbuff, int count) {
res = getamp(&(ampbuff[nam]), BLKAMP - nam); res = getamp(&(ampbuff[nam]), BLKAMP - nam);
nam += res; nam += res;
if (nam < m) if (nam < m)
return(n);
return n;
} }


// Gaussian FIR compensation filter // Gaussian FIR compensation filter
@@ -205,11 +205,11 @@ int getpixelv(float *pvbuff, int count) {
nam -= shift; nam -= shift;
} }
return(count);
return count;
} }


// Get an entire row of pixels, aligned with sync markers // Get an entire row of pixels, aligned with sync markers
// FIXME: occasionally skips noisy lines
// FIXME: skips noisy lines with no findable sync marker
int getpixelrow(float *pixelv, int nrow, int *zenith) { int getpixelrow(float *pixelv, int nrow, int *zenith) {
static float pixels[PixelLine + SyncFilterLen]; static float pixels[PixelLine + SyncFilterLen];
static int npv; static int npv;
@@ -228,8 +228,8 @@ int getpixelrow(float *pixelv, int nrow, int *zenith) {
if (npv < SyncFilterLen + 2) { if (npv < SyncFilterLen + 2) {
res = getpixelv(&(pixelv[npv]), SyncFilterLen + 2 - npv); res = getpixelv(&(pixelv[npv]), SyncFilterLen + 2 - npv);
npv += res; npv += res;
// Exit if there are no pixels left
if (npv < SyncFilterLen + 2) return(0);
if (npv < SyncFilterLen + 2)
return 0;
} }


// Calculate the frequency offset // Calculate the frequency offset
@@ -258,7 +258,7 @@ int getpixelrow(float *pixelv, int nrow, int *zenith) {
res = getpixelv(&(pixelv[npv]), PixelLine + SyncFilterLen - npv); res = getpixelv(&(pixelv[npv]), PixelLine + SyncFilterLen - npv);
npv += res; npv += res;
if (npv < PixelLine + SyncFilterLen) if (npv < PixelLine + SyncFilterLen)
return(0);
return 0;
} }


// Test every possible position until we get the best result // Test every possible position until we get the best result
@@ -289,7 +289,7 @@ int getpixelrow(float *pixelv, int nrow, int *zenith) {
res = getpixelv(&(pixelv[npv]), PixelLine - npv); res = getpixelv(&(pixelv[npv]), PixelLine - npv);
npv += res; npv += res;
if (npv < PixelLine) if (npv < PixelLine)
return(0);
return 0;
} }


// Move the sync lines into the output buffer with the calculated offset // Move the sync lines into the output buffer with the calculated offset
@@ -300,5 +300,5 @@ int getpixelrow(float *pixelv, int nrow, int *zenith) {
npv -= PixelLine; npv -= PixelLine;
} }


return(1);
return 1;
} }

+ 0
- 10
falsecolor.conf View File

@@ -1,10 +0,0 @@
28 44 95
23 78 37
240 250 255
50
10
24
34
14
141
114

+ 0
- 105
fcolor.c View File

@@ -1,105 +0,0 @@
/*
* This file is part of Aptdec.
* Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019-2020
*
* Aptdec 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, either version 2 of the License, or
* (at your option) any later version.
*
* 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 <https://www.gnu.org/licenses/>.
*
*/

#include <stdio.h>
#include <math.h>

#include "offsets.h"

typedef struct {
float r, g, b;
} rgb_t;

extern rgb_t RGBcomposite(rgb_t top, float top_a, rgb_t bottom, float bottom_a);

static struct {
rgb_t Sea, Land, Cloud;
int Seaintensity, Seaoffset;
int Landthreshold, Landintensity, Landoffset;
int Cloudthreshold, Cloudintensity;
} fcinfo = {
{28, 44, 95},
{23, 78, 37},
{240, 250, 255},
50, 10,
24, 34, 14,
141, 114
};

// Read the config file
int readfcconf(char *file) {
FILE *fin;
fin = fopen(file, "r");

if (fin == NULL)
return 0;

fscanf(fin, "%g %g %g\n", &fcinfo.Sea.r, &fcinfo.Sea.g, &fcinfo.Sea.b);
fscanf(fin, "%g %g %g\n", &fcinfo.Land.r, &fcinfo.Land.g, &fcinfo.Land.b);
fscanf(fin, "%g %g %g\n", &fcinfo.Cloud.r, &fcinfo.Cloud.g, &fcinfo.Cloud.b);
fscanf(fin, "%d\n", &fcinfo.Seaintensity);
fscanf(fin, "%d\n", &fcinfo.Seaoffset);
fscanf(fin, "%d\n", &fcinfo.Landthreshold);
fscanf(fin, "%d\n", &fcinfo.Landintensity);
fscanf(fin, "%d\n", &fcinfo.Landoffset);
fscanf(fin, "%d\n", &fcinfo.Cloudthreshold);
fscanf(fin, "%d", &fcinfo.Cloudintensity);
fclose(fin);

return 1;
};

rgb_t falsecolor(float vis, float temp){
rgb_t buffer;
float land = 0.0, sea, cloud;

// Calculate intensity of sea
sea = CLIP(vis+fcinfo.Seaoffset, 0, fcinfo.Seaintensity)/fcinfo.Seaintensity;

// Land
if(vis > fcinfo.Landthreshold)
land = CLIP(vis-fcinfo.Landoffset, 0, fcinfo.Landintensity)/fcinfo.Landintensity;
// Composite land on sea
buffer = RGBcomposite(fcinfo.Land, land, fcinfo.Sea, sea);

// Composite clouds on top
cloud = CLIP(temp-fcinfo.Cloudthreshold, 0, fcinfo.Cloudintensity)/fcinfo.Cloudintensity;
buffer = RGBcomposite(fcinfo.Cloud, cloud, buffer, 1);

return buffer;
}

// GVI (global vegetation index) false color
void Ngvi(float **prow, int nrow) {
printf("Computing GVI false color");

for (int n = 0; n < nrow; n++) {
float *pixelv = prow[n];

for (int i = 0; i < CH_WIDTH; i++) {
double gvi = (pixelv[i + CHA_OFFSET] - pixelv[i + CHB_OFFSET])/
(pixelv[i + CHA_OFFSET] + pixelv[i + CHB_OFFSET]);

gvi = (gvi + 0.1) * 340.0;
pixelv[i + CHB_OFFSET] = CLIP(gvi, 0, 255);
}
}
printf("\nDone\n");
};

+ 2
- 2
filter.c View File

@@ -29,7 +29,7 @@ float fir(float *buff, const float *coeff, const int len) {
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
r += buff[i] * coeff[i]; r += buff[i] * coeff[i];
} }
return(r);
return r;
} }


/* IQ finite impulse response /* IQ finite impulse response
@@ -63,5 +63,5 @@ float rsfir(double *buff, const float *coeff, const int len, const double offset
alpha = n - k; alpha = n - k;
out += buff[i] * (coeff[k] * (1.0 - alpha) + coeff[k + 1] * alpha); out += buff[i] * (coeff[k] * (1.0 - alpha) + coeff[k + 1] * alpha);
} }
return(out);
return out;
} }

+ 1
- 2
filter.h View File

@@ -19,5 +19,4 @@
float fir(float *buff, const float *coeff, const int len); float fir(float *buff, const float *coeff, const int len);
void iqfir(float *buff, const float *coeff, const int len, double *I, double *Q); void iqfir(float *buff, const float *coeff, const int len, double *I, double *Q);
float rsfir(double *buff, const float *coeff, const int len, const double offset, const double delta);
float rsfir(double *buff, const float *coeff, const int len, const double offset, const double delta);

+ 165
- 130
image.c View File

@@ -23,30 +23,14 @@
#include <math.h> #include <math.h>
#include <stdlib.h> #include <stdlib.h>


#include "common.h"
#include "offsets.h" #include "offsets.h"
#include "messages.h"


#define REGORDER 3 #define REGORDER 3
typedef struct { typedef struct {
double cf[REGORDER + 1]; double cf[REGORDER + 1];
} rgparam_t; } rgparam_t;


typedef struct {
float *prow[MAX_HEIGHT]; // Row buffers
int nrow; // Number of rows
int chA, chB; // ID of each channel
char name[256]; // Stripped filename
} image_t;

typedef struct {
char *type; // Output image type
char *effects;
int satnum; // The satellite number
char *map; // Path to a map file
char *path; // Output directory
int realtime;
} options_t;

extern void polyreg(const int m, const int n, const double x[], const double y[], double c[]); extern void polyreg(const int m, const int n, const double x[], const double y[], double c[]);


// Compute regression // Compute regression
@@ -54,7 +38,7 @@ static void rgcomp(double x[16], rgparam_t * rgpr) {
// { 0.106, 0.215, 0.324, 0.433, 0.542, 0.652, 0.78, 0.87, 0.0 } // { 0.106, 0.215, 0.324, 0.433, 0.542, 0.652, 0.78, 0.87, 0.0 }
const double y[9] = { 31.07, 63.02, 94.96, 126.9, 158.86, 191.1, 228.62, 255.0, 0.0 }; const double y[9] = { 31.07, 63.02, 94.96, 126.9, 158.86, 191.1, 228.62, 255.0, 0.0 };


polyreg(REGORDER, 9, x, y, rgpr -> cf);
polyreg(REGORDER, 9, x, y, rgpr->cf);
} }


// Convert a value to 0-255 based off the provided regression curve // Convert a value to 0-255 based off the provided regression curve
@@ -66,7 +50,7 @@ static double rgcal(float x, rgparam_t *rgpr) {
y += rgpr->cf[i] * p; y += rgpr->cf[i] * p;
p = p * x; p = p * x;
} }
return(y);
return y;
} }


static double tele[16]; static double tele[16];
@@ -77,7 +61,7 @@ void histogramEqualise(float **prow, int nrow, int offset, int width){
int histogram[256] = { 0 }; int histogram[256] = { 0 };
for(int y = 0; y < nrow; y++) for(int y = 0; y < nrow; y++)
for(int x = 0; x < width; x++) for(int x = 0; x < width; x++)
histogram[(int)floor(prow[y][x+offset])]++;
histogram[(int)CLIP(prow[y][x+offset], 0, 255)]++;


// Calculate cumulative frequency // Calculate cumulative frequency
long sum = 0, cf[256] = { 0 }; long sum = 0, cf[256] = { 0 };
@@ -96,59 +80,76 @@ void histogramEqualise(float **prow, int nrow, int offset, int width){
} }
} }


void linearEnhance(float **prow, int nrow, int offset, int width){
// Plot histogram
int histogram[256] = { 0 };
for(int y = 0; y < nrow; y++)
for(int x = 0; x < width; x++)
histogram[(int)CLIP(prow[y][x+offset], 0, 255)]++;

// Find min/max points
int min = -1, max = -1;
for(int i = 5; i < 250; i++){
if(histogram[i]/width/(nrow/255.0) > 0.25){
if(min == -1) min = i;
max = i;
}
}

// Stretch the brightness into the new range
for(int y = 0; y < nrow; y++)
for(int x = 0; x < width; x++)
prow[y][x+offset] = (prow[y][x+offset]-min) / (max-min) * 255.0;
}

// Brightness calibrate, including telemetry // Brightness calibrate, including telemetry
void calibrateImage(float **prow, int nrow, int offset, int width, rgparam_t regr){ void calibrateImage(float **prow, int nrow, int offset, int width, rgparam_t regr){
offset -= SYNC_WIDTH+SPC_WIDTH; offset -= SYNC_WIDTH+SPC_WIDTH;


for (int n = 0; n < nrow; n++) {
float *pixelv = prow[n];

for (int i = 0; i < width+SYNC_WIDTH+SPC_WIDTH+TELE_WIDTH; i++) {
float pv = rgcal(pixelv[i + offset], &regr);

pixelv[i + offset] = CLIP(pv, 0, 255);
for (int y = 0; y < nrow; y++) {
for (int x = 0; x < width+SYNC_WIDTH+SPC_WIDTH+TELE_WIDTH; x++) {
float pv = rgcal(prow[y][x + offset], &regr);
prow[y][x + offset] = CLIP(pv, 0, 255);
} }
} }
} }


double teleNoise(double wedges[16]){ double teleNoise(double wedges[16]){
int pattern[9] = { 31, 63, 95, 127, 159, 191, 223, 255, 0 };
double pattern[9] = { 31.07, 63.02, 94.96, 126.9, 158.86, 191.1, 228.62, 255.0, 0.0 };
double noise = 0; double noise = 0;
for(int i = 0; i < 9; i++) for(int i = 0; i < 9; i++)
noise += fabs(wedges[i] - (double)pattern[i]);
noise += fabs(wedges[i] - pattern[i]);
return noise; return noise;
} }


// Get telemetry data for thermal calibration/equalization // Get telemetry data for thermal calibration/equalization
int calibrate(float **prow, int nrow, int offset, int width) { int calibrate(float **prow, int nrow, int offset, int width) {
double teleline[MAX_HEIGHT] = { 0.0 };
double teleline[MAX_HEIGHT] = { 0.0 };
double wedge[16]; double wedge[16];
rgparam_t regr[30];
rgparam_t regr[MAX_HEIGHT/FRAME_LEN + 1];
int telestart, mtelestart = 0; int telestart, mtelestart = 0;
int channel = -1; int channel = -1;


// The minimum rows required to decode a full frame // The minimum rows required to decode a full frame
if (nrow < 192) { if (nrow < 192) {
fprintf(stderr, ERR_TELE_ROW);
fprintf(stderr, "Telemetry decoding error, not enough rows\n");
return 0; return 0;
} }


// Calculate average of a row of telemetry // Calculate average of a row of telemetry
for (int n = 0; n < nrow; n++) {
float *pixelv = prow[n];

// Average the center 40px
for (int i = 3; i < 43; i++)
teleline[n] += pixelv[i + offset + width];
teleline[n] /= 40.0;
for (int y = 0; y < nrow; y++) {
for (int x = 3; x < 43; x++)
teleline[y] += prow[y][x + offset + width];
teleline[y] /= 40.0;
} }


/* Wedge 7 is white and 8 is black, this will have the largest /* Wedge 7 is white and 8 is black, this will have the largest
* difference in brightness, this will always be in the center of
* the frame and can thus be used to find the start of the frame
* difference in brightness, this can be used to find the current
* position within the telemetry.
*/ */
double max = 0.0;
float max = 0.0f;
for (int n = nrow / 3 - 64; n < 2 * nrow / 3 - 64; n++) { for (int n = nrow / 3 - 64; n < 2 * nrow / 3 - 64; n++) {
float df; float df;


@@ -163,26 +164,26 @@ int calibrate(float **prow, int nrow, int offset, int width) {
} }
} }


// Find the start of the first frame
telestart = (mtelestart - FRAME_LEN/2) % FRAME_LEN;
telestart = (mtelestart - 64) % FRAME_LEN;


// Make sure that theres at least one full frame in the image
// Make sure that theres at least one full frame in the image
if (nrow < telestart + FRAME_LEN) { if (nrow < telestart + FRAME_LEN) {
fprintf(stderr, ERR_TELE_ROW);
return(0);
fprintf(stderr, "Telemetry decoding error, not enough rows\n");
return 0;
} }


// Find the least noisy frame // Find the least noisy frame
double minNoise = -1; double minNoise = -1;
int bestFrame = telestart;
for (int n = telestart, k = 0; n < nrow - FRAME_LEN; n += FRAME_LEN, k++) {
// Turn pixels into wedge values
for (int j = 0; j < 16; j++) {
wedge[j] = 0.0;
int bestFrame = -1;
for (int n = telestart, k = 0; n < nrow - FRAME_LEN; n += FRAME_LEN, k++) {
int j;

for (j = 0; j < 16; j++) {
int i;


// Average the middle 6px
for (int i = 1; i < 7; i++)
wedge[j] += teleline[(j * 8) + i + n];
wedge[j] = 0.0;
for (i = 1; i < 7; i++)
wedge[j] += teleline[n + j * 8 + i];
wedge[j] /= 6; wedge[j] /= 6;
} }


@@ -210,12 +211,108 @@ int calibrate(float **prow, int nrow, int offset, int width) {
min = df; min = df;
} }
} }

// Find the brightness of the minute marker, I don't really know what for
Cs = 0.0;
int i, j = n;
for (i = 0, j = n; j < n + FRAME_LEN; j++) {
float csline = 0.0;
for (int l = 3; l < 43; l++)
csline += prow[n][l + offset - SPC_WIDTH];
csline /= 40.0;

if (csline > 50.0) {
Cs += csline;
i++;
}
}
Cs = rgcal(Cs / i, &regr[k]);
} }
} }


if(bestFrame == -1){
fprintf(stderr, "Something has gone very wrong, please file a bug report.");
return 0;
}

calibrateImage(prow, nrow, offset, width, regr[bestFrame]); calibrateImage(prow, nrow, offset, width, regr[bestFrame]);


return channel + 1; return channel + 1;
}

void distrib(options_t *opts, image_t *img, char *chid) {
int max = 0;

// Options
options_t options;
options.path = opts->path;
options.effects = "";
options.map = "";

// Image options
image_t distrib;
strcpy(distrib.name, img->name);
distrib.nrow = 256;

// Assign memory
for(int i = 0; i < 256; i++)
distrib.prow[i] = (float *) malloc(sizeof(float) * 256);

for(int n = 0; n < img->nrow; n++) {
float *pixelv = img->prow[n];
for(int i = 0; i < CH_WIDTH; i++) {
int y = CLIP((int)pixelv[i + CHA_OFFSET], 0, 255);
int x = CLIP((int)pixelv[i + CHB_OFFSET], 0, 255);
distrib.prow[y][x]++;
if(distrib.prow[y][x] > max)
max = distrib.prow[y][x];
}
}

// Scale to 0-255
for(int x = 0; x < 256; x++)
for(int y = 0; y < 256; y++)
distrib.prow[y][x] = distrib.prow[y][x] / max * 255.0;
extern int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, char *chid, char *palette);
ImageOut(&options, &distrib, 0, 256, "Distribution", chid, NULL);
}

extern float quick_select(float arr[], int n);

// Biased median denoise, pretyt ugly
#define TRIG_LEVEL 40
void denoise(float **prow, int nrow, int offset, int width){
for(int y = 2; y < nrow-2; y++){
for(int x = offset+1; x < offset+width-1; x++){
if(prow[y][x+1] - prow[y][x] > TRIG_LEVEL ||
prow[y][x-1] - prow[y][x] > TRIG_LEVEL ||
prow[y+1][x] - prow[y][x] > TRIG_LEVEL ||
prow[y-1][x] - prow[y][x] > TRIG_LEVEL){
prow[y][x] = quick_select((float[]){
prow[y+2][x-1], prow[y+2][x], prow[y+2][x+1],
prow[y+1][x-1], prow[y+1][x], prow[y+1][x+1],
prow[y-1][x-1], prow[y-1][x], prow[y-1][x+1],
prow[y-2][x-1], prow[y-2][x], prow[y-2][x+1]
}, 12);
}
}
}
}
#undef TRIG_LEVEL

// Flips a channe, for southbound passes
void flipImage(image_t *img, int width, int offset){
for(int y = 1; y < img->nrow; y++){
for(int x = 1; x < ceil(width / 2.0); x++){
// Flip top-left & bottom-right
float buffer = img->prow[img->nrow - y][offset + x];
img->prow[img->nrow - y][offset + x] = img->prow[y][offset + (width - x)];
img->prow[y][offset + (width - x)] = buffer;
}
}
} }


// --- Temperature Calibration --- // // --- Temperature Calibration --- //
@@ -233,7 +330,7 @@ static void tempcomp(double t[16], int ch, int satnum, tempparam_t *tpr) {
double Tbb, T[4]; double Tbb, T[4];
double C; double C;


tpr -> ch = ch - 4;
tpr->ch = ch - 4;


// Compute equivalent T blackbody temperature // Compute equivalent T blackbody temperature
for (int n = 0; n < 4; n++) { for (int n = 0; n < 4; n++) {
@@ -245,17 +342,17 @@ static void tempcomp(double t[16], int ch, int satnum, tempparam_t *tpr) {
d2 = satcal[satnum].d[n][2]; d2 = satcal[satnum].d[n][2];
T[n] = d0; T[n] = d0;
T[n] += d1 * C; T[n] += d1 * C;
C = C * C;
C *= C;
T[n] += d2 * C; T[n] += d2 * C;
} }
Tbb = (T[0] + T[1] + T[2] + T[3]) / 4.0; Tbb = (T[0] + T[1] + T[2] + T[3]) / 4.0;
Tbb = satcal[satnum].rad[tpr->ch].A + satcal[satnum].rad[tpr->ch].B * Tbb; Tbb = satcal[satnum].rad[tpr->ch].A + satcal[satnum].rad[tpr->ch].B * Tbb;


// Compute radiance blackbody
// Compute blackbody radiance temperature
C = satcal[satnum].rad[tpr->ch].vc; C = satcal[satnum].rad[tpr->ch].vc;
tpr->Nbb = c1 * C * C * C / (expm1(c2 * C / Tbb)); tpr->Nbb = c1 * C * C * C / (expm1(c2 * C / Tbb));


// Store count blackbody and space
// Store blackbody count and space
tpr->Cs = Cs * 4.0; tpr->Cs = Cs * 4.0;
tpr->Cb = t[14] * 4.0; tpr->Cb = t[14] * 4.0;
} }
@@ -274,13 +371,15 @@ static double tempcal(float Ce, int satnum, tempparam_t * rgpr) {
Ne = Nl + Nc; Ne = Nl + Nc;


vc = satcal[satnum].rad[rgpr->ch].vc; vc = satcal[satnum].rad[rgpr->ch].vc;
T = c2 * vc / log1p(c1 * vc * vc * vc / Ne);
T = c2 * vc / log(c1 * vc * vc * vc / Ne + 1.0);
T = (T - satcal[satnum].rad[rgpr->ch].A) / satcal[satnum].rad[rgpr->ch].B; T = (T - satcal[satnum].rad[rgpr->ch].A) / satcal[satnum].rad[rgpr->ch].B;


// Rescale to 0-255 for -60'C to +40'C
T = (T - 273.15 + 60.0) / 100.0 * 256.0;
// Convert to celsius
T -= 273.15;
// Rescale to 0-255 for -100°C to +60°C
T = (T + 100.0) / 160.0 * 255.0;


return(T);
return T;
} }


// Temperature calibration wrapper // Temperature calibration wrapper
@@ -293,73 +392,9 @@ void temperature(options_t *opts, image_t *img, int offset, int width){
tempcomp(tele, img->chB, opts->satnum - 15, &temp); tempcomp(tele, img->chB, opts->satnum - 15, &temp);


for (int y = 0; y < img->nrow; y++) { for (int y = 0; y < img->nrow; y++) {
float *pixelv = img->prow[y];

for (int x = 0; x < width; x++) {
float pv = tempcal(pixelv[x + offset], opts->satnum - 15, &temp);
pixelv[x + offset] = CLIP(pv, 0, 255);
for (int x = 0; x < width; x++) {
img->prow[y][x + offset] = tempcal(img->prow[y][x + offset], opts->satnum - 15, &temp);
} }
} }
printf("Done\n"); printf("Done\n");
}

void distrib(options_t *opts, image_t *img, char *chid) {
int max = 0;

// Options
options_t options;
options.path = opts->path;

// Image options
image_t distrib;
strcpy(distrib.name, img->name);
distrib.nrow = 256;

// Assign memory
for(int i = 0; i < 256; i++)
distrib.prow[i] = (float *) malloc(sizeof(float) * 256);

for(int n = 0; n < img->nrow; n++) {
float *pixelv = img->prow[n];
for(int i = 0; i < CH_WIDTH; i++) {
int y = CLIP((int)pixelv[i + CHA_OFFSET], 0, 255);
int x = CLIP((int)pixelv[i + CHB_OFFSET], 0, 255);
distrib.prow[y][x]++;
if(distrib.prow[y][x] > max)
max = distrib.prow[y][x];
}
}

// Scale to 0-255
for(int x = 0; x < 256; x++)
for(int y = 0; y < 256; y++)
distrib.prow[y][x] = distrib.prow[y][x] / max * 255.0;
extern int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, char *chid, char *palette);
ImageOut(&options, &distrib, 0, 256, "Distribution", chid, NULL);
}

extern float quick_select(float arr[], int n);

// Recursive biased median denoise
#define TRIG_LEVEL 40
void denoise(float **prow, int nrow, int offset, int width){
for(int y = 2; y < nrow-2; y++){
for(int x = offset+1; x < offset+width-1; x++){
if(prow[y][x+1] - prow[y][x] > TRIG_LEVEL ||
prow[y][x-1] - prow[y][x] > TRIG_LEVEL ||
prow[y+1][x] - prow[y][x] > TRIG_LEVEL ||
prow[y-1][x] - prow[y][x] > TRIG_LEVEL){
prow[y][x] = quick_select((float[]){
prow[y+2][x-1], prow[y+2][x], prow[y+2][x+1],
prow[y+1][x-1], prow[y+1][x], prow[y+1][x+1],
prow[y-1][x-1], prow[y-1][x], prow[y-1][x+1],
prow[y-2][x-1], prow[y-2][x], prow[y-2][x+1]
}, 12);
}
}
}
}
#undef TRIG_LEVEL
}

+ 55
- 61
main.c View File

@@ -25,48 +25,30 @@
#include <math.h> #include <math.h>
#include <sndfile.h> #include <sndfile.h>
#include <errno.h> #include <errno.h>
#include <time.h>


#include "messages.h"
#include "common.h"
#include "offsets.h" #include "offsets.h"


#define FAILURE 0
#define SUCCESS 1

typedef struct {
char *type; // Output image type
char *effects;
int satnum; // The satellite number
char *map; // Path to a map file
char *path; // Output directory
int realtime;
} options_t;

typedef struct {
float *prow[MAX_HEIGHT]; // Row buffers
int nrow; // Number of rows
int chA, chB; // ID of each channel
char name[256]; // Stripped filename
} image_t;

// DSP // DSP
extern int getpixelrow(float *pixelv, int nrow, int *zenith);
extern int init_dsp(double F); extern int init_dsp(double F);
extern int getpixelrow(float *pixelv, int nrow, int *zenith);


// I/O // I/O
extern int readfcconf(char *file);
extern int readRawImage(char *filename, float **prow, int *nrow); extern int readRawImage(char *filename, float **prow, int *nrow);
extern int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, char *chid, char *palette); extern int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, char *chid, char *palette);
extern void closeWriter();
extern void pushRow(float *row, int width);
extern int initWriter(options_t *opts, image_t *img, int width, int height, char *desc, char *chid); extern int initWriter(options_t *opts, image_t *img, int width, int height, char *desc, char *chid);
extern void pushRow(float *row, int width);
extern void closeWriter();


// Image functions // Image functions
extern int calibrate(float **prow, int nrow, int offset, int width); extern int calibrate(float **prow, int nrow, int offset, int width);
extern void histogramEqualise(float **prow, int nrow, int offset, int width); extern void histogramEqualise(float **prow, int nrow, int offset, int width);
extern void linearEnhance(float **prow, int nrow, int offset, int width);
extern void temperature(options_t *opts, image_t *img, int offset, int width); extern void temperature(options_t *opts, image_t *img, int offset, int width);
extern int Ngvi(float **prow, int nrow);
extern void denoise(float **prow, int nrow, int offset, int width); extern void denoise(float **prow, int nrow, int offset, int width);
extern void distrib(options_t *opts, image_t *img, char *chid); extern void distrib(options_t *opts, image_t *img, char *chid);
extern void flipImage(image_t *img, int width, int offset);


// Palettes // Palettes
extern char GviPalette[256*3]; extern char GviPalette[256*3];
@@ -77,6 +59,7 @@ int zenith = 0;
// Audio file // Audio file
static SNDFILE *audioFile; static SNDFILE *audioFile;


// Function predeclarations
static int initsnd(char *filename); static int initsnd(char *filename);
int getsample(float *sample, int nb); int getsample(float *sample, int nb);
static int processAudio(char *filename, options_t *opts); static int processAudio(char *filename, options_t *opts);
@@ -85,11 +68,8 @@ static void usage(void);
int main(int argc, char **argv) { int main(int argc, char **argv) {
fprintf(stderr, VERSION"\n"); fprintf(stderr, VERSION"\n");


if(argc == 1)
usage();

// Check if there are actually any input files // Check if there are actually any input files
if(optind == argc){
if(argc == optind || argc == 1){
fprintf(stderr, "No input files provided.\n"); fprintf(stderr, "No input files provided.\n");
usage(); usage();
} }
@@ -98,14 +78,11 @@ int main(int argc, char **argv) {


// Parse arguments // Parse arguments
int opt; int opt;
while ((opt = getopt(argc, argv, "c:m:d:i:s:e:r")) != EOF) {
while ((opt = getopt(argc, argv, "m:d:i:s:e:r")) != EOF) {
switch (opt) { switch (opt) {
case 'd': case 'd':
opts.path = optarg; opts.path = optarg;
break; break;
case 'c':
readfcconf(optarg);
break;
case 'm': case 'm':
opts.map = optarg; opts.map = optarg;
break; break;
@@ -160,6 +137,13 @@ static int processAudio(char *filename, options_t *opts){
strcpy(path, dirname(path)); strcpy(path, dirname(path));
sscanf(basename(filename), "%[^.].%s", img.name, extension); sscanf(basename(filename), "%[^.].%s", img.name, extension);


// Set output filename to current time when in realtime mode
if(opts->realtime){
time_t t;
time(&t);
strncpy(img.name, ctime(&t), 24);
}

if(opts->realtime) initWriter(opts, &img, IMG_WIDTH, MAX_HEIGHT, "Unprocessed realtime image", "r"); if(opts->realtime) initWriter(opts, &img, IMG_WIDTH, MAX_HEIGHT, "Unprocessed realtime image", "r");


if(strcmp(extension, "png") == 0){ if(strcmp(extension, "png") == 0){
@@ -167,7 +151,7 @@ static int processAudio(char *filename, options_t *opts){
printf("Reading %s", filename); printf("Reading %s", filename);
if(readRawImage(filename, img.prow, &img.nrow) == 0){ if(readRawImage(filename, img.prow, &img.nrow) == 0){
fprintf(stderr, "Skipping %s; see above.\n", img.name); fprintf(stderr, "Skipping %s; see above.\n", img.name);
return FAILURE;
return 0;
} }
}else{ }else{
// Attempt to open the audio file // Attempt to open the audio file
@@ -175,6 +159,7 @@ static int processAudio(char *filename, options_t *opts){
exit(EPERM); exit(EPERM);


// Build image // Build image
// TODO: multithreading, would require some sort of input buffer
for (img.nrow = 0; img.nrow < MAX_HEIGHT; img.nrow++) { for (img.nrow = 0; img.nrow < MAX_HEIGHT; img.nrow++) {
// Allocate memory for this row // Allocate memory for this row
img.prow[img.nrow] = (float *) malloc(sizeof(float) * 2150); img.prow[img.nrow] = (float *) malloc(sizeof(float) * 2150);
@@ -216,36 +201,43 @@ static int processAudio(char *filename, options_t *opts){
denoise(img.prow, img.nrow, CHB_OFFSET, CH_WIDTH); denoise(img.prow, img.nrow, CHB_OFFSET, CH_WIDTH);
} }


// Flip, for southbound passes
if(CONTAINS(opts->effects, 'f')){
flipImage(&img, CH_WIDTH, CHA_OFFSET);
flipImage(&img, CH_WIDTH, CHB_OFFSET);
}

// Temperature // Temperature
if (CONTAINS(opts->type, 't') && img.chB >= 4) { if (CONTAINS(opts->type, 't') && img.chB >= 4) {
temperature(opts, &img, CHB_OFFSET, CH_WIDTH); temperature(opts, &img, CHB_OFFSET, CH_WIDTH);
ImageOut(opts, &img, CHB_OFFSET, CH_WIDTH, "Temperature", "t", (char *)TempPalette); ImageOut(opts, &img, CHB_OFFSET, CH_WIDTH, "Temperature", "t", (char *)TempPalette);
} }


// False color image
if(CONTAINS(opts->type, 'c')){
if (img.chA == 2 && img.chB >= 4) { // Normal false color
// TODO: use real MSA
// TODO: provide more than just "natural" color images
ImageOut(opts, &img, 0, CH_WIDTH, "False Color", "c", NULL);
} else if (img.chB == 2) { // GVI (global vegetation index) false color
Ngvi(img.prow, img.nrow);
ImageOut(opts, &img, CHB_OFFSET, CH_WIDTH, "GVI False Color", "c", (char *)GviPalette);
} else {
fprintf(stderr, "Skipping False Color generation; lacking required channels.\n");
}
}

// MCIR // MCIR
if (CONTAINS(opts->type, 'm')) if (CONTAINS(opts->type, 'm'))
ImageOut(opts, &img, 0, IMG_WIDTH, "MCIR", "m", NULL); ImageOut(opts, &img, 0, IMG_WIDTH, "MCIR", "m", NULL);


// Linear equalise
if(CONTAINS(opts->effects, 'l')){
linearEnhance(img.prow, img.nrow, CHA_OFFSET, CH_WIDTH);
linearEnhance(img.prow, img.nrow, CHB_OFFSET, CH_WIDTH);
}

// Histogram equalise // Histogram equalise
if(CONTAINS(opts->effects, 'h')){ if(CONTAINS(opts->effects, 'h')){
histogramEqualise(img.prow, img.nrow, CHA_OFFSET, CH_WIDTH); histogramEqualise(img.prow, img.nrow, CHA_OFFSET, CH_WIDTH);
histogramEqualise(img.prow, img.nrow, CHB_OFFSET, CH_WIDTH); histogramEqualise(img.prow, img.nrow, CHB_OFFSET, CH_WIDTH);
} }


// False color
if(CONTAINS(opts->type, 'c')){
if(img.chA == 2 && img.chB >= 4){
ImageOut(opts, &img, 0, CH_WIDTH, "False Color", "c", NULL);
}else{
fprintf(stderr, "Lacking channels required for false color computation\n");
}
}

// Raw image // Raw image
if (CONTAINS(opts->type, 'r')) { if (CONTAINS(opts->type, 'r')) {
sprintf(desc, "%s (%s) & %s (%s)", ch.id[img.chA], ch.name[img.chA], ch.id[img.chB], ch.name[img.chB]); sprintf(desc, "%s (%s) & %s (%s)", ch.id[img.chA], ch.name[img.chA], ch.id[img.chB], ch.name[img.chB]);
@@ -268,7 +260,7 @@ static int processAudio(char *filename, options_t *opts){
if (CONTAINS(opts->type, 'd')) if (CONTAINS(opts->type, 'd'))
distrib(opts, &img, "d"); distrib(opts, &img, "d");
return SUCCESS;
return 1;
} }


static int initsnd(char *filename) { static int initsnd(char *filename) {
@@ -279,28 +271,28 @@ static int initsnd(char *filename) {
infwav.format = 0; infwav.format = 0;
audioFile = sf_open(filename, SFM_READ, &infwav); audioFile = sf_open(filename, SFM_READ, &infwav);
if (audioFile == NULL) { if (audioFile == NULL) {
fprintf(stderr, ERR_FILE_READ, filename);
return FAILURE;
fprintf(stderr, "Could not open %s for reading\n", filename);
return 0;
} }


res = init_dsp(infwav.samplerate); res = init_dsp(infwav.samplerate);
printf("Input file: %s\n", filename); printf("Input file: %s\n", filename);
if(res < 0) { if(res < 0) {
fprintf(stderr, "Input sample rate too low: %d\n", infwav.samplerate); fprintf(stderr, "Input sample rate too low: %d\n", infwav.samplerate);
return FAILURE;
return 0;
}else if(res > 0) { }else if(res > 0) {
fprintf(stderr, "Input sample rate too high: %d\n", infwav.samplerate); fprintf(stderr, "Input sample rate too high: %d\n", infwav.samplerate);
return FAILURE;
return 0;
} }
printf("Input sample rate: %d\n", infwav.samplerate); printf("Input sample rate: %d\n", infwav.samplerate);


// TODO: accept stereo audio // TODO: accept stereo audio
if (infwav.channels != 1) { if (infwav.channels != 1) {
fprintf(stderr, "Too many channels in input file: %d\n", infwav.channels); fprintf(stderr, "Too many channels in input file: %d\n", infwav.channels);
return FAILURE;
return 0;
} }


return SUCCESS;
return 1;
} }


// Read samples from the wave file // Read samples from the wave file
@@ -312,11 +304,13 @@ static void usage(void) {
fprintf(stderr, fprintf(stderr,
"Aptdec [options] audio files ...\n" "Aptdec [options] audio files ...\n"
"Options:\n" "Options:\n"
" -e [t|h] Effects\n"
" -e [t|h|d|p|f|l] Effects\n"
" t: Crop telemetry\n" " t: Crop telemetry\n"
" h: Histogram equalise\n" " h: Histogram equalise\n"
" d: Denoise\n" " d: Denoise\n"
" p: Precipitation\n" " p: Precipitation\n"
" f: Flip image\n"
" l: Linear equalise\n"
" -i [r|a|b|c|t|m] Output image\n" " -i [r|a|b|c|t|m] Output image\n"
" r: Raw\n" " r: Raw\n"
" a: Channel A\n" " a: Channel A\n"
@@ -324,11 +318,11 @@ static void usage(void) {
" c: False color\n" " c: False color\n"
" t: Temperature\n" " t: Temperature\n"
" m: MCIR\n" " m: MCIR\n"
" -d <dir> Image destination directory.\n"
" -s [15-19] Satellite number\n"
" -c <file> False color config file\n"
" -m <file> Map file\n"
" -r Realtime decode\n");
" -d <dir> Image destination directory.\n"
" -s [15-19] Satellite number\n"
" -m <file> Map file\n"
" -r Realtime decode\n"
"\nRefer to the README for more infomation\n");


exit(EINVAL); exit(EINVAL);
} }

+ 0
- 5
offsets.h View File

@@ -27,8 +27,3 @@
#define CHA_OFFSET (SYNC_WIDTH+SPC_WIDTH) #define CHA_OFFSET (SYNC_WIDTH+SPC_WIDTH)
#define CHB_OFFSET (SYNC_WIDTH+SPC_WIDTH+CH_WIDTH+TELE_WIDTH+SYNC_WIDTH+SPC_WIDTH) #define CHB_OFFSET (SYNC_WIDTH+SPC_WIDTH+CH_WIDTH+TELE_WIDTH+SYNC_WIDTH+SPC_WIDTH)
#define TOTAL_TELE (SYNC_WIDTH+SPC_WIDTH+TELE_WIDTH+SYNC_WIDTH+SPC_WIDTH+TELE_WIDTH) #define TOTAL_TELE (SYNC_WIDTH+SPC_WIDTH+TELE_WIDTH+SYNC_WIDTH+SPC_WIDTH+TELE_WIDTH)
#define MAX_HEIGHT 3000

#define CLIP(val, bottom, top) (val > top ? top : (val > bottom ? val : bottom))
#define CONTAINS(str, char) (strchr(str, (int) char) != NULL)
#define MCOMPOSITE(m1, a1, m2, a2) (m1*a1 + m2*a2*(1-a1))

+ 36
- 50
pngio.c View File

@@ -23,34 +23,13 @@
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>


#include "common.h"
#include "offsets.h" #include "offsets.h"
#include "messages.h"

typedef struct {
float r, g, b;
} rgb_t;

typedef struct {
float *prow[MAX_HEIGHT]; // Row buffers
int nrow; // Number of rows
int chA, chB; // ID of each channel
char name[256]; // Stripped filename
} image_t;

typedef struct {
char *type; // Output image type
char *effects;
int satnum; // The satellite number
char *map; // Path to a map file
char *path; // Output directory
int realtime;
} options_t;


extern int zenith; extern int zenith;
extern char PrecipPalette[256*3]; extern char PrecipPalette[256*3];
extern rgb_t applyPalette(char *palette, int val); extern rgb_t applyPalette(char *palette, int val);
extern rgb_t RGBcomposite(rgb_t top, float top_a, rgb_t bottom, float bottom_a); extern rgb_t RGBcomposite(rgb_t top, float top_a, rgb_t bottom, float bottom_a);
extern rgb_t falsecolor(float vis, float temp);


int mapOverlay(char *filename, rgb_t **crow, int nrow, int zenith, int MCIR) { int mapOverlay(char *filename, rgb_t **crow, int nrow, int zenith, int MCIR) {
FILE *fp = fopen(filename, "rb"); FILE *fp = fopen(filename, "rb");
@@ -204,7 +183,7 @@ int readRawImage(char *filename, float **prow, int *nrow) {
return 1; return 1;
} }


png_text text_ptr[] = {
png_text meta[] = {
{PNG_TEXT_COMPRESSION_NONE, "Software", VERSION}, {PNG_TEXT_COMPRESSION_NONE, "Software", VERSION},
{PNG_TEXT_COMPRESSION_NONE, "Channel", "Unknown", 7}, {PNG_TEXT_COMPRESSION_NONE, "Channel", "Unknown", 7},
{PNG_TEXT_COMPRESSION_NONE, "Description", "NOAA satellite image", 20} {PNG_TEXT_COMPRESSION_NONE, "Description", "NOAA satellite image", 20}
@@ -214,13 +193,14 @@ int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, c
char outName[384]; char outName[384];
sprintf(outName, "%s/%s-%s.png", opts->path, img->name, chid); sprintf(outName, "%s/%s-%s.png", opts->path, img->name, chid);
text_ptr[1].text = desc;
text_ptr[1].text_length = sizeof(desc);
meta[1].text = desc;
meta[1].text_length = sizeof(desc);


FILE *pngfile; FILE *pngfile;


// Reduce the width of the image to componsate for the missing telemetry // Reduce the width of the image to componsate for the missing telemetry
int fcimage = strcmp(desc, "False Color") == 0;
int fc = strcmp(desc, "False Color") == 0;
int greyscale = 0;
int skiptele = 0; int skiptele = 0;
if(opts->effects != NULL && CONTAINS(opts->effects, 't')){ if(opts->effects != NULL && CONTAINS(opts->effects, 't')){
width -= TOTAL_TELE; width -= TOTAL_TELE;
@@ -231,18 +211,17 @@ int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, c
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr) { if (!png_ptr) {
png_destroy_write_struct(&png_ptr, (png_infopp) NULL); png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
fprintf(stderr, ERR_PNG_WRITE);
fprintf(stderr, "Could not create a PNG writer\n");
return 0; return 0;
} }
png_infop info_ptr = png_create_info_struct(png_ptr); png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) { if (!info_ptr) {
png_destroy_write_struct(&png_ptr, (png_infopp) NULL); png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
fprintf(stderr, ERR_PNG_INFO);
fprintf(stderr, "Could not create a PNG writer\n");
return 0; return 0;
} }


int greyscale = 0;
if(palette == NULL && !CONTAINS(opts->effects, 'p') && !fcimage && opts->map[0] == '\0' && strcmp(chid, "MCIR") != 0){
if(palette == NULL && !CONTAINS(opts->effects, 'p') && !fc && opts->map[0] == '\0' && strcmp(chid, "MCIR") != 0){
greyscale = 1; greyscale = 1;


// Greyscale image // Greyscale image
@@ -256,13 +235,13 @@ int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, c
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
} }


png_set_text(png_ptr, info_ptr, text_ptr, 3);
png_set_text(png_ptr, info_ptr, meta, 3);
png_set_pHYs(png_ptr, info_ptr, 3636, 3636, PNG_RESOLUTION_METER); png_set_pHYs(png_ptr, info_ptr, 3636, 3636, PNG_RESOLUTION_METER);


// Init I/O // Init I/O
pngfile = fopen(outName, "wb"); pngfile = fopen(outName, "wb");
if (!pngfile) { if (!pngfile) {
fprintf(stderr, ERR_FILE_WRITE, outName);
fprintf(stderr, "Could not open %s for writing\n", outName);
return 1; return 1;
} }
png_init_io(png_ptr, pngfile); png_init_io(png_ptr, pngfile);
@@ -270,13 +249,13 @@ int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, c


// Move prow into crow, crow ~ color rows // Move prow into crow, crow ~ color rows
rgb_t *crow[img->nrow]; rgb_t *crow[img->nrow];
if(!greyscale){
if(!greyscale && !fc){
for(int y = 0; y < img->nrow; y++){ for(int y = 0; y < img->nrow; y++){
crow[y] = (rgb_t *) malloc(sizeof(rgb_t) * IMG_WIDTH); crow[y] = (rgb_t *) malloc(sizeof(rgb_t) * IMG_WIDTH);


for(int x = 0; x < IMG_WIDTH; x++){ for(int x = 0; x < IMG_WIDTH; x++){
if(palette == NULL)
crow[y][x].r = crow[y][x].g = crow[y][x].b = CLIP(img->prow[y][x], 0, 255);
if(palette == NULL)
crow[y][x].r = crow[y][x].g = crow[y][x].b = img->prow[y][x];
else else
crow[y][x] = applyPalette(palette, img->prow[y][x]); crow[y][x] = applyPalette(palette, img->prow[y][x]);
} }
@@ -309,8 +288,8 @@ int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, c


// Build image // Build image
for (int y = 0; y < img->nrow; y++) { for (int y = 0; y < img->nrow; y++) {
png_color pix[width];
png_byte gpix[width];
png_color pix[width]; // Color
png_byte mpix[width]; // Mono
int skip = 0; int skip = 0;
for (int x = 0; x < width; x++) { for (int x = 0; x < width; x++) {
@@ -326,17 +305,24 @@ int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, c
} }


if(greyscale){ if(greyscale){
gpix[x] = img->prow[y][x + skip + offset];
}else if(fcimage){
rgb_t pixel = falsecolor(img->prow[y][x + CHA_OFFSET], img->prow[y][x + CHB_OFFSET]);
pix[x] = (png_color){pixel.r, pixel.g, pixel.b};
mpix[x] = img->prow[y][x + skip + offset];
}else if(fc){
pix[x] = (png_color){
CLIP(img->prow[y][x + CHA_OFFSET], 0, 255),
CLIP(img->prow[y][x + CHA_OFFSET], 0, 255),
CLIP(img->prow[y][x + CHB_OFFSET], 0, 255)
};
}else{ }else{
pix[x] = (png_color){crow[y][x + skip + offset].r, crow[y][x + skip + offset].g, crow[y][x + skip + offset].b};
pix[x] = (png_color){
CLIP(crow[y][x + skip + offset].r, 0, 255),
CLIP(crow[y][x + skip + offset].g, 0, 255),
CLIP(crow[y][x + skip + offset].b, 0, 255)
};
} }
} }
if(greyscale){ if(greyscale){
png_write_row(png_ptr, (png_bytep) gpix);
png_write_row(png_ptr, (png_bytep) mpix);
}else{ }else{
png_write_row(png_ptr, (png_bytep) pix); png_write_row(png_ptr, (png_bytep) pix);
} }
@@ -351,7 +337,7 @@ int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, c
return 1; return 1;
} }


// TODO: remove these from the global scope
// TODO: clean up everthing below this comment
png_structp rt_png_ptr; png_structp rt_png_ptr;
png_infop rt_info_ptr; png_infop rt_info_ptr;
FILE *rt_pngfile; FILE *rt_pngfile;
@@ -360,20 +346,20 @@ int initWriter(options_t *opts, image_t *img, int width, int height, char *desc,
char outName[384]; char outName[384];
sprintf(outName, "%s/%s-%s.png", opts->path, img->name, chid); sprintf(outName, "%s/%s-%s.png", opts->path, img->name, chid);


text_ptr[1].text = desc;
text_ptr[1].text_length = sizeof(desc);
meta[1].text = desc;
meta[1].text_length = sizeof(desc);


// Create writer // Create writer
rt_png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); rt_png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!rt_png_ptr) { if (!rt_png_ptr) {
png_destroy_write_struct(&rt_png_ptr, (png_infopp) NULL); png_destroy_write_struct(&rt_png_ptr, (png_infopp) NULL);
fprintf(stderr, ERR_PNG_WRITE);
fprintf(stderr, "Could not create a PNG writer\n");
return 0; return 0;
} }
rt_info_ptr = png_create_info_struct(rt_png_ptr); rt_info_ptr = png_create_info_struct(rt_png_ptr);
if (!rt_info_ptr) { if (!rt_info_ptr) {
png_destroy_write_struct(&rt_png_ptr, (png_infopp) NULL); png_destroy_write_struct(&rt_png_ptr, (png_infopp) NULL);
fprintf(stderr, ERR_PNG_INFO);
fprintf(stderr, "Could not create a PNG writer\n");
return 0; return 0;
} }


@@ -382,7 +368,7 @@ int initWriter(options_t *opts, image_t *img, int width, int height, char *desc,
8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_set_text(rt_png_ptr, rt_info_ptr, text_ptr, 3);
png_set_text(rt_png_ptr, rt_info_ptr, meta, 3);


// Channel = 25cm wide // Channel = 25cm wide
png_set_pHYs(rt_png_ptr, rt_info_ptr, 3636, 3636, PNG_RESOLUTION_METER); png_set_pHYs(rt_png_ptr, rt_info_ptr, 3636, 3636, PNG_RESOLUTION_METER);
@@ -390,7 +376,7 @@ int initWriter(options_t *opts, image_t *img, int width, int height, char *desc,
// Init I/O // Init I/O
rt_pngfile = fopen(outName, "wb"); rt_pngfile = fopen(outName, "wb");
if (!rt_pngfile) { if (!rt_pngfile) {
fprintf(stderr, ERR_FILE_WRITE, outName);
fprintf(stderr, "Could not open %s for writing\n", outName);
return 0; return 0;
} }
png_init_io(rt_png_ptr, rt_pngfile); png_init_io(rt_png_ptr, rt_pngfile);


Loading…
Cancel
Save