953 lines
22 KiB
C
953 lines
22 KiB
C
|
|
/*
|
|
* Argyll Color Correction System
|
|
* ChromCast dither pattern code
|
|
*
|
|
* Author: Graeme W. Gill
|
|
* Date: 11/12/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.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Locates minimal list of surrounders
|
|
* Find initial weight for them using minimizer
|
|
Itterates over
|
|
* Locate cell changes that match desired correction
|
|
with increasing randomness of choosing a poor match
|
|
* Reset to starting pattern when accumulated error over
|
|
best current is exceeded.
|
|
|
|
|
|
Problem is this is unreliable is locating good matches
|
|
for some cases.
|
|
|
|
Problem is that it doesn't seem to work - ChromeCast doesn't
|
|
decode the way we model it :-(
|
|
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
#include "copyright.h"
|
|
#include "aconfig.h"
|
|
#include "counters.h"
|
|
#ifndef SALONEINSTLIB
|
|
#include "numlib.h"
|
|
#else
|
|
#include "numsup.h"
|
|
#endif
|
|
#include "conv.h"
|
|
#include "base64.h"
|
|
#include "yajl.h"
|
|
#include "ccmdns.h"
|
|
#include "ccpacket.h"
|
|
#include "ccmes.h"
|
|
#include "ccast.h"
|
|
|
|
#ifdef STANDALONE_TEST
|
|
# define DIAGNOSTICS
|
|
#endif
|
|
|
|
#undef TEST_POINT
|
|
|
|
/* Even/Odd filter filter index ranges (inclusive) and total size */
|
|
#define EV_NEG -4
|
|
#define EV_POS 4
|
|
#define EV_WIDTH (-(EV_NEG) + 1 + EV_POS)
|
|
#define OD_NEG -4
|
|
#define OD_POS 3
|
|
#define OD_WIDTH (-(OD_NEG) + 1 + OD_POS)
|
|
|
|
/* Weightings [horiz/vert] */
|
|
double filt_v_ev[2][EV_WIDTH] = {
|
|
{ -0.003467, -0.048341, 0.028688, 0.418873, 0.704674, 0.427551, 0.032480, -0.050146, -0.003793 },
|
|
{ -0.013477, 0.000081, -0.087119, 0.357627, 1.000252, 0.378473, -0.083496, -0.000155, -0.009850 }
|
|
};
|
|
|
|
/* Weightings [horiz/vert] */
|
|
double filt_v_od[2][OD_WIDTH] = {
|
|
{ -0.026810, -0.045190, 0.186825, 0.614579, 0.623721, 0.206987, -0.040217, -0.026419 },
|
|
{ 0.008396, -0.078987, -0.013733, 0.796594, 0.813578, 0.013555, -0.086466, 0.004781 }
|
|
};
|
|
|
|
|
|
/* [horiz/vert][phase] */
|
|
double *filt[2][2] = { { &filt_v_ev[0][-(EV_NEG)], &filt_v_od[0][-(OD_NEG)] },
|
|
{ &filt_v_ev[1][-(EV_NEG)], &filt_v_od[1][-(OD_NEG)] } };
|
|
|
|
/* [phase] */
|
|
int fneg[2] = { EV_NEG, OD_NEG };
|
|
int fpos[2] = { EV_POS, OD_POS };
|
|
|
|
/* Dither input size */
|
|
#ifdef TEST_POINT
|
|
# define DISIZE 8
|
|
#else
|
|
# define DISIZE CCDITHSIZE
|
|
#endif
|
|
#define DOSIZE ((DISIZE * 3)/2)
|
|
|
|
#ifdef DIAGNOSTICS
|
|
int vv = 0;
|
|
#endif
|
|
|
|
/* Upsample ipat to opat */
|
|
/* And return the average RGB value */
|
|
static void upsample(double ret[3], double iipat[DISIZE][DISIZE][3]) {
|
|
double ipat[DISIZE][DISIZE][3];
|
|
double tpat[DOSIZE][DISIZE][3]; /* Intermediate pattern - horizontal up-sampled */
|
|
double opat[DOSIZE][DOSIZE][3]; /* Output pattern */
|
|
int x, y;
|
|
int i, j;
|
|
|
|
//printf("~1 doing conversion\n");
|
|
/* Convert from RGB to YCbCr */
|
|
for (x = 0; x < DISIZE; x++) {
|
|
for (y = 0; y < DISIZE; y++) {
|
|
ccast2YCbCr(NULL, ipat[x][y], iipat[x][y]);
|
|
// ipat[x][y][0] = iipat[x][y][0];
|
|
// ipat[x][y][1] = iipat[x][y][1];
|
|
// ipat[x][y][2] = iipat[x][y][2];
|
|
//printf("[%d][%d] %f %f %f -> %f %f %f\n",x,y, iipat[x][y][0], iipat[x][y][1], iipat[x][y][2], ipat[x][y][0], ipat[x][y][1], ipat[x][y][2]);
|
|
}
|
|
}
|
|
|
|
//printf("~1 doing horiz\n");
|
|
/* Up-sample in horizontal direction */
|
|
for (y = 0; y < DISIZE; y++) {
|
|
|
|
/* Zero the intermediate pattern */
|
|
for (i = 0; i < DOSIZE; i++) {
|
|
tpat[i][y][0] = 0.0;
|
|
tpat[i][y][1] = 0.0;
|
|
tpat[i][y][2] = 0.0;
|
|
}
|
|
|
|
/* Distribute the input according to the filter */
|
|
for (x = 0; x < DISIZE; x++) {
|
|
int ph, ii, k;
|
|
|
|
ph = x & 1;
|
|
ii = (int)floor(x * 1.5 + 0.5);
|
|
|
|
/* For all the filter cooeficients */
|
|
for (k = fneg[ph]; k <= fpos[ph]; k++) {
|
|
i = (ii + k);
|
|
while (i < 0)
|
|
i += DOSIZE;
|
|
while (i >= DOSIZE)
|
|
i -= DOSIZE;
|
|
tpat[i][y][0] += filt[0][ph][k] * ipat[x][y][0];
|
|
tpat[i][y][1] += filt[0][ph][k] * ipat[x][y][1];
|
|
tpat[i][y][2] += filt[0][ph][k] * ipat[x][y][2];
|
|
}
|
|
}
|
|
}
|
|
|
|
//printf("~1 doing vert\n");
|
|
/* Up-sample in vertical direction */
|
|
for (i = 0; i < DOSIZE; i++) {
|
|
|
|
/* Zero the output pattern */
|
|
for (j = 0; j < DOSIZE; j++) {
|
|
opat[i][j][0] = 0.0;
|
|
opat[i][j][1] = 0.0;
|
|
opat[i][j][2] = 0.0;
|
|
}
|
|
|
|
/* Distribute the input according to the filter */
|
|
for (y = 0; y < DISIZE; y++) {
|
|
int ph, jj, k;
|
|
|
|
ph = y & 1;
|
|
jj = (int)floor(y * 1.5 + 0.5);
|
|
|
|
/* For all the filter cooeficients */
|
|
for (k = fneg[ph]; k <= fpos[ph]; k++) {
|
|
j = (jj + k);
|
|
while (j < 0)
|
|
j += DOSIZE;
|
|
while (j >= DOSIZE)
|
|
j -= DOSIZE;
|
|
opat[i][j][0] += filt[1][ph][k] * tpat[i][y][0];
|
|
opat[i][j][1] += filt[1][ph][k] * tpat[i][y][1];
|
|
opat[i][j][2] += filt[1][ph][k] * tpat[i][y][2];
|
|
}
|
|
}
|
|
}
|
|
//printf("~1 doing conversion\n");
|
|
|
|
/* Convert from YCbCr to RGB */
|
|
for (i = 0; i < DOSIZE; i++) {
|
|
for (j = 0; j < DOSIZE; j++) {
|
|
YCbCr2ccast(NULL, opat[i][j], opat[i][j]);
|
|
}
|
|
}
|
|
//printf("~1 done\n");
|
|
|
|
if (ret != NULL) {
|
|
ret[0] = ret[1] = ret[2] = 0.0;
|
|
for (j = 0; j < DOSIZE; j++) {
|
|
for (i = 0; i < DOSIZE; i++) {
|
|
ret[0] += opat[i][j][0];
|
|
ret[1] += opat[i][j][1];
|
|
ret[2] += opat[i][j][2];
|
|
}
|
|
}
|
|
ret[0] /= (double)(DOSIZE * DOSIZE);
|
|
ret[1] /= (double)(DOSIZE * DOSIZE);
|
|
ret[2] /= (double)(DOSIZE * DOSIZE);
|
|
}
|
|
|
|
#ifdef DIAGNOSTICS
|
|
if (vv) {
|
|
printf("Result\n");
|
|
for (j = 0; j < DOSIZE; j++) {
|
|
for (i = 0; i < DOSIZE; i++) {
|
|
if (i > 0)
|
|
printf(", ");
|
|
printf("% 5.1f % 5.1f % 5.1f",opat[i][j][0],opat[i][j][1], opat[i][j][2]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
/* Given a quantized RGB target, return a quantized RGB that either exactly */
|
|
/* maps to it through YCbCr conversion, or is the closest in the */
|
|
/* direction away from the target value, or the closest clipped value */
|
|
static void quant_rgb(int n, double out[3], double rgb[3]) {
|
|
double ycc[3], base[3];
|
|
double tmp[3], chval[3];
|
|
double dist, bdist = 1e6;
|
|
double brgb[3], borgb[3];
|
|
int ix, k;
|
|
|
|
//printf("Quant RGB %f %f %f n %d\n", rgb[0], rgb[1], rgb[2], n);
|
|
|
|
/* Search current rgb and surround in the same direction */
|
|
/* it is from the target value, for a quantized rgb */
|
|
/* that is in that direction */
|
|
for (k = 0; k < 3; k++)
|
|
base[k] = rgb[k];
|
|
|
|
for (ix = 0; ix < 8; ix++) {
|
|
double dist;
|
|
|
|
/* Comp trial RGB */
|
|
for (k = 0; k < 3; k++) {
|
|
tmp[k] = base[k];
|
|
if (ix & (1 << k)) {
|
|
if (n & (1 << k)) /* Move in direction base point is in */
|
|
tmp[k] += 1.0;
|
|
else
|
|
tmp[k] -= 1.0;
|
|
if (tmp[k] < 0.0 || tmp[k] > 255.0)
|
|
break;
|
|
}
|
|
}
|
|
if (k < 3) /* Trial is out of gamut */
|
|
continue;
|
|
|
|
/* Quantize it */
|
|
ccast2YCbCr(NULL, ycc, tmp);
|
|
YCbCr2ccast(NULL, chval, ycc);
|
|
//printf("Trial RGB %f %f %f\n", tmp[0], tmp[1], tmp[2]);
|
|
//printf(" result %f %f %f\n", chval[0], chval[1], chval[2]);
|
|
|
|
/* It's OK if it is eual or greater in the desired */
|
|
/* direction than the input rgb. */
|
|
/* Best would be closest to input. */
|
|
/* Least worst would be what ? */
|
|
|
|
dist = 0.0;
|
|
for (k = 0; k < 3; k++) {
|
|
double tt;
|
|
if (n & (1 << k)) {
|
|
tt = chval[k] - base[k];
|
|
} else {
|
|
tt = base[k] - chval[k];
|
|
}
|
|
if (tt >= 0.0)
|
|
dist += 0.1 * tt;
|
|
else
|
|
dist += 2.0 * -tt;
|
|
}
|
|
//printf(" dist %f\n", dist);
|
|
|
|
/* Pick it if it is at least in the right direction */
|
|
if (dist < bdist) {
|
|
for (k = 0; k < 3; k++) {
|
|
rgb[k] = tmp[k];
|
|
out[k] = chval[k];
|
|
}
|
|
bdist = dist;
|
|
}
|
|
}
|
|
|
|
#ifdef NEVER
|
|
if (bdist > 0.0
|
|
&& rgb[0] != 0.0 && rgb[0] != 1.0 && rgb[0] != 254.0 && rgb[0] != 255.0
|
|
&& rgb[1] != 0.0 && rgb[1] != 1.0 && rgb[1] != 254.0 && rgb[1] != 255.0
|
|
&& rgb[2] != 0.0 && rgb[2] != 1.0 && rgb[2] != 254.0 && rgb[2] != 255.0) {
|
|
printf("quant_rgb failed with bdist %f\n",bdist);
|
|
printf(" rgb in %f %f %f\n",base[0],base[1],base[2]);
|
|
printf(" returning rgb %f %f %f\n",rgb[0],rgb[1],rgb[2]);
|
|
printf(" resrgb %f %f %f\n",out[0],out[1],out[2]);
|
|
}
|
|
#endif /* NEVER */
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
typedef struct optcntx {
|
|
int di;
|
|
double *val; /* Target */
|
|
double (*ressur)[8][3]; /* resulting RGB surrounding values */
|
|
} optcntx;
|
|
|
|
static double optfunc(void *fdata, double tp[]) {
|
|
optcntx *cntx = (optcntx *)fdata;
|
|
int i, k;
|
|
double iw, tmp[3];
|
|
double err = 0.0;
|
|
|
|
/* Compute interpolated result */
|
|
for (k = 0; k < 3; k++)
|
|
tmp[k] = 0.0;
|
|
|
|
iw = 1.0;
|
|
for (i = 0; i < cntx->di; i++) {
|
|
if (tp[i] < 0.0)
|
|
err += 1000.0 * tp[i] * tp[i];
|
|
else if (tp[i] > 1.0)
|
|
err += 1000.0 * (tp[i] - 1.0) * (tp[i] - 1.0);
|
|
|
|
for (k = 0; k < 3; k++)
|
|
tmp[k] += tp[i] * (*cntx->ressur)[i][k];
|
|
iw -= tp[i];
|
|
}
|
|
for (k = 0; k < 3; k++)
|
|
tmp[k] += iw * (*cntx->ressur)[i][k];
|
|
|
|
/* Compute error */
|
|
for (k = 0; k < 3; k++) {
|
|
double tt = tmp[k] - cntx->val[k];
|
|
err += tt * tt;
|
|
}
|
|
//printf("Returning %f from %f %f\n",err,tp[0],tp[1]);
|
|
return err;
|
|
}
|
|
|
|
/* Compute pattern */
|
|
/* return the delta to the target */
|
|
double get_ccast_dith(double ipat[DISIZE][DISIZE][3], double val[3]) {
|
|
double itpat[DISIZE][DISIZE][3], tpat[DISIZE][DISIZE][3];
|
|
double irtpat[DISIZE][DISIZE][3], rtpat[DISIZE][DISIZE][3]; /* resulting for tpat */
|
|
double berr = 0.0;
|
|
int n, k;
|
|
int x, y;
|
|
int i, j;
|
|
int ii;
|
|
struct {
|
|
int x, y;
|
|
} order[16] = {
|
|
// Dispersed:
|
|
{ 3, 1 },{ 1, 3 },{ 1, 1 },{ 3, 0 },{ 3, 3 },{ 1, 2 },{ 3, 2 },{ 2, 0 },
|
|
{ 1, 0 },{ 0, 3 },{ 0, 1 },{ 2, 1 },{ 2, 2 },{ 0, 0 },{ 0, 2 },{ 2, 3 }
|
|
// Clustered:
|
|
// { 0, 0 },{ 0, 1 },{ 1, 1 },{ 1, 0 },{ 2, 0 },{ 3, 0 },{ 3, 1 },{ 2, 1 },
|
|
// { 2, 2 },{ 3, 2 },{ 3, 3 },{ 2, 3 },{ 1, 3 },{ 1, 2 },{ 0, 2 },{ 0, 3 }
|
|
};
|
|
int cix = 0;
|
|
int nsur = 8, ncomb = 4;
|
|
double sur[8][3]; /* RGB surrounding values to use */
|
|
double ressur[8][3]; /* resulting RGB surrounding values */
|
|
int bcc[8]; /* Best combination */
|
|
double bw[8]; /* Best weight */
|
|
int biw[8]; /* Best integer weight/16 */
|
|
double dval[3]; /* Dithered value */
|
|
double err[3], werr;
|
|
double errxs;
|
|
/* Tuning params */
|
|
int nitters = 300; /* No itters */
|
|
int rand_count = 150;
|
|
double rand_start = 4.5; /* Ramp with itters */
|
|
double rand_end = 0.2;
|
|
double rand_pow = 1.5;
|
|
double mxerrxs = 2.5; /* accumulated error reset threshold */
|
|
unsigned int randv = 0x1234;
|
|
|
|
/* 32 bit pseudo random sequencer based on XOR feedback */
|
|
/* generates number between 1 and 4294967295 */
|
|
#define PSRAND32F(S) (((S) & 0x80000000) ? (((S) << 1) ^ 0xa398655d) : ((S) << 1))
|
|
|
|
/* Locate the 8 surrounding RGB vertices */
|
|
for (n = 0; n < 8; n++) {
|
|
for (k = 0; k < 3; k++) {
|
|
if (n & (1 << k))
|
|
sur[n][k] = ceil(val[k]);
|
|
else
|
|
sur[n][k] = floor(val[k]);
|
|
}
|
|
|
|
//printf("Input sur %d: %f %f %f\n",n,sur[n][0], sur[n][1], sur[n][2], sur[n][3]);
|
|
/* Figure out what RGB values to use to surround the point, */
|
|
/* and what actual RGB values to expect from using them. */
|
|
quant_rgb(n, ressur[n], sur[n]);
|
|
//printf("Quant sur %d: %f %f %f\n",n,sur[n][0], sur[n][1], sur[n][2], sur[n][3]);
|
|
//printf(" ressur %d: %f %f %f\n",n,ressur[n][0], ressur[n][1], ressur[n][2], ressur[n][3]);
|
|
// printf("\n");
|
|
}
|
|
|
|
/* Reduce this to unique surrounders */
|
|
for (nsur = 0, i = 0; i < 8; i++) {
|
|
|
|
/* Check if the i'th entry is already in the list */
|
|
for (j = 0; j < nsur; j++) {
|
|
for (k = 0; k < 3; k++) {
|
|
if (ressur[i][k] != ressur[j][k])
|
|
break;
|
|
}
|
|
if (k < 3)
|
|
continue; /* Unique */
|
|
break; /* Duplicate */
|
|
}
|
|
if (j < nsur) /* Duplicate */
|
|
continue;
|
|
|
|
/* Copy i'th to nsur */
|
|
for (k = 0; k < 3; k++) {
|
|
sur[nsur][k] = sur[i][k];
|
|
ressur[nsur][k] = ressur[i][k];
|
|
}
|
|
nsur++;
|
|
}
|
|
|
|
#ifdef DIAGNOSTICS
|
|
if (vv) {
|
|
printf("There are %d unique surrounders:\n",nsur);
|
|
for (n = 0; n < nsur; n++) {
|
|
printf("sur %f %f %f\n",ressur[n][0], ressur[n][1], ressur[n][2]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Use an optimzer to set the initial values using all the unique */
|
|
/* surrounders. */
|
|
{
|
|
double s[8];
|
|
optcntx cntx;
|
|
|
|
ncomb = nsur;
|
|
for (n = 0; n < nsur; n++) {
|
|
bcc[n] = n;
|
|
bw[n] = 1.0/nsur;
|
|
s[n] = 0.1;
|
|
}
|
|
|
|
cntx.di = nsur-1;
|
|
cntx.val = val;
|
|
cntx.ressur = &ressur;
|
|
|
|
powell(NULL, cntx.di, bw, s, 1e-4, 1000, optfunc, &cntx, NULL, NULL);
|
|
|
|
/* Compute baricentric values */
|
|
bw[nsur-1] = 0.0;
|
|
for (n = 0; n < (nsur-1); n++) {
|
|
if (bw[n] < 0.0)
|
|
bw[n] = 0.0;
|
|
else if (bw[n] > 1.0)
|
|
bw[n] = 1.0;
|
|
bw[nsur-1] += bw[n];
|
|
}
|
|
if (bw[nsur-1] > 1.0) { /* They summed to over 1.0 */
|
|
for (n = 0; n < (nsur-1); n++)
|
|
bw[n] *= 1.0/bw[nsur-1]; /* Scale them down */
|
|
bw[nsur-1] = 1.0;
|
|
}
|
|
bw[nsur-1] = 1.0 - bw[nsur-1]; /* Remainder */
|
|
}
|
|
|
|
/* Check the result */
|
|
#ifdef DIAGNOSTICS
|
|
if (vv) {
|
|
double tmp[3], err;
|
|
|
|
/* Compute interpolated result */
|
|
for (k = 0; k < 3; k++)
|
|
tmp[k] = 0.0;
|
|
for (n = 0; n < ncomb; n++) {
|
|
for (k = 0; k < 3; k++)
|
|
tmp[k] += bw[n] * ressur[bcc[n]][k];
|
|
}
|
|
/* Compute error */
|
|
err = 0.0;
|
|
for (k = 0; k < 3; k++) {
|
|
tmp[k] -= val[k];
|
|
err += tmp[k] * tmp[k];
|
|
}
|
|
err = sqrt(err);
|
|
for (n = 0; n < ncomb; n++)
|
|
printf("Comb %d weight %f rgb %f %f %f\n",bcc[n],bw[n],ressur[bcc[n]][0],ressur[bcc[n]][1],ressur[bcc[n]][2]);
|
|
printf("Error %f %f %f rms %f\n",tmp[0], tmp[1], tmp[2], err);
|
|
printf("\n");
|
|
}
|
|
#endif
|
|
|
|
/* Compute the number of pixels for each surounder value */
|
|
{
|
|
int sw[8], rem;
|
|
|
|
/* Sort the weightings from smallest to largest */
|
|
for (n = 0; n < 8; n++)
|
|
sw[n] = n;
|
|
|
|
for (i = 0; i < (ncomb-1); i++) {
|
|
for (j = i+1; j < ncomb; j++) {
|
|
if (bw[sw[j]] < bw[sw[i]]) {
|
|
int tt = sw[i]; /* Swap them */
|
|
sw[i] = sw[j];
|
|
sw[j] = tt;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Compute the nearest integer weighting out of 16 */
|
|
rem = 16;
|
|
for (i = 0; i < (ncomb-1) && rem > 0; i++) {
|
|
n = sw[i];
|
|
biw[n] = (int)(16.0 * bw[n] + 0.5);
|
|
rem -= biw[n];
|
|
if (rem <= 0)
|
|
rem = 0;
|
|
}
|
|
for (; i < ncomb; i++) {
|
|
n = sw[i];
|
|
biw[n] = rem;
|
|
}
|
|
#ifdef DIAGNOSTICS
|
|
if (vv) {
|
|
for (n = 0; n < ncomb; n++)
|
|
printf("Comb %d iweight %i rgb %f %f %f\n",bcc[n],biw[n],ressur[bcc[n]][0],ressur[bcc[n]][1],ressur[bcc[n]][2]);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Set the initial pattern according to the integer weighting */
|
|
for (cix = 0, n = 0; n < ncomb; n++) {
|
|
for (i = 0; i < biw[n]; i++) {
|
|
x = order[cix].x;
|
|
y = order[cix].y;
|
|
cix++;
|
|
for (k = 0; k < 3; k++) {
|
|
tpat[x][y][k] = itpat[x][y][k] = sur[bcc[n]][k];
|
|
rtpat[x][y][k] = irtpat[x][y][k] = ressur[bcc[n]][k];
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef DIAGNOSTICS
|
|
/* Check initial pattern error */
|
|
if (vv) {
|
|
printf("Input pat:\n");
|
|
for (x = 0; x < DISIZE; x++) {
|
|
for (y = 0; y < DISIZE; y++) {
|
|
if (y > 0)
|
|
printf(", ");
|
|
printf("%3.0f %3.0f %3.0f",rtpat[x][y][0], rtpat[x][y][1], rtpat[x][y][2]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
upsample(dval, rtpat);
|
|
|
|
werr = 0.0;
|
|
for (k = 0; k < 3; k++) {
|
|
err[k] = dval[k] - val[k];
|
|
if (fabs(err[k]) > werr)
|
|
werr = fabs(err[k]);
|
|
}
|
|
|
|
#ifdef DIAGNOSTICS
|
|
if (vv) {
|
|
printf("Target %f %f %f -> %f %f %f\n", val[0], val[1], val[2], dval[0], dval[1], dval[2]);
|
|
printf("Error %f %f %f werr %f\n", err[0], err[1], err[2], werr);
|
|
}
|
|
#endif
|
|
|
|
berr = werr;
|
|
for (x = 0; x < DISIZE; x++) {
|
|
for (y = 0; y < DISIZE; y++) {
|
|
for (k = 0; k < 3; k++)
|
|
ipat[x][y][k] = tpat[x][y][k];
|
|
}
|
|
}
|
|
|
|
/* Improve fit if needed */
|
|
/* This is a bit stocastic */
|
|
errxs = 0.0;
|
|
for (ii = 0; ii < nitters; ii++) { /* Until we give up */
|
|
double corr[3]; /* Correction direction needed */
|
|
double bdot;
|
|
int bx, by;
|
|
|
|
double wycc, mm;
|
|
double cell[3]; /* Cell being modified value */
|
|
double ccell[3]; /* Corrected cell */
|
|
double pcell[3]; /* Proposed new cell value */
|
|
|
|
for (k = 0; k < 3; k++)
|
|
corr[k] = val[k] - dval[k];
|
|
#ifdef DIAGNOSTICS
|
|
if (vv)
|
|
printf("corr needed %f %f %f\n", corr[0], corr[1], corr[2]);
|
|
#endif
|
|
|
|
/* Scale it and limit it */
|
|
for (k = 0; k < 3; k++) {
|
|
double dd = 16.0 * corr[k];
|
|
if (dd >= 1.0)
|
|
dd = 1.0;
|
|
else if (dd <= -1.0)
|
|
dd = -1.0;
|
|
else
|
|
dd = 0.0;
|
|
corr[k] = dd;
|
|
}
|
|
|
|
if (corr[0] == 0.0 && corr[1] == 0 && corr[2] == 0.0) {
|
|
#ifdef DIAGNOSTICS
|
|
if (vv)
|
|
printf("No correction possible - done\n");
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
#ifdef DIAGNOSTICS
|
|
if (vv)
|
|
printf("scaled corr %f %f %f\n", corr[0], corr[1], corr[2]);
|
|
#endif
|
|
|
|
/* Search dither cell and surrounder for a combination */
|
|
/* that is closest to the change we want to make. */
|
|
bdot = 1e6;
|
|
bx = by = n = 0;
|
|
for (x = 0; x < DISIZE; x++) {
|
|
double rlevel = rand_start + (rand_end - rand_start)
|
|
* pow((ii % rand_count)/rand_count, rand_pow);
|
|
|
|
for (y = 0; y < DISIZE; y++) {
|
|
for (i = 0; i < ncomb; i++) {
|
|
double dot = 0.0;
|
|
for (k = 0; k < 3; k++)
|
|
dot += (ressur[bcc[i]][k] - rtpat[x][y][k]) * corr[k];
|
|
|
|
/* Ramp the randomness up */
|
|
// dot += d_rand(0.0, 0.1 + (2.5-0.1) * ii/nitters);
|
|
// dot += d_rand(-rlevel, rlevel);
|
|
/* use a deterministic random element, so that */
|
|
/* the dither patterns are repeatable. */
|
|
randv = PSRAND32F(randv);
|
|
dot += rlevel * 2.0 * ((randv - 1)/4294967294.0 - 0.5);
|
|
|
|
if (dot <= 0.0)
|
|
dot = 1e7;
|
|
else {
|
|
dot = (dot - 1.0) * (dot - 1.0);
|
|
}
|
|
//printf("dot %f from sur %f %f %f to pat %f %f %f\n", dot, ressur[bcc[i]][0], ressur[bcc[i]][1], ressur[bcc[i]][2], rtpat[x][y][0], rtpat[x][y][1], rtpat[x][y][2]);
|
|
|
|
if (dot < bdot) {
|
|
bdot = dot;
|
|
bx = x;
|
|
by = y;
|
|
n = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef DIAGNOSTICS
|
|
if (vv) {
|
|
printf("Changing cell [%d][%d] %f %f %f with dot %f\n",bx,by,rtpat[bx][by][0],rtpat[bx][by][1],rtpat[bx][by][2],bdot);
|
|
printf(" to sur %d: %f %f %f\n",n, ressur[bcc[n]][0], ressur[bcc[n]][1], ressur[bcc[n]][2]);
|
|
}
|
|
#endif
|
|
|
|
/* Substitute the best correction for this cell */
|
|
for (k = 0; k < 3; k++) {
|
|
tpat[bx][by][k] = sur[bcc[n]][k];
|
|
rtpat[bx][by][k] = ressur[bcc[n]][k];
|
|
}
|
|
|
|
#ifdef DIAGNOSTICS
|
|
if (vv) {
|
|
printf("Input pat:\n");
|
|
for (x = 0; x < DISIZE; x++) {
|
|
for (y = 0; y < DISIZE; y++) {
|
|
if (y > 0)
|
|
printf(", ");
|
|
printf("%3.0f %3.0f %3.0f",rtpat[x][y][0], rtpat[x][y][1], rtpat[x][y][2]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
upsample(dval, rtpat);
|
|
|
|
werr = 0.0;
|
|
for (k = 0; k < 3; k++) {
|
|
err[k] = dval[k] - val[k];
|
|
if (fabs(err[k]) > werr)
|
|
werr = fabs(err[k]);
|
|
}
|
|
|
|
if (werr > berr) {
|
|
errxs += werr - berr;
|
|
}
|
|
|
|
/* New best */
|
|
if (werr < berr) {
|
|
berr = werr;
|
|
errxs = 0.0;
|
|
for (x = 0; x < DISIZE; x++) {
|
|
for (y = 0; y < DISIZE; y++) {
|
|
for (k = 0; k < 3; k++) {
|
|
ipat[x][y][k] = tpat[x][y][k];
|
|
|
|
itpat[x][y][k] = tpat[x][y][k];
|
|
irtpat[x][y][k] = rtpat[x][y][k];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef DIAGNOSTICS
|
|
if (vv) {
|
|
printf("Target %f %f %f -> %f %f %f\n", val[0], val[1], val[2], dval[0], dval[1], dval[2]);
|
|
printf("Error %f %f %f werr %f\n", err[0], err[1], err[2], werr);
|
|
}
|
|
#endif
|
|
|
|
if (berr < 0.11) {
|
|
#ifdef DIAGNOSTICS
|
|
if (vv)
|
|
printf("best error %f < 0.11 - give up\n",berr);
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
/* If we're not making progress, reset to the last best */
|
|
if (errxs > mxerrxs) {
|
|
#ifdef DIAGNOSTICS
|
|
if (vv)
|
|
printf("Restarting at ii %d \n",ii);
|
|
#endif
|
|
errxs = 0.0;
|
|
for (x = 0; x < DISIZE; x++) {
|
|
for (y = 0; y < DISIZE; y++) {
|
|
for (k = 0; k < 3; k++) {
|
|
tpat[x][y][k] = itpat[x][y][k];
|
|
rtpat[x][y][k] = irtpat[x][y][k];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef DIAGNOSTICS
|
|
if (vv) {
|
|
printf("Returning best error %f pat:\n",berr);
|
|
for (y = 0; y < DISIZE; y++) {
|
|
for (x = 0; x < DISIZE; x++) {
|
|
if (x > 0)
|
|
printf(", ");
|
|
printf("%3.0f %3.0f %3.0f",ipat[x][y][0], ipat[x][y][1], ipat[x][y][2]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return berr;
|
|
}
|
|
|
|
/* ====================================================================================== */
|
|
|
|
#ifdef STANDALONE_TEST
|
|
|
|
int
|
|
main(int argc,
|
|
char *argv[]
|
|
) {
|
|
double val[3], out[3], err;
|
|
double ipat[DISIZE][DISIZE][3];
|
|
double aerr, acount, xerr;
|
|
int x, y;
|
|
int i, j;
|
|
int k, nn;
|
|
|
|
printf("Hi there\n");
|
|
|
|
rand32(time(NULL));
|
|
|
|
#ifdef TEST_POINT
|
|
for (x = 0; x < DISIZE; x++) {
|
|
for (y = 0; y < DISIZE; y++) {
|
|
ipat[x][y][0] = 0.0;
|
|
ipat[x][y][1] = 0.0;
|
|
ipat[x][y][2] = 0.0;
|
|
}
|
|
}
|
|
|
|
ipat[5][4][0] = 255.0;
|
|
ipat[5][4][1] = 255.0;
|
|
ipat[5][4][2] = 255.0;
|
|
|
|
upsample(NULL, ipat);
|
|
|
|
#else
|
|
|
|
#ifdef NEVER
|
|
|
|
// val[0] = 201.500000;
|
|
// val[1] = 115.533403;
|
|
// val[2] = 76.300000;
|
|
|
|
// val[0] = 255.000000;
|
|
// val[1] = 115.533403;
|
|
// val[2] = 255.000000;
|
|
|
|
// val[0] = 221.689875;
|
|
// val[1] = 29.593255;
|
|
// val[2] = 140.820878;
|
|
|
|
// val[0] = 212.377797;
|
|
// val[1] = 228.338903;
|
|
// val[2] = 70.153296;
|
|
|
|
// val[0] = 231.554511;
|
|
// val[1] = 0.000000;
|
|
// val[2] = 51.958048;
|
|
|
|
// val[0] = 255.000000;
|
|
// val[1] = 144.768052;
|
|
// val[2] = 179.737212;
|
|
|
|
// val[0] = 194.854956;
|
|
// val[1] = 41.901887;
|
|
// val[2] = 20.434793;
|
|
|
|
// val[0] = 250.100121;
|
|
// val[1] = 83.484217;
|
|
// val[2] = 42.867603;
|
|
|
|
// val[0] = 255.000000;
|
|
// val[1] = 255.000000;
|
|
// val[2] = 228.534759;
|
|
|
|
// 1.71 -> 1.58, rand 2.2
|
|
// val[0] = 255.000000;
|
|
// val[1] = 176.894769;
|
|
// val[2] = 8.932806;
|
|
|
|
// 1.54 -> 0.762592, rand 0.3
|
|
// val[0] = 216.873703;
|
|
// val[1] = 250.908094;
|
|
|
|
// 1.05
|
|
// val[0] = 167.284458;
|
|
// val[1] = 248.945210;
|
|
// val[2] = 199.023452;
|
|
|
|
// 1.07
|
|
// val[0] = 211.045184;
|
|
// val[1] = 27.825141;
|
|
// val[2] = 63.883148;
|
|
|
|
// 1.928
|
|
// val[0] = 255.000000;
|
|
// val[1] = 0.439284;
|
|
// val[2] = 210.928135;
|
|
|
|
// 1.278
|
|
// val[0] = 218.693614;
|
|
// val[1] = 222.890101;
|
|
// val[2] = 174.779727;
|
|
|
|
// 1.334501
|
|
// val[0] = 253.931573;
|
|
// val[1] = 230.278945;
|
|
// val[2] = 185.677389;
|
|
|
|
// printf("In RGB %f %f %f\n",val[0],val[1],val[2]);
|
|
// ccast2YCbCr(NULL, out, val);
|
|
// printf("YCbCr %f %f %f\n",out[0],out[1],out[2]);
|
|
// YCbCr2ccast(NULL, out, out);
|
|
// printf("RGB %f %f %f\n",out[0],out[1],out[2]);
|
|
|
|
|
|
vv = 1;
|
|
err = get_ccast_dith(ipat, val);
|
|
|
|
printf("Got pat with err %f\n",err);
|
|
|
|
#else
|
|
aerr = 0.0;
|
|
acount = 0.0;
|
|
xerr = 0.0;
|
|
for (nn = 0; nn < 200000; nn++) {
|
|
|
|
for (k = 0; k < 3; k++) {
|
|
val[k] = d_rand(-5.0, 255.0 + 5.0);
|
|
if (val[k] < 0.0)
|
|
val[k] = 0.0;
|
|
else if (val[k] > 255.0)
|
|
val[k] = 255.0;
|
|
}
|
|
|
|
err = get_ccast_dith(ipat, val);
|
|
|
|
if (err >= 1.5 ||
|
|
( err >= 1.0
|
|
&& val[0] != 0.0 && val[0] != 255.0
|
|
&& val[1] != 0.0 && val[1] != 255.0
|
|
&& val[2] != 0.0 && val[2] != 255.0)) {
|
|
printf("Target RGB %f %f %f, err %f\n", val[0], val[1], val[2],err);
|
|
// vv = 1;
|
|
// comput_pat(ipat, val);
|
|
// break;
|
|
}
|
|
|
|
aerr += err;
|
|
acount++;
|
|
if (err > xerr)
|
|
xerr = err;
|
|
}
|
|
|
|
aerr /= acount;
|
|
printf("After %d trials, aerr = %f, maxerr = %f\n",nn,aerr,xerr);
|
|
#endif
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* STANDALONE_TEST */
|