Browse Source

Rewrite `ImageOut`, remove channel ID in filename

tags/v1.8.0
Xerbo 4 years ago
parent
commit
aee200e304
4 changed files with 144 additions and 103 deletions
  1. +2
    -1
      README.md
  2. +20
    -1
      common.h
  3. +21
    -23
      main.c
  4. +101
    -78
      pngio.c

+ 2
- 1
README.md View File

@@ -87,7 +87,8 @@ 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:

- `r` for raw images
- Sensor ID (`1`, `2`, `3A`, `3B`, `4`, `5`) for channel A|B images
- `a` channel A images
- `b` channel B images
- `p` for a paletted image
- `t` for temperature calibrated images
- `m` for MCIR images


+ 20
- 1
common.h View File

@@ -32,6 +32,7 @@ typedef struct {
typedef struct {
float *prow[MAX_HEIGHT]; // Row buffers
int nrow; // Number of rows
int zenith;
int chA, chB; // ID of each channel
char name[256]; // Stripped filename
char *palette; // Filename of palette
@@ -45,4 +46,22 @@ typedef struct {
int realtime; // Realtime decoding
char *filename; // Output filename
char *palette; // Filename of palette
} options_t;
} options_t;

enum imagetypes {
Raw_Image='r',
MCIR='m',
Palleted='p',
Temperature='t',
Channel_A='a',
Channel_B='b',
Distribution='d'
};
enum effects {
Crop_Telemetry='t',
Histogram_Equalise='h',
Denoise='d',
Precipitation_Overlay='p',
Flip_Image='f',
Linear_Equalise='l'
};

+ 21
- 23
main.c View File

@@ -54,8 +54,6 @@ extern void flipImage(image_t *img, int width, int offset);
extern char GviPalette[256*3];
extern char TempPalette[256*3];

// Row where the satellite is closest to the observer
int zenith = 0;
// Audio file
static SNDFILE *audioFile;
// Number of channels in audio file
@@ -174,7 +172,7 @@ static int processAudio(char *filename, options_t *opts){
img.prow[img.nrow] = (float *) malloc(sizeof(float) * 2150);

// Write into memory and break the loop when there are no more samples to read
if (getpixelrow(img.prow[img.nrow], img.nrow, &zenith, (img.nrow == 0)) == 0)
if (getpixelrow(img.prow[img.nrow], img.nrow, &img.zenith, (img.nrow == 0)) == 0)
break;

if(opts->realtime) pushRow(img.prow[img.nrow], IMG_WIDTH);
@@ -193,9 +191,9 @@ static int processAudio(char *filename, options_t *opts){

// Fallback for detecting the zenith
// TODO: encode metadata in raw images
if(opts->map != NULL && opts->map[0] != '\0' && zenith == 0){
if(opts->map != NULL && opts->map[0] != '\0' && img.zenith == 0){
fprintf(stderr, "Guessing zenith in image, map will most likely be misaligned.\n");
zenith = img.nrow / 2;
img.zenith = img.nrow / 2;
}

// Calibrate
@@ -205,67 +203,67 @@ static int processAudio(char *filename, options_t *opts){
printf("Channel B: %s (%s)\n", ch.id[img.chB], ch.name[img.chB]);

// Denoise
if(CONTAINS(opts->effects, 'd')){
if(CONTAINS(opts->effects, Denoise)){
denoise(img.prow, img.nrow, CHA_OFFSET, CH_WIDTH);
denoise(img.prow, img.nrow, CHB_OFFSET, CH_WIDTH);
}

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

// Temperature
if (CONTAINS(opts->type, 't') && img.chB >= 4) {
if (CONTAINS(opts->type, Temperature) && img.chB >= 4) {
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", Temperature, (char *)TempPalette);
}

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

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

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

// Raw image
if (CONTAINS(opts->type, 'r')) {
if (CONTAINS(opts->type, Raw_Image)) {
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);
ImageOut(opts, &img, 0, IMG_WIDTH, desc, Raw_Image, NULL);
}

// Palette image
if (CONTAINS(opts->type, 'p')) {
if (CONTAINS(opts->type, Palleted)) {
img.palette = opts->palette;
strcpy(desc, "Palette composite");
ImageOut(opts, &img, 0, 909, desc, "p", NULL);
ImageOut(opts, &img, CHA_OFFSET, 909, desc, Palleted, NULL);
}

// Channel A
if (CONTAINS(opts->type, 'a')) {
if (CONTAINS(opts->type, Channel_A)) {
sprintf(desc, "%s (%s)", ch.id[img.chA], ch.name[img.chA]);
ImageOut(opts, &img, CHA_OFFSET, CH_WIDTH, desc, ch.id[img.chA], NULL);
ImageOut(opts, &img, CHA_OFFSET, CH_WIDTH, desc, Channel_A, NULL);
}

// Channel B
if (CONTAINS(opts->type, 'b')) {
if (CONTAINS(opts->type, Channel_B)) {
sprintf(desc, "%s (%s)", ch.id[img.chB], ch.name[img.chB]);
ImageOut(opts, &img, CHB_OFFSET, CH_WIDTH, desc, ch.id[img.chB], NULL);
ImageOut(opts, &img, CHB_OFFSET, CH_WIDTH, desc, Channel_B, NULL);
}

// Value distribution image
if (CONTAINS(opts->type, 'd'))
distrib(opts, &img, "d");
if (CONTAINS(opts->type, Distribution))
distrib(opts, &img, Distribution);

return 1;
}


+ 101
- 78
pngio.c View File

@@ -26,7 +26,6 @@
#include "common.h"
#include "offsets.h"

extern int zenith;
extern char PrecipPalette[256*3];
extern rgb_t applyPalette(char *palette, int val);
extern rgb_t RGBcomposite(rgb_t top, float top_a, rgb_t bottom, float bottom_a);
@@ -193,7 +192,7 @@ int readRawImage(char *filename, float **prow, int *nrow) {
return 1;
}

int readPalette(char *filename, rgb_t **crow) {
int readPalette(char *filename, rgb_t **pixels) {
FILE *fp = fopen(filename, "r");
if(!fp) {
fprintf(stderr, "Cannot open %s\n", filename);
@@ -222,7 +221,7 @@ int readPalette(char *filename, rgb_t **crow) {
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");
fprintf(stderr, "Palette must be RGBA.\n");
return 0;
}

@@ -241,10 +240,10 @@ int readPalette(char *filename, rgb_t **crow) {

// Put into crow
for(int y = 0; y < height; y++) {
crow[y] = (rgb_t *) malloc(sizeof(rgb_t) * width);
pixels[y] = (rgb_t *) malloc(sizeof(rgb_t) * width);

for(int x = 0; x < width; x++)
crow[y][x] = (rgb_t){
pixels[y][x] = (rgb_t){
PNGrows[y][x*4],
PNGrows[y][x*4 + 1],
PNGrows[y][x*4 + 2]
@@ -254,35 +253,92 @@ int readPalette(char *filename, rgb_t **crow) {
return 1;
}

png_text meta[] = {
{PNG_TEXT_COMPRESSION_NONE, "Software", VERSION},
{PNG_TEXT_COMPRESSION_NONE, "Channel", "Unknown", 7},
{PNG_TEXT_COMPRESSION_NONE, "Description", "NOAA satellite image", 20}
};
void prow2crow(float **prow, int nrow, char palette, rgb_t **crow){
for(int y = 0; y < nrow; y++){
crow[y] = (rgb_t *) malloc(sizeof(rgb_t) * IMG_WIDTH);

int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, char *chid, char *palette){
char outName[384];
for(int x = 0; x < IMG_WIDTH; x++){
if(palette == NULL)
crow[y][x].r = crow[y][x].g = crow[y][x].b = prow[y][x];
else
crow[y][x] = applyPalette(palette, prow[y][x]);
}
}
}

int applyUserPalette(float **prow, int nrow, char *filename, rgb_t **crow){
rgb_t *pal_row[256];
if(!readPalette(filename, pal_row)){
fprintf(stderr, "Could not read palette");
return 0;
}

for(int y = 0; y < nrow; y++){
for(int x = 0; x < CH_WIDTH; x++){
int cha = prow[y][x + CHA_OFFSET];
int cbb = prow[y][x + CHB_OFFSET];
crow[y][x + CHA_OFFSET] = pal_row[cbb][cha];
}
}
return 1;
}

int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, char chid, char *palette){
char outName[512];
if(opts->filename == NULL || opts->filename[0] == '\0'){
sprintf(outName, "%s/%s-%s.png", opts->path, img->name, chid);
sprintf(outName, "%s/%s-%c.png", opts->path, img->name, chid);
}else{
sprintf(outName, "%s/%s", opts->path, opts -> filename);
sprintf(outName, "%s/%s", opts->path, opts->filename);
}

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

FILE *pngfile;
png_text meta[] = {
{PNG_TEXT_COMPRESSION_NONE, "Software", VERSION},
{PNG_TEXT_COMPRESSION_NONE, "Channel", desc, sizeof(desc)},
{PNG_TEXT_COMPRESSION_NONE, "Description", "NOAA satellite image", 20}
};

// Parse image type
int greyscale = 1;
switch (chid){
case Palleted:
greyscale = 0;
break;
case Temperature:
greyscale = 0;
break;
case MCIR:
greyscale = 0;
break;
case Raw_Image: break;
case Channel_A: break;
case Channel_B: break;
}

// Reduce the width of the image to componsate for the missing telemetry
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;
// Parse effects
int crop_telemetry = 0;
for(int i = 0; i < strlen(opts->effects); i++){
switch (opts->effects[i]) {
case Crop_Telemetry:
width -= TOTAL_TELE;
offset += SYNC_WIDTH + SPC_WIDTH;
crop_telemetry = 1;
break;
case Precipitation_Overlay:
greyscale = 0;
break;
case Flip_Image: break;
case Denoise: break;
case Histogram_Equalise: break;
case Linear_Equalise: break;
default:
fprintf(stderr, "NOTICE: Unrecognised effect, \"%c\"\n", opts->effects[i]);
break;
}
}

FILE *pngfile;

// Create writer
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr) {
@@ -297,9 +353,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' && !imgpalette){
greyscale = 1;

if(greyscale){
// Greyscale image
png_set_IHDR(png_ptr, info_ptr, width, img->nrow,
8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE,
@@ -323,52 +377,39 @@ int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, c
png_init_io(png_ptr, pngfile);
png_write_info(png_ptr, info_ptr);

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

for(int x = 0; x < IMG_WIDTH; x++){
if(palette == NULL)
crow[y][x].r = crow[y][x].g = crow[y][x].b = img->prow[y][x];
else
crow[y][x] = applyPalette(palette, img->prow[y][x]);
}
}
}
// Apply a user provided color palette
if(CONTAINS(opts->type, Palleted))
applyUserPalette(img->prow, img->nrow, opts->palette, crow);

// Precipitation
// TODO: use temperature calibration
if(CONTAINS(opts->effects, 'p')){
// TODO: use temperature calibration, and a better palette
if(CONTAINS(opts->effects, Precipitation_Overlay)){
for(int y = 0; y < img->nrow; y++){
for(int x = 0; x < CH_WIDTH; x++){
if(img->prow[y][x + CHB_OFFSET] > 191)
crow[y][x + CHB_OFFSET] = applyPalette(PrecipPalette, img->prow[y][x + CHB_OFFSET]);
crow[y][x + CHB_OFFSET] = crow[y][x + CHA_OFFSET] = applyPalette(PrecipPalette, img->prow[y][x + CHB_OFFSET]);
}
}
}

// Map stuff
if(opts->map != NULL && opts->map[0] != '\0'){
if(mapOverlay(opts->map, crow, img->nrow, zenith, (chid[0] == 'm')) == 0){
if(!mapOverlay(opts->map, crow, img->nrow, img->zenith, CONTAINS(opts->type, MCIR))){
fprintf(stderr, "Skipping MCIR generation.\n");
return 0;
}
}else if(chid[0] == 'm'){
}else if(CONTAINS(opts->type, MCIR)){
fprintf(stderr, "Skipping MCIR generation; no map provided.\n");
return 0;
}

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
@@ -376,33 +417,12 @@ int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, c

int skip = 0;
for (int x = 0; x < width; x++) {
if(skiptele){
switch (x) {
case 0:
skip += SYNC_WIDTH + SPC_WIDTH;
break;
case CH_WIDTH:
skip += TELE_WIDTH + SYNC_WIDTH + SPC_WIDTH;
break;
}
if(crop_telemetry && x == CH_WIDTH){
skip += TELE_WIDTH + SYNC_WIDTH + SPC_WIDTH;
}

if(greyscale){
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 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,
@@ -437,8 +457,11 @@ int initWriter(options_t *opts, image_t *img, int width, int height, char *desc,
char outName[384];
sprintf(outName, "%s/%s-%s.png", opts->path, img->name, chid);

meta[1].text = desc;
meta[1].text_length = sizeof(desc);
png_text meta[] = {
{PNG_TEXT_COMPRESSION_NONE, "Software", VERSION},
{PNG_TEXT_COMPRESSION_NONE, "Channel", desc, sizeof(desc)},
{PNG_TEXT_COMPRESSION_NONE, "Description", "NOAA satellite image", 20}
};

// Create writer
rt_png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);


Loading…
Cancel
Save