File: edgedetection.c

package info (click to toggle)
vlc 3.0.23-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 208,020 kB
  • sloc: ansic: 443,448; cpp: 111,223; objc: 36,399; sh: 6,737; makefile: 6,627; javascript: 4,902; xml: 1,611; asm: 1,355; yacc: 644; python: 321; lex: 88; perl: 77; sed: 16
file content (243 lines) | stat: -rw-r--r-- 9,138 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
/******************************************************************************
 * edgedetection.c : edge detection plugin for VLC
  *****************************************************************************
 * Copyright (C) 2016 VLC authors and VideoLAN
 * $Id$
 *
 * Authors: Odd-Arild Kristensen <oddarildkristensen@gmail.com>
 *
 * 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
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_filter.h>
#include <vlc_picture.h>

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
#define EDGE_DETECTION_DESCRIPTION N_( "Edge detection video filter" )
#define EDGE_DETECTION_TEXT N_( "Edge detection" )
#define EDGE_DETECTION_LONGTEXT N_( \
    "Detects edges in the frame and highlights them in white." )

#define WHITE 255

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static int Open( vlc_object_t * );
static int Close( vlc_object_t * );
static picture_t *new_frame( filter_t * );
static picture_t *Filter( filter_t *, picture_t * );
static uint8_t sobel( const uint8_t *, const int, const int, int, int);

/* Kernel for X axis */
static const signed char pi_kernel_x[3][3] = {
    {-1, 0, 1},
    {-2, 0, 2},
    {-1, 0, 1}
};

/* Kernel for Y axis */
static const signed char pi_kernel_y[3][3] = {
    {-1, -2, -1},
    {0, 0, 0},
    {1, 2, 1}
};

vlc_module_begin ()

    set_description( EDGE_DETECTION_DESCRIPTION )
    set_shortname( EDGE_DETECTION_TEXT )
    set_help( EDGE_DETECTION_LONGTEXT )
    set_category( CAT_VIDEO )
    set_subcategory( SUBCAT_VIDEO_VFILTER )
    set_capability( "video filter", 0 )
    set_callbacks( Open, Close )

vlc_module_end ()

/* Store the filter chain */
struct filter_sys_t
{
    filter_chain_t *p_chain;
};

/*****************************************************************************
 * Opens the filter.
 * Allocates and initializes data needed by the filter. The image needs to
 * be black-and-white in order to detect any edges. The Gaussian blur is
 * needed so that the Sobel operator does not give a high response for noise,
 * or small changes in the image.
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
    int i_ret;
    filter_t *p_filter = (filter_t *)p_this;
    filter_owner_t owner = {
        .sys = p_filter,
        .video = {
            .buffer_new = new_frame,
        },
    };
    /* Store the filter chain in p_sys */
    p_filter->p_sys = (filter_sys_t *)filter_chain_NewVideo( p_filter, true, &owner );

    if ( p_filter->p_sys == NULL)
    {
        msg_Err( p_filter, "Could not allocate filter chain" );
        free( p_filter->p_sys );
        return VLC_EGENERIC;
    }
    /* Clear filter chain */
    filter_chain_Reset( (filter_chain_t *)p_filter->p_sys, &p_filter->fmt_in, &p_filter->fmt_in);
    /* Add adjust filter to turn frame black-and-white */
    i_ret = filter_chain_AppendFromString( (filter_chain_t *)p_filter->p_sys,
                                           "adjust{saturation=0}" );
    if ( i_ret == -1 )
    {
        msg_Err( p_filter, "Could not append filter to filter chain" );
        filter_chain_Delete( (filter_chain_t *)p_filter->p_sys );
        return VLC_EGENERIC;
    }
    /* Add gaussian blur to the frame so to remove noise from the frame */
    i_ret = filter_chain_AppendFromString( (filter_chain_t *)p_filter->p_sys,
                                           "gaussianblur{deviation=1}" );
    if ( i_ret == -1 )
    {
        msg_Err( p_filter, "Could not append filter to filter chain" );
        filter_chain_Delete( (filter_chain_t *)p_filter->p_sys );
        return VLC_EGENERIC;
    }
    /* Set callback function */
    p_filter->pf_video_filter = Filter;
    return VLC_SUCCESS;
}

