Sequencer: Implement Tone Map strip modifier

Behaves same exact way as compositor node, but could be applied
in the sequencer and used as a grading tool.

Requested by the Nieve project artists.
This commit is contained in:
Sergey Sharybin 2016-01-19 15:53:43 +01:00
parent 70c690c6e4
commit 52f07ad724
4 changed files with 310 additions and 0 deletions

View File

@ -1127,6 +1127,18 @@ class SEQUENCER_PT_modifiers(SequencerButtonsPanel, Panel):
elif mod.type == 'WHITE_BALANCE':
col = box.column()
col.prop(mod, "white_value")
elif mod.type == 'TONEMAP':
col = box.column()
col.prop(mod, "tonemap_type")
if mod.tonemap_type == 'RD_PHOTORECEPTOR':
col.prop(mod, "intensity")
col.prop(mod, "contrast")
col.prop(mod, "adaptation")
col.prop(mod, "correction")
elif mod.tonemap_type == 'RH_SIMPLE':
col.prop(mod, "key")
col.prop(mod, "offset")
col.prop(mod, "gamma")
class SEQUENCER_PT_grease_pencil(GreasePencilDataPanel, SequencerButtonsPanel_Output, Panel):
bl_space_type = 'SEQUENCE_EDITOR'

View File

@ -635,6 +635,226 @@ static SequenceModifierTypeInfo seqModifier_Mask = {
maskmodifier_apply /* apply */
};
/* **** Tonemap Modifier **** */
typedef struct AvgLogLum {
SequencerTonemapModifierData *tmmd;
struct ColorSpace *colorspace;
float al;
float auto_key;
float lav;
float cav[4];
float igm;
} AvgLogLum;
static void tonemapmodifier_init_data(SequenceModifierData *smd)
{
SequencerTonemapModifierData *tmmd = (SequencerTonemapModifierData *) smd;
/* Same as tonemap compositor node. */
tmmd->type = SEQ_TONEMAP_RD_PHOTORECEPTOR;
tmmd->key = 0.18f;
tmmd->offset = 1.0f;
tmmd->gamma = 1.0f;
tmmd->intensity = 0.0f;
tmmd->contrast = 0.0f;
tmmd->adaptation = 1.0f;
tmmd->correction = 0.0f;
}
static void tonemapmodifier_apply_threaded_simple(int width,
int height,
unsigned char *rect,
float *rect_float,
unsigned char *mask_rect,
float *mask_rect_float,
void *data_v)
{
AvgLogLum *avg = (AvgLogLum *)data_v;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int pixel_index = (y * width + x) * 4;
float input[4], output[4], mask[3] = {1.0f, 1.0f, 1.0f};
/* Get input value. */
if (rect_float) {
copy_v4_v4(input, &rect_float[pixel_index]);
}
else {
straight_uchar_to_premul_float(input, &rect[pixel_index]);
}
IMB_colormanagement_colorspace_to_scene_linear_v3(input, avg->colorspace);
copy_v4_v4(output, input);
/* Get mask value. */
if (mask_rect_float) {
copy_v3_v3(mask, mask_rect_float + pixel_index);
}
else if (mask_rect) {
rgb_uchar_to_float(mask, mask_rect + pixel_index);
}
/* Apply correction. */
mul_v3_fl(output, avg->al);
float dr = output[0] + avg->tmmd->offset;
float dg = output[1] + avg->tmmd->offset;
float db = output[2] + avg->tmmd->offset;
output[0] /= ((dr == 0.0f) ? 1.0f : dr);
output[1] /= ((dg == 0.0f) ? 1.0f : dg);
output[2] /= ((db == 0.0f) ? 1.0f : db);
const float igm = avg->igm;
if (igm != 0.0f) {
output[0] = powf(max_ff(output[0], 0.0f), igm);
output[1] = powf(max_ff(output[1], 0.0f), igm);
output[2] = powf(max_ff(output[2], 0.0f), igm);
}
/* Apply mask. */
output[0] = input[0] * (1.0f - mask[0]) + output[0] * mask[0];
output[1] = input[1] * (1.0f - mask[1]) + output[1] * mask[1];
output[2] = input[2] * (1.0f - mask[2]) + output[2] * mask[2];
/* Copy result back. */
IMB_colormanagement_scene_linear_to_colorspace_v3(output, avg->colorspace);
if (rect_float) {
copy_v4_v4(&rect_float[pixel_index], output);
}
else {
premul_float_to_straight_uchar(&rect[pixel_index], output);
}
}
}
}
static void tonemapmodifier_apply_threaded_photoreceptor(int width,
int height,
unsigned char *rect,
float *rect_float,
unsigned char *mask_rect,
float *mask_rect_float,
void *data_v)
{
AvgLogLum *avg = (AvgLogLum *)data_v;
const float f = expf(-avg->tmmd->intensity);
const float m = (avg->tmmd->contrast > 0.0f) ? avg->tmmd->contrast : (0.3f + 0.7f * powf(avg->auto_key, 1.4f));
const float ic = 1.0f - avg->tmmd->correction, ia = 1.0f - avg->tmmd->adaptation;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int pixel_index = (y * width + x) * 4;
float input[4], output[4], mask[3] = {1.0f, 1.0f, 1.0f};
/* Get input value. */
if (rect_float) {
copy_v4_v4(input, &rect_float[pixel_index]);
}
else {
straight_uchar_to_premul_float(input, &rect[pixel_index]);
}
IMB_colormanagement_colorspace_to_scene_linear_v3(input, avg->colorspace);
copy_v4_v4(output, input);
/* Get mask value. */
if (mask_rect_float) {
copy_v3_v3(mask, mask_rect_float + pixel_index);
}
else if (mask_rect) {
rgb_uchar_to_float(mask, mask_rect + pixel_index);
}
/* Apply correction. */
const float L = IMB_colormanagement_get_luminance(output);
float I_l = output[0] + ic * (L - output[0]);
float I_g = avg->cav[0] + ic * (avg->lav - avg->cav[0]);
float I_a = I_l + ia * (I_g - I_l);
output[0] /= (output[0] + powf(f * I_a, m));
I_l = output[1] + ic * (L - output[1]);
I_g = avg->cav[1] + ic * (avg->lav - avg->cav[1]);
I_a = I_l + ia * (I_g - I_l);
output[1] /= (output[1] + powf(f * I_a, m));
I_l = output[2] + ic * (L - output[2]);
I_g = avg->cav[2] + ic * (avg->lav - avg->cav[2]);
I_a = I_l + ia * (I_g - I_l);
output[2] /= (output[2] + powf(f * I_a, m));
/* Apply mask. */
output[0] = input[0] * (1.0f - mask[0]) + output[0] * mask[0];
output[1] = input[1] * (1.0f - mask[1]) + output[1] * mask[1];
output[2] = input[2] * (1.0f - mask[2]) + output[2] * mask[2];
/* Copy result back. */
IMB_colormanagement_scene_linear_to_colorspace_v3(output, avg->colorspace);
if (rect_float) {
copy_v4_v4(&rect_float[pixel_index], output);
}
else {
premul_float_to_straight_uchar(&rect[pixel_index], output);
}
}
}
}
static void tonemapmodifier_apply(struct SequenceModifierData *smd,
ImBuf *ibuf,
ImBuf *mask)
{
SequencerTonemapModifierData *tmmd = (SequencerTonemapModifierData *) smd;
AvgLogLum data;
data.tmmd = tmmd;
data.colorspace = (ibuf->rect_float != NULL)
? ibuf->float_colorspace
: ibuf->rect_colorspace;
float lsum = 0.0f;
int p = ibuf->x * ibuf->y;
float *fp = ibuf->rect_float;
unsigned char *cp = (unsigned char *)ibuf->rect;
float avl, maxl = -FLT_MAX, minl = FLT_MAX;
const float sc = 1.0f / p;
float Lav = 0.f;
float cav[4] = {0.0f, 0.0f, 0.0f, 0.0f};
while (p--) {
float pixel[4];
if (fp != NULL) {
copy_v4_v4(pixel, fp);
}
else {
straight_uchar_to_premul_float(pixel, cp);
}
IMB_colormanagement_colorspace_to_scene_linear_v3(pixel, data.colorspace);
float L = IMB_colormanagement_get_luminance(pixel);
Lav += L;
add_v3_v3(cav, pixel);
lsum += logf(max_ff(L, 0.0f) + 1e-5f);
maxl = (L > maxl) ? L : maxl;
minl = (L < minl) ? L : minl;
if (fp != NULL) {
fp += 4;
} else {
cp += 4;
}
}
data.lav = Lav * sc;
mul_v3_v3fl(data.cav, cav, sc);
maxl = logf(maxl + 1e-5f);
minl = logf(minl + 1e-5f);
avl = lsum * sc;
data.auto_key = (maxl > minl) ? ((maxl - avl) / (maxl - minl)) : 1.0f;
float al = expf(avl);
data.al = (al == 0.0f) ? 0.0f : (tmmd->key / al);
data.igm = (tmmd->gamma == 0.0f) ? 1.0f : (1.0f / tmmd->gamma);
if (tmmd->type == SEQ_TONEMAP_RD_PHOTORECEPTOR) {
modifier_apply_threaded(ibuf,
mask,
tonemapmodifier_apply_threaded_photoreceptor,
&data);
}
else /* if (tmmd->type == SEQ_TONEMAP_RD_SIMPLE) */ {
modifier_apply_threaded(ibuf,
mask,
tonemapmodifier_apply_threaded_simple,
&data);
}
}
static SequenceModifierTypeInfo seqModifier_Tonemap = {
CTX_N_(BLT_I18NCONTEXT_ID_SEQUENCE, "Tonemap"), /* name */
"SequencerTonemapModifierData", /* struct_name */
sizeof(SequencerTonemapModifierData), /* struct_size */
tonemapmodifier_init_data, /* init_data */
NULL, /* free_data */
NULL, /* copy_data */
tonemapmodifier_apply /* apply */
};
/*********************** Modifier functions *************************/
static void sequence_modifier_type_info_init(void)
@ -647,6 +867,7 @@ static void sequence_modifier_type_info_init(void)
INIT_TYPE(BrightContrast);
INIT_TYPE(Mask);
INIT_TYPE(WhiteBalance);
INIT_TYPE(Tonemap);
#undef INIT_TYPE
}

