/* Verify and benchmark the imdi code */ /* * Copyright 2000 - 2006 Graeme W. Gill * All rights reserved. * * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :- * see the License.txt file for licencing details. */ #include #include #include #include "copyright.h" #include "aconfig.h" #include "numlib.h" #include "imdi.h" #include "refi.h" /* Test parameters */ #undef TEST1 /* Test just one combination */ #define FULL /* Test full range */ #undef VERBOSE /* Report every conversion */ #undef REPORT_ERRORS /* Report conversion with large errors */ #define TRAND /* Test random in/out table contents */ #define TCRAND /* Test random clut table contents */ #undef QUANTIZE /* Quantize the target table values */ #define TBUFSIZE (2 * 1024 * 1024) /* Default number of input bytes to test */ #define ITERS 10 /* Itterations */ double trans1(double in, double t); double trans2(double in, double t); /* Context for refi setup callbacks */ typedef struct { int id; int od; double icurve[MXDI]; /* Input curve factors */ double clut[MXDO][MXDI]; /* clut matrix factors */ double ocurve[MXDO]; /* Output curve factors */ } rcntx; /* Input curve function */ static void input_curves( void *cntx, double *out_vals, double *in_vals ) { int i; rcntx *rx = (rcntx *)cntx; for (i = 0; i < rx->id; i++) { double val = in_vals[i]; #ifdef TRAND val = trans1(val, rx->icurve[i]); #ifdef QUANTIZE val = ((int)(val * 255.0 + 0.5))/255.0; /* Quantize to 8 bits */ #endif #endif out_vals[i] = val; } } /* Multi-dim table function */ static void md_table( void *cntx, double *out_vals, double *in_vals ) { rcntx *rx = (rcntx *)cntx; int i, j; #ifdef TCRAND for (j = 0; j < rx->od; j++) { double val = 0.0; for (i = 0; i < rx->id; i++) { val += rx->clut[j][i] * in_vals[i]; } #ifdef QUANTIZE val = ((int)(val * 255.0 + 0.5))/255.0; /* Quantize to 8 bits */ #endif out_vals[j] = val; } #else for (j = 0; j < rx->od; j++) out_vals[j] = in_vals[j % rx->id]; #endif //printf("~1 out %f %f %f %f from %f\n", out_vals[0], out_vals[1], out_vals[2], out_vals[3], in_vals[0]); } /* Output curve function */ static void output_curves( void *cntx, double *out_vals, double *in_vals ) { int i; rcntx *rx = (rcntx *)cntx; for (i = 0; i < rx->od; i++) { double val = in_vals[i]; #ifdef TRAND val = trans1(val, rx->ocurve[i]); #ifdef QUANTIZE val = ((int)(val * 255.0 + 0.5))/255.0; /* Quantize to 8 bits */ #endif #endif out_vals[i] = val; } } /* Complete reference interpolation */ void refi_interp(refi *r, double *out_vals, double *in_vals); void usage(void) { fprintf(stderr,"Regression test imdi code Version %s\n",ARGYLL_VERSION_STR); fprintf(stderr,"usage: itest [-q] [-s]\n"); fprintf(stderr," -q Quick test\n"); fprintf(stderr," -s Stop on error\n"); fprintf(stderr," -r bits Specify bits of randomness in input data\n"); exit(1); } int main(int argc, char *argv[]) { int fa,nfa; /* argument we're looking at */ int quick = 0; int stop = 0; int rbits = 16; int n, i, j, e; int pix, iix, oix; int ip, op, id, od; int ires, cres, ores; clock_t stime, ttime; /* Start and total times */ double xtime; /* Total execution time in seconds */ double npixels; /* Number of pixels processed */ rcntx rx; refi *r; double ribuf[MXDI]; double robuf[MXDO]; imdi *s; int iters; unsigned long tbufsize; unsigned char *ibuf; unsigned char *obuf; unsigned char *inp[MXDI]; unsigned char *outp[MXDO]; unsigned short *ibuf2; /* 16 bit references */ unsigned short *obuf2; double omxerr = 0; /* Overall max error /1.0 */ /* Define combinations to test */ #ifdef TEST1 #pragma message("!!!!!!!!!!!!!!!!! TEST1 is defined !!!!!!!!!!!!!!!1") int ids[] = { 3, 0 }; /* Input dimensions */ int ods[] = { 3, 0 }; /* Output dimensions */ int iprs[] = { 16, 0 }; int oprs[] = { 16, 0 }; #else #ifndef FULL int ids[] = { 1, 3, 4, 8, 0 }; int ods[] = { 1, 3, 4, 8, 0 }; int iprs[] = { 8, 8, 16, 0}; int oprs[] = { 8, 16, 16, 0}; #else int ids[] = { 1, 3, 4, 5, 6, 7, /* 8, */ 0 }; int ods[] = { 1, 3, 4, 5, 6, 7, /* 8, */ 0 }; int iprs[] = { 8, 8, 16, 0}; int oprs[] = { 8, 16, 16, 0}; #endif #endif /* Process the arguments */ for(fa = 1;fa < argc;fa++) { nfa = fa; /* skip to nfa if next argument is used */ if (argv[fa][0] == '-') { /* Look for any flags */ char *na = NULL; /* next argument after flag, null if none */ if (argv[fa][2] != '\000') na = &argv[fa][2]; /* next is directly after flag */ else { if ((fa+1) < argc) { if (argv[fa+1][0] != '-') { nfa = fa + 1; na = argv[nfa]; /* next is seperate non-flag argument */ } } } if (argv[fa][1] == '?') usage(); /* Quick test */ else if (argv[fa][1] == 'q' || argv[fa][1] == 'Q') { quick = 1; } /* Stop on error */ else if (argv[fa][1] == 's' || argv[fa][1] == 'S') { stop = 1; } /* Degree of data randomness */ else if (argv[fa][1] == 'r' || argv[fa][1] == 'R') { fa = nfa; if (na == NULL) usage(); rbits = atoi(na); if (rbits < 0) rbits = 1; else if (rbits > 16) rbits = 16; } else usage(); } else break; } for (iix = 0; ids[iix] > 0; iix++) { /* All input dimensions */ for (oix = 0; ods[oix] > 0; oix++) { /* All output dimensions */ for (pix = 0; iprs[pix] > 0; pix++) { /* All precisions */ int rmask = 0; ip = iprs[pix]; /* Input precision */ op = oprs[pix]; /* Output precision */ id = ids[iix]; /* Input dimensions */ od = ods[oix]; /* Outpu dtimensions */ if (ip != 8 && ip != 16) error("Can't handle input precision of %d in testing\n",ip); if (op != 8 && op != 16) error("Can't handle output precision of %d in testing\n",op); if (id > MXDI) error("Can't handle id greater than %d in testing\n",MXDI); if (od > MXDO) error("Can't handle od greater than %d in testing\n",MXDO); printf("Testing id = %d, od = %d, ip = %d, op = %d\n",id,od,ip,op); ires = 256; switch (id) { case 1: cres = 257; break; case 2: case 3: cres = 33; break; case 4: cres = 17; break; case 5: cres = 7; break; case 6: cres = 7; break; case 7: cres = 5; break; case 8: cres = 5; break; default: cres = 3; } ores = 256; /* Setup error messages */ error_program = "itest"; /* Create the reference first */ printf("About to create refi\n"); /* Create test curves */ rx.id = id; rx.od = od; rand32(0x1234); for (i = 0; i < id; i++) { rx.icurve[i] = d_rand(-1.0, 1.0); } //printf("Matrix params =\n"); rand32(0x2345); for (j = 0; j < od; j++) { double s = 0.0; for (i = 0; i < id; i++) { double v = d_rand(1.0, 0.0); rx.clut[j][i] = v; /* Make them sum to 1.0 */ s += v; } for (i = 0; i < id; i++) { rx.clut[j][i] /= s; //printf(" %f",rx.clut[j][i]); } //printf("\n"); } rand32(0x3456); for (j = 0; j < od; j++) { rx.ocurve[j] = d_rand(-1.0, 1.0); } r = new_refi( id, /* Number of input dimensions */ od, /* Number of output dimensions */ ires, /* Input table resolution */ cres, /* clut resolution */ ores, /* Output table resolution */ input_curves, /* Callback functions */ md_table, output_curves, (void *)&rx /* Context to callbacks */ ); if (r == NULL) { error("new_refi failed"); } /* Now crate the imdi we want to test, using the */ /* reference as a template */ printf("About to create imdi\n"); s = new_imdi( id, /* Number of input dimensions */ od, /* Number of output dimensions */ ip == 8 ? pixint8 : pixint16, /* Input pixel representation */ 0x0, /* Treat every channel as unsigned */ NULL, /* No raster to callback mapping */ prec_min, /* Minimum of input and output precision */ op == 8 ? pixint8 : pixint16, /* Output pixel representation */ 0x0, /* Treat every channel as unsigned */ NULL, /* No raster to callback mapping */ cres, /* Desired table resolution */ oopts_none, /* Desired per channel output options */ NULL, /* Output channel check values */ opts_none, /* Desired processing direction and stride support */ refi_input, /* Callback functions */ refi_clut, refi_output, (void *)r /* Context to callbacks */ ); if (s == NULL) { error("new_imdi failed"); } if (quick) { iters = 1; tbufsize = 4096/id; } else { iters = ITERS; tbufsize = TBUFSIZE/id; } /* Allocate the test buffers */ if ((ibuf = malloc(sizeof(unsigned char) * ip/8 * id * tbufsize)) == NULL) error("Malloc of input buffer failed"); ibuf2 = (unsigned short *)ibuf; if ((obuf = malloc(sizeof(unsigned char) * op/8 * od * tbufsize)) == NULL) error("Malloc of output buffer failed"); obuf2 = (unsigned short *)obuf; /* Initialise the input buffer contents */ rand32(0x12345678); if (ip == 8) { unsigned ui; int rr = rbits; if (rr > 8) rr = 8; rmask = ((1 << rr) -1) << (8-rr); for (ui = 0; ui < tbufsize; ui += id) { for (e = 0; e < id; e++) { unsigned long ran = rand32(0); ibuf[ui + e] = (unsigned char)(ran & rmask); } } } else { unsigned ui; rmask = ((1 << rbits) -1) << (16-rbits); for (ui = 0; ui < tbufsize; ui += id) { for (e = 0; e < id; e++) { unsigned long ran = rand32(0); ibuf2[ui + e] = (unsigned short)(ran & rmask); } } } /* We are assuming packed pixel interleaved */ inp[0] = ibuf; outp[0] = obuf; /* Benchmark it */ stime = clock(); for (n = 0; n < iters; n++) { s->interp(s, (void **)outp, 0, (void **)inp, 0, tbufsize); } ttime = clock() - stime; xtime = (double)ttime/(double)CLOCKS_PER_SEC; npixels = (double)iters * (double)tbufsize; if (xtime > 0.0) printf("Speed = rate = %f Mpix/sec\n",1e-6 * npixels / xtime); else printf("Speed - too fast!\n"); { unsigned ui; double mxerr = 0.0; double avgerr = 0.0; /* Verify the accuracy against refi of each sample */ for (ui = j = 0; ui < tbufsize; ui += id, j += od) { int mxserr; if (ip == 8) { for (e = 0; e < id; e++) { ribuf[e] = ibuf[ui + e]/255.0; } } else { for (e = 0; e < id; e++) { ribuf[e] = ibuf2[ui + e]/65535.0; } } refi_interp(r, robuf, ribuf); mxserr = 0; if (op == 8) { for (e = 0; e < od; e++) { double err = robuf[e] * 255.0 - obuf[j + e]; if (err < 0) err = -err; if (err > mxerr) mxerr = err; if (err > mxserr) mxserr = err; avgerr += err; } } else { for (e = 0; e < od; e++) { double err = robuf[e] * 65535.0 - obuf2[j + e]; if (err < 0) err = -err; if (err > mxerr) mxerr = err; if (err > mxserr) mxserr = err; avgerr += err; } } #if defined(REPORT_ERRORS) || defined(VERBOSE) if (mxserr >= 37) { if (ip == 8) { if (id == 1) printf("in %d, ", ibuf[ui+0]); if (id == 2) printf("in %d %d, ", ibuf[ui+0], ibuf[ui+1]); if (id == 3) printf("in %d %d %d, ", ibuf[ui+0], ibuf[ui+1], ibuf[ui+2]); if (id == 4) printf("in %d %d %d, ", ibuf[ui+0], ibuf[ui+1], ibuf[ui+2], ibuf[ui+3]); } else { if (id == 1) printf("in %d, ", ibuf2[ui+0]); if (id == 2) printf("in %d %d, ", ibuf2[ui+0], ibuf2[ui+1]); if (id == 3) printf("~in %d %d %d, ", ibuf2[ui+0], ibuf2[ui+1], ibuf2[ui+2]); if (id == 4) printf("in %d %d %d, ", ibuf2[ui+0], ibuf2[ui+1], ibuf2[ui+2], ibuf2[ui+3]); } if (ip == 8) { if (od == 1) printf("is %d, should be %d\n", obuf[j+0], (int)(robuf[0] * 255.0 + 0.5)); if (od == 2) printf("is %d %d, should be %d %d\n", obuf[j+0], obuf[j+1], (int)(robuf[0] * 255.0 + 0.5), (int)(robuf[1] * 255.0 + 0.5)); if (od == 3) printf("is %d %d %d, should be %d %d %d\n", obuf[j+0], obuf[j+1], obuf[j+2], (int)(robuf[0] * 255.0 + 0.5), (int)(robuf[1] * 255.0 + 0.5), (int)(robuf[2] * 255.0 + 0.5)); if (od == 4) printf("is %d %d %d %d, should be %d %d %d %d\n", obuf[j+0], obuf[j+1], obuf[j+2], obuf[j+3], (int)(robuf[0] * 255.0 + 0.5), (int)(robuf[1] * 255.0 + 0.5), (int)(robuf[2] * 255.0 + 0.5), (int)(robuf[3] * 255.0 + 0.5)); } else { if (od == 1) printf("is %d, should be %d\n", obuf2[j+0], (int)(robuf[0] * 65535.0 + 0.5)); if (od == 2) printf("is %d %d, should be %d %d\n", obuf2[j+0], obuf2[j+1], (int)(robuf[0] * 65535.0 + 0.5), (int)(robuf[1] * 65535.0 + 0.5)); if (od == 3) printf("is %d %d %d, should be %d %d %d\n", obuf2[j+0], obuf2[j+1], obuf2[j+2], (int)(robuf[0] * 65535.0 + 0.5), (int)(robuf[1] * 65535.0 + 0.5), (int)(robuf[2] * 65535.0 + 0.5)); if (od == 4) printf("is %d %d %d %d, should be %d %d %d %d\n", obuf2[j+0], obuf2[j+1], obuf2[j+2], obuf2[j+3], (int)(robuf[0] * 65535.0 + 0.5), (int)(robuf[1] * 65535.0 + 0.5), (int)(robuf[2] * 65535.0 + 0.5), (int)(robuf[3] * 65535.0 + 0.5)); } } #endif /* VERBOSE || REPORT+ERRORS */ } avgerr /= ((double)tbufsize * od); { double fmxerr; /* Relative maximum error */ double favgerr; /* Relative average error */ /* Always relative to basic precision */ fmxerr = mxerr / ((1 << op) - 1.0); favgerr = avgerr / ((1 << op) - 1.0); printf("Worst error = %f = %f%%, average error = %f%%\n", mxerr, 100.0 * fmxerr, 100.0 * favgerr); printf("\n"); if (fmxerr > omxerr) omxerr = fmxerr; } } /* Free everything up */ free(ibuf); free(obuf); refi_free(r); s->del(s); if (stop && (100.0 * omxerr) > 1.2) { goto quit; } } } } quit:; printf("Overall worst error = %f%%\n", 100.0 * omxerr); return 0; } /* ------------------------------------------------- */ /* Support */ /* Run an interpolation though the whole refi */ void refi_interp( refi *r, double *out_vals, double *in_vals ) { #ifdef QUANTIZE int e; #endif double ivals[MXDI]; double ovals[MXDO]; refi_input((void *)r, ivals, in_vals); #ifdef QUANTIZE for (e = 0; e < r->id; e++) ivals[e] = ((int)(ivals[e] * 255.0 + 0.5))/255.0; /* Quantize to 8 bits */ #endif refi_clut((void *)r, ovals, ivals); #ifdef QUANTIZE for (e = 0; e < r->od; e++) ovals[e] = ((int)(ovals[e] * 255.0 + 0.5))/255.0; /* Quantize to 8 bits */ #endif refi_output((void *)r, out_vals, ovals); } /* ------------------------------------------------- */ /* Some test functions */ /* Single bump version */ double trans1( double in, double t /* Non-linearity factor. 0.0 to +/-1.0 */ ) { double out; out = in + t * in * (1.0 - in); return out; } /* Double bump version */ double trans2( double in, double t /* Non-linearity factor. 0.0 to +/-1.0 */ ) { double nf, out; if (in >= 0.5) { nf = -2.0 * (in - 0.5) * (1.0 - in); } else { nf = 2.0 * in * (0.5 - in); } out = in + t * nf; return out; }