videolevels: add auto adjust mode and fix calculations
This commit is contained in:
parent
fdfc42a3b5
commit
9257139bca
@ -58,14 +58,27 @@ enum
|
|||||||
PROP_LOWIN,
|
PROP_LOWIN,
|
||||||
PROP_HIGHIN,
|
PROP_HIGHIN,
|
||||||
PROP_LOWOUT,
|
PROP_LOWOUT,
|
||||||
PROP_HIGHOUT
|
PROP_HIGHOUT,
|
||||||
/* FILL ME */
|
PROP_AUTO,
|
||||||
|
PROP_INTERVAL/*,
|
||||||
|
PROP_NPIXSAT_LOW,
|
||||||
|
PROP_NPIXSAT_HIGH,
|
||||||
|
PROP_ROI_X,
|
||||||
|
PROP_ROI_Y,
|
||||||
|
PROP_ROI_WIDTH,
|
||||||
|
PROP_ROI_HEIGHT */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define DEFAULT_PROP_LOWIN 0.0
|
#define DEFAULT_PROP_LOWIN 0.0
|
||||||
#define DEFAULT_PROP_HIGHIN 1.0
|
#define DEFAULT_PROP_HIGHIN 1.0
|
||||||
#define DEFAULT_PROP_LOWOUT 0.0
|
#define DEFAULT_PROP_LOWOUT 0.0
|
||||||
#define DEFAULT_PROP_HIGHOUT 1.0
|
#define DEFAULT_PROP_HIGHOUT 1.0
|
||||||
|
#define DEFAULT_PROP_AUTO 0
|
||||||
|
#define DEFAULT_PROP_INTERVAL (GST_SECOND / 2)
|
||||||
|
#define DEFAULT_PROP_ROW_X 0
|
||||||
|
#define DEFAULT_PROP_ROW_Y 0
|
||||||
|
#define DEFAULT_PROP_ROW_WIDTH 0
|
||||||
|
#define DEFAULT_PROP_ROW_HEIGHT 0
|
||||||
|
|
||||||
static const GstElementDetails videolevels_details =
|
static const GstElementDetails videolevels_details =
|
||||||
GST_ELEMENT_DETAILS ("Video videolevels adjustment",
|
GST_ELEMENT_DETAILS ("Video videolevels adjustment",
|
||||||
@ -136,6 +149,10 @@ static void gst_videolevels_reset(GstVideoLevels* filter);
|
|||||||
static void gst_videolevels_calculate_tables (GstVideoLevels * videolevels);
|
static void gst_videolevels_calculate_tables (GstVideoLevels * videolevels);
|
||||||
static gboolean gst_videolevels_do_levels (GstVideoLevels * videolevels,
|
static gboolean gst_videolevels_do_levels (GstVideoLevels * videolevels,
|
||||||
gpointer indata, gpointer outdata);
|
gpointer indata, gpointer outdata);
|
||||||
|
static gboolean gst_videolevels_calculate_histogram (GstVideoLevels * videolevels,
|
||||||
|
guint16 * data);
|
||||||
|
static gboolean gst_videolevels_auto_adjust (GstVideoLevels * videolevels,
|
||||||
|
guint16 * data);
|
||||||
|
|
||||||
/* setup debug */
|
/* setup debug */
|
||||||
GST_DEBUG_CATEGORY_STATIC (videolevels_debug);
|
GST_DEBUG_CATEGORY_STATIC (videolevels_debug);
|
||||||
@ -211,16 +228,22 @@ gst_videolevels_class_init (GstVideoLevelsClass * object)
|
|||||||
/* Install GObject properties */
|
/* Install GObject properties */
|
||||||
g_object_class_install_property (obj_class, PROP_LOWIN,
|
g_object_class_install_property (obj_class, PROP_LOWIN,
|
||||||
g_param_spec_double ("low_in", "Lower Input Level", "Lower Input Level",
|
g_param_spec_double ("low_in", "Lower Input Level", "Lower Input Level",
|
||||||
0.0, 1.0, DEFAULT_PROP_LOWIN, G_PARAM_READWRITE));
|
0.0, G_MAXUINT16, DEFAULT_PROP_LOWIN, G_PARAM_READWRITE));
|
||||||
g_object_class_install_property (obj_class, PROP_HIGHIN,
|
g_object_class_install_property (obj_class, PROP_HIGHIN,
|
||||||
g_param_spec_double ("upper_in", "Upper Input Level", "Upper Input Level",
|
g_param_spec_double ("upper_in", "Upper Input Level", "Upper Input Level",
|
||||||
0.0, 1.0, DEFAULT_PROP_HIGHIN, G_PARAM_READWRITE));
|
0.0, G_MAXUINT16, DEFAULT_PROP_HIGHIN, G_PARAM_READWRITE));
|
||||||
g_object_class_install_property (obj_class, PROP_LOWOUT,
|
g_object_class_install_property (obj_class, PROP_LOWOUT,
|
||||||
g_param_spec_double ("low_out", "Lower Output Level", "Lower Output Level",
|
g_param_spec_double ("low_out", "Lower Output Level", "Lower Output Level",
|
||||||
0.0, 1.0, DEFAULT_PROP_LOWOUT, G_PARAM_READWRITE));
|
0.0, G_MAXUINT16, DEFAULT_PROP_LOWOUT, G_PARAM_READWRITE));
|
||||||
g_object_class_install_property (obj_class, PROP_HIGHOUT,
|
g_object_class_install_property (obj_class, PROP_HIGHOUT,
|
||||||
g_param_spec_double ("upper_out", "Upper Output Level", "Upper Output Level",
|
g_param_spec_double ("upper_out", "Upper Output Level", "Upper Output Level",
|
||||||
0.0, 1.0, DEFAULT_PROP_HIGHOUT, G_PARAM_READWRITE));
|
0.0, G_MAXUINT16, DEFAULT_PROP_HIGHOUT, G_PARAM_READWRITE));
|
||||||
|
g_object_class_install_property (obj_class, PROP_AUTO,
|
||||||
|
g_param_spec_int ("auto", "Auto Adjust", "Auto adjust contrast (0): off, (1): single-shot, (2): continuous",
|
||||||
|
0, 2, DEFAULT_PROP_AUTO, G_PARAM_READWRITE));
|
||||||
|
g_object_class_install_property (obj_class, PROP_INTERVAL,
|
||||||
|
g_param_spec_uint64 ("interval", "Interval", "Interval of time between adjustments (in nanoseconds)",
|
||||||
|
1, G_MAXUINT64, DEFAULT_PROP_INTERVAL, G_PARAM_READWRITE));
|
||||||
|
|
||||||
/* Register GstBaseTransform vmethods */
|
/* Register GstBaseTransform vmethods */
|
||||||
trans_class->transform_caps = GST_DEBUG_FUNCPTR (gst_videolevels_transform_caps);
|
trans_class->transform_caps = GST_DEBUG_FUNCPTR (gst_videolevels_transform_caps);
|
||||||
@ -269,19 +292,25 @@ gst_videolevels_set_property (GObject * object, guint prop_id,
|
|||||||
switch (prop_id) {
|
switch (prop_id) {
|
||||||
case PROP_LOWIN:
|
case PROP_LOWIN:
|
||||||
videolevels->lower_input = g_value_get_double (value);
|
videolevels->lower_input = g_value_get_double (value);
|
||||||
gst_videolevels_calculate_tables (videolevels);
|
//gst_videolevels_calculate_tables (videolevels);
|
||||||
break;
|
break;
|
||||||
case PROP_HIGHIN:
|
case PROP_HIGHIN:
|
||||||
videolevels->upper_input = g_value_get_double (value);
|
videolevels->upper_input = g_value_get_double (value);
|
||||||
gst_videolevels_calculate_tables (videolevels);
|
//gst_videolevels_calculate_tables (videolevels);
|
||||||
break;
|
break;
|
||||||
case PROP_LOWOUT:
|
case PROP_LOWOUT:
|
||||||
videolevels->lower_output = g_value_get_double (value);
|
videolevels->lower_output = g_value_get_double (value);
|
||||||
gst_videolevels_calculate_tables (videolevels);
|
//gst_videolevels_calculate_tables (videolevels);
|
||||||
break;
|
break;
|
||||||
case PROP_HIGHOUT:
|
case PROP_HIGHOUT:
|
||||||
videolevels->upper_output = g_value_get_double (value);
|
videolevels->upper_output = g_value_get_double (value);
|
||||||
gst_videolevels_calculate_tables (videolevels);
|
//gst_videolevels_calculate_tables (videolevels);
|
||||||
|
break;
|
||||||
|
case PROP_AUTO:
|
||||||
|
videolevels->auto_adjust = g_value_get_int (value);
|
||||||
|
break;
|
||||||
|
case PROP_INTERVAL:
|
||||||
|
videolevels->interval = g_value_get_uint64 (value);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
@ -318,6 +347,12 @@ gst_videolevels_get_property (GObject * object, guint prop_id, GValue * value,
|
|||||||
case PROP_HIGHOUT:
|
case PROP_HIGHOUT:
|
||||||
g_value_set_double (value, videolevels->upper_output);
|
g_value_set_double (value, videolevels->upper_output);
|
||||||
break;
|
break;
|
||||||
|
case PROP_AUTO:
|
||||||
|
g_value_set_int (value, videolevels->auto_adjust);
|
||||||
|
break;
|
||||||
|
case PROP_INTERVAL:
|
||||||
|
g_value_set_uint64 (value, videolevels->interval);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
@ -343,11 +378,8 @@ gst_videolevels_transform_caps (GstBaseTransform * base,
|
|||||||
GstPadDirection direction, GstCaps * caps)
|
GstPadDirection direction, GstCaps * caps)
|
||||||
{
|
{
|
||||||
GstVideoLevels *videolevels;
|
GstVideoLevels *videolevels;
|
||||||
GstCaps *static_caps;
|
|
||||||
GstCaps *newcaps;
|
GstCaps *newcaps;
|
||||||
GstStructure *structure;
|
GstStructure *structure;
|
||||||
const GValue * width;
|
|
||||||
const GValue * height;
|
|
||||||
|
|
||||||
videolevels = GST_VIDEOLEVELS (base);
|
videolevels = GST_VIDEOLEVELS (base);
|
||||||
|
|
||||||
@ -456,8 +488,13 @@ gst_videolevels_set_caps (GstBaseTransform * base, GstCaps * incaps,
|
|||||||
levels->stride_in = GST_ROUND_UP_4 (levels->width * levels->depth_in/8);
|
levels->stride_in = GST_ROUND_UP_4 (levels->width * levels->depth_in/8);
|
||||||
levels->stride_out = GST_ROUND_UP_4 (levels->width * levels->depth_out/8);
|
levels->stride_out = GST_ROUND_UP_4 (levels->width * levels->depth_out/8);
|
||||||
|
|
||||||
|
levels->lower_input = 0.0;
|
||||||
|
levels->upper_input = (1 << levels->bpp_in) - 1;
|
||||||
|
levels->lower_output = 0.0;
|
||||||
|
levels->upper_output = (1 << levels->bpp_out) - 1;
|
||||||
|
|
||||||
gst_videolevels_calculate_tables (levels);
|
|
||||||
|
//gst_videolevels_calculate_tables (levels);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -514,24 +551,32 @@ static GstFlowReturn
|
|||||||
gst_videolevels_transform (GstBaseTransform * base, GstBuffer * inbuf,
|
gst_videolevels_transform (GstBaseTransform * base, GstBuffer * inbuf,
|
||||||
GstBuffer * outbuf)
|
GstBuffer * outbuf)
|
||||||
{
|
{
|
||||||
GstVideoLevels *filter = GST_VIDEOLEVELS (base);
|
GstVideoLevels *videolevels = GST_VIDEOLEVELS (base);
|
||||||
gpointer input;
|
gpointer input;
|
||||||
gpointer output;
|
gpointer output;
|
||||||
gboolean ret;
|
gboolean ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We need to lock our filter params to prevent changing
|
* We need to lock our videolevels params to prevent changing
|
||||||
* caps in the middle of a transformation (nice way to get
|
* caps in the middle of a transformation (nice way to get
|
||||||
* segfaults)
|
* segfaults)
|
||||||
*/
|
*/
|
||||||
GST_OBJECT_LOCK (filter);
|
GST_OBJECT_LOCK (videolevels);
|
||||||
|
|
||||||
input = GST_BUFFER_DATA (inbuf);
|
input = GST_BUFFER_DATA (inbuf);
|
||||||
output = GST_BUFFER_DATA (outbuf);
|
output = GST_BUFFER_DATA (outbuf);
|
||||||
|
|
||||||
ret = gst_videolevels_do_levels (filter, input, output);
|
if (videolevels->auto_adjust == 1) {
|
||||||
|
gst_videolevels_auto_adjust (videolevels, input);
|
||||||
|
videolevels->auto_adjust = 0;
|
||||||
|
}
|
||||||
|
else if (videolevels->auto_adjust == 2) {
|
||||||
|
gst_videolevels_auto_adjust (videolevels, input);
|
||||||
|
}
|
||||||
|
|
||||||
GST_OBJECT_UNLOCK (filter);
|
ret = gst_videolevels_do_levels (videolevels, input, output);
|
||||||
|
|
||||||
|
GST_OBJECT_UNLOCK (videolevels);
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return GST_FLOW_OK;
|
return GST_FLOW_OK;
|
||||||
@ -578,6 +623,17 @@ static void gst_videolevels_reset(GstVideoLevels* videolevels)
|
|||||||
|
|
||||||
g_free (videolevels->lookup_table);
|
g_free (videolevels->lookup_table);
|
||||||
videolevels->lookup_table = NULL;
|
videolevels->lookup_table = NULL;
|
||||||
|
|
||||||
|
videolevels->auto_adjust = DEFAULT_PROP_AUTO;
|
||||||
|
videolevels->interval = DEFAULT_PROP_INTERVAL;
|
||||||
|
|
||||||
|
videolevels->lower_pix_sat = 0.01f;
|
||||||
|
videolevels->upper_pix_sat = 0.01f;
|
||||||
|
|
||||||
|
videolevels->nbins = 4096;
|
||||||
|
|
||||||
|
g_free (videolevels->histogram);
|
||||||
|
videolevels->histogram = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -590,7 +646,7 @@ static void
|
|||||||
gst_videolevels_calculate_tables (GstVideoLevels * videolevels)
|
gst_videolevels_calculate_tables (GstVideoLevels * videolevels)
|
||||||
{
|
{
|
||||||
gint i;
|
gint i;
|
||||||
guint8 loIn, hiIn;
|
guint16 loIn, hiIn;
|
||||||
guint8 loOut, hiOut;
|
guint8 loOut, hiOut;
|
||||||
gdouble slope;
|
gdouble slope;
|
||||||
guint8 * lut;
|
guint8 * lut;
|
||||||
@ -598,27 +654,102 @@ gst_videolevels_calculate_tables (GstVideoLevels * videolevels)
|
|||||||
GST_DEBUG ("calculating lookup table");
|
GST_DEBUG ("calculating lookup table");
|
||||||
|
|
||||||
if (!videolevels->lookup_table) {
|
if (!videolevels->lookup_table) {
|
||||||
videolevels->lookup_table = g_malloc (256);
|
videolevels->lookup_table = g_new (guint8, 65536);
|
||||||
}
|
}
|
||||||
lut = (guint8*) videolevels->lookup_table;
|
lut = (guint8*) videolevels->lookup_table;
|
||||||
|
|
||||||
loIn = (guint8) videolevels->lower_input * 255;
|
loIn = (guint16) (videolevels->lower_input * G_MAXUINT16);
|
||||||
hiIn = (guint8) videolevels->upper_input * 255;
|
hiIn = (guint16) (videolevels->upper_input * G_MAXUINT16);
|
||||||
loOut = (guint8) videolevels->lower_output * 255;
|
loOut = (guint8) (videolevels->lower_output * G_MAXUINT8);
|
||||||
hiOut = (guint8) videolevels->upper_output * 255;
|
hiOut = (guint8) (videolevels->upper_output * G_MAXUINT8);
|
||||||
|
|
||||||
|
GST_DEBUG ("input levels (%.3f, %.3f), output levels (%.3f, %.3f)",videolevels->lower_input,videolevels->upper_input,videolevels->lower_output,videolevels->upper_output);
|
||||||
|
|
||||||
|
GST_DEBUG ("input levels (%d, %d), output levels (%d, %d)",loIn,hiIn,loOut,hiOut);
|
||||||
if (hiIn == loIn)
|
if (hiIn == loIn)
|
||||||
slope = 0;
|
slope = 0;
|
||||||
else
|
else
|
||||||
slope = (videolevels->upper_output - videolevels->lower_output) /
|
slope = (videolevels->upper_output - videolevels->lower_output) /
|
||||||
(videolevels->upper_input - videolevels->lower_input);
|
(videolevels->upper_input - videolevels->lower_input);
|
||||||
|
|
||||||
for (i=0; i<loIn; i++)
|
GST_DEBUG ("slope = %f", slope);
|
||||||
|
|
||||||
|
if (videolevels->endianness_in == G_BYTE_ORDER) {
|
||||||
|
for (i = 0; i < loIn; i++)
|
||||||
lut[i] = loOut;
|
lut[i] = loOut;
|
||||||
for (i=loIn; i<hiIn; i++)
|
for (i = loIn; i < hiIn; i++)
|
||||||
lut[i] = loOut + (guint8) ( (i-loIn) * slope);
|
lut[i] = loOut + (guint8) ( (i - loIn) * slope);
|
||||||
for (i=hiIn; i<256; i++)
|
for (i = hiIn; i < 65536; i++)
|
||||||
lut[i] = hiOut;
|
lut[i] = hiOut;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (i = 0; i < loIn; i++)
|
||||||
|
lut[GUINT16_SWAP_LE_BE(i)] = loOut;
|
||||||
|
for (i = loIn; i < hiIn; i++)
|
||||||
|
lut[GUINT16_SWAP_LE_BE(i)] = loOut + (guint8) ((GUINT16_SWAP_LE_BE(i) - loIn) * slope);
|
||||||
|
for (i = hiIn; i < 65536; i++)
|
||||||
|
lut[GUINT16_SWAP_LE_BE(i)] = hiOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 65536; i += 128)
|
||||||
|
GST_DEBUG ("lut[%d / %d] = %d", i, GUINT16_SWAP_LE_BE(i), lut[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GINT_CLAMP(x, low, high) ((gint)(CLAMP((x),(low),(high))))
|
||||||
|
#define GUINT8_CLAMP(x, low, high) ((guint8)(CLAMP((x),(low),(high))))
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_videolevels_convert_uint16le_to_uint8(GstVideoLevels * videolevels,
|
||||||
|
guint16 * in, guint8 * out)
|
||||||
|
{
|
||||||
|
gint i;
|
||||||
|
const gint size = videolevels->stride_in / 2 * videolevels->height;
|
||||||
|
gdouble m;
|
||||||
|
gdouble b;
|
||||||
|
const guint8 low = (guint8) videolevels->lower_output;
|
||||||
|
const guint8 high = (guint8) videolevels->upper_output;
|
||||||
|
|
||||||
|
GST_DEBUG ("Applying linear mapping (%.3f, %.3f) -> (%.3f, %.3f)",
|
||||||
|
videolevels->lower_input, videolevels->upper_input,
|
||||||
|
videolevels->lower_output, videolevels->upper_output);
|
||||||
|
|
||||||
|
if (videolevels->lower_input == videolevels->upper_input)
|
||||||
|
m = 0;
|
||||||
|
else
|
||||||
|
m = (videolevels->upper_output - videolevels->lower_output) /
|
||||||
|
(videolevels->upper_input - videolevels->lower_input);
|
||||||
|
|
||||||
|
b = videolevels->lower_output - m * videolevels->lower_input;
|
||||||
|
|
||||||
|
for (i = 0; i < size; i++)
|
||||||
|
out[i] = GUINT8_CLAMP (m * GUINT16_FROM_LE (in[i]) + b, low, high);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_videolevels_convert_uint16be_to_uint8(GstVideoLevels * videolevels,
|
||||||
|
guint16 * in, guint8 * out)
|
||||||
|
{
|
||||||
|
gint i;
|
||||||
|
const gint size = videolevels->stride_in / 2 * videolevels->height;
|
||||||
|
gdouble m;
|
||||||
|
gdouble b;
|
||||||
|
const guint8 low = (guint8) videolevels->lower_output;
|
||||||
|
const guint8 high = (guint8) videolevels->upper_output;
|
||||||
|
|
||||||
|
GST_DEBUG ("Applying linear mapping (%.3f, %.3f) -> (%.3f, %.3f)",
|
||||||
|
videolevels->lower_input, videolevels->upper_input,
|
||||||
|
videolevels->lower_output, videolevels->upper_output);
|
||||||
|
|
||||||
|
if (videolevels->lower_input == videolevels->upper_input)
|
||||||
|
m = 0;
|
||||||
|
else
|
||||||
|
m = (videolevels->upper_output - videolevels->lower_output) /
|
||||||
|
(videolevels->upper_input - videolevels->lower_input);
|
||||||
|
|
||||||
|
b = videolevels->lower_output - m * videolevels->lower_input;
|
||||||
|
|
||||||
|
for (i = 0; i < size; i++)
|
||||||
|
out[i] = GUINT8_CLAMP (m * GUINT16_FROM_BE (in[i]) + b, low, high);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -636,27 +767,72 @@ gboolean
|
|||||||
gst_videolevels_do_levels (GstVideoLevels * videolevels, gpointer indata,
|
gst_videolevels_do_levels (GstVideoLevels * videolevels, gpointer indata,
|
||||||
gpointer outdata)
|
gpointer outdata)
|
||||||
{
|
{
|
||||||
guint8 * dst = outdata;
|
if (videolevels->endianness_in == G_LITTLE_ENDIAN)
|
||||||
guint16 * src = indata;
|
gst_videolevels_convert_uint16le_to_uint8 (videolevels, indata, outdata);
|
||||||
gint r, c;
|
else
|
||||||
guint8 * lut = (guint8 *) videolevels->lookup_table;
|
gst_videolevels_convert_uint16be_to_uint8 (videolevels, indata, outdata);
|
||||||
|
|
||||||
GST_DEBUG ("Converting frame using LUT");
|
return TRUE;
|
||||||
|
//guint8 * dst = outdata;
|
||||||
|
//guint16 * src = indata;
|
||||||
|
//gint r = 0, c = 0;
|
||||||
|
//guint8 * lut = (guint8 *) videolevels->lookup_table;
|
||||||
|
|
||||||
|
//GST_DEBUG ("Converting frame using LUT");
|
||||||
|
//
|
||||||
|
//for (r = 0; r < videolevels->height; r++) {
|
||||||
|
// for (c = 0; c < videolevels->width; c++) {
|
||||||
|
// dst[c+r*videolevels->stride_out] =
|
||||||
|
// lut[src[c+r*videolevels->stride_in/2]];
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_videolevels_calculate_histogram
|
||||||
|
* @videolevels: #GstVideoLevels
|
||||||
|
* @data: input frame data
|
||||||
|
*
|
||||||
|
* Calculate histogram of input frame
|
||||||
|
*
|
||||||
|
* Returns: TRUE on success
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
gst_videolevels_calculate_histogram (GstVideoLevels * videolevels, guint16 * data)
|
||||||
|
{
|
||||||
|
gint * hist;
|
||||||
|
gint nbins = videolevels->nbins;
|
||||||
|
gint r;
|
||||||
|
gint c;
|
||||||
|
gfloat factor = nbins/65536.0f;
|
||||||
|
|
||||||
|
if (videolevels->histogram == NULL) {
|
||||||
|
GST_DEBUG ("First call, allocate memory for histogram (%d bins)", nbins);
|
||||||
|
videolevels->histogram = g_new (gint, nbins);
|
||||||
|
}
|
||||||
|
|
||||||
|
hist = videolevels->histogram;
|
||||||
|
|
||||||
|
/* reset histogram */
|
||||||
|
memset (hist, 0, sizeof(gint)*nbins);
|
||||||
|
|
||||||
|
GST_DEBUG ("Calculating histogram");
|
||||||
if (!videolevels->is_signed_in) {
|
if (!videolevels->is_signed_in) {
|
||||||
if (videolevels->endianness_in == G_BYTE_ORDER) {
|
if (videolevels->endianness_in == G_BYTE_ORDER) {
|
||||||
for (r = 0; r < videolevels->height; r++) {
|
for (r = 0; r < videolevels->height; r++) {
|
||||||
for (c = 0; c < videolevels->width; c++) {
|
for (c = 0; c < videolevels->width; c++) {
|
||||||
dst[c+r*videolevels->stride_out] =
|
/* GST_DEBUG ("(%d, %d) = %d, hist[%d] = %d", r, c, data [c + r * videolevels->stride_in / 2], GINT_CLAMP (data [c + r * videolevels->stride_in / 2] * factor, 0, nbins - 1),
|
||||||
lut[src[c+r*videolevels->stride_in/2] >> 8];
|
hist [GINT_CLAMP (data [c + r * videolevels->stride_in / 2] * factor, 0, nbins - 1)] + 1);*/
|
||||||
|
hist [GINT_CLAMP (data [c + r * videolevels->stride_in / 2] * factor, 0, nbins - 1)]++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (r = 0; r < videolevels->height; r++) {
|
for (r = 0; r < videolevels->height; r++) {
|
||||||
for (c = 0; c < videolevels->width; c++) {
|
for (c = 0; c < videolevels->width; c++) {
|
||||||
dst[c+r*videolevels->stride_out] =
|
hist [GINT_CLAMP (GUINT16_FROM_BE (data [c + r * videolevels->stride_in / 2]) * factor, 0, nbins - 1)]++;
|
||||||
lut[GUINT16_FROM_BE(src[c+r*videolevels->stride_in/2]) >> 8];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -665,79 +841,78 @@ gst_videolevels_do_levels (GstVideoLevels * videolevels, gpointer indata,
|
|||||||
if (videolevels->endianness_in == G_BYTE_ORDER) {
|
if (videolevels->endianness_in == G_BYTE_ORDER) {
|
||||||
for (r = 0; r < videolevels->height; r++) {
|
for (r = 0; r < videolevels->height; r++) {
|
||||||
for (c = 0; c < videolevels->width; c++) {
|
for (c = 0; c < videolevels->width; c++) {
|
||||||
dst[c+r*videolevels->stride_out] =
|
hist [GINT_CLAMP ((data [c + r * videolevels->stride_in / 2] + 32767) * factor, 0, nbins - 1)]++;
|
||||||
lut[(src[c+r*videolevels->stride_in/2]+32767) >> 8];
|
|
||||||
}
|
}
|
||||||
GST_DEBUG ("Row %d", r);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (r = 0; r < videolevels->height; r++) {
|
for (r = 0; r < videolevels->height; r++) {
|
||||||
for (c = 0; c < videolevels->width; c++) {
|
for (c = 0; c < videolevels->width; c++) {
|
||||||
dst[c+r*videolevels->stride_out] =
|
hist [GINT_CLAMP ((GUINT16_FROM_BE (data [c + r * videolevels->stride_in / 2]) + 32767) * factor, 0, nbins - 1)]++;
|
||||||
lut[(GUINT16_FROM_BE(src[c+r*videolevels->stride_in/2])+32767) >> 8];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GST_DEBUG ("DONE converting frame using LUT");
|
//for (r = 0; r < videolevels->nbins; r++ ) {
|
||||||
|
// GST_DEBUG ("hist[%5d] = %d",r, hist[r]);
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
//gdouble loIn = videolevels->lower_input;
|
|
||||||
//gdouble hiIn = videolevels->upper_input;
|
|
||||||
//gdouble loOut = videolevels->lower_output;
|
|
||||||
//gdouble hiOut = videolevels->upper_output;
|
|
||||||
//gdouble slope;
|
|
||||||
//gdouble yintercept;
|
|
||||||
//guint32 dst_max;
|
|
||||||
//guint32 src_max;
|
|
||||||
//
|
|
||||||
///* y=mx+b */
|
|
||||||
///* check for division by zero */
|
|
||||||
//if (hiIn == loIn)
|
|
||||||
// slope=0;
|
|
||||||
//else
|
|
||||||
// slope = (hiOut-loOut)/(hiIn-loIn);
|
|
||||||
//yintercept = loOut - slope*loIn;
|
|
||||||
|
|
||||||
//src_max = (1 << videolevels->bpp_in) - 1;
|
|
||||||
//dst_max = (1 << videolevels->bpp_out) - 1;
|
|
||||||
|
|
||||||
/* TODO doesn't handle byte ordering */
|
|
||||||
//if (videolevels->depth_in == 32 && videolevels->depth_out == 32) {
|
|
||||||
// guint32 * src = indata;
|
|
||||||
// guint32 * dst = outdata;
|
|
||||||
// gint r;
|
|
||||||
// gint c;
|
|
||||||
// guint32 b = dst_max * yintercept;
|
|
||||||
// gdouble m = slope*dst_max/src_max;
|
|
||||||
|
|
||||||
|
|
||||||
// for (r = 0; r < videolevels->height; r++) {
|
|
||||||
// for (c = 0; c < videolevels->width; c++) {
|
|
||||||
// dst[c+r*videolevels->stride_out] = m*src[c+r*videolevels->stride_in] + b;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
//}
|
||||||
//else if (videolevels->depth_in == 16 && videolevels->depth_out == 8) {
|
|
||||||
// guint16 * src = indata;
|
|
||||||
// guint8 * dst = outdata;
|
|
||||||
// gint r;
|
|
||||||
// gint c;
|
|
||||||
// guint8 b = dst_max * yintercept;
|
|
||||||
// gdouble m = slope*dst_max/src_max;
|
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
// for (r = 0; r < videolevels->height; r++) {
|
}
|
||||||
// for (c = 0; c < videolevels->width; c++) {
|
|
||||||
// dst[c+r*videolevels->stride_out] = m*src[c+r*videolevels->stride_in] + b;
|
/**
|
||||||
// }
|
* gst_videolevels_auto_adjust
|
||||||
// }
|
* @videolevels: #GstVideoLevels
|
||||||
//}
|
* @data: input frame data
|
||||||
//else {
|
*
|
||||||
// return FALSE;
|
* Calculate lower and upper levels based on the histogram of the frame
|
||||||
//}
|
*
|
||||||
|
* Returns: TRUE on success
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
gst_videolevels_auto_adjust (GstVideoLevels * videolevels,
|
||||||
|
guint16 * data)
|
||||||
|
{
|
||||||
|
guint npixsat;
|
||||||
|
guint sum;
|
||||||
|
gint i;
|
||||||
|
gint size;
|
||||||
|
gdouble min = 0.0;
|
||||||
|
gdouble max = (1 << videolevels->bpp_in) - 1.0;
|
||||||
|
|
||||||
|
gst_videolevels_calculate_histogram (videolevels, data);
|
||||||
|
|
||||||
|
size = videolevels->width * videolevels->height;
|
||||||
|
|
||||||
|
/* pixels to saturate on low end */
|
||||||
|
npixsat = (guint)(videolevels->lower_pix_sat * size);
|
||||||
|
sum = 0;
|
||||||
|
for (i = 0; i < videolevels->nbins; i++) {
|
||||||
|
sum += videolevels->histogram[i];
|
||||||
|
if (sum > npixsat) {
|
||||||
|
videolevels->lower_input =
|
||||||
|
CLAMP (i / (gdouble)videolevels->nbins * max, min, max);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pixels to saturate on high end */
|
||||||
|
npixsat = (guint)(videolevels->upper_pix_sat * size);
|
||||||
|
sum = 0;
|
||||||
|
for (i = videolevels->nbins - 1; i >= 0; i--) {
|
||||||
|
sum += videolevels->histogram[i];
|
||||||
|
if (sum > npixsat) {
|
||||||
|
videolevels->upper_input =
|
||||||
|
CLAMP ((i + 1) / (gdouble)videolevels->nbins * max, min, max);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG ("Contrast stretch with npixsat=%d, (%.3f, %.3f)", npixsat,
|
||||||
|
videolevels->lower_input, videolevels->upper_input);
|
||||||
|
|
||||||
|
//gst_videolevels_calculate_tables (videolevels);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -68,6 +68,13 @@ struct _GstVideoLevels
|
|||||||
|
|
||||||
/* tables */
|
/* tables */
|
||||||
guint8* levels_table;
|
guint8* levels_table;
|
||||||
|
|
||||||
|
gint auto_adjust;
|
||||||
|
guint64 interval;
|
||||||
|
gfloat lower_pix_sat;
|
||||||
|
gfloat upper_pix_sat;
|
||||||
|
gint nbins;
|
||||||
|
gint * histogram;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstVideoLevelsClass
|
struct _GstVideoLevelsClass
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user