[vlc-devel] [PATCH 1/1] Add support for top-bottom anaglyph 3D

Steve Lhomme robux4 at ycbcr.xyz
Mon Jan 4 09:39:50 UTC 2021


Hi,

On 2020-12-22 7:13, amindfv via vlc-devel wrote:
> ---
>   modules/video_filter/anaglyph.c | 170 +++++++++++++++++++++++++++++++-
>   1 file changed, 167 insertions(+), 3 deletions(-)
> 
> diff --git a/modules/video_filter/anaglyph.c b/modules/video_filter/anaglyph.c
> index b29dfda528..98abbcaa2f 100644
> --- a/modules/video_filter/anaglyph.c
> +++ b/modules/video_filter/anaglyph.c
> @@ -4,6 +4,7 @@
>    * Copyright (C) 2000-2012 VLC authors and VideoLAN
>    *
>    * Authors: Antoine Cellerier <dionoea .t videolan d at t org>
> + *          Tom Murphy <amindfv .t mailbox d at t org>
>    *
>    * This program is free software; you can redistribute it and/or modify it
>    * under the terms of the GNU Lesser General Public License as published by
> @@ -32,9 +33,12 @@
>   
>   static int Create(filter_t *);
>   static void combine_side_by_side_yuv420(picture_t *, picture_t *, int, int);
> +static void combine_top_bottom_yuv420(picture_t *, picture_t *, int, int);
>   
>   #define SCHEME_TEXT N_("Color scheme")
>   #define SCHEME_LONGTEXT N_("Define the glasses' color scheme")
> +#define ORIENTATION_TEXT N_("Orientation")
> +#define ORIENTATION_LONGTEXT N_("Orientation of the video's two images")
>   
>   #define FILTER_PREFIX "anaglyph-"
>   
> @@ -49,6 +53,12 @@ enum scheme_e
>       magenta_cyan,
>   };
>   
> +enum orientation_e
> +{
> +    side_by_side = 1,
> +    top_bottom,
> +};
> +
>   static const char *const ppsz_scheme_values[] = {
>       "red-green",
>       "red-blue",
> @@ -64,6 +74,15 @@ static const char *const ppsz_scheme_descriptions[] = {
>       "magenta (left)  cyan (right)",
>       };
>   
> +static const char *const orientation_values[] = {
> +    "side-by-side",
> +    "top-bottom",
> +    };
> +static const char *const orientation_descriptions[] = {
> +    "side-by-side",
> +    "top-bottom",
> +    };

If it's really the same table you can reuse the first one twice.

> +
>   vlc_module_begin()
>       set_description(N_("Convert 3D picture to anaglyph image video filter"));
>       set_shortname(N_("Anaglyph"))
> @@ -71,16 +90,19 @@ vlc_module_begin()
>       set_subcategory(SUBCAT_VIDEO_VFILTER)
>       add_string(FILTER_PREFIX "scheme", "red-cyan", SCHEME_TEXT, SCHEME_LONGTEXT, false)
>           change_string_list(ppsz_scheme_values, ppsz_scheme_descriptions)
> +    add_string(FILTER_PREFIX "orientation", "side-by-side", ORIENTATION_TEXT, ORIENTATION_LONGTEXT, false)
> +        change_string_list(orientation_values, orientation_descriptions)
>       set_callback_video_filter(Create)
>   vlc_module_end()
>   
>   static const char *const ppsz_filter_options[] = {
> -    "scheme", NULL
> +    "scheme", "orientation", NULL
>   };
>   
>   typedef struct
>   {
>       int left, right;
> +    enum orientation_e orientation;
>   } filter_sys_t;
>   
>   VIDEO_FILTER_WRAPPER(Filter)
> @@ -100,6 +122,8 @@ static int Create(filter_t *p_filter)
>               return VLC_EGENERIC;
>       }
>   
> +
> +

Not needed.

>       p_filter->p_sys = vlc_obj_malloc(VLC_OBJECT(p_filter), sizeof(filter_sys_t));
>       if (unlikely(!p_filter->p_sys))
>           return VLC_ENOMEM;
> @@ -152,6 +176,23 @@ static int Create(filter_t *p_filter)
>               break;
>       }
>   
> +    char *orientation_choice = var_CreateGetStringCommand(p_filter,
> +                                                          FILTER_PREFIX "orientation");
> +    p_sys->orientation = side_by_side;

