mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-23 12:43:46 +02:00
swscale: add two spatially stable dithering methods
Both of these dithering methods are from http://pippin.gimp.org/a_dither/ for GIF they can be considered better than bayer (provides more gray-levels), and spatial stability - often more than twice as good compression and less visual flicker than error diffusion methods (the methods also avoids error-shadow artifacts of diffusion dithers). These methods are similar to blue/green noise type dither masks; but are simple enough to generate their mask on the fly. They are still research work in progress; though more expensive to generate masks (which can be used in a LUT) like 'void and cluster' and similar methods will yield superior results
This commit is contained in:
parent
a490970af2
commit
3e6016622e
@ -112,6 +112,14 @@ bayer dither
|
|||||||
|
|
||||||
@item ed
|
@item ed
|
||||||
error diffusion dither
|
error diffusion dither
|
||||||
|
|
||||||
|
@item a_dither
|
||||||
|
arithmetic dither, based using addition
|
||||||
|
|
||||||
|
@item x_dither
|
||||||
|
arithmetic dither, based using xor (more random/less apparent patterning that
|
||||||
|
a_dither).
|
||||||
|
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
@end table
|
@end table
|
||||||
|
@ -69,10 +69,12 @@ static const AVOption swscale_options[] = {
|
|||||||
{ "dst_v_chr_pos", "destination vertical chroma position in luma grid/256" , OFFSET(dst_v_chr_pos), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 512, VE },
|
{ "dst_v_chr_pos", "destination vertical chroma position in luma grid/256" , OFFSET(dst_v_chr_pos), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 512, VE },
|
||||||
{ "dst_h_chr_pos", "destination horizontal chroma position in luma grid/256", OFFSET(dst_h_chr_pos), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 512, VE },
|
{ "dst_h_chr_pos", "destination horizontal chroma position in luma grid/256", OFFSET(dst_h_chr_pos), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 512, VE },
|
||||||
|
|
||||||
{ "sws_dither", "set dithering algorithm", OFFSET(dither), AV_OPT_TYPE_INT, { .i64 = SWS_DITHER_AUTO }, 0, NB_SWS_DITHER, VE, "sws_dither" },
|
{ "sws_dither", "set dithering algorithm", OFFSET(dither), AV_OPT_TYPE_INT, { .i64 = SWS_DITHER_AUTO }, 0, NB_SWS_DITHER, VE, "sws_dither" },
|
||||||
{ "auto", "leave choice to sws", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_DITHER_AUTO }, INT_MIN, INT_MAX, VE, "sws_dither" },
|
{ "auto", "leave choice to sws", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_DITHER_AUTO }, INT_MIN, INT_MAX, VE, "sws_dither" },
|
||||||
{ "bayer", "bayer dither", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_DITHER_BAYER }, INT_MIN, INT_MAX, VE, "sws_dither" },
|
{ "bayer", "bayer dither", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_DITHER_BAYER }, INT_MIN, INT_MAX, VE, "sws_dither" },
|
||||||
{ "ed", "error diffusion", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_DITHER_ED }, INT_MIN, INT_MAX, VE, "sws_dither" },
|
{ "ed", "error diffusion", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_DITHER_ED }, INT_MIN, INT_MAX, VE, "sws_dither" },
|
||||||
|
{ "a_dither", "arithmetic addition dither", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_DITHER_A_DITHER}, INT_MIN, INT_MAX, VE, "sws_dither" },
|
||||||
|
{ "x_dither", "arithmetic xor dither", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_DITHER_X_DITHER}, INT_MIN, INT_MAX, VE, "sws_dither" },
|
||||||
|
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
@ -1508,24 +1508,71 @@ static av_always_inline void yuv2rgb_write_full(SwsContext *c,
|
|||||||
case AV_PIX_FMT_RGB8:
|
case AV_PIX_FMT_RGB8:
|
||||||
{
|
{
|
||||||
int r,g,b;
|
int r,g,b;
|
||||||
R >>= 22;
|
|
||||||
G >>= 22;
|
switch (c->dither) {
|
||||||
B >>= 22;
|
default:
|
||||||
R += (7*err[0] + 1*c->dither_error[0][i] + 5*c->dither_error[0][i+1] + 3*c->dither_error[0][i+2])>>4;
|
case SWS_DITHER_AUTO:
|
||||||
G += (7*err[1] + 1*c->dither_error[1][i] + 5*c->dither_error[1][i+1] + 3*c->dither_error[1][i+2])>>4;
|
case SWS_DITHER_ED:
|
||||||
B += (7*err[2] + 1*c->dither_error[2][i] + 5*c->dither_error[2][i+1] + 3*c->dither_error[2][i+2])>>4;
|
R >>= 22;
|
||||||
c->dither_error[0][i] = err[0];
|
G >>= 22;
|
||||||
c->dither_error[1][i] = err[1];
|
B >>= 22;
|
||||||
c->dither_error[2][i] = err[2];
|
R += (7*err[0] + 1*c->dither_error[0][i] + 5*c->dither_error[0][i+1] + 3*c->dither_error[0][i+2])>>4;
|
||||||
r = R >> (isrgb8 ? 5 : 7);
|
G += (7*err[1] + 1*c->dither_error[1][i] + 5*c->dither_error[1][i+1] + 3*c->dither_error[1][i+2])>>4;
|
||||||
g = G >> (isrgb8 ? 5 : 6);
|
B += (7*err[2] + 1*c->dither_error[2][i] + 5*c->dither_error[2][i+1] + 3*c->dither_error[2][i+2])>>4;
|
||||||
b = B >> (isrgb8 ? 6 : 7);
|
c->dither_error[0][i] = err[0];
|
||||||
r = av_clip(r, 0, isrgb8 ? 7 : 1);
|
c->dither_error[1][i] = err[1];
|
||||||
g = av_clip(g, 0, isrgb8 ? 7 : 3);
|
c->dither_error[2][i] = err[2];
|
||||||
b = av_clip(b, 0, isrgb8 ? 3 : 1);
|
r = R >> (isrgb8 ? 5 : 7);
|
||||||
err[0] = R - r*(isrgb8 ? 36 : 255);
|
g = G >> (isrgb8 ? 5 : 6);
|
||||||
err[1] = G - g*(isrgb8 ? 36 : 85);
|
b = B >> (isrgb8 ? 6 : 7);
|
||||||
err[2] = B - b*(isrgb8 ? 85 : 255);
|
r = av_clip(r, 0, isrgb8 ? 7 : 1);
|
||||||
|
g = av_clip(g, 0, isrgb8 ? 7 : 3);
|
||||||
|
b = av_clip(b, 0, isrgb8 ? 3 : 1);
|
||||||
|
err[0] = R - r*(isrgb8 ? 36 : 255);
|
||||||
|
err[1] = G - g*(isrgb8 ? 36 : 85);
|
||||||
|
err[2] = B - b*(isrgb8 ? 85 : 255);
|
||||||
|
break;
|
||||||
|
case SWS_DITHER_A_DITHER:
|
||||||
|
if (isrgb8) {
|
||||||
|
/* see http://pippin.gimp.org/a_dither/ for details/origin */
|
||||||
|
#define A_DITHER(u,v) (((((u)+((v)*236))*119)&0xff))
|
||||||
|
r = (((R >> 19) + A_DITHER(i,y) -96)>>8);
|
||||||
|
g = (((G >> 19) + A_DITHER(i + 17,y) - 96)>>8);
|
||||||
|
b = (((B >> 20) + A_DITHER(i + 17*2,y) -96)>>8);
|
||||||
|
r = av_clip(r, 0, 7);
|
||||||
|
g = av_clip(g, 0, 7);
|
||||||
|
b = av_clip(b, 0, 3);
|
||||||
|
} else {
|
||||||
|
r = (((R >> 21) + A_DITHER(i,y)-256)>>8);
|
||||||
|
g = (((G >> 19) + A_DITHER(i + 17,y)-256)>>8);
|
||||||
|
b = (((B >> 21) + A_DITHER(i + 17*2,y)-256)>>8);
|
||||||
|
r = av_clip(r, 0, 1);
|
||||||
|
g = av_clip(g, 0, 3);
|
||||||
|
b = av_clip(b, 0, 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SWS_DITHER_X_DITHER:
|
||||||
|
if (isrgb8) {
|
||||||
|
/* see http://pippin.gimp.org/a_dither/ for details/origin */
|
||||||
|
#define X_DITHER(u,v) (((((u)^((v)*237))*181)&0x1ff)/2)
|
||||||
|
r = (((R >> 19) + X_DITHER(i,y) - 96)>>8);
|
||||||
|
g = (((G >> 19) + X_DITHER(i + 17,y) - 96)>>8);
|
||||||
|
b = (((B >> 20) + X_DITHER(i + 17*2,y) - 96)>>8);
|
||||||
|
r = av_clip(r, 0, 7);
|
||||||
|
g = av_clip(g, 0, 7);
|
||||||
|
b = av_clip(b, 0, 3);
|
||||||
|
} else {
|
||||||
|
r = (((R >> 21) + X_DITHER(i,y)-256)>>8);
|
||||||
|
g = (((G >> 19) + X_DITHER(i + 17,y)-256)>>8);
|
||||||
|
b = (((B >> 21) + X_DITHER(i + 17*2,y)-256)>>8);
|
||||||
|
r = av_clip(r, 0, 1);
|
||||||
|
g = av_clip(g, 0, 3);
|
||||||
|
b = av_clip(b, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if(target == AV_PIX_FMT_BGR4_BYTE) {
|
if(target == AV_PIX_FMT_BGR4_BYTE) {
|
||||||
dest[0] = r + 2*g + 8*b;
|
dest[0] = r + 2*g + 8*b;
|
||||||
} else if(target == AV_PIX_FMT_RGB4_BYTE) {
|
} else if(target == AV_PIX_FMT_RGB4_BYTE) {
|
||||||
|
@ -66,6 +66,8 @@ typedef enum SwsDither {
|
|||||||
SWS_DITHER_AUTO,
|
SWS_DITHER_AUTO,
|
||||||
SWS_DITHER_BAYER,
|
SWS_DITHER_BAYER,
|
||||||
SWS_DITHER_ED,
|
SWS_DITHER_ED,
|
||||||
|
SWS_DITHER_A_DITHER,
|
||||||
|
SWS_DITHER_X_DITHER,
|
||||||
NB_SWS_DITHER,
|
NB_SWS_DITHER,
|
||||||
} SwsDither;
|
} SwsDither;
|
||||||
|
|
||||||
|
@ -1250,7 +1250,7 @@ av_cold int sws_init_context(SwsContext *c, SwsFilter *srcFilter,
|
|||||||
if (c->dither == SWS_DITHER_AUTO)
|
if (c->dither == SWS_DITHER_AUTO)
|
||||||
c->dither = (flags & SWS_FULL_CHR_H_INT) ? SWS_DITHER_ED : SWS_DITHER_BAYER;
|
c->dither = (flags & SWS_FULL_CHR_H_INT) ? SWS_DITHER_ED : SWS_DITHER_BAYER;
|
||||||
if (!(flags & SWS_FULL_CHR_H_INT)) {
|
if (!(flags & SWS_FULL_CHR_H_INT)) {
|
||||||
if (c->dither == SWS_DITHER_ED) {
|
if (c->dither == SWS_DITHER_ED || c->dither == SWS_DITHER_A_DITHER || c->dither == SWS_DITHER_X_DITHER) {
|
||||||
av_log(c, AV_LOG_DEBUG,
|
av_log(c, AV_LOG_DEBUG,
|
||||||
"Desired dithering only supported in full chroma interpolation for destination format '%s'\n",
|
"Desired dithering only supported in full chroma interpolation for destination format '%s'\n",
|
||||||
av_get_pix_fmt_name(dstFormat));
|
av_get_pix_fmt_name(dstFormat));
|
||||||
|
Loading…
Reference in New Issue
Block a user