diff --git a/.gitignore b/.gitignore
index 4ccc436..a7c4171 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,8 +43,9 @@
# Program specifics
*.png
!textlogo.png
+!palettes/*
*.wav
aptdec
# VSCode
-.vscode
\ No newline at end of file
+.vscode
diff --git a/Makefile b/Makefile
index b63097a..fa7f7d9 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
CC = gcc
BIN = /usr/bin
INCLUDES = -I.
-CFLAGS = -O3 -DNDEBUG -Wall -Wextra $(INCLUDES)
+CFLAGS = -O3 -g -Wall -Wextra $(INCLUDES)
OBJS = main.o image.o dsp.o filter.o reg.o pngio.o median.o color.o
aptdec: $(OBJS)
diff --git a/README.md b/README.md
index 211092f..8d81412 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ For each audio file up to 6 images can be generated:
2. Calibrated channel A image
3. Calibrated channel B image
4. Temperature compensated IR image
-5. False color image
+5. Palleted image
6. MCIR (Map Color InfraRed) image
The input audio file must be mono with a sample rate in the range of 4160-62400 Hz, lower samples rates will process faster.
@@ -51,9 +51,9 @@ To uninstall
## Options
```
--i [r|a|b|c|t|m]
+-i [r|a|b|t|m|p]
Output image type
-Raw (r), Channel A (a), Channel B (b), False Color (c), Temperature (t) or MCIR (m)
+Raw (r), Channel A (a), Channel B (b), Palleted (p), Temperature (t) or MCIR (m)
Default: "ab"
-d
@@ -88,18 +88,18 @@ Image names are `audiofile-x.png`, where `x` is:
- `r` for raw images
- Sensor ID (`1`, `2`, `3A`, `3B`, `4`, `5`) for channel A|B images
- - `c` for false color
+ - `p` for a paletted image
- `t` for temperature calibrated images
- `m` for MCIR images
Currently there are 6 available effects:
- `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
- `d` for a median denoise filter
- `p` for a precipitation overlay
- `f` to flip the image (for southbound passes)
- - `l` to linearly equalise the image (recommended for falsecolor images)
+ - `l` to linearly equalise the image, stretch the colors in the image to black and white
## Examples
@@ -111,6 +111,10 @@ This will process all `.wav` files in the current directory, generate calibrated
Decode `audio.wav` with denoise and histogram equalization and save it into the current directory.
+`aptdec -e d -p palettes/N19-June-High-Vegetation.png -i p audio.wav`
+
+Create a false color image from the `N19-June-High-Vegetation.pn` palette.
+
## Realtime decoding
As of recently a realtime output was added allowing realtime decoding of images.
@@ -121,7 +125,7 @@ aptdec /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`. To stop the decode kill the `sox` process
## Further reading
diff --git a/common.h b/common.h
index 9e79c44..2a4a50f 100644
--- a/common.h
+++ b/common.h
@@ -34,6 +34,7 @@ typedef struct {
int nrow; // Number of rows
int chA, chB; // ID of each channel
char name[256]; // Stripped filename
+ char *palette; // Filename of palette
} image_t;
typedef struct {
char *type; // Output image type
@@ -43,4 +44,5 @@ typedef struct {
char *path; // Output directory
int realtime; // Realtime decoding
char *filename; // Output filename
+ char *palette; // Filename of palette
} options_t;
\ No newline at end of file
diff --git a/dsp.c b/dsp.c
index 06f1eae..503e160 100755
--- a/dsp.c
+++ b/dsp.c
@@ -248,7 +248,7 @@ int getpixelrow(float *pixelv, int nrow, int *zenith, int reset) {
// The point in which the pixel offset is recalculated
if (corr < 0.75 * max) {
- //synced = 0;
+ synced = 0;
FreqLine = 1.0;
}
max = corr;
diff --git a/main.c b/main.c
index dc2a7a1..4acfca4 100644
--- a/main.c
+++ b/main.c
@@ -80,7 +80,7 @@ int main(int argc, char **argv) {
// Parse arguments
int opt;
- while ((opt = getopt(argc, argv, "o:m:d:i:s:e:r")) != EOF) {
+ while ((opt = getopt(argc, argv, "o:m:d:i:s:e:rp:")) != EOF) {
switch (opt) {
case 'd':
opts.path = optarg;
@@ -107,6 +107,9 @@ int main(int argc, char **argv) {
case 'o':
opts.filename = optarg;
break;
+ case 'p':
+ opts.palette = optarg;
+ break;
default:
usage();
}
@@ -235,21 +238,19 @@ static int processAudio(char *filename, options_t *opts){
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
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]);
ImageOut(opts, &img, 0, IMG_WIDTH, desc, "r", NULL);
}
+ // Palette image
+ if (CONTAINS(opts->type, 'p')) {
+ img.palette = opts->palette;
+ strcpy(desc, "Palette composite");
+ ImageOut(opts, &img, 0, 909, desc, "p", NULL);
+ }
+
// Channel A
if (CONTAINS(opts->type, 'a')) {
sprintf(desc, "%s (%s)", ch.id[img.chA], ch.name[img.chA]);
@@ -317,24 +318,25 @@ static void usage(void) {
"Aptdec [options] audio files ...\n"
"Options:\n"
" -e [t|h|d|p|f|l] Effects\n"
- " t: Crop telemetry\n"
- " h: Histogram equalise\n"
- " d: Denoise\n"
- " p: Precipitation\n"
- " f: Flip image\n"
- " l: Linear equalise\n"
- " -i [r|a|b|c|t|m] Output image\n"
- " r: Raw\n"
- " a: Channel A\n"
- " b: Channel B\n"
- " c: False color\n"
- " t: Temperature\n"
- " m: MCIR\n"
+ " t: Crop telemetry\n"
+ " h: Histogram equalise\n"
+ " d: Denoise\n"
+ " p: Precipitation\n"
+ " f: Flip image\n"
+ " l: Linear equalise\n"
+ " -i [r|a|b|c|t|m|p] Output image\n"
+ " r: Raw\n"
+ " a: Channel A\n"
+ " b: Channel B\n"
+ " t: Temperature\n"
+ " m: MCIR\n"
+ " p: Paletted image\n"
" -d Image destination directory.\n"
" -o Output filename\n"
" -s [15-19] Satellite number\n"
" -m Map file\n"
" -r Realtime decode\n"
+ " -p Path to palette\n"
"\nRefer to the README for more infomation\n");
exit(EINVAL);
diff --git a/palettes/N19-June-High-Vegetation.png b/palettes/N19-June-High-Vegetation.png
new file mode 100644
index 0000000..4f24cb4
Binary files /dev/null and b/palettes/N19-June-High-Vegetation.png differ
diff --git a/pngio.c b/pngio.c
index e1364e0..3f4b5c7 100644
--- a/pngio.c
+++ b/pngio.c
@@ -193,6 +193,67 @@ int readRawImage(char *filename, float **prow, int *nrow) {
return 1;
}
+int readPalette(char *filename, rgb_t **crow) {
+ FILE *fp = fopen(filename, "r");
+ if(!fp) {
+ fprintf(stderr, "Cannot open %s\n", filename);
+ return 0;
+ }
+
+ // Create reader
+ png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if(!png) return 0;
+ png_infop info = png_create_info_struct(png);
+ if(!info) return 0;
+ png_init_io(png, fp);
+
+ // Read info from header
+ png_read_info(png, info);
+ int width = png_get_image_width(png, info);
+ int height = png_get_image_height(png, info);
+ png_byte color_type = png_get_color_type(png, info);
+ png_byte bit_depth = png_get_bit_depth(png, info);
+
+ // Check the image
+ if(width != 256 && height != 256){
+ fprintf(stderr, "Palette must be 256x256.\n");
+ return 0;
+ }else if(bit_depth != 8){
+ fprintf(stderr, "Palette must be 8 bit color.\n");
+ return 0;
+ }else if(color_type != PNG_COLOR_TYPE_RGBA){
+ fprintf(stderr, "Palette must be RGB.\n");
+ return 0;
+ }
+
+ // Create row buffers
+ png_bytep *PNGrows = NULL;
+ PNGrows = (png_bytep *) malloc(sizeof(png_bytep) * height);
+ for(int y = 0; y < height; y++)
+ PNGrows[y] = (png_byte *) malloc(png_get_rowbytes(png, info));
+
+ // Read image
+ png_read_image(png, PNGrows);
+
+ // Tidy up
+ fclose(fp);
+ png_destroy_read_struct(&png, &info, NULL);
+
+ // Put into crow
+ for(int y = 0; y < height; y++) {
+ crow[y] = (rgb_t *) malloc(sizeof(rgb_t) * width);
+
+ for(int x = 0; x < width; x++)
+ crow[y][x] = (rgb_t){
+ PNGrows[y][x*4],
+ PNGrows[y][x*4 + 1],
+ PNGrows[y][x*4 + 2]
+ };
+ }
+
+ return 1;
+}
+
png_text meta[] = {
{PNG_TEXT_COMPRESSION_NONE, "Software", VERSION},
{PNG_TEXT_COMPRESSION_NONE, "Channel", "Unknown", 7},
@@ -216,6 +277,7 @@ int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, c
int fc = (chid[0] == 'c');
int greyscale = 0;
int skiptele = 0;
+ int imgpalette = (chid[0] == 'p');
if(opts->effects != NULL && CONTAINS(opts->effects, 't')){
width -= TOTAL_TELE;
skiptele = 1;
@@ -235,7 +297,7 @@ int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, c
return 0;
}
- if(palette == NULL && !CONTAINS(opts->effects, 'p') && !fc && opts->map[0] == '\0' && chid[0] != 'm'){
+ if(palette == NULL && !CONTAINS(opts->effects, 'p') && !fc && opts->map[0] == '\0' && chid[0] != 'm' && !imgpalette){
greyscale = 1;
// Greyscale image
@@ -300,6 +362,13 @@ int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, c
printf("Writing %s", outName);
+ rgb_t *pal_row[256];
+ if(imgpalette){
+ if(!readPalette(img->palette, pal_row)){
+ return 0;
+ }
+ }
+
// Build image
for (int y = 0; y < img->nrow; y++) {
png_color pix[width]; // Color
@@ -326,6 +395,14 @@ int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, c
CLIP(img->prow[y][x + CHA_OFFSET], 0, 255),
CLIP(img->prow[y][x + CHB_OFFSET], 0, 255)
};
+ }else if(imgpalette){
+ int cha = img->prow[y][x + CHA_OFFSET];
+ int cbb = img->prow[y][x + CHB_OFFSET];
+ pix[x] = (png_color){
+ pal_row[cbb][cha].r,
+ pal_row[cbb][cha].g,
+ pal_row[cbb][cha].b
+ };
}else{
pix[x] = (png_color){
crow[y][x + skip + offset].r,
@@ -414,4 +491,4 @@ void closeWriter(){
png_write_end(rt_png_ptr, rt_info_ptr);
fclose(rt_pngfile);
png_destroy_write_struct(&rt_png_ptr, &rt_info_ptr);
-}
\ No newline at end of file
+}