There is already an enum for that, it's the video_multiview_mode_t. 
There is MULTIVIEW_STEREO_SBS and MULTIVIEW_STEREO_TB for what you need. 
This value is set in some samples I have but not always (we read it from 
H264, HEVC, lavc, mkv).

The user selection should only be used when neither of these values are 
used. Or there could be a third option: "auto" which uses the source 
value and assumes the source is Side By Side otherwise. The "auto" 
setting should be the default behavior.

I gave it a try and it's a little trickier as the filter is opened with 
the proper multiview value and then none. Probably something with the 
decoder restart when the input format changes, even though it's not a 
real change.

> +    if (orientation_choice)
> +    {
> +        if (!strcmp(orientation_choice, "side-by-side"))
> +            p_sys->orientation = side_by_side;
> +        else if (!strcmp(orientation_choice, "top-bottom"))
> +            p_sys->orientation = top_bottom;
> +        else
> +            msg_Err(p_filter, "Unknown anaglyph orientation '%s'", orientation_choice);
> +    }
> +    free(orientation_choice);
> +
> +
> +
> +
>       p_filter->ops = &Filter_ops;
>       return VLC_SUCCESS;
>   }
> @@ -165,8 +206,23 @@ static void Filter(filter_t *p_filter, picture_t *p_pic, picture_t *p_outpic)
>           case VLC_CODEC_I420:
>           case VLC_CODEC_J420:
>           case VLC_CODEC_YV12:
> -            combine_side_by_side_yuv420(p_pic, p_outpic,
> -                                        p_sys->left, p_sys->right);
> +            switch (p_sys->orientation)
> +            {
> +                case side_by_side:
> +                    combine_side_by_side_yuv420(p_pic, p_outpic,
> +                                                p_sys->left, p_sys->right);
> +                    break;
> +                case top_bottom:
> +                    combine_top_bottom_yuv420(p_pic, p_outpic,
> +                                                p_sys->left, p_sys->right);
> +                    break;
> +                default:
> +                    msg_Warn(p_filter, "Unsupported orientation (%4.4s)",
> +                             (char*)&(p_sys->orientation));

Same remark as Marvin.

> +                    picture_Release(p_pic);

The source picture is automatically released by the Filter caller. 
That's why combine_side_by_side_yuv420() doesn't release it.

> +                    return NULL;

This function doesn't return a value.

> +            }
> +
>               break;
>   
>           default:
> @@ -275,3 +331,111 @@ static void combine_side_by_side_yuv420(picture_t *p_inpic, picture_t *p_outpic,
>           vout += p_outpic->p[V_PLANE].i_pitch - uv_visible_pitch;
>       }
>   }
> +
> +static void combine_top_bottom_yuv420(picture_t *p_inpic, picture_t *p_outpic,
> +                                        int left, int right)
> +{
> +    uint8_t *yino = p_inpic->p[Y_PLANE].p_pixels;
> +    uint8_t *uino = p_inpic->p[U_PLANE].p_pixels;
> +    uint8_t *vino = p_inpic->p[V_PLANE].p_pixels;
> +
> +    uint8_t *yout = p_outpic->p[Y_PLANE].p_pixels;
> +    uint8_t *uout = p_outpic->p[U_PLANE].p_pixels;
> +    uint8_t *vout = p_outpic->p[V_PLANE].p_pixels;
> +
> +    const int in_pitch = p_inpic->p[Y_PLANE].i_pitch;
> +
> +    const int y_visible_pitch = p_inpic->p[Y_PLANE].i_visible_pitch;
> +    const int y_visible_lines = p_inpic->p[Y_PLANE].i_visible_lines;
> +    const int uv_visible_pitch = p_inpic->p[U_PLANE].i_visible_pitch;
> +    const int uv_visible_lines = p_inpic->p[U_PLANE].i_visible_lines;
> +
> +    const int y_half_size = (y_visible_lines * y_visible_pitch) / 2;
> +    const int uv_half_size = (uv_visible_lines * uv_visible_pitch) / 2;
> +
> +    const uint8_t *yend = yino + y_half_size;
> +
> +    int rshift = !!((0xff0000&left) && (0xff0000&right));
> +    int gshift = !!((0x00ff00&left) && (0x00ff00&right));
> +    int bshift = !!((0x0000ff&left) && (0x0000ff&right));
> +
> +    while (yino < yend)
> +    {
> +        uint8_t *yinu = yino + y_half_size;
> +        uint8_t *uinu = uino + uv_half_size;
> +        uint8_t *vinu = vino + uv_half_size;
> +
> +        const uint8_t *yend = yino + in_pitch;
> +
> +        while (yino < yend)
> +        {
> +            int ro, go, bo, ru, gu, bu, r, g, b;
> +
> +            yuv_to_rgb(&ro, &go, &bo, *yino, *uino, *vino);
> +            yuv_to_rgb(&ru, &gu, &bu, *yinu, *uinu, *vinu);
> +
> +            r = ((!!(0xff0000&left))*ro + (!!(0xff0000&right))*ru)>>rshift;
> +            g = ((!!(0x00ff00&left))*go + (!!(0x00ff00&right))*gu)>>gshift;
> +            b = ((!!(0x0000ff&left))*bo + (!!(0x0000ff&right))*bu)>>bshift;
> +            rgb_to_yuv(yout, uout, vout, r, g, b);
> +            // Mirroring a second line below, to make up for the fact it's been compresed to half-height:
> +            yout[y_visible_pitch] = *yout;
> +
> +
> +            yuv_to_rgb(&ro, &go, &bo, *(yino+y_visible_pitch), *uino, *vino);
> +            yuv_to_rgb(&ru, &gu, &bu, *(yinu+y_visible_pitch), *uinu, *vinu);
> +
> +            r = ((!!(0xff0000&left))*ro + (!!(0xff0000&right))*ru)>>rshift;
> +            g = ((!!(0x00ff00&left))*go + (!!(0x00ff00&right))*gu)>>gshift;
> +            b = ((!!(0x0000ff&left))*bo + (!!(0x0000ff&right))*bu)>>bshift;
> +
> +            rgb_to_yuv(yout+(y_visible_pitch*2), uout, vout, r, g, b);
> +            yout[y_visible_pitch*3] = *(yout+(y_visible_pitch*2));
> +
> +            yino++;
> +            yinu++;
> +            yout++;
> +
> +
> +            yuv_to_rgb(&ro, &go, &bo, *yino, *uino, *vino);
> +            yuv_to_rgb(&ru, &gu, &bu, *yinu, *uinu, *vinu);
> +
> +            r = ((!!(0xff0000&left))*ro + (!!(0xff0000&right))*ru)>>rshift;
> +            g = ((!!(0x00ff00&left))*go + (!!(0x00ff00&right))*gu)>>gshift;
> +            b = ((!!(0x0000ff&left))*bo + (!!(0x0000ff&right))*bu)>>bshift;
> +            rgb_to_yuv(yout, uout, vout, r, g, b);
> +            yout[y_visible_pitch] = *yout;
> +
> +
> +            yuv_to_rgb(&ro, &go, &bo, *(yino+y_visible_pitch), *uino, *vino);
> +            yuv_to_rgb(&ru, &gu, &bu, *(yinu+y_visible_pitch), *uinu, *vinu);
> +
> +            r = ((!!(0xff0000&left))*ro + (!!(0xff0000&right))*ru)>>rshift;
> +            g = ((!!(0x00ff00&left))*go + (!!(0x00ff00&right))*gu)>>gshift;
> +            b = ((!!(0x0000ff&left))*bo + (!!(0x0000ff&right))*bu)>>bshift;
> +            rgb_to_yuv(yout+(y_visible_pitch*2), uout+uv_visible_pitch, vout+uv_visible_pitch, r, g, b);
> +            yout[y_visible_pitch*3] = *(yout+(y_visible_pitch*2));
> +
> +            yino++;
> +            yinu++;
> +            yout++;
> +
> +            // There are 1/4 as many U & Y pixels so we write them 1/4 as often:
> +            uino++;
> +            uinu++;
> +            uout++;
> +            vino++;
> +            vinu++;
> +            vout++;
> +        }
> +
> +        // We skip these ins because we looked a row below for the Ys already:
> +        yino += in_pitch;
> +        yinu += in_pitch;
> +
> +        yout += y_visible_pitch * 3;
> +
> +        uout += uv_visible_pitch;
> +        vout += uv_visible_pitch;
> +    }
> +}

Tested the algorithm with samples I have and it's working.

> -- 
> 2.20.1
> 
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel
> 


More information about the vlc-devel mailing list