New regression, only linear as per recommendation of the Users' Guidetags/v1.8.0
@@ -11,7 +11,7 @@ find_package(PNG) | |||||
# libsndfile | # libsndfile | ||||
find_package(LibSndFile) | find_package(LibSndFile) | ||||
set(LIB_C_SOURCE_FILES src/color.c src/dsp.c src/filter.c src/image.c src/libs/reg.c src/libs/median.c) | |||||
set(LIB_C_SOURCE_FILES src/color.c src/dsp.c src/filter.c src/image.c src/algebra.c src/libs/median.c) | |||||
set(EXE_C_SOURCE_FILES src/main.c src/pngio.c src/libs/argparse.c) | set(EXE_C_SOURCE_FILES src/main.c src/pngio.c src/libs/argparse.c) | ||||
set(LIB_C_HEADER_FILES src/apt.h) | set(LIB_C_HEADER_FILES src/apt.h) | ||||
@@ -0,0 +1,59 @@ | |||||
/* | |||||
* aptdec - A lightweight FOSS (NOAA) APT decoder | |||||
* Copyright (C) 2019-2022 Xerbo (xerbo@protonmail.com) | |||||
* | |||||
* This program 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 "algebra.h" | |||||
#include <math.h> | |||||
// Find the best linear equation to estimate the value of the | |||||
// dependent variable from the independent variable | |||||
linear_t linear_regression(const float *independent, const float *dependent, size_t len) { | |||||
// Calculate mean of the dependent and independent variables (this is the centoid) | |||||
float dependent_mean = 0.0f; | |||||
float independent_mean = 0.0f; | |||||
for (size_t i = 0; i < len; i++) { | |||||
dependent_mean += dependent[i] / (float)len; | |||||
independent_mean += independent[i] / (float)len; | |||||
} | |||||
// Calculate slope | |||||
float a = 0.0f; | |||||
{ | |||||
float a_numerator = 0.0f; | |||||
float a_denominator = 0.0f; | |||||
for (size_t i = 0; i < len; i++) { | |||||
a_numerator += (independent[i] - independent_mean) * (dependent[i] - dependent_mean); | |||||
a_denominator += powf(independent[i] - independent_mean, 2.0f); | |||||
} | |||||
a = a_numerator/a_denominator; | |||||
} | |||||
// We can now solve for the y-intercept since we know the slope | |||||
// and the centoid, which the line must pass through | |||||
float b = dependent_mean - a * independent_mean; | |||||
//printf("y(x) = %fx + %f\n", a, b); | |||||
return (linear_t){a, b}; | |||||
} | |||||
float linear_calc(float x, linear_t line) { | |||||
return x*line.a + line.b; | |||||
} | |||||
float quadratic_calc(float x, quadratic_t quadratic) { | |||||
return x*x*quadratic.a + x*quadratic.b + quadratic.c; | |||||
} |
@@ -0,0 +1,38 @@ | |||||
/* | |||||
* aptdec - A lightweight FOSS (NOAA) APT decoder | |||||
* Copyright (C) 2019-2022 Xerbo (xerbo@protonmail.com) | |||||
* | |||||
* This program 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/>. | |||||
*/ | |||||
#ifndef APTDEC_ALGEBRA_H | |||||
#define APTDEC_ALGEBRA_H | |||||
#include <stddef.h> | |||||
// A linear equation in the form of y(x) = ax + b | |||||
typedef struct { | |||||
float a, b; | |||||
} linear_t; | |||||
// A quadratic equation in the form of y(x) = ax^2 + bx + c | |||||
typedef struct { | |||||
float a, b, c; | |||||
} quadratic_t; | |||||
linear_t linear_regression(const float *independent, const float *dependent, size_t len); | |||||
float linear_calc(float x, linear_t line); | |||||
float quadratic_calc(float x, quadratic_t line); | |||||
#endif |
@@ -23,32 +23,14 @@ | |||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include "apt.h" | #include "apt.h" | ||||
#include "libs/reg.h" | |||||
#include "algebra.h" | |||||
#include "image.h" | #include "image.h" | ||||
#define REGORDER 3 | |||||
typedef struct { | |||||
double cf[REGORDER + 1]; | |||||
} rgparam_t; | |||||
// Compute regression | |||||
static void rgcomp(double x[16], rgparam_t * rgpr) { | |||||
static linear_t compute_regression(float *wedges) { | |||||
// { 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 }; | |||||
polyreg(REGORDER, 9, x, y, rgpr->cf); | |||||
} | |||||
// Convert a value to 0-255 based off the provided regression curve | |||||
static double rgcal(float x, rgparam_t *rgpr) { | |||||
double y, p; | |||||
int i; | |||||
const float teleramp[9] = { 31.07, 63.02, 94.96, 126.9, 158.86, 191.1, 228.62, 255.0, 0.0 }; | |||||
for (i = 0, y = 0.0, p = 1.0; i < REGORDER + 1; i++) { | |||||
y += rgpr->cf[i] * p; | |||||
p = p * x; | |||||
} | |||||
return y; | |||||
return linear_regression(wedges, teleramp, 9); | |||||
} | } | ||||
static double tele[16]; | static double tele[16]; | ||||
@@ -104,18 +86,18 @@ void apt_linearEnhance(float **prow, int nrow, int offset, int width){ | |||||
} | } | ||||
// 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, linear_t regr){ | |||||
offset -= APT_SYNC_WIDTH+APT_SPC_WIDTH; | offset -= APT_SYNC_WIDTH+APT_SPC_WIDTH; | ||||
for (int y = 0; y < nrow; y++) { | for (int y = 0; y < nrow; y++) { | ||||
for (int x = 0; x < width+APT_SYNC_WIDTH+APT_SPC_WIDTH+APT_TELE_WIDTH; x++) { | for (int x = 0; x < width+APT_SYNC_WIDTH+APT_SPC_WIDTH+APT_TELE_WIDTH; x++) { | ||||
float pv = (float)rgcal(prow[y][x + offset], ®r); | |||||
float pv = linear_calc(prow[y][x + offset], regr); | |||||
prow[y][x + offset] = CLIP(pv, 0, 255); | prow[y][x + offset] = CLIP(pv, 0, 255); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
double teleNoise(double wedges[16]){ | |||||
double teleNoise(float *wedges){ | |||||
double pattern[9] = { 31.07, 63.02, 94.96, 126.9, 158.86, 191.1, 228.62, 255.0, 0.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++) | ||||
@@ -127,8 +109,8 @@ double teleNoise(double wedges[16]){ | |||||
// Get telemetry data for thermal calibration | // Get telemetry data for thermal calibration | ||||
apt_channel_t apt_calibrate(float **prow, int nrow, int offset, int width) { | apt_channel_t apt_calibrate(float **prow, int nrow, int offset, int width) { | ||||
double teleline[APT_MAX_HEIGHT] = { 0.0 }; | double teleline[APT_MAX_HEIGHT] = { 0.0 }; | ||||
double wedge[16]; | |||||
rgparam_t regr[APT_MAX_HEIGHT/APT_FRAME_LEN + 1]; | |||||
float wedge[16]; | |||||
linear_t regr[APT_MAX_HEIGHT/APT_FRAME_LEN + 1]; | |||||
int telestart, mtelestart = 0; | int telestart, mtelestart = 0; | ||||
int channel = -1; | int channel = -1; | ||||
@@ -194,9 +176,9 @@ apt_channel_t apt_calibrate(float **prow, int nrow, int offset, int width) { | |||||
bestFrame = k; | bestFrame = k; | ||||
// Compute & apply regression on the wedges | // Compute & apply regression on the wedges | ||||
rgcomp(wedge, ®r[k]); | |||||
regr[k] = compute_regression(wedge); | |||||
for (int j = 0; j < 16; j++) | for (int j = 0; j < 16; j++) | ||||
tele[j] = (float)rgcal((float)wedge[j], ®r[k]); | |||||
tele[j] = linear_calc(wedge[j], regr[k]); | |||||
/* Compare the channel ID wedge to the reference | /* Compare the channel ID wedge to the reference | ||||
* wedges, the wedge with the closest match will | * wedges, the wedge with the closest match will | ||||
@@ -227,7 +209,7 @@ apt_channel_t apt_calibrate(float **prow, int nrow, int offset, int width) { | |||||
i++; | i++; | ||||
} | } | ||||
} | } | ||||
Cs = rgcal((float)(Cs / i), ®r[k]); | |||||
Cs = linear_calc((Cs / i), regr[k]); | |||||
} | } | ||||
} | } | ||||
@@ -1,143 +0,0 @@ | |||||
/* --------------------------------------------------------------------------- | |||||
Polynomial regression, freely adapted from: | |||||
NUMERICAL METHODS: C Programs, (c) John H. Mathews 1995 | |||||
Algorithm translated to C by: Dr. Norman Fahrer | |||||
NUMERICAL METHODS for Mathematics, Science and Engineering, 2nd Ed, 1992 | |||||
Prentice Hall, International Editions: ISBN 0-13-625047-5 | |||||
This free software is compliments of the author. | |||||
E-mail address: in%"mathews@fullerton.edu" | |||||
*/ | |||||
#include <math.h> | |||||
#include "reg.h" | |||||
#define DMAX 5 /* Maximum degree of polynomial */ | |||||
#define NMAX 10 /* Maximum number of points */ | |||||
static void FactPiv(int N, double A[DMAX][DMAX], double B[], double Cf[]); | |||||
void polyreg(const int M, const int N, const double X[], const double Y[], double C[]) { | |||||
int R, K, J; /* Loop counters */ | |||||
double A[DMAX][DMAX]; /* A */ | |||||
double B[DMAX]; | |||||
double P[2 * DMAX + 1]; | |||||
double x, y; | |||||
double p; | |||||
/* Zero the array */ | |||||
for (R = 0; R < M + 1; R++) | |||||
B[R] = 0; | |||||
/* Compute the column vector */ | |||||
for (K = 0; K < N; K++) { | |||||
y = Y[K]; | |||||
x = X[K]; | |||||
p = 1.0; | |||||
for (R = 0; R < M + 1; R++) { | |||||
B[R] += y * p; | |||||
p = p * x; | |||||
} | |||||
} | |||||
/* Zero the array */ | |||||
for (J = 1; J <= 2 * M; J++) | |||||
P[J] = 0; | |||||
P[0] = N; | |||||
/* Compute the sum of powers of x_(K-1) */ | |||||
for (K = 0; K < N; K++) { | |||||
x = X[K]; | |||||
p = X[K]; | |||||
for (J = 1; J <= 2 * M; J++) { | |||||
P[J] += p; | |||||
p = p * x; | |||||
} | |||||
} | |||||
/* Determine the matrix entries */ | |||||
for (R = 0; R < M + 1; R++) { | |||||
for (K = 0; K < M + 1; K++) | |||||
A[R][K] = P[R + K]; | |||||
} | |||||
/* Solve the linear system of M + 1 equations: A*C = B | |||||
for the coefficient vector C = (c_1,c_2,..,c_M,c_(M+1)) */ | |||||
FactPiv(M + 1, A, B, C); | |||||
} /* end main */ | |||||
/*--------------------------------------------------------*/ | |||||
static void FactPiv(int N, double A[DMAX][DMAX], double B[], double Cf[]) { | |||||
int K, P, C, J; /* Loop counters */ | |||||
int Row[NMAX]; /* Field with row-number */ | |||||
double X[DMAX], Y[DMAX]; | |||||
double SUM, DET = 1.0; | |||||
int T; | |||||
/* Initialize the pointer vector */ | |||||
for (J = 0; J < NMAX; J++) | |||||
Row[J] = J; | |||||
/* Start LU factorization */ | |||||
for (P = 0; P < N - 1; P++) { | |||||
/* Find pivot element */ | |||||
for (K = P + 1; K < N; K++) { | |||||
if (fabs(A[Row[K]][P]) > fabs(A[Row[P]][P])) { | |||||
/* Switch the index for the p-1 th pivot row if necessary */ | |||||
T = Row[P]; | |||||
Row[P] = Row[K]; | |||||
Row[K] = T; | |||||
DET = -DET; | |||||
} | |||||
} /* End of simulated row interchange */ | |||||
if (A[Row[P]][P] == 0) { | |||||
/* The matrix is SINGULAR! */ | |||||
return; | |||||
} | |||||
/* Multiply the diagonal elements */ | |||||
DET = DET * A[Row[P]][P]; | |||||
/* Form multiplier */ | |||||
for (K = P + 1; K < N; K++) { | |||||
A[Row[K]][P] = A[Row[K]][P] / A[Row[P]][P]; | |||||
/* Eliminate X_(p-1) */ | |||||
for (C = P + 1; C < N + 1; C++) { | |||||
A[Row[K]][C] -= A[Row[K]][P] * A[Row[P]][C]; | |||||
} | |||||
} | |||||
} | |||||
/* End of L*U factorization routine */ | |||||
DET = DET * A[Row[N - 1]][N - 1]; | |||||
/* Start the forward substitution */ | |||||
for (K = 0; K < N; K++) | |||||
Y[K] = B[K]; | |||||
Y[0] = B[Row[0]]; | |||||
for (K = 1; K < N; K++) { | |||||
SUM = 0; | |||||
for (C = 0; C <= K - 1; C++) | |||||
SUM += A[Row[K]][C] * Y[C]; | |||||
Y[K] = B[Row[K]] - SUM; | |||||
} | |||||
/* Start the back substitution */ | |||||
X[N - 1] = Y[N - 1] / A[Row[N - 1]][N - 1]; | |||||
for (K = N - 2; K >= 0; K--) { | |||||
SUM = 0; | |||||
for (C = K + 1; C < N; C++) { | |||||
SUM += A[Row[K]][C] * X[C]; | |||||
} | |||||
X[K] = (Y[K] - SUM) / A[Row[K]][K]; | |||||
} /* End of back substitution */ | |||||
/* Output */ | |||||
for (K = 0; K < N; K++) | |||||
Cf[K] = X[K]; | |||||
} |
@@ -1 +0,0 @@ | |||||
void polyreg(const int M, const int N, const double X[], const double Y[], double C[]); |