forked from FFmpeg/FFmpeg
lavfi/curves: do not automatically insert points at x=0 and x=1
There is actually a need for the origin and end point not to be defined. We can not automatically insert them with the y value of the first and last point as it will influence the curves in a wrong way. Fixes #5397
This commit is contained in:
parent
783a2568b2
commit
5c14018fc4
3 changed files with 52 additions and 49 deletions
|
@ -7,6 +7,7 @@ version <next>:
|
||||||
- Changed metadata print option to accept general urls
|
- Changed metadata print option to accept general urls
|
||||||
- Alias muxer for Ogg Video (.ogv)
|
- Alias muxer for Ogg Video (.ogv)
|
||||||
- VP8 in Ogg muxing
|
- VP8 in Ogg muxing
|
||||||
|
- curves filter doesn't automatically insert points at x=0 and x=1 anymore
|
||||||
|
|
||||||
|
|
||||||
version 3.1:
|
version 3.1:
|
||||||
|
|
|
@ -5710,10 +5710,6 @@ strictly increasing over the x-axis, and their @var{x} and @var{y} values must
|
||||||
be in the @var{[0;1]} interval. If the computed curves happened to go outside
|
be in the @var{[0;1]} interval. If the computed curves happened to go outside
|
||||||
the vector spaces, the values will be clipped accordingly.
|
the vector spaces, the values will be clipped accordingly.
|
||||||
|
|
||||||
If there is no key point defined in @code{x=0}, the filter will automatically
|
|
||||||
insert a @var{(0;0)} point. In the same way, if there is no key point defined
|
|
||||||
in @code{x=1}, the filter will automatically insert a @var{(1;1)} point.
|
|
||||||
|
|
||||||
The filter accepts the following options:
|
The filter accepts the following options:
|
||||||
|
|
||||||
@table @option
|
@table @option
|
||||||
|
@ -5765,13 +5761,13 @@ defined using the following syntax: @code{x0/y0 x1/y1 x2/y2 ...}.
|
||||||
@item
|
@item
|
||||||
Increase slightly the middle level of blue:
|
Increase slightly the middle level of blue:
|
||||||
@example
|
@example
|
||||||
curves=blue='0.5/0.58'
|
curves=blue='0/0 0.5/0.58 1/1'
|
||||||
@end example
|
@end example
|
||||||
|
|
||||||
@item
|
@item
|
||||||
Vintage effect:
|
Vintage effect:
|
||||||
@example
|
@example
|
||||||
curves=r='0/0.11 .42/.51 1/0.95':g='0.50/0.48':b='0/0.22 .49/.44 1/0.8'
|
curves=r='0/0.11 .42/.51 1/0.95':g='0/0 0.50/0.48 1/1':b='0/0.22 .49/.44 1/0.8'
|
||||||
@end example
|
@end example
|
||||||
Here we obtain the following coordinates for each components:
|
Here we obtain the following coordinates for each components:
|
||||||
@table @var
|
@table @var
|
||||||
|
@ -5798,7 +5794,7 @@ curves=vintage
|
||||||
@item
|
@item
|
||||||
Use a Photoshop preset and redefine the points of the green component:
|
Use a Photoshop preset and redefine the points of the green component:
|
||||||
@example
|
@example
|
||||||
curves=psfile='MyCurvesPresets/purple.acv':green='0.45/0.53'
|
curves=psfile='MyCurvesPresets/purple.acv':green='0/0 0.45/0.53 1/1'
|
||||||
@end example
|
@end example
|
||||||
@end itemize
|
@end itemize
|
||||||
|
|
||||||
|
|
|
@ -110,25 +110,25 @@ static const struct {
|
||||||
const char *master;
|
const char *master;
|
||||||
} curves_presets[] = {
|
} curves_presets[] = {
|
||||||
[PRESET_COLOR_NEGATIVE] = {
|
[PRESET_COLOR_NEGATIVE] = {
|
||||||
"0/1 0.129/1 0.466/0.498 0.725/0 1/0",
|
"0.129/1 0.466/0.498 0.725/0",
|
||||||
"0/1 0.109/1 0.301/0.498 0.517/0 1/0",
|
"0.109/1 0.301/0.498 0.517/0",
|
||||||
"0/1 0.098/1 0.235/0.498 0.423/0 1/0",
|
"0.098/1 0.235/0.498 0.423/0",
|
||||||
},
|
},
|
||||||
[PRESET_CROSS_PROCESS] = {
|
[PRESET_CROSS_PROCESS] = {
|
||||||
"0.25/0.156 0.501/0.501 0.686/0.745",
|
"0/0 0.25/0.156 0.501/0.501 0.686/0.745 1/1",
|
||||||
"0.25/0.188 0.38/0.501 0.745/0.815 1/0.815",
|
"0/0 0.25/0.188 0.38/0.501 0.745/0.815 1/0.815",
|
||||||
"0.231/0.094 0.709/0.874",
|
"0/0 0.231/0.094 0.709/0.874 1/1",
|
||||||
},
|
},
|
||||||
[PRESET_DARKER] = { .master = "0.5/0.4" },
|
[PRESET_DARKER] = { .master = "0/0 0.5/0.4 1/1" },
|
||||||
[PRESET_INCREASE_CONTRAST] = { .master = "0.149/0.066 0.831/0.905 0.905/0.98" },
|
[PRESET_INCREASE_CONTRAST] = { .master = "0/0 0.149/0.066 0.831/0.905 0.905/0.98 1/1" },
|
||||||
[PRESET_LIGHTER] = { .master = "0.4/0.5" },
|
[PRESET_LIGHTER] = { .master = "0/0 0.4/0.5 1/1" },
|
||||||
[PRESET_LINEAR_CONTRAST] = { .master = "0.305/0.286 0.694/0.713" },
|
[PRESET_LINEAR_CONTRAST] = { .master = "0/0 0.305/0.286 0.694/0.713 1/1" },
|
||||||
[PRESET_MEDIUM_CONTRAST] = { .master = "0.286/0.219 0.639/0.643" },
|
[PRESET_MEDIUM_CONTRAST] = { .master = "0/0 0.286/0.219 0.639/0.643 1/1" },
|
||||||
[PRESET_NEGATIVE] = { .master = "0/1 1/0" },
|
[PRESET_NEGATIVE] = { .master = "0/1 1/0" },
|
||||||
[PRESET_STRONG_CONTRAST] = { .master = "0.301/0.196 0.592/0.6 0.686/0.737" },
|
[PRESET_STRONG_CONTRAST] = { .master = "0/0 0.301/0.196 0.592/0.6 0.686/0.737 1/1" },
|
||||||
[PRESET_VINTAGE] = {
|
[PRESET_VINTAGE] = {
|
||||||
"0/0.11 0.42/0.51 1/0.95",
|
"0/0.11 0.42/0.51 1/0.95",
|
||||||
"0.50/0.48",
|
"0/0 0.50/0.48 1/1",
|
||||||
"0/0.22 0.49/0.44 1/0.8",
|
"0/0.22 0.49/0.44 1/0.8",
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -177,28 +177,11 @@ static int parse_points_str(AVFilterContext *ctx, struct keypoint **points, cons
|
||||||
last = point;
|
last = point;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* auto insert first key point if missing at x=0 */
|
if (*points && !(*points)->next) {
|
||||||
if (!*points) {
|
av_log(ctx, AV_LOG_WARNING, "Only one point (at (%f;%f)) is defined, "
|
||||||
last = make_point(0, 0, NULL);
|
"this is unlikely to behave as you expect. You probably want"
|
||||||
if (!last)
|
"at least 2 points.",
|
||||||
return AVERROR(ENOMEM);
|
(*points)->x, (*points)->y);
|
||||||
last->x = last->y = 0;
|
|
||||||
*points = last;
|
|
||||||
} else if ((*points)->x != 0.) {
|
|
||||||
struct keypoint *newfirst = make_point(0, 0, *points);
|
|
||||||
if (!newfirst)
|
|
||||||
return AVERROR(ENOMEM);
|
|
||||||
*points = newfirst;
|
|
||||||
}
|
|
||||||
|
|
||||||
av_assert0(last);
|
|
||||||
|
|
||||||
/* auto insert last key point if missing at x=1 */
|
|
||||||
if (last->x != 1.) {
|
|
||||||
struct keypoint *point = make_point(1, 1, NULL);
|
|
||||||
if (!point)
|
|
||||||
return AVERROR(ENOMEM);
|
|
||||||
last->next = point;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -222,14 +205,28 @@ static int get_nb_points(const struct keypoint *d)
|
||||||
static int interpolate(AVFilterContext *ctx, uint8_t *y, const struct keypoint *points)
|
static int interpolate(AVFilterContext *ctx, uint8_t *y, const struct keypoint *points)
|
||||||
{
|
{
|
||||||
int i, ret = 0;
|
int i, ret = 0;
|
||||||
const struct keypoint *point;
|
const struct keypoint *point = points;
|
||||||
double xprev = 0;
|
double xprev = 0;
|
||||||
|
|
||||||
|
double (*matrix)[3];
|
||||||
|
double *h, *r;
|
||||||
int n = get_nb_points(points); // number of splines
|
int n = get_nb_points(points); // number of splines
|
||||||
|
|
||||||
double (*matrix)[3] = av_calloc(n, sizeof(*matrix));
|
if (n == 0) {
|
||||||
double *h = av_malloc((n - 1) * sizeof(*h));
|
for (i = 0; i < 256; i++)
|
||||||
double *r = av_calloc(n, sizeof(*r));
|
y[i] = i;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n == 1) {
|
||||||
|
for (i = 0; i < 256; i++)
|
||||||
|
y[i] = av_clip_uint8(point->y * 255);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
matrix = av_calloc(n, sizeof(*matrix));
|
||||||
|
h = av_malloc((n - 1) * sizeof(*h));
|
||||||
|
r = av_calloc(n, sizeof(*r));
|
||||||
|
|
||||||
if (!matrix || !h || !r) {
|
if (!matrix || !h || !r) {
|
||||||
ret = AVERROR(ENOMEM);
|
ret = AVERROR(ENOMEM);
|
||||||
|
@ -277,9 +274,14 @@ static int interpolate(AVFilterContext *ctx, uint8_t *y, const struct keypoint *
|
||||||
for (i = n - 2; i >= 0; i--)
|
for (i = n - 2; i >= 0; i--)
|
||||||
r[i] = r[i] - matrix[i][AD] * r[i + 1];
|
r[i] = r[i] - matrix[i][AD] * r[i + 1];
|
||||||
|
|
||||||
/* compute the graph with x=[0..255] */
|
|
||||||
i = 0;
|
|
||||||
point = points;
|
point = points;
|
||||||
|
|
||||||
|
/* left padding */
|
||||||
|
for (i = 0; i < (int)(point->x * 255); i++)
|
||||||
|
y[i] = av_clip_uint8(point->y * 255);
|
||||||
|
|
||||||
|
/* compute the graph with x=[x0..xN] */
|
||||||
|
i = 0;
|
||||||
av_assert0(point->next); // always at least 2 key points
|
av_assert0(point->next); // always at least 2 key points
|
||||||
while (point->next) {
|
while (point->next) {
|
||||||
double yc = point->y;
|
double yc = point->y;
|
||||||
|
@ -300,7 +302,7 @@ static int interpolate(AVFilterContext *ctx, uint8_t *y, const struct keypoint *
|
||||||
for (x = x_start; x <= x_end; x++) {
|
for (x = x_start; x <= x_end; x++) {
|
||||||
double xx = (x - x_start) * 1/255.;
|
double xx = (x - x_start) * 1/255.;
|
||||||
double yy = a + b*xx + c*xx*xx + d*xx*xx*xx;
|
double yy = a + b*xx + c*xx*xx + d*xx*xx*xx;
|
||||||
y[x] = av_clipf(yy, 0, 1) * 255;
|
y[x] = av_clip_uint8(yy * 255);
|
||||||
av_log(ctx, AV_LOG_DEBUG, "f(%f)=%f -> y[%d]=%d\n", xx, yy, x, y[x]);
|
av_log(ctx, AV_LOG_DEBUG, "f(%f)=%f -> y[%d]=%d\n", xx, yy, x, y[x]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,6 +310,10 @@ static int interpolate(AVFilterContext *ctx, uint8_t *y, const struct keypoint *
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* right padding */
|
||||||
|
for (i = (int)(point->x * 255); i <= 255; i++)
|
||||||
|
y[i] = av_clip_uint8(point->y * 255);
|
||||||
|
|
||||||
end:
|
end:
|
||||||
av_free(matrix);
|
av_free(matrix);
|
||||||
av_free(h);
|
av_free(h);
|
||||||
|
|
Loading…
Add table
Reference in a new issue