from Alexander Kuznetsov (alexk) --- copied from the tracker Every image inside Blender is in linear color space and gets converted to SRGB upon saving. Level node analyzed the linear image, which was not the one user saw because other output nodes converted image to sRGB. This fix analyzes the image that user see (converting it to correct color space). Here is difference: http://www.pasteall.org/pic/show.php?id=5559 First histogram (before the fix) tells that image is underexposed, which is not the case.
349 lines
7.9 KiB
C
349 lines
7.9 KiB
C
/**
|
|
* $Id$
|
|
*
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
*
|
|
* 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, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* The Original Code is Copyright (C) 2006 Blender Foundation.
|
|
* All rights reserved.
|
|
*
|
|
* The Original Code is: all of this file.
|
|
*
|
|
* Contributor(s): Bob Holcomb.
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
#include "../CMP_util.h"
|
|
|
|
|
|
/* **************** LEVELS ******************** */
|
|
static bNodeSocketType cmp_node_view_levels_in[]= {
|
|
{ SOCK_RGBA, 1, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
|
|
{ -1, 0, "" }
|
|
};
|
|
|
|
static bNodeSocketType cmp_node_view_levels_out[]={
|
|
{SOCK_VALUE, 0,"Mean",0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
|
|
{SOCK_VALUE, 0,"Std Dev",0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
|
|
{-1,0,""}
|
|
};
|
|
|
|
static void rgb_tobw(float r, float g, float b, float* out)
|
|
{
|
|
*out= r*0.35f + g*0.45f + b*0.2f;
|
|
}
|
|
|
|
static void fill_bins(bNode* node, CompBuf* in, int* bins, int colorcor)
|
|
{
|
|
float value[4];
|
|
int ivalue=0;
|
|
int x,y;
|
|
|
|
/*fill bins */
|
|
for(y=0; y<in->y; y++) {
|
|
for(x=0; x<in->x; x++) {
|
|
|
|
/* get the pixel */
|
|
qd_getPixel(in, x, y, value);
|
|
|
|
if(value[3] > 0.0) { /* don't count transparent pixels */
|
|
switch(node->custom1) {
|
|
case 1: { /* all colors */
|
|
if(colorcor)
|
|
linearrgb_to_srgb_v3_v3(&value[0],&value[0]);
|
|
rgb_tobw(value[0],value[1],value[2], &value[0]);
|
|
value[0]=value[0]*255; /* scale to 0-255 range */
|
|
ivalue=(int)value[0];
|
|
break;
|
|
}
|
|
case 2: { /* red channel */
|
|
if(colorcor)
|
|
value[0]=linearrgb_to_srgb(value[0]);
|
|
value[0]=value[0]*255; /* scale to 0-255 range */
|
|
ivalue=(int)value[0];
|
|
break;
|
|
}
|
|
case 3: { /* green channel */
|
|
if(colorcor)
|
|
value[1]=linearrgb_to_srgb(value[1]);
|
|
value[1]=value[1]*255; /* scale to 0-255 range */
|
|
ivalue=(int)value[1];
|
|
break;
|
|
}
|
|
case 4: /*blue channel */
|
|
{
|
|
if(colorcor)
|
|
value[2]=linearrgb_to_srgb(value[2]);
|
|
value[2]=value[2]*255; /* scale to 0-255 range */
|
|
ivalue=(int)value[2];
|
|
break;
|
|
}
|
|
case 5: /* luminence */
|
|
{
|
|
if(colorcor)
|
|
linearrgb_to_srgb_v3_v3(&value[0],&value[0]);
|
|
rgb_to_yuv(value[0],value[1],value[2], &value[0], &value[1], &value[2]);
|
|
value[0]=value[0]*255; /* scale to 0-255 range */
|
|
ivalue=(int)value[0];
|
|
break;
|
|
}
|
|
} /*end switch */
|
|
|
|
/*clip*/
|
|
if(ivalue<0) ivalue=0;
|
|
if(ivalue>255) ivalue=255;
|
|
|
|
/*put in the correct bin*/
|
|
bins[ivalue]+=1;
|
|
} /*end if alpha */
|
|
}
|
|
}
|
|
}
|
|
|
|
static float brightness_mean(bNode* node, CompBuf* in)
|
|
{
|
|
float sum=0.0;
|
|
int numPixels=0.0;
|
|
int x,y;
|
|
float value[4];
|
|
|
|
for(x=0; x< in->x; x++) {
|
|
for(y=0; y < in->y; y++) {
|
|
|
|
/* get the pixel */
|
|
qd_getPixel(in, x, y, value);
|
|
|
|
if(value[3] > 0.0) { /* don't count transparent pixels */
|
|
numPixels++;
|
|
switch(node->custom1)
|
|
{
|
|
case 1:
|
|
{
|
|
rgb_tobw(value[0],value[1],value[2], &value[0]);
|
|
sum+=value[0];
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
sum+=value[0];
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
sum+=value[1];
|
|
break;
|
|
}
|
|
case 4:
|
|
{
|
|
sum+=value[2];
|
|
break;
|
|
}
|
|
case 5:
|
|
{
|
|
rgb_to_yuv(value[0],value[1],value[2], &value[0], &value[1], &value[2]);
|
|
sum+=value[0];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return sum/numPixels;
|
|
}
|
|
|
|
static float brightness_standard_deviation(bNode* node, CompBuf* in, float mean)
|
|
{
|
|
float sum=0.0;
|
|
int numPixels=0.0;
|
|
int x,y;
|
|
float value[4];
|
|
|
|
for(x=0; x< in->x; x++) {
|
|
for(y=0; y < in->y; y++) {
|
|
|
|
/* get the pixel */
|
|
qd_getPixel(in, x, y, value);
|
|
|
|
if(value[3] > 0.0) { /* don't count transparent pixels */
|
|
numPixels++;
|
|
switch(node->custom1)
|
|
{
|
|
case 1:
|
|
{
|
|
rgb_tobw(value[0],value[1],value[2], &value[0]);
|
|
sum+=(value[0]-mean)*(value[0]-mean);
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
sum+=value[0];
|
|
sum+=(value[0]-mean)*(value[0]-mean);
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
sum+=value[1];
|
|
sum+=(value[1]-mean)*(value[1]-mean);
|
|
break;
|
|
}
|
|
case 4:
|
|
{
|
|
sum+=value[2];
|
|
sum+=(value[2]-mean)*(value[2]-mean);
|
|
break;
|
|
}
|
|
case 5:
|
|
{
|
|
rgb_to_yuv(value[0],value[1],value[2], &value[0], &value[1], &value[2]);
|
|
sum+=(value[0]-mean)*(value[0]-mean);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return sqrt(sum/(float)(numPixels-1));
|
|
}
|
|
|
|
static void draw_histogram(bNode *node, CompBuf *out, int* bins)
|
|
{
|
|
int x,y;
|
|
float color[4];
|
|
float value;
|
|
int max;
|
|
|
|
/* find max value */
|
|
max=0;
|
|
for(x=0; x<256; x++) {
|
|
if(bins[x]>max) max=bins[x];
|
|
}
|
|
|
|
/*draw histogram in buffer */
|
|
for(x=0; x<out->x; x++) {
|
|
for(y=0;y<out->y; y++) {
|
|
|
|
/* get normalized value (0..255) */
|
|
value=((float)bins[x]/(float)max)*255.0;
|
|
|
|
if(y < (int)value) { /*if the y value is below the height of the bar for this line then draw with the color */
|
|
switch (node->custom1) {
|
|
case 1: { /* draw in black */
|
|
color[0]=0.0; color[1]=0.0; color[2]=0.0; color[3]=1.0;
|
|
break;
|
|
}
|
|
case 2: { /* draw in red */
|
|
color[0]=1.0; color[1]=0.0; color[2]=0.0; color[3]=1.0;
|
|
break;
|
|
}
|
|
case 3: { /* draw in green */
|
|
color[0]=0.0; color[1]=1.0; color[2]=0.0; color[3]=1.0;
|
|
break;
|
|
}
|
|
case 4: { /* draw in blue */
|
|
color[0]=0.0; color[1]=0.0; color[2]=1.0; color[3]=1.0;
|
|
break;
|
|
}
|
|
case 5: { /* draw in white */
|
|
color[0]=1.0; color[1]=1.0; color[2]=1.0; color[3]=1.0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else{
|
|
color[0]=0.8; color[1]=0.8; color[2]=0.8; color[3]=1.0;
|
|
}
|
|
|
|
/* set the color */
|
|
qd_setPixel(out, x, y, color);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void node_composit_exec_view_levels(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
|
|
{
|
|
CompBuf* cbuf;
|
|
CompBuf* histogram;
|
|
RenderData *rd=data;
|
|
float mean, std_dev;
|
|
int bins[256];
|
|
int x;
|
|
|
|
if(in[0]->hasinput==0) return;
|
|
if(in[0]->data==NULL) return;
|
|
|
|
histogram=alloc_compbuf(256, 256, CB_RGBA, 1);
|
|
cbuf=typecheck_compbuf(in[0]->data, CB_RGBA);
|
|
|
|
/*initalize bins*/
|
|
for(x=0; x<256; x++) {
|
|
bins[x]=0;
|
|
}
|
|
|
|
/*fill bins */
|
|
fill_bins(node, in[0]->data, bins, rd->color_mgt_flag & R_COLOR_MANAGEMENT);
|
|
|
|
/* draw the histogram chart */
|
|
draw_histogram(node, histogram, bins);
|
|
|
|
/* calculate the average brightness and contrast */
|
|
mean=brightness_mean(node, in[0]->data);
|
|
std_dev=brightness_standard_deviation(node, in[0]->data, mean);
|
|
|
|
/* Printf debuging ;)
|
|
printf("Mean: %f\n", mean);
|
|
printf("Std Dev: %f\n", std_dev);
|
|
*/
|
|
|
|
if(out[0]->hasoutput)
|
|
out[0]->vec[0]= mean;
|
|
if(out[1]->hasoutput)
|
|
out[1]->vec[0]= std_dev;
|
|
|
|
generate_preview(data, node, histogram);
|
|
|
|
if(cbuf!=in[0]->data)
|
|
free_compbuf(cbuf);
|
|
free_compbuf(histogram);
|
|
}
|
|
|
|
static void node_composit_init_view_levels(bNode* node)
|
|
{
|
|
node->custom1=1; /*All channels*/
|
|
}
|
|
|
|
bNodeType cmp_node_view_levels= {
|
|
/* *next,*prev */ NULL, NULL,
|
|
/* type code */ CMP_NODE_VIEW_LEVELS,
|
|
/* name */ "Levels",
|
|
/* widthrange */ 140, 100, 320,
|
|
/* classopts */ NODE_CLASS_OUTPUT, NODE_OPTIONS|NODE_PREVIEW,
|
|
/* input sock */ cmp_node_view_levels_in,
|
|
/* output sock */ cmp_node_view_levels_out,
|
|
/* storage */ "ImageUser",
|
|
/* execfunc */ node_composit_exec_view_levels,
|
|
/* butfunc */ NULL,
|
|
/* initfunc */ node_composit_init_view_levels,
|
|
/* freestoragefunc */ NULL,
|
|
/* copystoragefunc */ NULL,
|
|
/* id */ NULL
|
|
|
|
};
|
|
|