summaryrefslogtreecommitdiff
path: root/ccast/filt.c
diff options
context:
space:
mode:
Diffstat (limited to 'ccast/filt.c')
-rw-r--r--ccast/filt.c531
1 files changed, 531 insertions, 0 deletions
diff --git a/ccast/filt.c b/ccast/filt.c
new file mode 100644
index 0000000..ab075ae
--- /dev/null
+++ b/ccast/filt.c
@@ -0,0 +1,531 @@
+
+/*
+ * Argyll Color Correction System
+ * ChromCast up filter test code.
+ *
+ * Author: Graeme W. Gill
+ * Date: 28/8/2014
+ *
+ * Copyright 2014 Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License2.txt file for licencing details.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <sys/types.h>
+#include <time.h>
+#include "copyright.h"
+#include "aconfig.h"
+#ifndef SALONEINSTLIB
+#include "numlib.h"
+#else
+#include "numsup.h"
+#endif
+#include "yajl.h"
+#include "conv.h"
+#include "base64.h"
+#include "ccmdns.h"
+#include "ccpacket.h"
+#include "ccmes.h"
+#include "yajl.h"
+
+#define DO_WEIGHTING
+#define SUM_CONSTRAINT
+
+//#define VERT 1 /* 1 for vertical */
+
+#ifndef DBL_PI
+# define DBL_PI 3.1415926535897932384626433832795
+#endif
+
+double lanczos3(double wi, double x) {
+ double y;
+
+ x = fabs(1.0 * x/wi);
+ if (x >= 3.0)
+ return 0.0;
+ if (x < 1e-6)
+ return 1.0;
+ y = sin(DBL_PI * x)/(DBL_PI * x) * sin(DBL_PI * x/3.0)/(DBL_PI * x/3.0);
+
+ return y;
+}
+
+double lanczos2(double wi, double x) {
+ double y;
+
+ x = fabs(1.0 * x/wi);
+ if (x >= 2.0)
+ return 0.0;
+ if (x < 1e-6)
+ return 1.0;
+ y = sin(DBL_PI * x)/(DBL_PI * x) * sin(DBL_PI * x/2.0)/(DBL_PI * x/2.0);
+
+ return y;
+}
+
+double in[2][72] = {
+{ // Horizontal
+255, 0, 0, 0, 0, 0, 0, 0, 0,
+255, 0, 0, 0, 0, 0, 0, 0, 0,
+254, 0, 0, 0, 0, 0, 0, 0, 0,
+254, 0, 0, 0, 0, 0, 0, 0, 0,
+253, 0, 0, 0, 0, 0, 0, 0, 0,
+253, 0, 0, 0, 0, 0, 0, 0, 0,
+252, 0, 0, 0, 0, 0, 0, 0, 0,
+252, 0, 0, 0, 0, 0, 0, 0, 0
+}, { // Vertical slice input target
+255, 0, 0, 0, 0, 0, 0, 0, 0,
+255, 0, 0, 0, 0, 0, 0, 0, 0,
+254, 0, 0, 0, 0, 0, 0, 0, 0,
+254, 0, 0, 0, 0, 0, 0, 0, 0,
+253, 0, 0, 0, 0, 0, 0, 0, 0,
+253, 0, 0, 0, 0, 0, 0, 0, 0,
+252, 0, 0, 0, 0, 0, 0, 0, 0,
+252, 0, 0, 0, 0, 0, 0, 0, 0
+} };
+
+#ifdef NEVER
+double out[2][] = {
+{ // Horizontal
+170, 110, 23, 5, 15, 17, 16, 16, 16, 17, 10, 6, 57,
+151, 153, 61, 7, 10, 16, 16, 16, 16, 17, 15, 5, 22,
+107, 169, 109, 23, 5, 15, 17, 16, 16, 16, 17, 10, 6, 57,
+150, 152, 61, 7, 10, 16, 16, 16, 16, 17, 15, 5, 22,
+107, 168, 109, 23, 5, 15, 17, 16, 16, 16, 17, 10, 6, 56,
+149, 151, 61, 7, 10, 16, 16, 16, 16, 17, 15, 6, 22,
+106, 167, 108, 23, 5, 15, 17, 16, 16, 16, 17, 10, 6, 56,
+148, 150
+},
+{ // Vertical slice target output
+235, 100, 0, 16, 16, 16, 16, 16, 16, 16, 20, 1, 13,
+191, 194, 19, 0, 20, 16, 16, 16, 16, 16, 16, 16, 0, 95,
+234, 99, 0, 16, 16, 16, 16, 16, 16, 16, 20, 1, 13,
+190, 194, 19, 0, 20, 16, 16, 16, 16, 16, 16, 16, 0, 94,
+233, 99, 0, 16, 16, 16, 16, 16, 16, 16, 20, 1, 13,
+189, 193, 19, 0, 20, 16, 16, 16, 16, 16, 16, 16, 0, 94,
+232, 99, 0, 16, 16, 16, 16, 16, 16, 16, 20, 1, 13,
+188, 192
+}
+};
+#else
+//#define N1 -9
+//#define N2 -8
+#define N1 0
+#define N2 0
+
+//#define N3 1
+#define N4 0
+
+double out[2][96] = {
+{ // Horozontal
+170, 110, 23, 5, 15, 17, 16, 16, 16, 17, 10, 6, 57,
+151, 153, 61, 7, 10, 16, 16, 16, 16, 17, 15, 5, 22,
+107, 169, 109, 23, 5, 15, 17, 16, 16, 16, 17, 10, 6, 57,
+150, 152, 61, 7, 10, 16, 16, 16, 16, 17, 15, 5, 22,
+107, 168, 109, 23, 5, 15, 17, 16, 16, 16, 17, 10, 6, 56,
+149, 151, 61, 7, 10, 16, 16, 16, 16, 17, 15, 6, 22,
+106, 167, 108, 23, 5, 15, 17, 16, 16, 16, 17, 10, 6, 56,
+148, 150
+},
+{ // Vertical slice target output
+235, 100, N2, 16, 16, 16, 16, 16, 16, 16, 20, 1, 13,
+191, 194, 19, N4, 20, 16, 16, 16, 16, 16, 16, 16, N1, 95,
+234, 99, N2, 16, 16, 16, 16, 16, 16, 16, 20, 1, 13,
+190, 194, 19, N4, 20, 16, 16, 16, 16, 16, 16, 16, N1, 94,
+233, 99, N2, 16, 16, 16, 16, 16, 16, 16, 20, 1, 13,
+189, 193, 19, N4, 20, 16, 16, 16, 16, 16, 16, 16, N1, 94,
+232, 99, N2, 16, 16, 16, 16, 16, 16, 16, 20, 1, 13,
+188, 192
+}
+};
+#endif
+
+// Computed the input to output filters.
+// There will be two phases, depending on whether
+// the input pixel has an even or odd address.
+// Although in this case they seem like they
+// almost interleave, they are actually mirror
+// images with offset, so it's easier to keep them
+// separate, rather than trying to figure the offset out.
+// The index is the output pixel around the closest one
+// to the scaled input pixel - ie. floor(in * 1.5 + 0.5)
+#define FWIDTH 6
+#define NWIDTH (2 * FWIDTH + 1)
+
+double filt_v[2][2][NWIDTH];
+double filt_vx[2][2][NWIDTH]; /* max */
+double filt_vn[2][2][NWIDTH]; /* min */
+
+double *filt[2][2] = { { &filt_v[0][0][FWIDTH], &filt_v[0][1][FWIDTH] },
+ { &filt_v[1][0][FWIDTH], &filt_v[1][1][FWIDTH] } };
+
+double *filtx[2][2] = { { &filt_vx[0][0][FWIDTH], &filt_vx[0][1][FWIDTH] },
+ { &filt_vx[1][0][FWIDTH], &filt_vx[1][1][FWIDTH] } };
+
+double *filtn[2][2] = { { &filt_vn[0][0][FWIDTH], &filt_vn[0][1][FWIDTH] },
+ { &filt_vn[1][0][FWIDTH], &filt_vn[1][1][FWIDTH] } };
+
+//int fneg[2] = { -5, -4 }; /* Negative index range (inclusive) */
+//int fpos[2] = { 5, 3 }; /* Positive index range (inclusive) */
+int fneg[2][2] = { { -4, -4 }, /* Negative index range (inclusive) */
+ { -4, -4 } };
+int fpos[2][2] = { { 4, 3 }, /* Positive index range (inclusive) */
+ { 4, 3 } };
+
+/* Weightings [horiz/vert][phase] */
+double filtw_v[2][2][NWIDTH] = {
+ /* -6, -5, -4, -3, -2, -1, 0, +1, +2, +3, +4, +5, +6 */
+// { { 1.0, 1.0, 1.0, 5.0, 3.0, 42.0, 80.0, 43.0, 3.0, 5.0, 1.0, 1.0, 1.0 },
+// { 1.0, 1.0, 3.0, 4.0, 19.0, 62.0, 62.0, 21.0, 4.0, 3.0, 1.0, 1.0, 1.0 } },
+ { { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 },
+ { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 } },
+ /* -6, -5, -4, -3, -2, -1, 0, +1, +2, +3, +4, +5, +6 */
+// { { 1.0, 1.0, 1.0, 3.0, 17.0, 36.0, 100.0, 40.0, 15.0, 1.0, 3.0, 1.0, 1.0 },
+// { 1.0, 1.0, 1.0, 8.0, 2.0, 80.0, 81.0, 2.0, 8.0, 2.0, 1.0, 1.0, 1.0 } }
+ { { 1.0, 1.0, 1.0, 1.0, 1.0, 2.0, 4.0, 2.0, 1.0, 1.0, 1.0, 1.0, 1.0 },
+ { 1.0, 1.0, 1.0, 1.0, 1.0, 3.0, 3.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 } },
+};
+double *filtw[2][2] = { { &filtw_v[0][0][FWIDTH], &filtw_v[0][1][FWIDTH] },
+ { &filtw_v[1][0][FWIDTH], &filtw_v[1][1][FWIDTH] } };
+
+#define MX 2.0
+#define MN -0.5
+
+// Compute the filter shapes
+// vv = 0 for horizontal, 1 for vertical
+static void compute(int vv) {
+ int ph, ii, i, jj, j;
+ double iv, ov;
+ int fcount[2];
+ int niv = sizeof(in[vv])/sizeof(double);
+ int nov = sizeof(out[vv])/sizeof(double);
+
+ // Clear the filters
+ for (ph = 0; ph < 2; ph++) {
+ for (j = -FWIDTH; j <= FWIDTH; j++) {
+ filt[vv][ph][j] = 0.0;
+ filtx[vv][ph][j] = MX;
+ filtn[vv][ph][j] = MN;
+ }
+ fcount[ph] = 0;
+ }
+
+ // Discover an input value
+ for (ii = 0; ii < niv; ii++) {
+ double rgb[3], ycc[3];
+ double prop, propx, propn;
+
+ if (in[ii] == 0)
+ continue;
+
+ iv = in[vv][ii];
+ rgb[0] = rgb[1] = rgb[2] = iv;
+ ccast2YCbCr(NULL, ycc, rgb);
+ iv = ycc[0];
+
+ ph = ii & 1;
+ jj = (int)floor(ii * 1.5 + 0.5);
+
+ for (j = -FWIDTH; j <= FWIDTH; j++) {
+ int k = jj + j;
+ if (k < 0 || k >= nov)
+ continue;
+ ov = out[vv][k];
+
+ prop = ((ov - 16.0)/219.0)/((iv - 16.0)/219.0);
+ propx = ((ov - 16.0)/219.0)/((iv - 0.5 - 16.0)/219.0);
+ propn = ((ov - 16.0)/219.0)/((iv + 0.5 - 16.0)/219.0);
+
+ if (propx < propn) {
+ double tt = propn;
+ propn = propx;
+ propx = tt;
+ }
+
+//printf("~1 phase %d, off %d, iv %f ov %f, prop %f\n",ph,j,iv,ov,prop);
+ filt[vv][ph][j] += prop;
+ if (propx < filtx[vv][ph][j])
+ filtx[vv][ph][j] = propx;
+ if (propn > filtn[vv][ph][j])
+ filtn[vv][ph][j] = propn;
+ }
+ fcount[ph]++;
+ }
+
+ // Compute average values
+ for (ph = 0; ph < 2; ph++) {
+ for (j = -FWIDTH; j <= FWIDTH; j++)
+ filt[vv][ph][j] /= (double)fcount[ph];
+ }
+}
+
+#define FCO2IX(bank, off) (bank ? fpos[vv][0] - fneg[vv][0] + 1 + off - fneg[vv][1] : off - fneg[vv][0])
+
+// Compute the filter shapes using SVD
+// vv = 0 for horizontal, 1 for vertical
+static void compute2(int vv) {
+ int niv = sizeof(in[vv])/sizeof(double); /* Number of input values */
+ int nov = sizeof(out[vv])/sizeof(double); /* Number of output values */
+ int novextra = 0;
+ int nfc = fpos[vv][0] - fneg[vv][0] + 1
+ + fpos[vv][1] - fneg[vv][1] + 1; /* Number of filter coeficients */
+ double **A, *b;
+ int oe; /* Even or odd */
+ int j, i;
+
+#ifdef SUM_CONSTRAINT
+ novextra = 3;
+#endif
+
+ nov += novextra; /* Extra constraint of sum */
+
+ /* We assume nov > nfc */
+ A = dmatrixz(0, nov-1, 0, nfc-1);
+ b = dvectorz(0, nov-1);
+
+ /* For each output value */
+ for (j = 0; j < (nov-novextra); j++) {
+ double ww = 1.0;
+
+#ifdef DO_WEIGHTING
+ /* Figure out the weighting */
+ for (oe = 0; oe < 2; oe++) {
+ int fix; /* Filter index */
+
+ /* For offset range of filter */
+ for (fix = fneg[vv][oe]; fix <= fpos[vv][oe]; fix++) {
+ int ocx, icx, ph;
+
+ ocx = j - fix; /* Output center of filter */
+ if (ocx < 0 || ocx >= nov)
+ continue; /* Filter would never get applied */
+ if (((2 * ocx) % 3) == 2)
+ continue; /* Would never get applied */
+ icx = (int)floor(ocx / 1.5); /* Input center index for this output */
+ if (icx < 0 || icx >= niv)
+ continue; /* Filter would never get applied */
+ ph = icx & 1; /* Phase of filter */
+ if (ph != oe) /* Not a filter that would appear at this ouput */
+ continue;
+ if (in[vv][icx] >= 200.0)
+ ww = filtw[vv][ph][fix];
+ }
+ }
+#endif /* DO_WEIGHTING */
+
+ /* For even and odd filters */
+ for (oe = 0; oe < 2; oe++) {
+ int fix; /* Filter index */
+
+ /* For offset range of filter */
+ for (fix = fneg[vv][oe]; fix <= fpos[vv][oe]; fix++) {
+ double rgb[3], ycc[3];
+ int ocx, icx, ph;
+
+ ocx = j - fix; /* Output center of filter */
+ if (ocx < 0 || ocx >= nov)
+ continue; /* Filter would never get applied */
+ if (((2 * ocx) % 3) == 2)
+ continue; /* Would never get applied */
+ icx = (int)floor(ocx / 1.5); /* Input center index for this output */
+ if (icx < 0 || icx >= niv)
+ continue; /* Filter would never get applied */
+ ph = icx & 1; /* Phase of filter */
+ if (ph != oe) /* Not a filter that would appear at this ouput */
+ continue;
+//printf("j = %d/%d, k = %d/%d, ix = %d/%d\n",j,nov,oe * NWIDTH + FWIDTH + fix,nfc,ix,niv);
+ rgb[0] = rgb[1] = rgb[2] = in[vv][icx];
+ ccast2YCbCr(NULL, ycc, rgb);
+ A[j][FCO2IX(oe, fix)] += ww * ycc[0];
+//printf("A[%d][%d] = %f\n",j,FCO2IX(oe, fix),A[j][FCO2IX(oe, fix)]);
+ }
+ }
+ b[j] = ww * out[vv][j];
+//printf("b[%d] = %f\n",j,b[j]);
+ }
+
+#ifdef SUM_CONSTRAINT
+ /* Add sum constraints */
+ /* For 3 repeating output slots */
+ for (j = nov-novextra; j < nov; j++) {
+ double ww = 10000.0;
+ int jj = j - (nov-novextra);
+
+ b[j] = ww;
+
+ /* For even and odd filters */
+ for (oe = 0; oe < 2; oe++) {
+ int fix; /* Filter index */
+
+ /* For offset range of filter */
+ for (fix = fneg[vv][oe]; fix <= fpos[vv][oe]; fix++) {
+ double rgb[3], ycc[3];
+ int ocx, ocx2, icx, ph;
+
+ ocx = j - fix; /* Output center of filter */
+ ocx2 = 2 * ocx;
+
+ while (ocx2 < 0)
+ ocx2 += 3;
+ while (ocx2 >= 3)
+ ocx2 -= 3;
+
+ if (ocx2 == 2)
+ continue; /* Would never get applied */
+
+ while (ocx < 0)
+ ocx += 3;
+ while (ocx >= 3)
+ ocx -= 3;
+
+ icx = (int)floor(ocx / 1.5); /* Input center index for this output */
+ ph = icx & 1; /* Phase of filter */
+ if (ph != oe) /* Not a filter that would appear at this ouput */
+ continue;
+ A[j][FCO2IX(oe, fix)] = ww;
+printf("A[%d][%d] = %f\n",j,FCO2IX(oe, fix),A[j][FCO2IX(oe, fix)]);
+ }
+ }
+ }
+#endif /* SUM_CONSTRAINT */
+
+ /* Solve the equation A.x = b using SVD */
+ /* (The w[] values are thresholded for best accuracy) */
+ /* Return non-zero if no solution found */
+ if (svdsolve(A, b, nov, nfc))
+ error("svdsolve failed");
+
+ /* Print the filter shape */
+ /* and copy to the filter */
+ printf("SVD computed for %s:\n", vv ? "vertical" : "horizontal");
+ for (oe = 0; oe < 2; oe++) {
+ int fix; /* Filter index */
+ double sum = 0.0;
+
+ printf("Phase %d\n",oe);
+// for (fix = -FWIDTH; fix <= FWIDTH; fix++) {
+ for (fix = fneg[vv][oe]; fix <= fpos[vv][oe]; fix++) {
+ printf(" %d -> %f\n",fix, b[FCO2IX(oe, fix)]);
+ sum += b[FCO2IX(oe, fix)];
+ filt[vv][oe][fix] = b[FCO2IX(oe, fix)];
+ }
+ printf("sum = %f\n",sum);
+ }
+}
+
+void check(int vv) {
+ double *chout;
+ int niv = sizeof(in[vv])/sizeof(double); /* Number of input values */
+ int nov = sizeof(out[vv])/sizeof(double); /* Number of output values */
+
+ int range, i, ii, j;
+ double xc, x, iv, tw, w, y;
+ double cout, terr = 0.0;
+
+printf("~1 nov = %d\n",nov);
+ if ((chout = (double *)malloc(sizeof(double) * nov)) == NULL)
+ error("Malloc failed");
+
+ // Clear the output
+ for (i = 0; i < nov; i++)
+ chout[i] = 0.0;
+
+ // For all the input value
+ for (ii = 0; ii < niv; ii++) {
+ int ph, jj;
+ double rgb[3], ycc[3];
+ double prop;
+
+ iv = in[vv][ii];
+ rgb[0] = rgb[1] = rgb[2] = iv;
+ ccast2YCbCr(NULL, ycc, rgb);
+ iv = ycc[0];
+
+ ph = ii & 1;
+ jj = (int)floor(ii * 1.5 + 0.5);
+
+ for (j = -FWIDTH; j <= FWIDTH; j++) {
+ int k = jj + j;
+ if (k < 0 || k >= nov)
+ continue;
+
+//if ((jj + j) == 4) printf("[%d] += w %f * iv %f\n",jj + j, filt[ph][j], iv);
+ chout[k] += filt[vv][ph][j] * iv;
+ }
+ }
+
+ for (i = 0; i < nov; i++) {
+ double ov, ee;
+ ov = chout[i];
+ ov = floor(ov + 0.5);
+#ifdef NEVER
+ if (ov < 0.0)
+ ov = 0.0;
+ else if (ov > 255.0)
+ ov = 255.0;
+#endif
+ ee = ov - out[vv][i];
+ terr += ee * ee;
+ printf("out %d = %f should be %f err %f\n",i,chout[i],out[vv][i],ee);
+ }
+
+ printf("Total err = %f RMS\n",sqrt(terr));
+}
+
+int
+main(int argc,
+ char *argv[]
+) {
+ int ph, vv, j;
+
+ double err;
+ double cp[2]; /* Initial starting point */
+ double s[2]; /* Size of initial search area */
+
+ printf("Hi there\n");
+
+#ifdef NEVER
+ compute(1);
+
+ // Print filter shape
+ printf("Directly computed:\n");
+ for (ph = 0; ph < 2; ph++) {
+ printf("Phase %d\n",ph);
+ for (j = -FWIDTH; j <= FWIDTH; j++)
+ printf(" %d -> min %f, avg %f, max %f\n",j,filtn[1][ph][j],filt[1][ph][j],filtx[1][ph][j]);
+ }
+#endif
+
+ for (vv = 0; vv < 2; vv++) {
+ compute2(vv);
+ check(vv);
+ }
+
+ /* Output code to stdout */
+ for (ph = 0; ph < 2; ph++) {
+ fprintf(stderr,"/* Weightings [horiz/vert] */\n");
+ fprintf(stderr,"double filt_v_%s[2][%s_WIDTH] = {\n",ph ? "od" : "ev", ph ? "OD" : "EV");
+
+ for (vv = 0; vv < 2; vv++) {
+ int fix; /* Filter index */
+
+ fprintf(stderr,"{ ");
+ for (fix = fneg[vv][ph]; fix <= fpos[vv][ph]; fix++) {
+ if (fix > fneg[vv][ph])
+ fprintf(stderr,", ");
+ fprintf(stderr,"%f", filt[vv][ph][fix]);
+ }
+ fprintf(stderr," }%s\n",vv == 0 ? "," : "");
+ }
+ fprintf(stderr,"};\n\n");
+ }
+
+ return 0;
+}
+