/******************************************************************************
 * Closes the filter and cleans up all dynamically allocated data.
 ******************************************************************************/
static int Close( vlc_object_t *p_this )
{
    filter_t *p_filter = (filter_t *)p_this;
    filter_chain_Delete( (filter_chain_t *)p_filter->p_sys );
    return VLC_SUCCESS;
}

/* *****************************************************************************
 * Allocates a new buffer for the filter chain.
 ******************************************************************************/
static picture_t *new_frame( filter_t *p_filter)
{
    return filter_NewPicture( p_filter->owner.sys );
}

/******************************************************************************
 * Callback function.
 * This function implements the sobel operator which can be used to detect
 * edges in a black-and-white image. The sobel operator is applied to
 * every pixel in the frame for both X and Y axis.
 ******************************************************************************/
static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
{
    picture_t *p_filtered_frame =
        filter_chain_VideoFilter( (filter_chain_t *)p_filter->p_sys, p_pic );
    picture_t *p_out_frame = picture_NewFromFormat( &p_pic->format );
    if ( p_out_frame == NULL )
    {
        picture_Release( p_filtered_frame );
        msg_Err( p_filter, "Could not allocate memory for new frame" );
        return NULL;
    }
    const int i_lines = p_filtered_frame->p[Y_PLANE].i_visible_lines;
    const int i_pitch = p_filtered_frame->p[Y_PLANE].i_pitch;
    /* Loop through every pixel in the frame */
    for ( int i_line = 0; i_line < i_lines; i_line++ )
    {
        for ( int i_col = 0; i_col < i_pitch; i_col++ )
        {
            /* Set the new value for the pixel */
            *( p_out_frame->p[Y_PLANE].p_pixels + ((i_pitch * i_line) + i_col) ) =
                sobel( p_filtered_frame->p[Y_PLANE].p_pixels,
                       i_pitch, i_lines, i_col, i_line );
        }
    }
    picture_Release( p_filtered_frame );
    return p_out_frame;
}

/******************************************************************************
 * Sobel Operator.
 * Calculates the gradients for both X and Y directions in a frame.
 ******************************************************************************/
static uint8_t sobel( const uint8_t *p_pixels, const int i_pitch,
                      const int i_lines, int i_col, int i_line )
{
    int i_x_val = 0;
    int i_y_val = 0;
    int i_col_offset;
    int i_line_offset;
    for ( int i_x = 0; i_x < 3; i_x++ )
    {
        /* Check if we are at the first pixel on the scanline */
        if (i_x == 0 && i_col == 0)
        {
            /* Duplicate the first pixel on the scanline */
            i_col_offset = 0;
        }
        /* Check if we are on the last pixel on the scanline */
        else if (i_x == 2 && i_col == i_pitch - 1)
        {
            /* Duplicate the last pixel on the scanline */
            i_col_offset = i_pitch - 1;
        }
        else
        {
            i_col_offset = i_col + i_x - 1;
        }
        for ( int i_y = 0; i_y < 3; i_y++ )
        {
            /* Check if we are at the top scanline */
            if (i_y == 0 && i_line == 0)
            {
                /* Duplicate the top pixel */
                i_line_offset = 0;
            }
            /* Check if we are the bottom scanline */
            else if (i_y == 2 && i_line == i_lines - 1 )
            {
                /* Duplicate the bottom pixel */
                i_line_offset = i_pitch * (i_lines - 1);
            }
            else {
                i_line_offset = i_pitch * (i_line + i_y - 1);
            }
            /* Apply the kernel to the pixel */
            i_x_val += pi_kernel_x[i_x][i_y] * p_pixels[i_line_offset + i_col_offset];
            i_y_val += pi_kernel_y[i_x][i_y] * p_pixels[i_line_offset + i_col_offset];
        }
    }
    int i_ret = abs(i_x_val) + abs(i_y_val);
    return (i_ret > WHITE) ? WHITE : i_ret;
}