PDA

Ver la versión completa : Función scale_colors() comentada



Guillermo Luijk
24/05/2008, 17:40
Jugando con la última versión del revelador de Manuel que trae el punto de negro y saturación, me he dado cuenta de un funcionamiento de los mismos que no me esperaba (lo comento al final, es lo de menos); para entender el porqué me he estudiado un poco la función scale_colors() que es la que aplica dichas correcciones.

La pongo aquí porque es quizá la función más interesante de DCRAW y que reciclaremos para Perfect Blend. scale_colors() tiene las funciones:

Calcular el balance de blancos automático
Llamada a la reducción de ruido Wavelet
Calcular los multiplicadores normalizados del balance de blancos
Aplicar la sustracción del nivel negro y escalar al punto de saturación
Aplicar el balance de blancos (se hace a la vez que el anterior punto)
Aplicar la corrección de la Aberración cromática






void CLASS scale_colors()
{
unsigned bottom, right, size, row, col, ur, uc, i, x, y, c, sum[8];

/* DARKNESS: dark */
int val, dark, sat;
double dsum[8], dmin, dmax;
float scale_mul[4], fr, fc;
ushort *img=0, *pix;

if (user_mul[0])
memcpy (pre_mul, user_mul, sizeof pre_mul);

/* WB automático */
if (use_auto_wb || (use_camera_wb && cam_mul[0] == -1)) {
memset (dsum, 0, sizeof dsum);
bottom = MIN (greybox[1]+greybox[3], height);
right = MIN (greybox[0]+greybox[2], width);
for (row=greybox[1]; row < bottom; row += 8)
for (col=greybox[0]; col < right; col += 8) {
memset (sum, 0, sizeof sum);
for (y=row; y < row+8 && y < bottom; y++)
for (x=col; x < col+8 && x < right; x++)
FORC4 {
if (filters) {
c = FC(y,x);
val = BAYER(y,x);
} else
val = image[y*width+x][c];

/* Nivel cercano a saturar (maximum-25) */
/* se descarta para cálculo de WB */
if (val > maximum-25) goto skip_block;
if ((val -= black) < 0) val = 0;
sum[c] += val;
sum[c+4]++;
if (filters) break;
}
FORC(8) dsum[c] += sum[c];
skip_block: ;
}
FORC4 if (dsum[c]) pre_mul[c] = dsum[c+4] / dsum[c];
}

/* Se ha pedido WB de la cámara Y existe WB de la cámara */
if (use_camera_wb && cam_mul[0] != -1) {
memset (sum, 0, sizeof sum);
for (row=0; row < 8; row++)
for (col=0; col < 8; col++) {
c = FC(row,col);
if ((val = white[row][col] - black) > 0)
sum[c] += val;
sum[c+4]++;
}
if (sum[0] && sum[1] && sum[2] && sum[3])
FORC4 pre_mul[c] = (float) sum[c+4] / sum[c];
else if (cam_mul[0] && cam_mul[2])
memcpy (pre_mul, cam_mul, sizeof pre_mul);
else
fprintf (stderr,_("%s: Cannot use camera white balance.\n"), ifname);
}

if (pre_mul[3] == 0) pre_mul[3] = colors < 4 ? pre_mul[1] : 1;

/* Nivel de negro calculado de píxels ocultos: black */
dark = black;
sat = maximum;
if (threshold) wavelet_denoise();

/* maximum se corrige a la baja por el nivel de negro */
maximum -= black;

/* dmax normalizará los multiplicadores para que */
/* el menor (o el mayor) valga siempre 1.0 */
for (dmin=DBL_MAX, dmax=c=0; c < 4; c++) {
if (dmin > pre_mul[c])
dmin = pre_mul[c];
if (dmax < pre_mul[c])
dmax = pre_mul[c];
}
if (!highlight) dmax = dmin;

/* Se introduce EN LOS MULTIPLICADORES el escalado para */
/* ajustar por maximum corregido: 65535.0 / maximum */
FORC4 scale_mul[c] = (pre_mul[c] /= dmax) * 65535.0 / maximum;
if (verbose) {
fprintf (stderr,
_("Scaling with darkness %d, saturation %d, and\nmultipliers"), dark, sat);
FORC4 fprintf (stderr, " %f", pre_mul[c]);
fputc ('\n', stderr);
}

/* Se aplica sobre la imagen el reescalado y el */
/* balance de blancos a la vez */
size = iheight*iwidth;
for (i=0; i < size*4; i++) {
val = image[0][i];
if (!val) continue; /* Nivel vacío (aún no se ha interpolado Bayer) */
val -= black; /* Restamos nivel de negro */
val *= scale_mul[i & 3]; /* Escalamos (WB + ajuste sat.) */
image[0][i] = CLIP(val);
}

/* Corrección de aberración cromática */
if ((aber[0] != 1 || aber[2] != 1) && colors == 3) {
if (verbose)
fprintf (stderr,_("Correcting chromatic aberration...\n"));
for (c=0; c < 4; c+=2) {
if (aber[c] == 1) continue;
img = (ushort *) malloc (size * sizeof *img);
merror (img, "scale_colors()");
for (i=0; i < size; i++)
img[i] = image[i][c];
for (row=0; row < iheight; row++) {
ur = fr = (row - iheight*0.5) * aber[c] + iheight*0.5;
if (ur > iheight-2) continue;
fr -= ur;
for (col=0; col < iwidth; col++) {
uc = fc = (col - iwidth*0.5) * aber[c] + iwidth*0.5;
if (uc > iwidth-2) continue;
fc -= uc;
pix = img + ur*iwidth + uc;
image[row*iwidth+col][c] =
(pix[ 0]*(1-fc) + pix[ 1]*fc) * (1-fr) +
(pix[iwidth]*(1-fc) + pix[iwidth+1]*fc) * fr;
}
}
free(img);
}
}
}



Me he dado cuenta de que cuando se revela en DCRAW indicando un nivel de saturación con -S, no necesariamente los niveles que queden por encima del valor suministrado en cada canal serán recortados. Cuando el balance de blancos es conservador (multiplicadores <=1.0) puede haber valores de los canales corregidos superiores al nivel de saturación que por la acción de dicho escalado de balance de blancos entren a formar parte de la imagen final al quedar por debajo del punto de saturación, y por tanto resultar tras el reescalado con valor <=65535.

No tiene gran trascendencia porque el punto de saturación sirve para eso, para saber donde satura el RAW de una cámara y pasado dicho punto no debería haber ningún nivel. Pero el comportamiento comentado significa que -S no se puede usar para establecer el nivel a partir del cual se quieren recortar las altas luces; solo ocurrirá cuando se revele con multiplicadores >=1 (-H 0) o directamente sin balance de blancos (-r 1 1 1 1).

Una pregunta sobre nomenclatura C:
Para aplicar el balance de blancos se accede a la imagen con:



for (i=0; i < size*4; i++) {
val = image[0][i];

pero en otro punto se refiere al mismo array con:



for (c=0; c < 4; c++)
for (y=row; y < row+8 && y < bottom; y++)
for (x=col; x < col+8 && x < right; x++)
val = image[y*width+x][c];

Entiendo que son equivalentes no? se hace así por comodidad, simplemente image es una tira de memoria llena de datos y en un caso ha sido más fácil accederla con 3 índices y en el otro a piñón como un array monodimensional.