View File

@ -352,6 +352,19 @@ typedef struct WhiteBalanceModifierData {
float pad;
} WhiteBalanceModifierData;
typedef struct SequencerTonemapModifierData {
SequenceModifierData modifier;
float key, offset, gamma;
float intensity, contrast, adaptation, correction;
int type;
} SequencerTonemapModifierData;
enum {
SEQ_TONEMAP_RH_SIMPLE = 0,
SEQ_TONEMAP_RD_PHOTORECEPTOR = 1,
};
/* ***************** Scopes ****************** */
typedef struct SequencerScopes {
@ -528,6 +541,7 @@ enum {
seqModifierType_BrightContrast = 4,
seqModifierType_Mask = 5,
seqModifierType_WhiteBalance = 6,
seqModifierType_Tonemap = 7,
NUM_SEQUENCE_MODIFIER_TYPES
};

View File

@ -67,6 +67,7 @@ EnumPropertyItem rna_enum_sequence_modifier_type_items[] = {
{seqModifierType_BrightContrast, "BRIGHT_CONTRAST", ICON_NONE, "Bright/Contrast", ""},
{seqModifierType_Mask, "MASK", ICON_NONE, "Mask", ""},
{seqModifierType_WhiteBalance, "WHITE_BALANCE", ICON_NONE, "White Balance", ""},
{seqModifierType_Tonemap, "TONEMAP", ICON_NONE, "Tone Map", ""},
{0, NULL, 0, NULL, NULL}
};
@ -953,6 +954,8 @@ static StructRNA *rna_SequenceModifier_refine(struct PointerRNA *ptr)
return &RNA_BrightContrastModifier;
case seqModifierType_WhiteBalance:
return &RNA_WhiteBalanceModifier;
case seqModifierType_Tonemap:
return &RNA_SequencerTonemapModifierData;
default:
return &RNA_SequenceModifier;
}
@ -2559,6 +2562,65 @@ static void rna_def_brightcontrast_modifier(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceModifier_update");
}
static void rna_def_tonemap_modifier(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
static EnumPropertyItem type_items[] = {
{SEQ_TONEMAP_RD_PHOTORECEPTOR, "RD_PHOTORECEPTOR", 0, "R/D Photoreceptor", ""},
{SEQ_TONEMAP_RH_SIMPLE, "RH_SIMPLE", 0, "Rh Simple", ""},
{0, NULL, 0, NULL, NULL}
};
srna = RNA_def_struct(brna, "SequencerTonemapModifierData", "SequenceModifier");
RNA_def_struct_sdna(srna, "SequencerTonemapModifierData");
RNA_def_struct_ui_text(srna, "SequencerTonemapModifierData",
"Tone mapping modifier");
prop = RNA_def_property(srna, "tonemap_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "type");
RNA_def_property_enum_items(prop, type_items);
RNA_def_property_ui_text(prop, "Tonemap Type", "");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceModifier_update");
prop = RNA_def_property(srna, "key", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(prop, "Key", "The value the average luminance is mapped to");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceModifier_update");
prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 0.001f, 10.0f);
RNA_def_property_ui_text(prop, "Offset",
"Normally always 1, but can be used as an extra control to alter the brightness curve");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceModifier_update");
prop = RNA_def_property(srna, "gamma", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 0.001f, 3.0f);
RNA_def_property_ui_text(prop, "Gamma", "If not used, set to 1");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceModifier_update");
prop = RNA_def_property(srna, "intensity", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, -8.0f, 8.0f);
RNA_def_property_ui_text(prop, "Intensity", "If less than zero, darkens image; otherwise, makes it brighter");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceModifier_update");
prop = RNA_def_property(srna, "contrast", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(prop, "Contrast", "Set to 0 to use estimate from input image");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceModifier_update");
prop = RNA_def_property(srna, "adaptation", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(prop, "Adaptation", "If 0, global; if 1, based on pixel intensity");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceModifier_update");
prop = RNA_def_property(srna, "correction", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(prop, "Color Correction", "If 0, same for all channels; if 1, each independent");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceModifier_update");
}
static void rna_def_modifiers(BlenderRNA *brna)
{
rna_def_modifier(brna);
@ -2568,6 +2630,7 @@ static void rna_def_modifiers(BlenderRNA *brna)
rna_def_hue_modifier(brna);
rna_def_brightcontrast_modifier(brna);
rna_def_whitebalance_modifier(brna);
rna_def_tonemap_modifier(brna);
}
void RNA_def_sequencer(BlenderRNA *brna)