@@ -15,7 +15,6 @@ Aptdec can turn the audio recordings into PNG images and generate images such as | |||||
- Individual channel: one of the channels form the image | - Individual channel: one of the channels form the image | ||||
- Temperature image: a temperature compensated image derived from the IR channel | - Temperature image: a temperature compensated image derived from the IR channel | ||||
- Palleted image: a image where the color is derived from a palette (false color, etc) | - Palleted image: a image where the color is derived from a palette (false color, etc) | ||||
- MCIR: a false color that uses a underlay map for color | |||||
The input audio format can be anything supported by `libsndfile` (although only tested with WAV and FLAC). Sample rate doesn't matter, although lower samples rates will process faster. | The input audio format can be anything supported by `libsndfile` (although only tested with WAV and FLAC). Sample rate doesn't matter, although lower samples rates will process faster. | ||||
@@ -71,11 +70,9 @@ Apply a falsecolor palette | |||||
-o <path> Output filename | -o <path> Output filename | ||||
-d <path> Destination directory | -d <path> Destination directory | ||||
-s (15-19) Satellite number | -s (15-19) Satellite number | ||||
-m <path> Path to WXtoImg map | |||||
-p <path> Path to palette | -p <path> Path to palette | ||||
-r Realtime decode | -r Realtime decode | ||||
-g Gamma adjustment (1.0 = off) | -g Gamma adjustment (1.0 = off) | ||||
-k Map offset (in px, default: 0) | |||||
``` | ``` | ||||
### Image output types | ### Image output types | ||||
@@ -84,7 +81,6 @@ Apply a falsecolor palette | |||||
- `a`: Channel A | - `a`: Channel A | ||||
- `b`: Channel B | - `b`: Channel B | ||||
- `t`: Temperature | - `t`: Temperature | ||||
- `m`: MCIR (Map Color InfraRed) | |||||
- `p`: Palleted | - `p`: Palleted | ||||
### Post-Processing Effects | ### Post-Processing Effects | ||||
@@ -31,18 +31,15 @@ typedef struct { | |||||
char *type; // Output image type | char *type; // Output image type | ||||
char *effects; // Effects on the image | char *effects; // Effects on the image | ||||
int satnum; // The satellite number | int satnum; // The satellite number | ||||
char *map; // Path to a map file | |||||
char *path; // Output directory | char *path; // Output directory | ||||
int realtime; // Realtime decoding | int realtime; // Realtime decoding | ||||
char *filename; // Output filename | char *filename; // Output filename | ||||
char *palette; // Filename of palette | char *palette; // Filename of palette | ||||
float gamma; // Gamma | float gamma; // Gamma | ||||
int mapOffset; | |||||
} options_t; | } options_t; | ||||
enum imagetypes { | enum imagetypes { | ||||
Raw_Image='r', | Raw_Image='r', | ||||
MCIR='m', | |||||
Palleted='p', | Palleted='p', | ||||
Temperature='t', | Temperature='t', | ||||
Channel_A='a', | Channel_A='a', | ||||
@@ -67,7 +67,16 @@ static char *basename(char *path) | |||||
#endif | #endif | ||||
int main(int argc, const char **argv) { | int main(int argc, const char **argv) { | ||||
options_t opts = { "r", "", 19, "", ".", 0, "", "", 1.0, 0 }; | |||||
options_t opts = { | |||||
.type = "r", | |||||
.effects = "", | |||||
.satnum = 19, | |||||
.path = ".", | |||||
.realtime = 0, | |||||
.filename = "", | |||||
.palette = "", | |||||
.gamma = 1.0 | |||||
}; | |||||
static const char *const usages[] = { | static const char *const usages[] = { | ||||
"aptdec [options] [[--] sources]", | "aptdec [options] [[--] sources]", | ||||
@@ -87,13 +96,11 @@ int main(int argc, const char **argv) { | |||||
OPT_GROUP("Paths"), | OPT_GROUP("Paths"), | ||||
OPT_STRING('p', "palette", &opts.palette, "path to a palette", NULL, 0, 0), | OPT_STRING('p', "palette", &opts.palette, "path to a palette", NULL, 0, 0), | ||||
OPT_STRING('m', "map", &opts.map, "path to a WXtoImg map", NULL, 0, 0), | |||||
OPT_STRING('o', "filename", &opts.filename, "filename of the output image", NULL, 0, 0), | OPT_STRING('o', "filename", &opts.filename, "filename of the output image", NULL, 0, 0), | ||||
OPT_STRING('d', "output", &opts.path, "output directory (must exist first)", NULL, 0, 0), | OPT_STRING('d', "output", &opts.path, "output directory (must exist first)", NULL, 0, 0), | ||||
OPT_GROUP("Misc"), | OPT_GROUP("Misc"), | ||||
OPT_BOOLEAN('r', "realtime", &opts.realtime, "decode in realtime", NULL, 0, 0), | OPT_BOOLEAN('r', "realtime", &opts.realtime, "decode in realtime", NULL, 0, 0), | ||||
OPT_INTEGER('k', "map-offset", &opts.mapOffset, "Map offset (in px, default 0)", NULL, 0, 0), | |||||
OPT_END(), | OPT_END(), | ||||
}; | }; | ||||
@@ -182,13 +189,6 @@ static int processAudio(char *filename, options_t *opts){ | |||||
printf("Total rows: %d\n", img.nrow); | printf("Total rows: %d\n", img.nrow); | ||||
// Fallback for detecting the zenith | |||||
// TODO: encode metadata in raw images | |||||
if(opts->map != NULL && opts->map[0] != '\0' && img.zenith == 0){ | |||||
warning("Guessing zenith in image, map will most likely be misaligned"); | |||||
img.zenith = img.nrow / 2; | |||||
} | |||||
// Calibrate | // Calibrate | ||||
img.chA = apt_calibrate(img.prow, img.nrow, APT_CHA_OFFSET, APT_CH_WIDTH); | img.chA = apt_calibrate(img.prow, img.nrow, APT_CHA_OFFSET, APT_CH_WIDTH); | ||||
img.chB = apt_calibrate(img.prow, img.nrow, APT_CHB_OFFSET, APT_CH_WIDTH); | img.chB = apt_calibrate(img.prow, img.nrow, APT_CHB_OFFSET, APT_CH_WIDTH); | ||||
@@ -240,10 +240,6 @@ static int processAudio(char *filename, options_t *opts){ | |||||
ImageOut(opts, &tmpimg, APT_CHA_OFFSET, APT_CH_WIDTH, "Visible", Visible, (char *)apt_TempPalette); | ImageOut(opts, &tmpimg, APT_CHA_OFFSET, APT_CH_WIDTH, "Visible", Visible, (char *)apt_TempPalette); | ||||
} | } | ||||
// MCIR | |||||
if (CONTAINS(opts->type, MCIR)) | |||||
ImageOut(opts, &img, APT_CHA_OFFSET, APT_CH_WIDTH, "MCIR", MCIR, NULL); | |||||
// Linear equalise | // Linear equalise | ||||
if(CONTAINS(opts->effects, Linear_Equalise)){ | if(CONTAINS(opts->effects, Linear_Equalise)){ | ||||
apt_linearEnhance(img.prow, img.nrow, APT_CHA_OFFSET, APT_CH_WIDTH); | apt_linearEnhance(img.prow, img.nrow, APT_CHA_OFFSET, APT_CH_WIDTH); | ||||
@@ -27,121 +27,6 @@ | |||||
#include "pngio.h" | #include "pngio.h" | ||||
int mapOverlay(char *filename, apt_rgb_t **crow, int nrow, int zenith, int MCIR) { | |||||
FILE *fp = fopen(filename, "rb"); | |||||
if(!fp) { | |||||
error_noexit("Cannot open map"); | |||||
return 0; | |||||
} | |||||
// Create reader | |||||
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | |||||
if(!png) { | |||||
fclose(fp); | |||||
return 0; | |||||
} | |||||
png_infop info = png_create_info_struct(png); | |||||
if(!info) { | |||||
fclose(fp); | |||||
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 != 1040){ | |||||
error_noexit("Map must be 1040px wide"); | |||||
return 0; | |||||
}else if(bit_depth != 16){ | |||||
error_noexit("Map must be 16 bit color"); | |||||
return 0; | |||||
}else if(color_type != PNG_COLOR_TYPE_RGB){ | |||||
error_noexit("Map must be RGB"); | |||||
return 0; | |||||
}else if(zenith > height/2 || nrow-zenith > height/2){ | |||||
warning("Map is too short to cover entire image"); | |||||
} | |||||
// Create row buffers | |||||
png_bytep *mapRows = NULL; | |||||
mapRows = (png_bytep *) malloc(sizeof(png_bytep) * height); | |||||
for(int y = 0; y < height; y++) | |||||
mapRows[y] = (png_byte *) malloc(png_get_rowbytes(png, info)); | |||||
// Read image | |||||
png_read_image(png, mapRows); | |||||
// Tidy up | |||||
fclose(fp); | |||||
png_destroy_read_struct(&png, &info, NULL); | |||||
printf("Adding map overlay\n"); | |||||
// Map overlay / MCIR / Precipitation | |||||
int mapOffset = (height/2)-zenith; | |||||
for(int y = 0; y < nrow; y++) { | |||||
for(int x = 49; x < width - 82; x++){ | |||||
// Maps are 16 bit / channel | |||||
png_bytep px = &mapRows[CLIP(y + mapOffset, 0, height-1)][x * 6]; | |||||
apt_rgb_t map = { | |||||
(px[0] << 8) | px[1], | |||||
(px[2] << 8) | px[3], | |||||
(px[4] << 8) | px[5] | |||||
}; | |||||
// Pixel offsets | |||||
int chb = x + APT_CHB_OFFSET - 49; | |||||
int cha = x + 36; | |||||
// Fill in map | |||||
if(MCIR){ | |||||
if(map.b < 128 && map.g > 128){ | |||||
// Land | |||||
float darken = ((255-crow[y][chb].r)-100)/50; | |||||
float green = CLIP(map.g/300, 0, 1); | |||||
float blue = 0.15 - CLIP(map.b/960.0, 0, 1); | |||||
crow[y][cha] = (apt_rgb_t){blue*1000*darken, green*98*darken, blue*500.0*darken}; | |||||
}else{ | |||||
// Sea | |||||
crow[y][cha] = (apt_rgb_t){9, 17, 74}; | |||||
} | |||||
} | |||||
// Color -> alpha: composite | |||||
int composite = map.r + map.g + map.b; | |||||
// Color -> alpha: flattern and convert to 8 bits / channel | |||||
float factor = (255 * 255 * 2.0) / composite; | |||||
map.r *= factor/257.0; map.g *= factor/257.0; map.b *= factor/257.0; | |||||
// Color -> alpha: convert black to alpha | |||||
float alpha = CLIP(composite / 65535.0, 0, 1); | |||||
// Clip | |||||
map.r = CLIP(map.r, 0, 255.0); | |||||
map.g = CLIP(map.g, 0, 255.0); | |||||
map.b = CLIP(map.b, 0, 255.0); | |||||
// Map overlay on channel A | |||||
crow[y][cha] = apt_RGBcomposite(map, alpha, crow[y][cha], 1); | |||||
// Map overlay on channel B | |||||
if(!MCIR) | |||||
crow[y][chb] = apt_RGBcomposite(map, alpha, crow[y][chb], 1); | |||||
// Cloud overlay on channel A | |||||
if(MCIR){ | |||||
float cloud = CLIP((crow[y][chb].r - 113) / 90.0, 0, 1); | |||||
crow[y][cha] = apt_RGBcomposite((apt_rgb_t){255, 250, 245}, cloud, crow[y][cha], 1); | |||||
} | |||||
} | |||||
} | |||||
return 1; | |||||
} | |||||
int readRawImage(char *filename, float **prow, int *nrow) { | int readRawImage(char *filename, float **prow, int *nrow) { | ||||
FILE *fp = fopen(filename, "rb"); | FILE *fp = fopen(filename, "rb"); | ||||
printf("%s", filename); | printf("%s", filename); | ||||
@@ -210,14 +95,8 @@ int readRawImage(char *filename, float **prow, int *nrow) { | |||||
int readPalette(char *filename, apt_rgb_t **pixels) { | int readPalette(char *filename, apt_rgb_t **pixels) { | ||||
FILE *fp = fopen(filename, "rb"); | FILE *fp = fopen(filename, "rb"); | ||||
if(!fp) { | if(!fp) { | ||||
char buffer[1024]; | |||||
// PALETTE_DIR is set through CMake | |||||
sprintf(buffer, PALETTE_DIR"/%s", filename); | |||||
fp = fopen(buffer, "rb"); | |||||
if(!fp){ | |||||
error_noexit("Cannot open palette"); | |||||
return 0; | |||||
} | |||||
error_noexit("Cannot open palette"); | |||||
return 0; | |||||
} | } | ||||
// Create reader | // Create reader | ||||
@@ -334,9 +213,6 @@ int ImageOut(options_t *opts, apt_image_t *img, int offset, int width, char *des | |||||
case Temperature: | case Temperature: | ||||
greyscale = 0; | greyscale = 0; | ||||
break; | break; | ||||
case MCIR: | |||||
greyscale = 0; | |||||
break; | |||||
case Raw_Image: break; | case Raw_Image: break; | ||||
case Channel_A: break; | case Channel_A: break; | ||||
case Channel_B: break; | case Channel_B: break; | ||||
@@ -368,10 +244,6 @@ int ImageOut(options_t *opts, apt_image_t *img, int offset, int width, char *des | |||||
} | } | ||||
} | } | ||||
if(opts->map != NULL && opts->map[0] != '\0'){ | |||||
greyscale = 0; | |||||
} | |||||
FILE *pngfile; | FILE *pngfile; | ||||
// Create writer | // Create writer | ||||
@@ -433,17 +305,6 @@ int ImageOut(options_t *opts, apt_image_t *img, int offset, int width, char *des | |||||
} | } | ||||
} | } | ||||
// Map stuff | |||||
if(opts->map != NULL && opts->map[0] != '\0'){ | |||||
if(!mapOverlay(opts->map, crow, img->nrow, img->zenith+opts->mapOffset, CONTAINS(opts->type, MCIR))){ | |||||
warning("Skipping MCIR generation"); | |||||
return 0; | |||||
} | |||||
}else if(CONTAINS(opts->type, MCIR)){ | |||||
warning("Skipping MCIR generation; no map provided"); | |||||
return 0; | |||||
} | |||||
printf("Writing %s", outName); | printf("Writing %s", outName); | ||||
// Float power macro (for gamma adjustment) | // Float power macro (for gamma adjustment) | ||||
@@ -1,7 +1,6 @@ | |||||
#include "apt.h" | #include "apt.h" | ||||
#include "common.h" | #include "common.h" | ||||
int mapOverlay(char *filename, apt_rgb_t **crow, int nrow, int zenith, int MCIR); | |||||
int readRawImage(char *filename, float **prow, int *nrow); | int readRawImage(char *filename, float **prow, int *nrow); | ||||
int readPalette(char *filename, apt_rgb_t **pixels); | int readPalette(char *filename, apt_rgb_t **pixels); | ||||
void prow2crow(float **prow, int nrow, char *palette, apt_rgb_t **crow); | void prow2crow(float **prow, int nrow, char *palette, apt_rgb_t **crow); | ||||